From c133170e563592dfc15a95daced1f8447327a09a Mon Sep 17 00:00:00 2001 From: Dhaya <154633+dhayab@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:09:58 +0200 Subject: [PATCH] feat: rename packages (#5787) --------- Co-authored-by: Haroen Viaene --- .circleci/config.yml | 8 +- .codesandbox/ci.json | 7 +- .eslintignore | 4 +- .eslintrc.js | 39 +- .github/ISSUE_TEMPLATE/BUG_REPORT.yml | 1 - CONTRIBUTING.md | 15 +- README.md | 14 +- babel.config.js | 7 - bundlesize.config.json | 26 +- examples/react-hooks/default-theme/index.html | 30 - .../react-hooks/default-theme/package.json | 23 - examples/react-hooks/e-commerce/App.css | 341 -- .../react-hooks/e-commerce/App.mobile.css | 264 -- examples/react-hooks/e-commerce/Theme.css | 544 --- .../e-commerce/assets/cover-mobile.png | Bin 808641 -> 0 bytes .../react-hooks/e-commerce/assets/cover.png | Bin 498063 -> 0 bytes .../react-hooks/e-commerce/assets/favicon.png | Bin 1922 -> 0 bytes .../e-commerce/assets/manifest.webmanifest | 15 - examples/react-hooks/e-commerce/index.html | 38 - examples/react-hooks/e-commerce/package.json | 26 - .../react-hooks/getting-started/.editorconfig | 8 - .../react-hooks/getting-started/.gitignore | 23 - .../react-hooks/getting-started/README.md | 23 - .../react-hooks/getting-started/favicon.png | Bin 1922 -> 0 bytes .../react-hooks/getting-started/index.html | 32 - .../react-hooks/getting-started/package.json | 23 - .../react-hooks/getting-started/src/App.css | 71 - examples/react-hooks/next-routing/README.md | 18 - .../react-hooks/next-routing/package.json | 28 - examples/react-hooks/next/.gitignore | 34 - examples/react-hooks/next/README.md | 18 - .../react-native/.expo-shared/assets.json | 4 - examples/react-hooks/react-native/.gitignore | 14 - examples/react-hooks/react-native/README.md | 20 - examples/react-hooks/react-native/app.json | 30 - .../react-hooks/react-native/assets/icon.png | Bin 22380 -> 0 bytes .../react-native/assets/splash.png | Bin 47346 -> 0 bytes .../react-hooks/react-native/babel.config.js | 6 - .../react-hooks/react-native/package.json | 30 - examples/react/autocomplete/README.md | 26 - .../react/autocomplete/__mocks__/styleMock.js | 2 - examples/react/autocomplete/index.html | 18 - examples/react/autocomplete/package.json | 47 - .../react/autocomplete/src/App-Mentions.js | 37 - .../react/autocomplete/src/App-Multi-Index.js | 96 - .../autocomplete/src/__tests__/App.test.js | 30 - .../__tests__/__snapshots__/App.test.js.snap | 38 - examples/react/autocomplete/src/index.js | 12 - examples/react/autocomplete/src/style.css | 115 - examples/react/default-theme/App.css | 84 - examples/react/default-theme/App.js | 171 - examples/react/default-theme/URLSync.js | 56 - examples/react/default-theme/index.html | 7 +- examples/react/default-theme/index.js | 6 - examples/react/default-theme/package.json | 21 +- .../default-theme/src/App.css | 0 .../default-theme/src/App.tsx | 2 +- .../src/components/NumericMenu.tsx | 2 +- .../default-theme/src/components/Panel.tsx | 0 .../src/components/QueryRuleContext.tsx | 2 +- .../src/components/QueryRuleCustomData.tsx | 2 +- .../default-theme/src/components/Refresh.css | 0 .../default-theme/src/components/Refresh.tsx | 2 +- .../default-theme/src/components/index.ts | 0 .../src/components/layout/Tabs.css | 0 .../src/components/layout/Tabs.tsx | 0 .../src/components/layout/index.tsx | 0 .../default-theme/src/cx.ts | 0 .../default-theme/src/env.js | 0 .../default-theme/src/index.tsx | 0 .../default-theme/src/isModifierClick.ts | 0 examples/react/e-commerce/.editorconfig | 9 - examples/react/e-commerce/.gitignore | 21 - examples/react/e-commerce/.prettierrc | 5 - examples/react/e-commerce/AlgoliaSvg.js | 14 - examples/react/e-commerce/App.js | 369 -- .../{react-hooks => react}/e-commerce/App.tsx | 2 +- examples/react/e-commerce/URLSync.js | 263 -- .../e-commerce/components/AlgoliaSvg.tsx | 0 .../e-commerce/components/ClearFilters.tsx | 2 +- .../components/ClearFiltersMobile.tsx | 2 +- .../e-commerce/components/NoResults.tsx | 2 +- .../components/NoResultsBoundary.tsx | 2 +- .../e-commerce/components/Pagination.css | 0 .../e-commerce/components/Panel.tsx | 0 .../e-commerce/components/PriceSlider.css | 0 .../e-commerce/components/PriceSlider.tsx | 2 +- .../e-commerce/components/Ratings.tsx | 2 +- .../components/ResultsNumberMobile.tsx | 2 +- .../components/SaveFiltersMobile.tsx | 2 +- .../e-commerce/components/ScrollTo.tsx | 2 +- .../e-commerce/components/index.ts | 0 .../{react-hooks => react}/e-commerce/cx.ts | 0 .../{react-hooks => react}/e-commerce/env.js | 0 examples/react/e-commerce/index.html | 13 +- examples/react/e-commerce/index.js | 6 - .../e-commerce/index.tsx | 0 examples/react/e-commerce/package.json | 26 +- .../e-commerce/routing.ts | 0 examples/react/e-commerce/utils.js | 3 - .../e-commerce/utils/index.ts | 0 .../e-commerce/widgets/ClearFiltersMobile.js | 20 - .../react/e-commerce/widgets/NoResults.js | 125 - .../react/e-commerce/widgets/Pagination.css | 3 - .../react/e-commerce/widgets/PriceSlider.css | 49 - .../react/e-commerce/widgets/PriceSlider.js | 143 - examples/react/e-commerce/widgets/Ratings.js | 84 - .../e-commerce/widgets/ResultsNumberMobile.js | 12 - .../e-commerce/widgets/SaveFiltersMobile.js | 12 - examples/react/e-commerce/widgets/index.js | 6 - examples/react/geo-search/README.md | 18 - examples/react/geo-search/index.html | 14 - examples/react/geo-search/package.json | 44 - examples/react/geo-search/src/App.js | 100 - .../geo-search/src/__tests__/App.test.js | 16 - .../__tests__/__snapshots__/App.test.js.snap | 71 - examples/react/geo-search/src/index.js | 8 - examples/react/geo-search/src/style.css | 24 - examples/react/getting-started/.editorconfig | 1 - examples/react/getting-started/.gitignore | 38 +- examples/react/getting-started/index.html | 6 +- examples/react/getting-started/package.json | 13 +- examples/react/getting-started/src/App.css | 11 + examples/react/getting-started/src/App.js | 78 - .../getting-started/src/App.tsx | 6 +- .../getting-started/src/Panel.tsx | 0 .../getting-started/src/env.js | 0 examples/react/getting-started/src/index.css | 10 - examples/react/getting-started/src/index.js | 7 - .../getting-started/src/index.tsx | 0 examples/react/media/App.css | 311 -- examples/react/media/App.js | 198 - examples/react/media/URLSync.js | 56 - examples/react/media/index.html | 39 - examples/react/media/index.js | 6 - examples/react/media/package.json | 12 - examples/react/multi-index/README.md | 18 - examples/react/multi-index/index.html | 13 - examples/react/multi-index/package.json | 42 - examples/react/multi-index/src/App.js | 22 - .../multi-index/src/__tests__/App.test.js | 16 - .../__tests__/__snapshots__/App.test.js.snap | 90 - examples/react/multi-index/src/index.js | 7 - .../next-routing/.eslintrc | 0 .../next-routing/.gitignore | 0 examples/react/next-routing/README.md | 18 + .../next-routing/components/Panel.tsx | 0 .../next-routing/next-env.d.ts | 0 .../next-routing/next.config.js | 0 .../next => react/next-routing}/package.json | 7 +- .../next-routing/pages/_app.js | 0 .../next-routing/pages/index.tsx | 8 +- .../next-routing/pages/other-page.tsx | 0 .../next-routing/styles/globals.css | 0 .../next-routing/tsconfig.json | 0 .../next-routing/utils/cx.ts | 0 .../{react-hooks => react}/next/.eslintrc | 0 examples/react/next/.gitignore | 25 +- examples/react/next/README.md | 2 +- .../next/components/Panel.tsx | 0 examples/react/next/components/app.js | 63 - examples/react/next/components/head.js | 35 - examples/react/next/components/index.js | 2 - .../{react-hooks => react}/next/next-env.d.ts | 0 examples/react/next/package.json | 40 +- .../{react-hooks => react}/next/pages/_app.js | 0 examples/react/next/pages/index.js | 79 - .../next/pages/index.tsx | 8 +- examples/react/next/public/favicon.ico | Bin 15086 -> 0 bytes examples/react/next/public/instantsearch.css | 53 - examples/react/next/sandbox.config.json | 5 - .../next/styles/globals.css | 0 .../tests/__snapshots__/index.test.js.snap | 108 - examples/react/next/tests/index.test.js | 16 - .../{react-hooks => react}/next/tsconfig.json | 0 .../{react-hooks => react}/next/utils/cx.ts | 0 .../.expo-shared/assets.json | 4 - .../react-native-query-suggestions/.gitignore | 14 - .../react-native-query-suggestions/App.js | 382 -- .../App.test.js | 9 - .../Highlight.js | 24 - .../react-native-query-suggestions/app.json | 28 - .../assets/icon.png | Bin 642 -> 0 bytes .../assets/splash.png | Bin 9306 -> 0 bytes .../babel.config.js | 6 - .../package.json | 39 - .../tests/App.test.js | 14 - .../tests/__snapshots__/App.test.js.snap | 108 - .../react-native/.eslintignore | 0 .../react-native/.eslintrc | 0 examples/react/react-native/.gitignore | 6 +- examples/react/react-native/App.js | 74 - .../react-native/App.tsx | 2 +- .../{react-router => react-native}/README.md | 12 +- examples/react/react-native/app.json | 22 +- .../react-native/assets/adaptive-icon.png | Bin .../react-native/assets/favicon.png | Bin examples/react/react-native/assets/icon.png | Bin 642 -> 22380 bytes examples/react/react-native/assets/splash.png | Bin 9306 -> 47346 bytes examples/react/react-native/package.json | 46 +- examples/react/react-native/src/Categories.js | 216 - examples/react/react-native/src/Filters.js | 193 - .../react-native/src/Highlight.tsx | 0 examples/react/react-native/src/Home.js | 351 -- .../react-native/src/InfiniteHits.tsx | 2 +- examples/react/react-native/src/Price.js | 176 - examples/react/react-native/src/Rating.js | 188 - .../react-native/src/SearchBox.tsx | 2 +- examples/react/react-native/src/Type.js | 204 - .../react-native/src/components/Highlight.js | 22 - .../react-native/src/components/Spinner.js | 23 - .../react-native/src/components/Stats.js | 47 - examples/react/react-native/tests/App.test.js | 15 - .../tests/__snapshots__/App.test.js.snap | 368 -- .../react-native/tsconfig.json | 0 .../react-native/types/ProductHit.ts | 0 examples/react/react-router-v3/README.md | 18 - examples/react/react-router-v3/index.html | 13 - examples/react/react-router-v3/package.json | 46 - examples/react/react-router-v3/src/App.js | 125 - .../react-router-v3/src/__tests__/App.test.js | 23 - .../__tests__/__snapshots__/App.test.js.snap | 749 ---- examples/react/react-router-v3/src/index.js | 13 - examples/react/react-router/index.html | 13 - examples/react/react-router/package.json | 46 - examples/react/react-router/src/App.js | 120 - .../react-router/src/__tests__/App.test.js | 24 - .../__tests__/__snapshots__/App.test.js.snap | 749 ---- examples/react/react-router/src/index.js | 13 - examples/react/server-side-rendering/.babelrc | 14 - .../react/server-side-rendering/.gitignore | 5 - .../react/server-side-rendering/README.md | 20 - .../react/server-side-rendering/package.json | 36 - .../react/server-side-rendering/src/App.js | 53 - .../server-side-rendering/src/App.test.js | 23 - .../src/__snapshots__/App.test.js.snap | 87 - .../server-side-rendering/src/browser.js | 15 - .../server-side-rendering/src/createApp.js | 23 - .../react/server-side-rendering/src/server.js | 50 - .../server-side-rendering/src/template.js | 16 - .../server-side-rendering/webpack.config.js | 64 - examples/{react-hooks => react}/ssr/.babelrc | 0 .../{react-hooks => react}/ssr/.eslintrc.js | 0 .../{react-hooks => react}/ssr/.gitignore | 0 examples/{react-hooks => react}/ssr/README.md | 8 +- .../{react-hooks => react}/ssr/package.json | 5 +- .../{react-hooks => react}/ssr/src/App.js | 2 +- .../{react-hooks => react}/ssr/src/browser.js | 0 examples/{react-hooks => react}/ssr/src/cx.js | 0 .../ssr/src/searchClient.js | 0 .../{react-hooks => react}/ssr/src/server.js | 6 +- .../ssr/webpack.config.js | 0 examples/react/tourism/App.css | 448 -- examples/react/tourism/App.js | 356 -- examples/react/tourism/URLSync.js | 56 - examples/react/tourism/index.html | 39 - examples/react/tourism/index.js | 6 - examples/react/tourism/package.json | 13 - global.d.ts | 6 +- jest.config.js | 5 +- packages/instantsearch.css/README.md | 25 +- .../breadcrumb/connectBreadcrumb.ts | 2 +- .../__tests__/createInsightsMiddleware.ts | 4 +- .../react-instantsearch-core/CHANGELOG.md | 213 +- packages/react-instantsearch-core/README.md | 72 +- .../global.d.ts | 0 .../react-instantsearch-core/package.json | 29 +- .../react-instantsearch-core/rollup.config.js | 8 +- .../scripts/version.cjs | 0 .../scripts/version.js | 12 - .../src/__tests__/compat.test.tsx | 4 +- .../src/__tests__/insights.test.tsx | 0 .../src/components/Configure.tsx | 0 .../src/components/DynamicWidgets.tsx | 2 +- .../src/components/Index.tsx | 0 .../src/components/InstantSearch.tsx | 0 .../components/InstantSearchSSRProvider.tsx | 0 .../components/InstantSearchServerContext.ts | 0 .../components/__tests__/Configure.test.tsx | 0 .../__tests__/DynamicWidgets.test.tsx | 2 +- .../src/components/__tests__/Index.test.tsx | 0 .../__tests__/InstantSearch.test.tsx | 10 +- .../InstantSearchSSRProvider.test.tsx | 2 +- .../InstantSearchServerContext.test.tsx | 0 .../__tests__/routing/dispose-start.test.tsx | 6 +- .../routing/external-influence.test.tsx | 6 +- .../__tests__/routing/modal.test.tsx | 2 +- .../__tests__/routing/spa-debounced.test.tsx | 2 +- .../routing/spa-replace-state.test.tsx | 2 +- .../components/__tests__/routing/spa.test.tsx | 2 +- .../__tests__/connectAutoComplete.js | 264 -- .../connectors/__tests__/connectBreadcrumb.js | 279 -- .../connectors/__tests__/connectConfigure.js | 271 -- .../__tests__/connectConfigureRelatedItems.ts | 583 --- .../__tests__/connectCurrentRefinements.js | 124 - .../__tests__/connectDynamicWidgets.js | 504 --- .../connectors/__tests__/connectGeoSearch.js | 1434 ------- .../__tests__/connectHierarchicalMenu.js | 1123 ----- .../__tests__/connectHitInsights.ts | 140 - .../src/connectors/__tests__/connectHits.js | 116 - .../__tests__/connectHitsPerPage.js | 246 -- .../__tests__/connectInfiniteHits.js | 1059 ----- .../src/connectors/__tests__/connectMenu.js | 1087 ----- .../__tests__/connectNumericMenu.js | 710 ---- .../connectors/__tests__/connectPagination.js | 189 - .../__tests__/connectPoweredBy.jsdom.js | 31 - .../__tests__/connectPoweredBy.node.js | 19 - .../connectors/__tests__/connectQueryRules.ts | 686 --- .../src/connectors/__tests__/connectRange.js | 1739 -------- .../__tests__/connectRefinementList.js | 865 ---- .../__tests__/connectRelevantSort.ts | 229 - .../connectors/__tests__/connectScrollTo.js | 77 - .../connectors/__tests__/connectSearchBox.js | 155 - .../src/connectors/__tests__/connectSortBy.js | 150 - .../__tests__/connectStateResults.js | 85 - .../src/connectors/__tests__/connectStats.ts | 74 - .../__tests__/connectToggleRefinement.js | 933 ----- .../__tests__/connectVoiceSearch.js | 350 -- .../__tests__/useBreadcrumb.test.tsx | 0 .../__tests__/useClearRefinements.test.tsx | 0 .../__tests__/useConfigure.test.tsx | 0 .../__tests__/useCurrentRefinements.test.tsx | 0 .../__tests__/useDynamicWidgets.test.tsx | 0 .../__tests__/useGeoSearch.test.tsx | 0 .../__tests__/useHierarchicalMenu.test.tsx | 0 .../src/connectors/__tests__/useHits.test.tsx | 0 .../__tests__/useHitsPerPage.test.tsx | 0 .../__tests__/useInfiniteHits.test.tsx | 0 .../src/connectors/__tests__/useMenu.test.tsx | 0 .../__tests__/useNumericMenu.test.tsx | 0 .../__tests__/usePagination.test.tsx | 0 .../__tests__/usePoweredBy.test.tsx | 0 .../__tests__/useQueryRules.test.tsx | 0 .../connectors/__tests__/useRange.test.tsx | 0 .../__tests__/useRefinementList.test.tsx | 0 .../__tests__/useSearchBox.test.tsx | 0 .../connectors/__tests__/useSortBy.test.tsx | 0 .../connectors/__tests__/useStats.test.tsx | 0 .../__tests__/useToggleRefinement.test.tsx | 0 .../src/connectors/connectAutoComplete.js | 124 - .../src/connectors/connectBreadcrumb.js | 130 - .../src/connectors/connectConfigure.js | 71 - .../connectConfigureRelatedItems.ts | 233 -- .../connectors/connectCurrentRefinements.js | 66 - .../src/connectors/connectDynamicWidgets.ts | 90 - .../src/connectors/connectGeoSearch.js | 246 -- .../src/connectors/connectHierarchicalMenu.js | 312 -- .../src/connectors/connectHighlight.js | 78 - .../src/connectors/connectHitInsights.ts | 83 - .../src/connectors/connectHits.js | 86 - .../src/connectors/connectHitsPerPage.js | 106 - .../src/connectors/connectInfiniteHits.js | 150 - .../src/connectors/connectMenu.js | 265 -- .../src/connectors/connectNumericMenu.js | 252 -- .../src/connectors/connectPagination.js | 105 - .../src/connectors/connectPoweredBy.js | 31 - .../src/connectors/connectQueryRules.ts | 201 - .../src/connectors/connectRange.js | 343 -- .../src/connectors/connectRefinementList.js | 293 -- .../src/connectors/connectRelevantSort.ts | 79 - .../src/connectors/connectScrollTo.js | 84 - .../src/connectors/connectSearchBox.js | 121 - .../src/connectors/connectSortBy.js | 107 - .../src/connectors/connectStateResults.js | 74 - .../src/connectors/connectStats.ts | 38 - .../src/connectors/connectToggleRefinement.js | 190 - .../src/connectors/connectVoiceSearch.js | 161 - .../src/connectors/useBreadcrumb.ts | 0 .../src/connectors/useClearRefinements.ts | 0 .../src/connectors/useConfigure.ts | 0 .../src/connectors/useCurrentRefinements.ts | 0 .../src/connectors/useDynamicWidgets.ts | 0 .../src/connectors/useGeoSearch.ts | 0 .../src/connectors/useHierarchicalMenu.ts | 0 .../src/connectors/useHits.ts | 0 .../src/connectors/useHitsPerPage.ts | 0 .../src/connectors/useInfiniteHits.ts | 0 .../src/connectors/useMenu.ts | 0 .../src/connectors/useNumericMenu.ts | 0 .../src/connectors/usePagination.ts | 0 .../src/connectors/usePoweredBy.ts | 0 .../src/connectors/useQueryRules.ts | 0 .../src/connectors/useRange.ts | 0 .../src/connectors/useRefinementList.ts | 0 .../src/connectors/useSearchBox.ts | 0 .../src/connectors/useSortBy.ts | 0 .../src/connectors/useStats.ts | 0 .../src/connectors/useToggleRefinement.ts | 0 .../src/core/__tests__/createConnector.js | 1422 ------- .../createInstantSearchManager.derived.js | 699 ---- .../createInstantSearchManager.error.js | 209 - .../__tests__/createInstantSearchManager.js | 997 ----- .../createInstantSearchManager.result.js | 244 -- .../src/core/__tests__/createStore.js | 49 - .../core/__tests__/createWidgetsManager.js | 39 - .../src/core/__tests__/highlight.js | 148 - .../src/core/__tests__/indexUtils.js | 756 ---- .../src/core/__tests__/metadata.tsx | 259 -- .../src/core/__tests__/translatable.js | 43 - .../src/core/__tests__/utils.js | 364 -- .../src/core/context.ts | 40 - .../src/core/createConnector.tsx | 403 -- .../src/core/createInstantSearchManager.d.ts | 7 - .../src/core/createInstantSearchManager.js | 644 --- .../src/core/createStore.ts | 35 - .../src/core/createWidgetsManager.ts | 49 - .../src/core/highlight.js | 93 - .../src/core/indexUtils.js | 327 -- .../src/core/metadata.ts | 58 - .../src/core/translatable.js | 52 - .../src/core/utils.ts | 162 - .../src/core/version.js | 1 - .../src/hooks/__tests__/useConnector.test.tsx | 0 .../hooks/__tests__/useInstantSearch.test.tsx | 40 +- .../src/hooks/useConnector.ts | 0 .../src/hooks/useInstantSearch.ts | 0 .../react-instantsearch-core/src/index.ts | 124 +- .../src/lib/IndexContext.ts | 0 .../src/lib/InstantSearchContext.ts | 0 .../src/lib/InstantSearchSSRContext.ts | 0 .../src/lib/__tests__/IndexContext.test.tsx | 0 .../__tests__/InstantSearchContext.test.tsx | 0 .../InstantSearchSSRContext.test.tsx | 0 .../lib/__tests__/createSearchResults.test.ts | 0 .../src/lib/__tests__/dequal.test.ts | 0 .../src/lib/__tests__/invariant.test.ts | 0 .../lib/__tests__/useSearchResults.test.tsx | 2 +- .../src/lib/__tests__/useSearchState.test.tsx | 18 +- .../src/lib/__tests__/warn.test.ts | 0 .../src/lib/createSearchResults.ts | 0 .../src/lib/dequal.ts | 0 .../src/lib/getIndexSearchResults.ts | 0 .../src/lib/invariant.ts | 0 .../src/lib/noop.ts | 0 .../src/lib/useForceUpdate.ts | 0 .../src/lib/useIndex.ts | 0 .../src/lib/useIndexContext.ts | 0 .../src/lib/useInstantSearchApi.ts | 8 +- .../src/lib/useInstantSearchContext.ts | 2 +- .../src/lib/useInstantSearchSSRContext.ts | 0 .../src/lib/useInstantSearchServerContext.ts | 0 .../src/lib/useIsomorphicLayoutEffect.ts | 0 .../src/lib/useSearchResults.ts | 0 .../src/lib/useSearchState.ts | 0 .../src/lib/useStableValue.ts | 0 .../src/lib/useWidget.ts | 0 .../src/lib/warn.ts | 0 .../getServerState.test.tsx.snap | 0 .../server}/__tests__/getServerState.test.tsx | 8 +- .../src/server}/getServerState.tsx | 12 +- .../src/server}/index.ts | 0 .../src/types/algoliasearch.ts | 26 - .../src/types/index.ts | 1 - .../src/types/translatable.ts | 1 - .../src/version.ts | 0 .../src/widgets/Configure.js | 43 - .../src/widgets/ConfigureRelatedItems.tsx | 17 - .../src/widgets/DynamicWidgets.tsx | 68 - .../src/widgets/Index.tsx | 157 - .../src/widgets/InstantSearch.tsx | 337 -- .../src/widgets/QueryRuleContext.ts | 8 - .../widgets/__tests__/DynamicWidgets.test.tsx | 439 -- .../src/widgets/__tests__/Index.js | 216 - .../__tests__/InstantSearch.integration.js | 187 - .../src/widgets/__tests__/InstantSearch.js | 598 --- .../test/module/is-cjs-module.cjs | 9 + .../test/module/is-es-module.mjs | 8 + .../tsconfig.declaration.json | 0 .../react-instantsearch-dom-maps/CHANGELOG.md | 1868 --------- .../react-instantsearch-dom-maps/README.md | 13 - .../react-instantsearch-dom-maps/package.json | 56 - .../rollup.config.js | 77 - .../scripts/version.js | 14 - .../src/Connector.js | 120 - .../src/Control.js | 76 - .../src/CustomMarker.js | 112 - .../src/GeoSearch.js | 90 - .../src/GeoSearchContext.ts | 21 - .../src/GoogleMaps.js | 155 - .../src/GoogleMapsContext.ts | 16 - .../src/GoogleMapsLoader.js | 53 - .../src/Marker.js | 67 - .../src/Provider.js | 131 - .../react-instantsearch-dom-maps/src/Redo.js | 53 - .../src/__tests__/Connector.js | 241 -- .../src/__tests__/Control.js | 90 - .../src/__tests__/CustomMarker.js | 602 --- .../src/__tests__/GeoSearch.js | 493 --- .../src/__tests__/GoogleMaps.js | 635 --- .../src/__tests__/GoogleMapsLoader.jsdom.js | 147 - .../src/__tests__/GoogleMapsLoader.node.js | 9 - .../src/__tests__/Marker.js | 280 -- .../src/__tests__/Provider.js | 531 --- .../src/__tests__/Redo.js | 60 - .../__tests__/__snapshots__/Control.js.snap | 50 - .../__snapshots__/CustomMarker.js.snap | 113 - .../__tests__/__snapshots__/GeoSearch.js.snap | 112 - .../__snapshots__/GoogleMaps.js.snap | 355 -- .../src/__tests__/__snapshots__/Redo.js.snap | 29 - .../src/__tests__/utils.js | 235 -- .../src/__tests__/withGoogleMaps.tsx | 106 - .../elements/__tests__/createHTMLMarker.js | 261 -- .../src/elements/createHTMLMarker.js | 88 - .../react-instantsearch-dom-maps/src/index.js | 8 - .../src/propTypes.js | 15 - .../react-instantsearch-dom-maps/src/utils.js | 33 - .../src/withGoogleMaps.tsx | 37 - .../test/mockGoogleMaps.js | 89 - .../.storybook/addons.ts | 4 - .../.storybook/config.ts | 26 - .../.storybook/postcss.config.js | 5 - .../.storybook/preview-head.html | 4 - .../.storybook/public/default.css | 14 - .../.storybook/public/react-autosuggest.css | 69 - .../.storybook/public/rheostat.css | 46 - .../.storybook/public/util.css | 272 -- .../.storybook/webpack.config.js | 26 - packages/react-instantsearch-dom/CHANGELOG.md | 1891 --------- packages/react-instantsearch-dom/README.md | 13 - packages/react-instantsearch-dom/package.json | 62 - .../react-instantsearch-dom/rollup.config.js | 75 - packages/react-instantsearch-dom/server.js | 19 - .../src/components/Breadcrumb.js | 95 - .../src/components/ClearRefinements.js | 42 - .../src/components/CurrentRefinements.js | 72 - .../src/components/HierarchicalMenu.js | 82 - .../src/components/Highlight.js | 13 - .../src/components/Highlighter.js | 101 - .../src/components/Hits.tsx | 69 - .../src/components/HitsPerPage.js | 46 - .../src/components/InfiniteHits.js | 92 - .../src/components/Link.js | 22 - .../src/components/LinkList.js | 65 - .../src/components/List.js | 168 - .../src/components/Menu.js | 106 - .../src/components/MenuSelect.js | 82 - .../src/components/NumericMenu.js | 68 - .../src/components/Pagination.js | 194 - .../src/components/Panel.js | 54 - .../src/components/PanelCallbackHandler.d.ts | 6 - .../src/components/PanelCallbackHandler.js | 43 - .../src/components/PoweredBy.js | 48 - .../src/components/QueryRuleCustomData.tsx | 34 - .../src/components/RangeInput.js | 127 - .../src/components/RatingMenu.js | 188 - .../src/components/RefinementList.js | 112 - .../src/components/RelevantSort.tsx | 59 - .../src/components/ScrollTo.js | 31 - .../src/components/SearchBox.js | 316 -- .../src/components/Select.js | 50 - .../src/components/Snippet.js | 13 - .../src/components/SortBy.js | 46 - .../src/components/Stats.tsx | 67 - .../src/components/ToggleRefinement.js | 41 - .../src/components/VoiceSearch.tsx | 170 - .../src/components/__tests__/Breadcrumb.js | 239 -- .../components/__tests__/ClearRefinements.js | 76 - .../__tests__/CurrentRefinements.js | 154 - .../components/__tests__/HierarchicalMenu.js | 194 - .../src/components/__tests__/Highlight.js | 123 - .../src/components/__tests__/Highlighter.js | 312 -- .../src/components/__tests__/Hits.tsx | 28 - .../src/components/__tests__/HitsPerPage.js | 122 - .../src/components/__tests__/InfiniteHits.js | 144 - .../src/components/__tests__/List.js | 148 - .../src/components/__tests__/Menu.js | 320 -- .../src/components/__tests__/MenuSelect.js | 117 - .../src/components/__tests__/NumericMenu.js | 268 -- .../src/components/__tests__/Pagination.js | 395 -- .../src/components/__tests__/Panel.js | 118 - .../__tests__/PanelCallbackHandler.js | 125 - .../src/components/__tests__/PoweredBy.js | 42 - .../__tests__/QueryRuleCustomData.tsx | 71 - .../src/components/__tests__/RangeInput.js | 320 -- .../src/components/__tests__/RatingMenu.js | 319 -- .../components/__tests__/RefinementList.js | 368 -- .../src/components/__tests__/RelevantSort.tsx | 194 - .../src/components/__tests__/ScrollTo.js | 70 - .../src/components/__tests__/SearchBox.js | 367 -- .../src/components/__tests__/Snippet.js | 122 - .../src/components/__tests__/SortBy.js | 101 - .../src/components/__tests__/Stats.tsx | 93 - .../components/__tests__/ToggleRefinement.js | 78 - .../src/components/__tests__/VoiceSearch.tsx | 125 - .../__snapshots__/Breadcrumb.js.snap | 316 -- .../__snapshots__/ClearRefinements.js.snap | 43 - .../__snapshots__/CurrentRefinements.js.snap | 240 -- .../__snapshots__/HierarchicalMenu.js.snap | 333 -- .../__tests__/__snapshots__/Highlight.js.snap | 97 - .../__snapshots__/Highlighter.js.snap | 482 --- .../__tests__/__snapshots__/Hits.tsx.snap | 65 - .../__snapshots__/HitsPerPage.js.snap | 51 - .../__snapshots__/InfiniteHits.js.snap | 79 - .../__tests__/__snapshots__/List.js.snap | 206 - .../__tests__/__snapshots__/Menu.js.snap | 489 --- .../__snapshots__/MenuSelect.js.snap | 212 - .../__snapshots__/NumericMenu.js.snap | 445 -- .../__snapshots__/Pagination.js.snap | 2919 ------------- .../__tests__/__snapshots__/Panel.js.snap | 149 - .../__tests__/__snapshots__/PoweredBy.js.snap | 97 - .../QueryRuleCustomData.tsx.snap | 36 - .../__snapshots__/RangeInput.js.snap | 586 --- .../__snapshots__/RatingMenu.js.snap | 2597 ------------ .../__snapshots__/RefinementList.js.snap | 615 --- .../__tests__/__snapshots__/ScrollTo.js.snap | 9 - .../__tests__/__snapshots__/SearchBox.js.snap | 743 ---- .../__tests__/__snapshots__/Snippet.js.snap | 97 - .../__tests__/__snapshots__/SortBy.js.snap | 33 - .../__snapshots__/ToggleRefinement.js.snap | 89 - .../__snapshots__/VoiceSearch.tsx.snap | 111 - .../src/components/index.js | 18 - .../__tests__/createInstantSearchServer.js | 1255 ------ .../getInsightsAnonymousUserToken.ts | 41 - .../src/core/__tests__/utils.js | 110 - .../src/core/createInstantSearchServer.js | 213 - .../src/core/getInsightsAnonymousUserToken.ts | 20 - .../react-instantsearch-dom/src/core/utils.ts | 68 - .../src/hooks/useAnswers.js | 98 - packages/react-instantsearch-dom/src/index.js | 93 - .../src/lib/createConcurrentSafePromise.js | 43 - .../src/lib/debounce.ts | 28 - .../__tests__/sessionStorage.js | 101 - .../src/lib/infiniteHitsCache/index.js | 1 - .../lib/infiniteHitsCache/sessionStorage.js | 56 - .../lib/voiceSearchHelper/__tests__/index.ts | 218 - .../src/lib/voiceSearchHelper/index.ts | 181 - .../react-instantsearch-dom/src/server.js | 1 - .../src/widgets/Answers.js | 50 - .../src/widgets/Breadcrumb.js | 103 - .../src/widgets/ClearRefinements.js | 49 - .../src/widgets/CurrentRefinements.js | 57 - .../src/widgets/DynamicWidgets.js | 16 - .../src/widgets/HierarchicalMenu.js | 106 - .../src/widgets/Highlight.js | 46 - .../src/widgets/Hits.js | 38 - .../src/widgets/HitsPerPage.js | 50 - .../src/widgets/InfiniteHits.js | 44 - .../src/widgets/Menu.js | 62 - .../src/widgets/MenuSelect.js | 52 - .../src/widgets/NumericMenu.js | 61 - .../src/widgets/Pagination.js | 69 - .../src/widgets/Panel.js | 40 - .../src/widgets/PoweredBy.js | 35 - .../src/widgets/QueryRuleCustomData.tsx | 23 - .../src/widgets/RangeInput.js | 57 - .../src/widgets/RangeSlider.js | 102 - .../src/widgets/RatingMenu.js | 63 - .../src/widgets/RefinementList.js | 67 - .../src/widgets/RelevantSort.tsx | 7 - .../src/widgets/ScrollTo.js | 35 - .../src/widgets/SearchBox.js | 53 - .../src/widgets/Snippet.js | 49 - .../src/widgets/SortBy.js | 45 - .../src/widgets/Stats.ts | 33 - .../src/widgets/ToggleRefinement.js | 50 - .../src/widgets/VoiceSearch.ts | 7 - .../src/widgets/__tests__/Answers.js | 118 - .../widgets/__tests__/DynamicWidgets.test.js | 112 - .../src/widgets/__tests__/InstantSearch.tsx | 258 -- .../stories/3rdPartyIntegrations.stories.js | 98 - .../stories/Answers.stories.js | 29 - .../stories/Breadcrumb.stories.js | 122 - .../stories/ClearRefinements.stories.js | 55 - .../stories/Conditional.stories.js | 47 - .../stories/Configure.stories.js | 30 - .../stories/ConfigureRelatedItems.stories.tsx | 118 - .../stories/CurrentRefinements.stories.js | 101 - .../stories/DynamicWidgets.stories.js | 65 - .../stories/GeoSearch.stories.js | 587 --- .../stories/HierarchicalMenu.stories.js | 108 - .../stories/Highlight.stories.js | 59 - .../stories/Hits.stories.js | 84 - .../stories/HitsPerPage.stories.js | 68 - .../stories/InfiniteHits.stories.js | 178 - .../stories/InstantSearch.stories.js | 40 - .../stories/Menu.stories.js | 84 - .../stories/MenuSelect.stories.js | 75 - .../stories/MultiIndex.stories.js | 285 -- .../stories/NumericMenu.stories.js | 81 - .../stories/Pagination.stories.js | 61 - .../stories/PoweredBy.stories.js | 13 - .../stories/QueryRuleContext.stories.tsx | 166 - .../stories/QueryRuleCustomData.stories.tsx | 150 - .../stories/RangeInput.stories.js | 105 - .../stories/RangeSlider.stories.js | 66 - .../stories/RatingMenu.stories.js | 96 - .../stories/RefinementList.stories.js | 97 - .../stories/RefreshCache.stories.js | 176 - .../stories/RelevantSort.stories.js | 32 - .../stories/ScrollTo.stories.js | 16 - .../stories/SearchBox.stories.js | 177 - .../stories/Snippet.stories.js | 61 - .../stories/SortBy.stories.js | 47 - .../stories/Stats.stories.js | 49 - .../stories/ToggleRefinement.stories.js | 39 - .../stories/VoiceSearch.stories.tsx | 172 - .../stories/places/connector.js | 29 - .../stories/places/index.js | 45 - .../react-instantsearch-dom/stories/util.d.ts | 24 - .../react-instantsearch-dom/stories/util.js | 196 - .../CHANGELOG.md | 2004 --------- .../README.md | 74 - .../package.json | 58 - .../test/module/is-cjs-module.cjs | 9 - .../test/module/is-es-module.mjs | 8 - .../CHANGELOG.md | 2042 --------- .../react-instantsearch-hooks-web/README.md | 124 - .../package.json | 59 - .../rollup.config.js | 80 - .../src/index.ts | 2 - .../test/module/is-cjs-module.cjs | 9 - .../test/module/is-es-module.mjs | 8 - .../react-instantsearch-hooks/CHANGELOG.md | 2060 --------- packages/react-instantsearch-hooks/README.md | 81 - .../react-instantsearch-hooks/global.d.ts | 1 - .../react-instantsearch-hooks/package.json | 63 - .../rollup.config.js | 80 - .../src/__tests__/index.test.ts | 15 - .../react-instantsearch-hooks/src/index.ts | 37 - .../test/module/is-cjs-module.cjs | 9 - .../test/module/is-es-module.mjs | 8 - .../tsconfig.declaration.json | 3 - .../react-instantsearch-native/CHANGELOG.md | 1855 --------- packages/react-instantsearch-native/README.md | 13 - .../react-instantsearch-native/package.json | 48 - .../react-instantsearch-native/src/index.ts | 47 - .../CHANGELOG.md | 0 .../README.md | 34 +- .../__tests__/e2e/backButton.test.ts | 0 .../__tests__/e2e/nextLink.test.ts | 0 .../__tests__/e2e/utils.ts | 0 .../__tests__/module/is-cjs-module.cjs | 4 +- .../__tests__/module/is-es-module.mjs | 4 +- .../e2e.d.ts | 0 .../package.json | 10 +- .../src/index.ts | 4 +- .../tsconfig.declaration.json | 0 .../tsconfig.wdio.json | 0 .../wdio.conf.cjs | 0 packages/react-instantsearch/CHANGELOG.md | 210 +- packages/react-instantsearch/README.md | 115 +- packages/react-instantsearch/connectors.js | 29 - packages/react-instantsearch/dom.js | 34 - .../global.d.ts | 0 packages/react-instantsearch/index.js | 1 - packages/react-instantsearch/native.js | 8 - packages/react-instantsearch/package.json | 50 +- packages/react-instantsearch/rollup.config.js | 55 +- packages/react-instantsearch/server.js | 1 - .../src/__tests__/common-connectors.test.tsx | 0 .../src/__tests__/common-shared.test.tsx | 0 .../src/__tests__/common-widgets.test.tsx | 2 +- packages/react-instantsearch/src/index.ts | 2 + .../src/types/PartialKeys.ts | 0 .../src/types/Translatable.ts | 0 .../src/types/index.ts | 0 .../src/ui/Breadcrumb.tsx | 2 +- .../src/ui/ClearRefinements.tsx | 0 .../src/ui/CurrentRefinements.tsx | 0 .../src/ui/HierarchicalMenu.tsx | 2 +- .../src/ui/Highlight.tsx | 0 .../src/ui/Hits.tsx | 0 .../src/ui/HitsPerPage.tsx | 0 .../src/ui/InfiniteHits.tsx | 0 .../src/ui/InternalHighlight.tsx | 0 .../src/ui/Menu.tsx | 0 .../src/ui/Pagination.tsx | 0 .../src/ui/PoweredBy.tsx | 0 .../src/ui/RangeInput.tsx | 2 +- .../src/ui/RefinementList.tsx | 0 .../src/ui/SearchBox.tsx | 0 .../src/ui/ShowMoreButton.tsx | 0 .../src/ui/Snippet.tsx | 0 .../src/ui/SortBy.tsx | 2 +- .../src/ui/Stats.tsx | 0 .../src/ui/ToggleRefinement.tsx | 0 .../src/ui/__tests__/Breadcrumb.test.tsx | 0 .../ui/__tests__/ClearRefinements.test.tsx | 0 .../ui/__tests__/CurrentRefinements.test.tsx | 0 .../ui/__tests__/HierarchicalMenu.test.tsx | 0 .../src/ui/__tests__/Hits.test.tsx | 0 .../src/ui/__tests__/HitsPerPage.test.tsx | 0 .../src/ui/__tests__/InfiniteHits.test.tsx | 0 .../ui/__tests__/InternalHighlight.test.tsx | 0 .../src/ui/__tests__/Menu.test.tsx | 0 .../src/ui/__tests__/Pagination.test.tsx | 0 .../src/ui/__tests__/PoweredBy.test.tsx | 0 .../src/ui/__tests__/RangeInput.test.tsx | 0 .../src/ui/__tests__/RefinementList.test.tsx | 0 .../src/ui/__tests__/SearchBox.test.tsx | 0 .../src/ui/__tests__/ShowMoreButton.test.tsx | 0 .../src/ui/__tests__/SortBy.test.tsx | 0 .../src/ui/__tests__/Stats.test.tsx | 0 .../ui/__tests__/ToggleRefinement.test.tsx | 0 .../ui/lib/__tests__/isModifierClick.test.tsx | 0 .../src/ui/lib/cx.ts | 0 .../src/ui/lib/isModifierClick.ts | 0 .../src/widgets/Breadcrumb.tsx | 4 +- .../src/widgets/ClearRefinements.tsx | 4 +- .../src/widgets/CurrentRefinements.tsx | 4 +- .../src/widgets/HierarchicalMenu.tsx | 4 +- .../src/widgets/Highlight.tsx | 0 .../src/widgets/Hits.tsx | 4 +- .../src/widgets/HitsPerPage.tsx | 4 +- .../src/widgets/InfiniteHits.tsx | 4 +- .../src/widgets/Menu.tsx | 4 +- .../src/widgets/Pagination.tsx | 4 +- .../src/widgets/PoweredBy.tsx | 2 +- .../src/widgets/RangeInput.tsx | 4 +- .../src/widgets/RefinementList.tsx | 4 +- .../src/widgets/SearchBox.tsx | 4 +- .../src/widgets/Snippet.tsx | 0 .../src/widgets/SortBy.tsx | 4 +- .../src/widgets/Stats.tsx | 4 +- .../src/widgets/ToggleRefinement.tsx | 4 +- .../src/widgets/__tests__/Breadcrumb.test.tsx | 14 +- .../__tests__/ClearRefinements.test.tsx | 38 +- .../__tests__/CurrentRefinements.test.tsx | 42 +- .../__tests__/HierarchicalMenu.test.tsx | 50 +- .../src/widgets/__tests__/Highlight.test.tsx | 0 .../src/widgets/__tests__/Hits.test.tsx | 14 +- .../widgets/__tests__/HitsPerPage.test.tsx | 18 +- .../widgets/__tests__/InfiniteHits.test.tsx | 38 +- .../src/widgets/__tests__/Menu.test.tsx | 50 +- .../src/widgets/__tests__/Pagination.test.tsx | 54 +- .../src/widgets/__tests__/PoweredBy.test.tsx | 22 +- .../src/widgets/__tests__/RangeInput.test.tsx | 26 +- .../widgets/__tests__/RefinementList.test.tsx | 62 +- .../src/widgets/__tests__/SearchBox.test.tsx | 46 +- .../src/widgets/__tests__/Snippet.test.tsx | 0 .../src/widgets/__tests__/SortBy.test.tsx | 18 +- .../src/widgets/__tests__/Stats.test.tsx | 26 +- .../__tests__/ToggleRefinement.test.tsx | 26 +- .../__tests__/__utils__/all-widgets.tsx | 2 +- .../widgets/__tests__/all-components.test.tsx | 0 .../widgets/__tests__/all-widgets.test.tsx | 0 .../src/widgets/index.ts | 0 .../test/module/is-cjs-module.cjs | 9 + .../test/module/is-es-module.mjs | 8 + .../tsconfig.declaration.json | 0 scripts/website/README.md | 3 - scripts/website/package.json | 21 - scripts/website/webpack.config.js | 102 - tests/common/README.md | 4 +- tests/e2e/flavors.js | 2 +- ...apper.tsx => InstantSearchTestWrapper.tsx} | 10 +- tests/utils/createInstantSearchSpy.tsx | 8 +- tests/utils/index.ts | 2 +- tests/utils/setupTests.ts | 2 +- tests/versions/index.js | 17 +- tsconfig.json | 4 +- website/_redirects | 5 +- yarn.lock | 3683 +---------------- 853 files changed, 1560 insertions(+), 88917 deletions(-) delete mode 100644 examples/react-hooks/default-theme/index.html delete mode 100644 examples/react-hooks/default-theme/package.json delete mode 100644 examples/react-hooks/e-commerce/App.css delete mode 100644 examples/react-hooks/e-commerce/App.mobile.css delete mode 100644 examples/react-hooks/e-commerce/Theme.css delete mode 100755 examples/react-hooks/e-commerce/assets/cover-mobile.png delete mode 100755 examples/react-hooks/e-commerce/assets/cover.png delete mode 100644 examples/react-hooks/e-commerce/assets/favicon.png delete mode 100644 examples/react-hooks/e-commerce/assets/manifest.webmanifest delete mode 100644 examples/react-hooks/e-commerce/index.html delete mode 100644 examples/react-hooks/e-commerce/package.json delete mode 100644 examples/react-hooks/getting-started/.editorconfig delete mode 100644 examples/react-hooks/getting-started/.gitignore delete mode 100644 examples/react-hooks/getting-started/README.md delete mode 100644 examples/react-hooks/getting-started/favicon.png delete mode 100644 examples/react-hooks/getting-started/index.html delete mode 100644 examples/react-hooks/getting-started/package.json delete mode 100644 examples/react-hooks/getting-started/src/App.css delete mode 100644 examples/react-hooks/next-routing/README.md delete mode 100644 examples/react-hooks/next-routing/package.json delete mode 100644 examples/react-hooks/next/.gitignore delete mode 100644 examples/react-hooks/next/README.md delete mode 100644 examples/react-hooks/react-native/.expo-shared/assets.json delete mode 100644 examples/react-hooks/react-native/.gitignore delete mode 100644 examples/react-hooks/react-native/README.md delete mode 100644 examples/react-hooks/react-native/app.json delete mode 100644 examples/react-hooks/react-native/assets/icon.png delete mode 100644 examples/react-hooks/react-native/assets/splash.png delete mode 100644 examples/react-hooks/react-native/babel.config.js delete mode 100644 examples/react-hooks/react-native/package.json delete mode 100644 examples/react/autocomplete/README.md delete mode 100644 examples/react/autocomplete/__mocks__/styleMock.js delete mode 100644 examples/react/autocomplete/index.html delete mode 100644 examples/react/autocomplete/package.json delete mode 100644 examples/react/autocomplete/src/App-Mentions.js delete mode 100644 examples/react/autocomplete/src/App-Multi-Index.js delete mode 100644 examples/react/autocomplete/src/__tests__/App.test.js delete mode 100644 examples/react/autocomplete/src/__tests__/__snapshots__/App.test.js.snap delete mode 100644 examples/react/autocomplete/src/index.js delete mode 100644 examples/react/autocomplete/src/style.css delete mode 100644 examples/react/default-theme/App.css delete mode 100644 examples/react/default-theme/App.js delete mode 100644 examples/react/default-theme/URLSync.js delete mode 100644 examples/react/default-theme/index.js rename examples/{react-hooks => react}/default-theme/src/App.css (100%) rename examples/{react-hooks => react}/default-theme/src/App.tsx (99%) rename examples/{react-hooks => react}/default-theme/src/components/NumericMenu.tsx (98%) rename examples/{react-hooks => react}/default-theme/src/components/Panel.tsx (100%) rename examples/{react-hooks => react}/default-theme/src/components/QueryRuleContext.tsx (95%) rename examples/{react-hooks => react}/default-theme/src/components/QueryRuleCustomData.tsx (97%) rename examples/{react-hooks => react}/default-theme/src/components/Refresh.css (100%) rename examples/{react-hooks => react}/default-theme/src/components/Refresh.tsx (91%) rename examples/{react-hooks => react}/default-theme/src/components/index.ts (100%) rename examples/{react-hooks => react}/default-theme/src/components/layout/Tabs.css (100%) rename examples/{react-hooks => react}/default-theme/src/components/layout/Tabs.tsx (100%) rename examples/{react-hooks => react}/default-theme/src/components/layout/index.tsx (100%) rename examples/{react-hooks => react}/default-theme/src/cx.ts (100%) rename examples/{react-hooks => react}/default-theme/src/env.js (100%) rename examples/{react-hooks => react}/default-theme/src/index.tsx (100%) rename examples/{react-hooks => react}/default-theme/src/isModifierClick.ts (100%) delete mode 100644 examples/react/e-commerce/.editorconfig delete mode 100644 examples/react/e-commerce/.gitignore delete mode 100644 examples/react/e-commerce/.prettierrc delete mode 100644 examples/react/e-commerce/AlgoliaSvg.js delete mode 100644 examples/react/e-commerce/App.js rename examples/{react-hooks => react}/e-commerce/App.tsx (99%) delete mode 100644 examples/react/e-commerce/URLSync.js rename examples/{react-hooks => react}/e-commerce/components/AlgoliaSvg.tsx (100%) rename examples/{react-hooks => react}/e-commerce/components/ClearFilters.tsx (94%) rename examples/{react-hooks => react}/e-commerce/components/ClearFiltersMobile.tsx (87%) rename examples/{react-hooks => react}/e-commerce/components/NoResults.tsx (98%) rename examples/{react-hooks => react}/e-commerce/components/NoResultsBoundary.tsx (88%) rename examples/{react-hooks => react}/e-commerce/components/Pagination.css (100%) rename examples/{react-hooks => react}/e-commerce/components/Panel.tsx (100%) rename examples/{react-hooks => react}/e-commerce/components/PriceSlider.css (100%) rename examples/{react-hooks => react}/e-commerce/components/PriceSlider.tsx (98%) rename examples/{react-hooks => react}/e-commerce/components/Ratings.tsx (97%) rename examples/{react-hooks => react}/e-commerce/components/ResultsNumberMobile.tsx (80%) rename examples/{react-hooks => react}/e-commerce/components/SaveFiltersMobile.tsx (83%) rename examples/{react-hooks => react}/e-commerce/components/ScrollTo.tsx (93%) rename examples/{react-hooks => react}/e-commerce/components/index.ts (100%) rename examples/{react-hooks => react}/e-commerce/cx.ts (100%) rename examples/{react-hooks => react}/e-commerce/env.js (100%) delete mode 100644 examples/react/e-commerce/index.js rename examples/{react-hooks => react}/e-commerce/index.tsx (100%) rename examples/{react-hooks => react}/e-commerce/routing.ts (100%) delete mode 100644 examples/react/e-commerce/utils.js rename examples/{react-hooks => react}/e-commerce/utils/index.ts (100%) delete mode 100644 examples/react/e-commerce/widgets/ClearFiltersMobile.js delete mode 100644 examples/react/e-commerce/widgets/NoResults.js delete mode 100644 examples/react/e-commerce/widgets/Pagination.css delete mode 100644 examples/react/e-commerce/widgets/PriceSlider.css delete mode 100644 examples/react/e-commerce/widgets/PriceSlider.js delete mode 100644 examples/react/e-commerce/widgets/Ratings.js delete mode 100644 examples/react/e-commerce/widgets/ResultsNumberMobile.js delete mode 100644 examples/react/e-commerce/widgets/SaveFiltersMobile.js delete mode 100644 examples/react/e-commerce/widgets/index.js delete mode 100644 examples/react/geo-search/README.md delete mode 100644 examples/react/geo-search/index.html delete mode 100644 examples/react/geo-search/package.json delete mode 100644 examples/react/geo-search/src/App.js delete mode 100644 examples/react/geo-search/src/__tests__/App.test.js delete mode 100644 examples/react/geo-search/src/__tests__/__snapshots__/App.test.js.snap delete mode 100644 examples/react/geo-search/src/index.js delete mode 100644 examples/react/geo-search/src/style.css delete mode 100644 examples/react/getting-started/src/App.js rename examples/{react-hooks => react}/getting-started/src/App.tsx (93%) rename examples/{react-hooks => react}/getting-started/src/Panel.tsx (100%) rename examples/{react-hooks => react}/getting-started/src/env.js (100%) delete mode 100644 examples/react/getting-started/src/index.css delete mode 100644 examples/react/getting-started/src/index.js rename examples/{react-hooks => react}/getting-started/src/index.tsx (100%) delete mode 100644 examples/react/media/App.css delete mode 100644 examples/react/media/App.js delete mode 100644 examples/react/media/URLSync.js delete mode 100644 examples/react/media/index.html delete mode 100644 examples/react/media/index.js delete mode 100644 examples/react/media/package.json delete mode 100644 examples/react/multi-index/README.md delete mode 100644 examples/react/multi-index/index.html delete mode 100644 examples/react/multi-index/package.json delete mode 100644 examples/react/multi-index/src/App.js delete mode 100644 examples/react/multi-index/src/__tests__/App.test.js delete mode 100644 examples/react/multi-index/src/__tests__/__snapshots__/App.test.js.snap delete mode 100644 examples/react/multi-index/src/index.js rename examples/{react-hooks => react}/next-routing/.eslintrc (100%) rename examples/{react-hooks => react}/next-routing/.gitignore (100%) create mode 100644 examples/react/next-routing/README.md rename examples/{react-hooks => react}/next-routing/components/Panel.tsx (100%) rename examples/{react-hooks => react}/next-routing/next-env.d.ts (100%) rename examples/{react-hooks => react}/next-routing/next.config.js (100%) rename examples/{react-hooks/next => react/next-routing}/package.json (70%) rename examples/{react-hooks => react}/next-routing/pages/_app.js (100%) rename examples/{react-hooks => react}/next-routing/pages/index.tsx (93%) rename examples/{react-hooks => react}/next-routing/pages/other-page.tsx (100%) rename examples/{react-hooks => react}/next-routing/styles/globals.css (100%) rename examples/{react-hooks => react}/next-routing/tsconfig.json (100%) rename examples/{react-hooks => react}/next-routing/utils/cx.ts (100%) rename examples/{react-hooks => react}/next/.eslintrc (100%) rename examples/{react-hooks => react}/next/components/Panel.tsx (100%) delete mode 100644 examples/react/next/components/app.js delete mode 100644 examples/react/next/components/head.js delete mode 100644 examples/react/next/components/index.js rename examples/{react-hooks => react}/next/next-env.d.ts (100%) rename examples/{react-hooks => react}/next/pages/_app.js (100%) delete mode 100644 examples/react/next/pages/index.js rename examples/{react-hooks => react}/next/pages/index.tsx (92%) delete mode 100644 examples/react/next/public/favicon.ico delete mode 100644 examples/react/next/public/instantsearch.css delete mode 100644 examples/react/next/sandbox.config.json rename examples/{react-hooks => react}/next/styles/globals.css (100%) delete mode 100644 examples/react/next/tests/__snapshots__/index.test.js.snap delete mode 100644 examples/react/next/tests/index.test.js rename examples/{react-hooks => react}/next/tsconfig.json (100%) rename examples/{react-hooks => react}/next/utils/cx.ts (100%) delete mode 100644 examples/react/react-native-query-suggestions/.expo-shared/assets.json delete mode 100644 examples/react/react-native-query-suggestions/.gitignore delete mode 100644 examples/react/react-native-query-suggestions/App.js delete mode 100644 examples/react/react-native-query-suggestions/App.test.js delete mode 100644 examples/react/react-native-query-suggestions/Highlight.js delete mode 100644 examples/react/react-native-query-suggestions/app.json delete mode 100644 examples/react/react-native-query-suggestions/assets/icon.png delete mode 100644 examples/react/react-native-query-suggestions/assets/splash.png delete mode 100644 examples/react/react-native-query-suggestions/babel.config.js delete mode 100644 examples/react/react-native-query-suggestions/package.json delete mode 100644 examples/react/react-native-query-suggestions/tests/App.test.js delete mode 100644 examples/react/react-native-query-suggestions/tests/__snapshots__/App.test.js.snap rename examples/{react-hooks => react}/react-native/.eslintignore (100%) rename examples/{react-hooks => react}/react-native/.eslintrc (100%) delete mode 100644 examples/react/react-native/App.js rename examples/{react-hooks => react}/react-native/App.tsx (95%) rename examples/react/{react-router => react-native}/README.md (54%) rename examples/{react-hooks => react}/react-native/assets/adaptive-icon.png (100%) rename examples/{react-hooks => react}/react-native/assets/favicon.png (100%) delete mode 100644 examples/react/react-native/src/Categories.js delete mode 100644 examples/react/react-native/src/Filters.js rename examples/{react-hooks => react}/react-native/src/Highlight.tsx (100%) delete mode 100644 examples/react/react-native/src/Home.js rename examples/{react-hooks => react}/react-native/src/InfiniteHits.tsx (97%) delete mode 100644 examples/react/react-native/src/Price.js delete mode 100644 examples/react/react-native/src/Rating.js rename examples/{react-hooks => react}/react-native/src/SearchBox.tsx (99%) delete mode 100644 examples/react/react-native/src/Type.js delete mode 100644 examples/react/react-native/src/components/Highlight.js delete mode 100644 examples/react/react-native/src/components/Spinner.js delete mode 100644 examples/react/react-native/src/components/Stats.js delete mode 100644 examples/react/react-native/tests/App.test.js delete mode 100644 examples/react/react-native/tests/__snapshots__/App.test.js.snap rename examples/{react-hooks => react}/react-native/tsconfig.json (100%) rename examples/{react-hooks => react}/react-native/types/ProductHit.ts (100%) delete mode 100644 examples/react/react-router-v3/README.md delete mode 100644 examples/react/react-router-v3/index.html delete mode 100644 examples/react/react-router-v3/package.json delete mode 100644 examples/react/react-router-v3/src/App.js delete mode 100644 examples/react/react-router-v3/src/__tests__/App.test.js delete mode 100644 examples/react/react-router-v3/src/__tests__/__snapshots__/App.test.js.snap delete mode 100644 examples/react/react-router-v3/src/index.js delete mode 100644 examples/react/react-router/index.html delete mode 100644 examples/react/react-router/package.json delete mode 100644 examples/react/react-router/src/App.js delete mode 100644 examples/react/react-router/src/__tests__/App.test.js delete mode 100644 examples/react/react-router/src/__tests__/__snapshots__/App.test.js.snap delete mode 100644 examples/react/react-router/src/index.js delete mode 100644 examples/react/server-side-rendering/.babelrc delete mode 100644 examples/react/server-side-rendering/.gitignore delete mode 100644 examples/react/server-side-rendering/README.md delete mode 100644 examples/react/server-side-rendering/package.json delete mode 100644 examples/react/server-side-rendering/src/App.js delete mode 100644 examples/react/server-side-rendering/src/App.test.js delete mode 100644 examples/react/server-side-rendering/src/__snapshots__/App.test.js.snap delete mode 100644 examples/react/server-side-rendering/src/browser.js delete mode 100644 examples/react/server-side-rendering/src/createApp.js delete mode 100644 examples/react/server-side-rendering/src/server.js delete mode 100644 examples/react/server-side-rendering/src/template.js delete mode 100644 examples/react/server-side-rendering/webpack.config.js rename examples/{react-hooks => react}/ssr/.babelrc (100%) rename examples/{react-hooks => react}/ssr/.eslintrc.js (100%) rename examples/{react-hooks => react}/ssr/.gitignore (100%) rename examples/{react-hooks => react}/ssr/README.md (67%) rename examples/{react-hooks => react}/ssr/package.json (80%) rename examples/{react-hooks => react}/ssr/src/App.js (98%) rename examples/{react-hooks => react}/ssr/src/browser.js (100%) rename examples/{react-hooks => react}/ssr/src/cx.js (100%) rename examples/{react-hooks => react}/ssr/src/searchClient.js (100%) rename examples/{react-hooks => react}/ssr/src/server.js (93%) rename examples/{react-hooks => react}/ssr/webpack.config.js (100%) delete mode 100644 examples/react/tourism/App.css delete mode 100644 examples/react/tourism/App.js delete mode 100644 examples/react/tourism/URLSync.js delete mode 100644 examples/react/tourism/index.html delete mode 100644 examples/react/tourism/index.js delete mode 100644 examples/react/tourism/package.json rename packages/{react-instantsearch-hooks-server => react-instantsearch-core}/global.d.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/scripts/version.cjs (100%) delete mode 100755 packages/react-instantsearch-core/scripts/version.js rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/__tests__/compat.test.tsx (87%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/__tests__/insights.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/Configure.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/DynamicWidgets.tsx (98%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/Index.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/InstantSearch.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/InstantSearchSSRProvider.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/InstantSearchServerContext.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/Configure.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/DynamicWidgets.test.tsx (99%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/Index.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/InstantSearch.test.tsx (99%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/InstantSearchSSRProvider.test.tsx (99%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/InstantSearchServerContext.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/routing/dispose-start.test.tsx (96%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/routing/external-influence.test.tsx (95%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/routing/modal.test.tsx (95%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/routing/spa-debounced.test.tsx (97%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/routing/spa-replace-state.test.tsx (97%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/components/__tests__/routing/spa.test.tsx (96%) delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectAutoComplete.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectBreadcrumb.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectConfigureRelatedItems.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectCurrentRefinements.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectDynamicWidgets.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectGeoSearch.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectHitInsights.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectHitsPerPage.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectInfiniteHits.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectNumericMenu.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.jsdom.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.node.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectQueryRules.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectRange.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectRelevantSort.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectScrollTo.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectSortBy.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectStateResults.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectStats.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectToggleRefinement.js delete mode 100644 packages/react-instantsearch-core/src/connectors/__tests__/connectVoiceSearch.js rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useBreadcrumb.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useClearRefinements.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useConfigure.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useCurrentRefinements.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useDynamicWidgets.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useGeoSearch.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useHierarchicalMenu.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useHits.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useHitsPerPage.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useInfiniteHits.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useMenu.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useNumericMenu.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/usePagination.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/usePoweredBy.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useQueryRules.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useRange.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useRefinementList.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useSearchBox.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useSortBy.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useStats.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/__tests__/useToggleRefinement.test.tsx (100%) delete mode 100644 packages/react-instantsearch-core/src/connectors/connectAutoComplete.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectBreadcrumb.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectConfigure.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectConfigureRelatedItems.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/connectCurrentRefinements.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectDynamicWidgets.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/connectGeoSearch.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectHighlight.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectHitInsights.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/connectHits.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectHitsPerPage.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectInfiniteHits.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectMenu.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectNumericMenu.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectPagination.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectPoweredBy.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectQueryRules.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/connectRange.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectRefinementList.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectRelevantSort.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/connectScrollTo.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectSearchBox.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectSortBy.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectStateResults.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectStats.ts delete mode 100644 packages/react-instantsearch-core/src/connectors/connectToggleRefinement.js delete mode 100644 packages/react-instantsearch-core/src/connectors/connectVoiceSearch.js rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useBreadcrumb.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useClearRefinements.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useConfigure.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useCurrentRefinements.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useDynamicWidgets.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useGeoSearch.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useHierarchicalMenu.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useHits.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useHitsPerPage.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useInfiniteHits.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useMenu.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useNumericMenu.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/usePagination.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/usePoweredBy.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useQueryRules.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useRange.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useRefinementList.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useSearchBox.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useSortBy.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useStats.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/connectors/useToggleRefinement.ts (100%) delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/createConnector.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.derived.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.error.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.result.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/createStore.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/createWidgetsManager.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/highlight.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/indexUtils.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/metadata.tsx delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/translatable.js delete mode 100644 packages/react-instantsearch-core/src/core/__tests__/utils.js delete mode 100644 packages/react-instantsearch-core/src/core/context.ts delete mode 100644 packages/react-instantsearch-core/src/core/createConnector.tsx delete mode 100644 packages/react-instantsearch-core/src/core/createInstantSearchManager.d.ts delete mode 100644 packages/react-instantsearch-core/src/core/createInstantSearchManager.js delete mode 100644 packages/react-instantsearch-core/src/core/createStore.ts delete mode 100644 packages/react-instantsearch-core/src/core/createWidgetsManager.ts delete mode 100644 packages/react-instantsearch-core/src/core/highlight.js delete mode 100644 packages/react-instantsearch-core/src/core/indexUtils.js delete mode 100644 packages/react-instantsearch-core/src/core/metadata.ts delete mode 100644 packages/react-instantsearch-core/src/core/translatable.js delete mode 100644 packages/react-instantsearch-core/src/core/utils.ts delete mode 100644 packages/react-instantsearch-core/src/core/version.js rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/hooks/__tests__/useConnector.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/hooks/__tests__/useInstantSearch.test.tsx (91%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/hooks/useConnector.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/hooks/useInstantSearch.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/IndexContext.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/InstantSearchContext.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/InstantSearchSSRContext.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/IndexContext.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/InstantSearchContext.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/InstantSearchSSRContext.test.tsx (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/createSearchResults.test.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/dequal.test.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/invariant.test.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/useSearchResults.test.tsx (97%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/useSearchState.test.tsx (95%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/__tests__/warn.test.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/createSearchResults.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/dequal.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/getIndexSearchResults.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/invariant.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/noop.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useForceUpdate.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useIndex.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useIndexContext.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useInstantSearchApi.ts (97%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useInstantSearchContext.ts (84%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useInstantSearchSSRContext.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useInstantSearchServerContext.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useIsomorphicLayoutEffect.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useSearchResults.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useSearchState.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useStableValue.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/useWidget.ts (100%) rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/lib/warn.ts (100%) rename packages/{react-instantsearch-hooks-server/src => react-instantsearch-core/src/server}/__tests__/__snapshots__/getServerState.test.tsx.snap (100%) rename packages/{react-instantsearch-hooks-server/src => react-instantsearch-core/src/server}/__tests__/getServerState.test.tsx (98%) rename packages/{react-instantsearch-hooks-server/src => react-instantsearch-core/src/server}/getServerState.tsx (96%) rename packages/{react-instantsearch-hooks-server/src => react-instantsearch-core/src/server}/index.ts (100%) delete mode 100644 packages/react-instantsearch-core/src/types/algoliasearch.ts delete mode 100644 packages/react-instantsearch-core/src/types/index.ts delete mode 100644 packages/react-instantsearch-core/src/types/translatable.ts rename packages/{react-instantsearch-hooks => react-instantsearch-core}/src/version.ts (100%) delete mode 100644 packages/react-instantsearch-core/src/widgets/Configure.js delete mode 100644 packages/react-instantsearch-core/src/widgets/ConfigureRelatedItems.tsx delete mode 100644 packages/react-instantsearch-core/src/widgets/DynamicWidgets.tsx delete mode 100644 packages/react-instantsearch-core/src/widgets/Index.tsx delete mode 100644 packages/react-instantsearch-core/src/widgets/InstantSearch.tsx delete mode 100644 packages/react-instantsearch-core/src/widgets/QueryRuleContext.ts delete mode 100644 packages/react-instantsearch-core/src/widgets/__tests__/DynamicWidgets.test.tsx delete mode 100644 packages/react-instantsearch-core/src/widgets/__tests__/Index.js delete mode 100644 packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.integration.js delete mode 100644 packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.js create mode 100644 packages/react-instantsearch-core/test/module/is-cjs-module.cjs create mode 100644 packages/react-instantsearch-core/test/module/is-es-module.mjs rename packages/{react-instantsearch-hooks-server => react-instantsearch-core}/tsconfig.declaration.json (100%) delete mode 100644 packages/react-instantsearch-dom-maps/CHANGELOG.md delete mode 100644 packages/react-instantsearch-dom-maps/README.md delete mode 100644 packages/react-instantsearch-dom-maps/package.json delete mode 100644 packages/react-instantsearch-dom-maps/rollup.config.js delete mode 100755 packages/react-instantsearch-dom-maps/scripts/version.js delete mode 100644 packages/react-instantsearch-dom-maps/src/Connector.js delete mode 100644 packages/react-instantsearch-dom-maps/src/Control.js delete mode 100644 packages/react-instantsearch-dom-maps/src/CustomMarker.js delete mode 100644 packages/react-instantsearch-dom-maps/src/GeoSearch.js delete mode 100644 packages/react-instantsearch-dom-maps/src/GeoSearchContext.ts delete mode 100644 packages/react-instantsearch-dom-maps/src/GoogleMaps.js delete mode 100644 packages/react-instantsearch-dom-maps/src/GoogleMapsContext.ts delete mode 100644 packages/react-instantsearch-dom-maps/src/GoogleMapsLoader.js delete mode 100644 packages/react-instantsearch-dom-maps/src/Marker.js delete mode 100644 packages/react-instantsearch-dom-maps/src/Provider.js delete mode 100644 packages/react-instantsearch-dom-maps/src/Redo.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/Connector.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/Control.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/CustomMarker.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/GeoSearch.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/GoogleMaps.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.jsdom.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.node.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/Marker.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/Provider.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/Redo.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Control.js.snap delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/CustomMarker.js.snap delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GeoSearch.js.snap delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GoogleMaps.js.snap delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Redo.js.snap delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/utils.js delete mode 100644 packages/react-instantsearch-dom-maps/src/__tests__/withGoogleMaps.tsx delete mode 100644 packages/react-instantsearch-dom-maps/src/elements/__tests__/createHTMLMarker.js delete mode 100644 packages/react-instantsearch-dom-maps/src/elements/createHTMLMarker.js delete mode 100644 packages/react-instantsearch-dom-maps/src/index.js delete mode 100644 packages/react-instantsearch-dom-maps/src/propTypes.js delete mode 100644 packages/react-instantsearch-dom-maps/src/utils.js delete mode 100644 packages/react-instantsearch-dom-maps/src/withGoogleMaps.tsx delete mode 100644 packages/react-instantsearch-dom-maps/test/mockGoogleMaps.js delete mode 100644 packages/react-instantsearch-dom/.storybook/addons.ts delete mode 100644 packages/react-instantsearch-dom/.storybook/config.ts delete mode 100644 packages/react-instantsearch-dom/.storybook/postcss.config.js delete mode 100644 packages/react-instantsearch-dom/.storybook/preview-head.html delete mode 100644 packages/react-instantsearch-dom/.storybook/public/default.css delete mode 100644 packages/react-instantsearch-dom/.storybook/public/react-autosuggest.css delete mode 100644 packages/react-instantsearch-dom/.storybook/public/rheostat.css delete mode 100644 packages/react-instantsearch-dom/.storybook/public/util.css delete mode 100644 packages/react-instantsearch-dom/.storybook/webpack.config.js delete mode 100644 packages/react-instantsearch-dom/CHANGELOG.md delete mode 100644 packages/react-instantsearch-dom/README.md delete mode 100644 packages/react-instantsearch-dom/package.json delete mode 100644 packages/react-instantsearch-dom/rollup.config.js delete mode 100644 packages/react-instantsearch-dom/server.js delete mode 100644 packages/react-instantsearch-dom/src/components/Breadcrumb.js delete mode 100644 packages/react-instantsearch-dom/src/components/ClearRefinements.js delete mode 100644 packages/react-instantsearch-dom/src/components/CurrentRefinements.js delete mode 100644 packages/react-instantsearch-dom/src/components/HierarchicalMenu.js delete mode 100644 packages/react-instantsearch-dom/src/components/Highlight.js delete mode 100644 packages/react-instantsearch-dom/src/components/Highlighter.js delete mode 100644 packages/react-instantsearch-dom/src/components/Hits.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/HitsPerPage.js delete mode 100644 packages/react-instantsearch-dom/src/components/InfiniteHits.js delete mode 100644 packages/react-instantsearch-dom/src/components/Link.js delete mode 100644 packages/react-instantsearch-dom/src/components/LinkList.js delete mode 100644 packages/react-instantsearch-dom/src/components/List.js delete mode 100644 packages/react-instantsearch-dom/src/components/Menu.js delete mode 100644 packages/react-instantsearch-dom/src/components/MenuSelect.js delete mode 100644 packages/react-instantsearch-dom/src/components/NumericMenu.js delete mode 100644 packages/react-instantsearch-dom/src/components/Pagination.js delete mode 100644 packages/react-instantsearch-dom/src/components/Panel.js delete mode 100644 packages/react-instantsearch-dom/src/components/PanelCallbackHandler.d.ts delete mode 100644 packages/react-instantsearch-dom/src/components/PanelCallbackHandler.js delete mode 100644 packages/react-instantsearch-dom/src/components/PoweredBy.js delete mode 100644 packages/react-instantsearch-dom/src/components/QueryRuleCustomData.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/RangeInput.js delete mode 100644 packages/react-instantsearch-dom/src/components/RatingMenu.js delete mode 100644 packages/react-instantsearch-dom/src/components/RefinementList.js delete mode 100644 packages/react-instantsearch-dom/src/components/RelevantSort.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/ScrollTo.js delete mode 100644 packages/react-instantsearch-dom/src/components/SearchBox.js delete mode 100644 packages/react-instantsearch-dom/src/components/Select.js delete mode 100644 packages/react-instantsearch-dom/src/components/Snippet.js delete mode 100644 packages/react-instantsearch-dom/src/components/SortBy.js delete mode 100644 packages/react-instantsearch-dom/src/components/Stats.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/ToggleRefinement.js delete mode 100644 packages/react-instantsearch-dom/src/components/VoiceSearch.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Breadcrumb.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/ClearRefinements.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/CurrentRefinements.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/HierarchicalMenu.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Highlight.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Highlighter.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Hits.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/HitsPerPage.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/InfiniteHits.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/List.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Menu.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/MenuSelect.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/NumericMenu.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Pagination.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Panel.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/PanelCallbackHandler.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/PoweredBy.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/QueryRuleCustomData.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/RangeInput.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/RatingMenu.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/RefinementList.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/RelevantSort.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/ScrollTo.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/SearchBox.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Snippet.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/SortBy.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/Stats.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/ToggleRefinement.js delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/VoiceSearch.tsx delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Breadcrumb.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ClearRefinements.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/CurrentRefinements.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HierarchicalMenu.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlight.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlighter.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Hits.tsx.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HitsPerPage.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/InfiniteHits.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/List.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Menu.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/MenuSelect.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/NumericMenu.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Pagination.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Panel.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/PoweredBy.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/QueryRuleCustomData.tsx.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RangeInput.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RatingMenu.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RefinementList.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ScrollTo.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SearchBox.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Snippet.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SortBy.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ToggleRefinement.js.snap delete mode 100644 packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/VoiceSearch.tsx.snap delete mode 100644 packages/react-instantsearch-dom/src/components/index.js delete mode 100644 packages/react-instantsearch-dom/src/core/__tests__/createInstantSearchServer.js delete mode 100644 packages/react-instantsearch-dom/src/core/__tests__/getInsightsAnonymousUserToken.ts delete mode 100644 packages/react-instantsearch-dom/src/core/__tests__/utils.js delete mode 100644 packages/react-instantsearch-dom/src/core/createInstantSearchServer.js delete mode 100644 packages/react-instantsearch-dom/src/core/getInsightsAnonymousUserToken.ts delete mode 100644 packages/react-instantsearch-dom/src/core/utils.ts delete mode 100644 packages/react-instantsearch-dom/src/hooks/useAnswers.js delete mode 100644 packages/react-instantsearch-dom/src/index.js delete mode 100644 packages/react-instantsearch-dom/src/lib/createConcurrentSafePromise.js delete mode 100644 packages/react-instantsearch-dom/src/lib/debounce.ts delete mode 100644 packages/react-instantsearch-dom/src/lib/infiniteHitsCache/__tests__/sessionStorage.js delete mode 100644 packages/react-instantsearch-dom/src/lib/infiniteHitsCache/index.js delete mode 100644 packages/react-instantsearch-dom/src/lib/infiniteHitsCache/sessionStorage.js delete mode 100644 packages/react-instantsearch-dom/src/lib/voiceSearchHelper/__tests__/index.ts delete mode 100644 packages/react-instantsearch-dom/src/lib/voiceSearchHelper/index.ts delete mode 100644 packages/react-instantsearch-dom/src/server.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Answers.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Breadcrumb.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/ClearRefinements.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/CurrentRefinements.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/DynamicWidgets.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/HierarchicalMenu.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Highlight.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Hits.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/HitsPerPage.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/InfiniteHits.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Menu.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/MenuSelect.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/NumericMenu.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Pagination.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Panel.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/PoweredBy.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/QueryRuleCustomData.tsx delete mode 100644 packages/react-instantsearch-dom/src/widgets/RangeInput.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/RangeSlider.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/RatingMenu.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/RefinementList.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/RelevantSort.tsx delete mode 100644 packages/react-instantsearch-dom/src/widgets/ScrollTo.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/SearchBox.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Snippet.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/SortBy.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/Stats.ts delete mode 100644 packages/react-instantsearch-dom/src/widgets/ToggleRefinement.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/VoiceSearch.ts delete mode 100644 packages/react-instantsearch-dom/src/widgets/__tests__/Answers.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/__tests__/DynamicWidgets.test.js delete mode 100644 packages/react-instantsearch-dom/src/widgets/__tests__/InstantSearch.tsx delete mode 100644 packages/react-instantsearch-dom/stories/3rdPartyIntegrations.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Answers.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Breadcrumb.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/ClearRefinements.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Conditional.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Configure.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/ConfigureRelatedItems.stories.tsx delete mode 100644 packages/react-instantsearch-dom/stories/CurrentRefinements.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/DynamicWidgets.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/GeoSearch.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/HierarchicalMenu.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Highlight.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Hits.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/HitsPerPage.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/InfiniteHits.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/InstantSearch.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Menu.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/MenuSelect.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/MultiIndex.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/NumericMenu.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Pagination.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/PoweredBy.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/QueryRuleContext.stories.tsx delete mode 100644 packages/react-instantsearch-dom/stories/QueryRuleCustomData.stories.tsx delete mode 100644 packages/react-instantsearch-dom/stories/RangeInput.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/RangeSlider.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/RatingMenu.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/RefinementList.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/RefreshCache.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/RelevantSort.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/ScrollTo.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/SearchBox.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Snippet.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/SortBy.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/Stats.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/ToggleRefinement.stories.js delete mode 100644 packages/react-instantsearch-dom/stories/VoiceSearch.stories.tsx delete mode 100644 packages/react-instantsearch-dom/stories/places/connector.js delete mode 100644 packages/react-instantsearch-dom/stories/places/index.js delete mode 100644 packages/react-instantsearch-dom/stories/util.d.ts delete mode 100644 packages/react-instantsearch-dom/stories/util.js delete mode 100644 packages/react-instantsearch-hooks-server/CHANGELOG.md delete mode 100644 packages/react-instantsearch-hooks-server/README.md delete mode 100644 packages/react-instantsearch-hooks-server/package.json delete mode 100644 packages/react-instantsearch-hooks-server/test/module/is-cjs-module.cjs delete mode 100644 packages/react-instantsearch-hooks-server/test/module/is-es-module.mjs delete mode 100644 packages/react-instantsearch-hooks-web/CHANGELOG.md delete mode 100644 packages/react-instantsearch-hooks-web/README.md delete mode 100644 packages/react-instantsearch-hooks-web/package.json delete mode 100644 packages/react-instantsearch-hooks-web/rollup.config.js delete mode 100644 packages/react-instantsearch-hooks-web/src/index.ts delete mode 100644 packages/react-instantsearch-hooks-web/test/module/is-cjs-module.cjs delete mode 100644 packages/react-instantsearch-hooks-web/test/module/is-es-module.mjs delete mode 100644 packages/react-instantsearch-hooks/CHANGELOG.md delete mode 100644 packages/react-instantsearch-hooks/README.md delete mode 100644 packages/react-instantsearch-hooks/global.d.ts delete mode 100644 packages/react-instantsearch-hooks/package.json delete mode 100644 packages/react-instantsearch-hooks/rollup.config.js delete mode 100644 packages/react-instantsearch-hooks/src/__tests__/index.test.ts delete mode 100644 packages/react-instantsearch-hooks/src/index.ts delete mode 100644 packages/react-instantsearch-hooks/test/module/is-cjs-module.cjs delete mode 100644 packages/react-instantsearch-hooks/test/module/is-es-module.mjs delete mode 100644 packages/react-instantsearch-hooks/tsconfig.declaration.json delete mode 100644 packages/react-instantsearch-native/CHANGELOG.md delete mode 100644 packages/react-instantsearch-native/README.md delete mode 100644 packages/react-instantsearch-native/package.json delete mode 100644 packages/react-instantsearch-native/src/index.ts rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/CHANGELOG.md (100%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/README.md (82%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/__tests__/e2e/backButton.test.ts (100%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/__tests__/e2e/nextLink.test.ts (100%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/__tests__/e2e/utils.ts (100%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/__tests__/module/is-cjs-module.cjs (65%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/__tests__/module/is-es-module.mjs (64%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/e2e.d.ts (100%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/package.json (86%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/src/index.ts (98%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/tsconfig.declaration.json (100%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/tsconfig.wdio.json (100%) rename packages/{react-instantsearch-hooks-router-nextjs => react-instantsearch-router-nextjs}/wdio.conf.cjs (100%) delete mode 100644 packages/react-instantsearch/connectors.js delete mode 100644 packages/react-instantsearch/dom.js rename packages/{react-instantsearch-hooks-web => react-instantsearch}/global.d.ts (100%) delete mode 100644 packages/react-instantsearch/index.js delete mode 100644 packages/react-instantsearch/native.js delete mode 100644 packages/react-instantsearch/server.js rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/__tests__/common-connectors.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/__tests__/common-shared.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/__tests__/common-widgets.test.tsx (98%) create mode 100644 packages/react-instantsearch/src/index.ts rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/types/PartialKeys.ts (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/types/Translatable.ts (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/types/index.ts (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/Breadcrumb.tsx (98%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/ClearRefinements.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/CurrentRefinements.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/HierarchicalMenu.tsx (98%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/Highlight.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/Hits.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/HitsPerPage.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/InfiniteHits.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/InternalHighlight.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/Menu.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/Pagination.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/PoweredBy.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/RangeInput.tsx (98%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/RefinementList.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/SearchBox.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/ShowMoreButton.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/Snippet.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/SortBy.tsx (95%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/Stats.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/ToggleRefinement.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/Breadcrumb.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/ClearRefinements.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/CurrentRefinements.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/HierarchicalMenu.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/Hits.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/HitsPerPage.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/InfiniteHits.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/InternalHighlight.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/Menu.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/Pagination.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/PoweredBy.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/RangeInput.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/RefinementList.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/SearchBox.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/ShowMoreButton.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/SortBy.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/Stats.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/__tests__/ToggleRefinement.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/lib/__tests__/isModifierClick.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/lib/cx.ts (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/ui/lib/isModifierClick.ts (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/Breadcrumb.tsx (88%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/ClearRefinements.tsx (93%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/CurrentRefinements.tsx (92%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/HierarchicalMenu.tsx (95%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/Highlight.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/Hits.tsx (87%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/HitsPerPage.tsx (87%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/InfiniteHits.tsx (93%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/Menu.tsx (91%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/Pagination.tsx (93%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/PoweredBy.tsx (89%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/RangeInput.tsx (89%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/RefinementList.tsx (98%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/SearchBox.tsx (94%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/Snippet.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/SortBy.tsx (85%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/Stats.tsx (91%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/ToggleRefinement.tsx (93%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/Breadcrumb.test.tsx (86%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/ClearRefinements.test.tsx (93%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/CurrentRefinements.test.tsx (95%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/HierarchicalMenu.test.tsx (91%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/Highlight.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/Hits.test.tsx (90%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/HitsPerPage.test.tsx (89%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/InfiniteHits.test.tsx (92%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/Menu.test.tsx (94%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/Pagination.test.tsx (97%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/PoweredBy.test.tsx (96%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/RangeInput.test.tsx (89%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/RefinementList.test.tsx (95%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/SearchBox.test.tsx (91%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/Snippet.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/SortBy.test.tsx (92%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/Stats.test.tsx (86%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/ToggleRefinement.test.tsx (89%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/__utils__/all-widgets.tsx (99%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/all-components.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/__tests__/all-widgets.test.tsx (100%) rename packages/{react-instantsearch-hooks-web => react-instantsearch}/src/widgets/index.ts (100%) create mode 100644 packages/react-instantsearch/test/module/is-cjs-module.cjs create mode 100644 packages/react-instantsearch/test/module/is-es-module.mjs rename packages/{react-instantsearch-hooks-web => react-instantsearch}/tsconfig.declaration.json (100%) delete mode 100644 scripts/website/README.md delete mode 100644 scripts/website/package.json delete mode 100644 scripts/website/webpack.config.js rename tests/utils/{InstantSearchHooksTestWrapper.tsx => InstantSearchTestWrapper.tsx} (73%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6d691087dd..dc3b6382ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -150,13 +150,7 @@ jobs: - packages/instantsearch.js/dist - packages/react-instantsearch/dist - packages/react-instantsearch-core/dist - - packages/react-instantsearch-dom/dist - - packages/react-instantsearch-dom-maps/dist - - packages/react-instantsearch-native/dist - - packages/react-instantsearch-hooks/dist - - packages/react-instantsearch-hooks-web/dist - - packages/react-instantsearch-hooks-router-nextjs/dist - - packages/react-instantsearch-hooks-server/dist + - packages/react-instantsearch-router-nextjs/dist - packages/vue-instantsearch/vue2 - packages/vue-instantsearch/vue3 diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 63bdb294e1..03ffbfc08e 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -2,17 +2,14 @@ "sandboxes": [ "instantsearchjs-es-template-pcw1k", "github/algolia/instantsearch.js/tree/templates/react-instantsearch", - "/examples/react-hooks/default-theme", + "/examples/react/default-theme", "/examples/vue/default-theme" ], "buildCommand": "build --no-private --ignore *-maps --ignore *-native", "packages": [ "packages/instantsearch.js", + "packages/react-instantsearch", "packages/react-instantsearch-core", - "packages/react-instantsearch-dom", - "packages/react-instantsearch-hooks", - "packages/react-instantsearch-hooks-web", - "packages/react-instantsearch-hooks-server", "packages/vue-instantsearch", "packages/instantsearch.css" ], diff --git a/.eslintignore b/.eslintignore index 73aed5b1bd..5c6e52ee58 100644 --- a/.eslintignore +++ b/.eslintignore @@ -21,9 +21,9 @@ coverage # Non-lintable source files ## React-Native TypeScript wasn't supporting the mix of react 18 and 17 -examples/react-hooks/react-native +examples/react/react-native ## Excluded from global typescript config -examples/react-hooks/next/next-env.d.ts +examples/react/next/next-env.d.ts ## templates that don't get installed packages/create-instantsearch-app/src/templates ## test fixtures for codemods diff --git a/.eslintrc.js b/.eslintrc.js index b2faaa6001..07617403a0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -122,14 +122,7 @@ const config = { caseInsensitive: true, }, 'newlines-between': 'always', - groups: [ - 'builtin', - 'external', - 'parent', - 'sibling', - 'index', - 'type', - ], + groups: ['builtin', 'external', 'parent', 'sibling', 'index', 'type'], pathGroups: [ { pattern: '@/**/*', @@ -200,8 +193,8 @@ const config = { }, { files: [ - 'packages/react-instantsearch-hooks/**/*', - 'packages/react-instantsearch-hooks-*/**/*', + 'packages/react-instantsearch/**/*', + 'packages/react-instantsearch-*/**/*', ], rules: { '@typescript-eslint/consistent-type-assertions': 'off', @@ -250,25 +243,6 @@ const config = { }, }, // Disable stricter rules introduced for the next versions of the libraries. - { - files: [ - 'packages/react-instantsearch-core/**/*', - 'packages/react-instantsearch-dom/**/*', - ], - rules: { - '@typescript-eslint/consistent-type-assertions': 'off', - '@typescript-eslint/ban-types': 'off', - }, - }, - { - files: [ - 'examples/react-hooks/react-native/**/*.ts', - 'examples/react-hooks/react-native/**/*.tsx', - ], - parserOptions: { - project: 'examples/react-hooks/react-native/tsconfig.json', - }, - }, { files: [ 'packages/instantsearch.js/src/**/*.ts', @@ -373,12 +347,9 @@ const config = { }, }, { - files: [ - 'packages/react-instantsearch-hooks-router-nextjs/__tests__/e2e/**/*', - ], + files: ['packages/react-instantsearch-router-nextjs/__tests__/e2e/**/*'], parserOptions: { - project: - 'packages/react-instantsearch-hooks-router-nextjs/tsconfig.json', + project: 'packages/react-instantsearch-router-nextjs/tsconfig.json', }, }, { diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml index f0f7083648..5a818ea6e2 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -36,7 +36,6 @@ body: Sandboxes InstantSearch.js React InstantSearch - React InstantSearch Hooks Vue InstantSearch Angular InstantSearch diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 45823c294f..a3c25ce9ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,15 +127,8 @@ Here are the main files and folders of the project. ``` ▸ examples/ << Examples, grouped per flavor ▸ packages/ << Packages of the project - ▸ react-instantsearch/ << Bundled React InstantSearch library - ▸ react-instantsearch-core/ << Runtime-independent React InstantSearch version - ▸ react-instantsearch-dom/ << DOM-specific React InstantSearch version - ▸ react-instantsearch-native/ << React Native-specific InstantSearch version - ▸ react-instantsearch-dom-maps/ << DOM-specific React InstantSearch version with Google Maps - - ▸ react-instantsearch-hooks/ << React InstantSearch Hooks library - ▸ react-instantsearch-hooks-web/ << DOM-specific React InstantSearch Hooks version - ▸ react-instantsearch-hooks-server/ << Server-specific React InstantSearch Hooks version + ▸ react-instantsearch/ << Bundled React InstantSearch library + ▸ react-instantsearch-core/ << Runtime-independent React InstantSearch version ▸ instantsearch.js/ << The InstantSearch.js library ▸ tests/ << The test utilites @@ -163,7 +156,7 @@ This monorepo has as goal to be used for all InstantSearch flavors and tools. To 8. replace commit messages which refer to issues/PRs with #xxx by also referencing the original repo: `git filter-branch --msg-filter 'sed -E "s/(#[[:digit:]]+)/algolia\/myproject\1/"' master..feat/import-myproject` 9. make any changes necessary to make the project work in the monorepo and commit those 10. make a pull request and _merge using rebase or merge_ (if you merge using squash the history will be lost) -11. manually tag and push the latest release commit from the imported project to let shipjs know which version to take into account during the next release process +11. manually tag and push the latest release commit from the imported project to let shipjs know which version to take into account during the next release process ## Tests @@ -171,7 +164,7 @@ The general philosophy of testing in InstantSearch follows [Testing Library's gu >The more your tests resemble the way your software is used, the more confidence they can give you. -We rely on [Jest](https://jestjs.io/) for unit tests on all flavors of InstantSearch. In addition, [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) is used to test interactions in React InstantSearch Hooks. +We rely on [Jest](https://jestjs.io/) for unit tests on all flavors of InstantSearch. In addition, [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) is used to test interactions in React InstantSearch. ### How to write a test diff --git a/README.md b/README.md index d1c9d6c55f..b5ec2ccf84 100644 --- a/README.md +++ b/README.md @@ -44,14 +44,8 @@ It is part of the InstantSearch family which is designed for different platforms | [`create-instantsearch-app`](packages/create-instantsearch-app) | [![create-instantsearch-app npm version](https://img.shields.io/npm/v/create-instantsearch-app.svg?style=flat-square)](https://npmjs.org/package/create-instantsearch-app) | Command-line utility to quickly bootstrap a project with InstantSearch | | [`instantsearch.css`](packages/instantsearch.css) | [![instantsearch.css npm version](https://img.shields.io/npm/v/instantsearch.css.svg?style=flat-square)](https://npmjs.org/package/instantsearch.css) | Default CSS themes for InstantSearch | | [`instantsearch.js`](packages/instantsearch.js) | [![instantsearch.js npm version](https://img.shields.io/npm/v/instantsearch.js.svg?style=flat-square)](https://npmjs.org/package/instantsearch.js) | InstantSearch.js | -| [`react-instantsearch`](packages/react-instantsearch) | [![react-instantsearch npm version](https://img.shields.io/npm/v/react-instantsearch.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch)| React InstantSearch (prefer using `react-instantsearch-hooks-web` instead) | -| [`react-instantsearch-core`](packages/react-instantsearch-core) | [![react-instantsearch-core npm version](https://img.shields.io/npm/v/react-instantsearch-core.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch-core) | React InstantSearch Core (prefer using `react-instantsearch-hooks` instead) | -| [`react-instantsearch-dom`](packages/react-instantsearch-dom) | [![react-instantsearch-dom npm version](https://img.shields.io/npm/v/react-instantsearch-dom.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch-dom) | React InstantSearch DOM (prefer using `react-instantsearch-hooks-web` instead) | -| [`react-instantsearch-dom-maps`](packages/react-instantsearch-dom-maps) | [![react-instantsearch-dom-maps npm version](https://img.shields.io/npm/v/react-instantsearch-dom-maps.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch-dom-maps)| GeoSearch widget for React InstantSearch DOM | -| [`react-instantsearch-hooks`](packages/react-instantsearch-hooks) | [![react-instantsearch-hooks npm version](https://img.shields.io/npm/v/react-instantsearch-hooks?style=flat-square)](https://npmjs.org/package/react-instantsearch-hooks) | React InstantSearch Hooks | -| [`react-instantsearch-hooks-server`](packages/react-instantsearch-hooks-server) | [![react-instantsearch-hooks-server npm version](https://img.shields.io/npm/v/react-instantsearch-hooks-server.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch-hooks-server) | Utilities to do server-side rendering with React InstantSearch Hooks | -| [`react-instantsearch-hooks-web`](packages/react-instantsearch-hooks-web) | [![react-instantsearch-hooks-web npm version](https://img.shields.io/npm/v/react-instantsearch-hooks-web.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch-hooks-web) | React InstantSearch Hooks bundled with UI components | -| [`react-instantsearch-native`](packages/react-instantsearch-native) | [![react-instantsearch-native npm version](https://img.shields.io/npm/v/react-instantsearch-native.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch-native) | React InstantSearch Native (prefer using `react-instantsearch-hooks` instead) | +| [`react-instantsearch`](packages/react-instantsearch) | [![react-instantsearch npm version](https://img.shields.io/npm/v/react-instantsearch.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch)| React InstantSearch bundled library | +| [`react-instantsearch-core`](packages/react-instantsearch-core) | [![react-instantsearch-core npm version](https://img.shields.io/npm/v/react-instantsearch-core.svg?style=flat-square)](https://npmjs.org/package/react-instantsearch-core) | Runtime-independent React InstantSearch version | | [`vue-instantsearch`](packages/vue-instantsearch) | [![vue-instantsearch npm version](https://img.shields.io/npm/v/vue-instantsearch.svg?style=flat-square)](https://npmjs.org/package/vue-instantsearch) | Vue InstantSearch | ## Contributing @@ -68,7 +62,7 @@ To start contributing to code, you need to: 1. [Fork the project](https://help.github.com/articles/fork-a-repo/) 1. [Clone the repository](https://help.github.com/articles/cloning-a-repository/) 1. Install the dependencies: `yarn` -1. [Pick a package to work on](#packages) and cd into it (e.g. `cd packages/react-instantsearch-hooks`) +1. [Pick a package to work on](#packages) and cd into it (e.g. `cd packages/react-instantsearch`) Please read [our contribution process](CONTRIBUTING.md) to learn more. @@ -87,7 +81,7 @@ InstantSearch is [MIT licensed][license-url]. [algolia-website]: https://www.algolia.com/?utm_source=instantsearch.js&utm_campaign=repository "Algolia's website" [instantsearch-docs]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/?utm_source=instantsearch.js&utm_campaign=repository "InstantSearch.js documentation" -[react-instantsearch-docs]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/?utm_source=instantsearch.js&utm_campaign=repository "React InstantSearch documentation" +[react-instantsearch-docs]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/?utm_source=instantsearch.js&utm_campaign=repository "React InstantSearch documentation" [vue-instantsearch-docs]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/vue/?utm_source=instantsearch.js&utm_campaign=repository "Vue InstantSearch documentation" [angular-instantsearch-docs]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/angular/?utm_source=instantsearch.js&utm_campaign=repository "Angular InstantSearch documentation" [instantsearch-android-github]: https://github.com/algolia/instantsearch-android diff --git a/babel.config.js b/babel.config.js index c07aace4b9..114bb8d518 100644 --- a/babel.config.js +++ b/babel.config.js @@ -119,13 +119,6 @@ module.exports = (api) => { ], ], }, - { - test: 'packages/react-instantsearch-dom-maps', - plugins: clean([ - '@babel/plugin-syntax-dynamic-import', - !isRollup && 'babel-plugin-dynamic-import-node', - ]), - }, ], // jsx is transpiled, so the comment should no longer be present in the final files shouldPrintComment: (value) => value !== '* @jsx h ', diff --git a/bundlesize.config.json b/bundlesize.config.json index 4b8dfb8540..8d83809581 100644 --- a/bundlesize.config.json +++ b/bundlesize.config.json @@ -16,38 +16,14 @@ "path": "./packages/instantsearch.js/dist/instantsearch.development.js", "maxSize": "165 kB" }, - { - "path": "packages/react-instantsearch/dist/umd/Core.min.js", - "maxSize": "3.50 kB" - }, - { - "path": "packages/react-instantsearch/dist/umd/Connectors.min.js", - "maxSize": "26 kB" - }, - { - "path": "packages/react-instantsearch/dist/umd/Dom.min.js", - "maxSize": "44.50 kB" - }, { "path": "packages/react-instantsearch-core/dist/umd/ReactInstantSearchCore.min.js", - "maxSize": "29.50 kB" - }, - { - "path": "packages/react-instantsearch-hooks/dist/umd/ReactInstantSearchHooks.min.js", "maxSize": "46.25 kB" }, { - "path": "packages/react-instantsearch-hooks-web/dist/umd/ReactInstantSearchHooksDOM.min.js", + "path": "packages/react-instantsearch/dist/umd/ReactInstantSearch.min.js", "maxSize": "56 kB" }, - { - "path": "packages/react-instantsearch-dom/dist/umd/ReactInstantSearchDOM.min.js", - "maxSize": "44.75 kB" - }, - { - "path": "packages/react-instantsearch-dom-maps/dist/umd/ReactInstantSearchDOMMaps.min.js", - "maxSize": "7.25 kB" - }, { "path": "packages/vue-instantsearch/vue2/umd/index.js", "maxSize": "63 kB" diff --git a/examples/react-hooks/default-theme/index.html b/examples/react-hooks/default-theme/index.html deleted file mode 100644 index 536b79cc4e..0000000000 --- a/examples/react-hooks/default-theme/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - React InstantSearch - Hooks - - - - - -
- - - - - diff --git a/examples/react-hooks/default-theme/package.json b/examples/react-hooks/default-theme/package.json deleted file mode 100644 index 44055a11b4..0000000000 --- a/examples/react-hooks/default-theme/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "example-react-instantsearch-hooks-default-theme", - "version": "6.50.0", - "private": true, - "scripts": { - "build": "BABEL_ENV=parcel parcel build index.html", - "start": "BABEL_ENV=parcel parcel index.html" - }, - "dependencies": { - "algoliasearch": "4.14.3", - "instantsearch.js": "4.56.8", - "react": "18.1.0", - "react-dom": "18.1.0", - "react-instantsearch-hooks-web": "6.47.3" - }, - "devDependencies": { - "@parcel/core": "2.8.0", - "@parcel/packager-raw-url": "2.8.0", - "@parcel/transformer-webmanifest": "2.8.0", - "parcel": "2.8.0", - "typescript": "5.1.3" - } -} diff --git a/examples/react-hooks/e-commerce/App.css b/examples/react-hooks/e-commerce/App.css deleted file mode 100644 index b426e86e8e..0000000000 --- a/examples/react-hooks/e-commerce/App.css +++ /dev/null @@ -1,341 +0,0 @@ -* { - box-sizing: border-box; -} - -body, -h1, -h2 { - margin: 0; - padding: 0; -} - -body { - font-family: Open Sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, - Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', - 'Segoe UI Symbol'; - -webkit-font-smoothing: antialiased; -} - -h2 { - color: #21243d; - font-family: Hind, sans-serif; - font-size: 1.5rem; - font-weight: bold; -} - -/* Header */ - -.header { - align-items: center; - background: linear-gradient(to bottom, #fbc300, #c98a00); - background-image: url('./assets/cover.png'); - background-image: url('./assets/cover.png'), - linear-gradient(to bottom, #fbc300, #c98a00); - background-position: center; - background-size: cover; - color: #fff; - display: flex; - flex-direction: column; - justify-content: center; - min-height: 368px; - padding: 0.5rem 1rem; - text-align: center; -} - -.header-logo { - margin: 0; -} - -.header-logo svg { - height: 24px; - width: 92px; -} - -.header-title { - font-size: 38px; - font-weight: 300; -} - -/* Containers */ - -.container { - display: flex; - margin: 0 auto; - max-width: 1300px; - padding: 2rem 1rem; -} - -.container-filters { - flex: 1; - margin-right: 60px; - max-width: 260px; -} - -.container-header { - align-items: center; - display: flex; - justify-content: space-between; - min-height: 80px; -} - -.container-results { - flex: 3; -} - -.container-options { - border-bottom: 1px solid #ebecf3; - display: flex; - justify-content: flex-end; - margin-bottom: 30px; - padding: 30px 0; -} - -.container-options .container-option:not(:first-child) { - margin-left: 48px; -} - -.container-options select { - min-width: 100px; -} - -.container-footer { - margin: 4rem 0; -} - -/* Styles the SFFV highlightings */ - -em { - font-style: normal; -} - -em, -mark { - background: rgba(226, 164, 0, 0.4); -} - -/* Clear refinements container */ - -.clear-filters { - align-items: center; - display: flex; -} - -.clear-filters svg { - margin-right: 8px; -} - -/* Panel */ - -.container-body .ais-Panel { - border-top: 1px solid #ebecf3; - padding-bottom: 2rem; - padding-top: 2rem; -} - -.ais-Panel-header { - font-family: Hind, sans-serif; -} - -/* Search box */ - -.header .ais-SearchBox { - height: 64px; - width: 740px; -} - -.header .ais-SearchBox .ais-SearchBox-input { - background-color: #fff; - border-radius: 8px; - box-shadow: 0 4px 48px 0 rgba(0, 0, 0, 0.2); - font-family: Hind, sans-serif; - height: 64px; - /* - The "Hind" font family is vertically off-balance. - Adding 4px of padding top makes it more vertically aligned. - */ - padding: 4px 48px 0 64px; -} - -.header .ais-SearchBox-submit { - padding: 0 1rem 0 2rem; - width: 64px; -} - -.header .ais-SearchBox .ais-SearchBox-input::placeholder { - color: rgba(33, 36, 61, 0.24); - opacity: 1; /* Firefox */ -} - -.ais-SearchBox-input:-ms-input-placeholder { - color: rgba(33, 36, 61, 0.24); -} - -.ais-SearchBox-input::-ms-input-placeholder { - color: rgba(33, 36, 61, 0.24); -} - -.ais-SearchBox-submit { - color: #e2a400; -} - -.ais-RefinementList .ais-SearchBox-input { - font-family: Hind, sans-serif; - /* - The "Hind" font family is vertically off-balance. - Adding some padding top makes it more vertically aligned. - */ - padding-top: 2px; -} - -/* Hits */ - -.hit { - color: #21243d; - font-size: 14px; - line-height: 18px; -} - -.hit h1 { - font-size: 14px; -} - -.hit-category { - color: #21243d; - font-size: 12px; - font-weight: 600; - line-height: 1; - margin-bottom: 8px; - opacity: 0.7; - text-transform: uppercase; -} - -.hit-description { - margin-top: 2px; -} - -.hit-info-container { - overflow-wrap: break-word; - word-break: break-word; -} - -.hit-image-container { - align-items: center; - display: flex; - height: 174px; - justify-content: center; - margin: auto; - width: 174px; -} - -.hit-image { - height: auto; - max-height: 100%; - max-width: 100%; -} - -.hit-em { - color: #e2a400; - font-size: 11px; - font-weight: 600; -} - -.hit-rating { - border: 1px solid rgba(226, 164, 0, 0.5); - border-radius: 4px; - margin-left: 4px; - padding: 0 4px; -} - -.hits-empty-state { - align-items: center; - display: flex; - flex-direction: column; - margin: auto; - max-width: 300px; -} - -.hits-empty-state-title { - font-family: Hind; - font-size: 1.5rem; - font-weight: bold; - margin-bottom: 0; - text-align: center; -} - -.hits-empty-state-description { - color: rgba(35, 37, 51, 0.6); - font-size: 0.875rem; - text-align: center; -} - -.hits-empty-state .ais-ClearRefinements { - margin-top: 1rem; -} - -.hits-empty-state .ais-ClearRefinements-button--disabled { - display: none; -} - -.hits-empty-state .ais-ClearRefinements-button { - background: rgba(10, 8, 41, 0.04); - border-radius: 3px; - color: #21243d; - min-height: 48px; - padding: 16px 24px; -} - -/* ToggleRefinement */ - -.ais-ToggleRefinement-label { - display: flex; - flex-direction: row-reverse; - justify-content: space-between; -} - -.ais-ToggleRefinement-checkbox { - font: inherit; - margin-left: 1rem; - margin-right: 0; - position: relative; -} - -.ais-ToggleRefinement-checkbox:checked::before { - color: #e2a400; -} - -.ais-ToggleRefinement-checkbox::before { - align-items: center; - color: rgba(33, 36, 61, 0.32); - content: 'No'; - display: flex; - font-size: 0.8rem; - height: 16px; - position: absolute; - right: 38px; -} - -.ais-ToggleRefinement-checkbox:checked::before { - content: 'Yes'; -} - -.ais-ToggleRefinement-count { - display: none; -} - -/* RatingMenu */ - -.ais-RatingMenu-item:not(.ais-RatingMenu-item--selected) { - opacity: 0.5; -} - -.ais-RatingMenu-starIcon { - margin-right: 0.5rem; -} - -/* Hide all mobile-specific design on desktop */ - -@media (min-width: 900px) { - [data-layout='mobile'] { - display: none; - } -} diff --git a/examples/react-hooks/e-commerce/App.mobile.css b/examples/react-hooks/e-commerce/App.mobile.css deleted file mode 100644 index 339c08cbc7..0000000000 --- a/examples/react-hooks/e-commerce/App.mobile.css +++ /dev/null @@ -1,264 +0,0 @@ -@media (max-width: 899px) { - /* Filters overlay */ - - .container-filters { - background: #fff; - border-radius: 16px; - left: 0; - max-width: initial; - padding-bottom: 4rem; - position: fixed; - top: 0; - transform: translateY(120vh); - transition: transform 300ms cubic-bezier(0.465, 0.183, 0.153, 0.946); - width: 100%; - will-change: transform; - z-index: 1; - } - - .container-filters .container-header, - .container-filters .container-body { - padding: 2rem 2rem 0 2rem; - } - - .filtering .header { - /* - Closing panel on outter click didn't work on mobile safari. - This is one of the workarounds from the following: - https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#Safari_Mobile - */ - cursor: pointer; - } - - .filtering .header-logo { - left: 50%; - pointer-events: none; - position: absolute; - top: 1.5rem; - transform: translateX(-50%); - } - - .filtering .header-title, - .filtering .container-results, - .container-filters-footer { - display: none; - } - - .filtering .container-filters { - position: absolute; - transform: translateY(4rem); - } - - .filtering .container-filters-footer { - background-color: #fff; - border-top: 1px solid #ebecf3; - bottom: 0; - display: flex; - justify-content: space-between; - margin: 0 -2rem; - padding: 1rem; - position: fixed; - width: 100%; - z-index: 5; /* avoid collision with slider UI */ - } - - .container-filters-footer-button-wrapper { - width: calc(50% - 0.5rem); - } - - .container-filters-footer .ais-ClearRefinements-button, - .container-filters-footer .button { - background-color: rgba(65, 66, 71, 0.08); - border: none; - border-radius: 8px; - cursor: pointer; - display: flex; - font: inherit; - font-size: 0.75rem; - font-weight: bold; - justify-content: center; - padding: 12px; - text-align: center; - width: 100%; - } - - .container-filters-footer .button-primary { - background-color: #e2a400; - color: #fff; - } - - /* Filters button that triggers the overlay */ - - .filtering .filters-button { - display: none; - } - - .filters-button { - align-items: center; - background-color: #e2a400; - border: none; - border-radius: 8px; - bottom: 2rem; - box-shadow: 0 4px 22px 0 rgba(185, 135, 0, 0.5); - color: #fff; - cursor: pointer; - display: flex; - font: inherit; - font-size: 0.875rem; - font-weight: bold; - justify-content: center; - left: 50%; - min-height: 40px; - min-width: 112px; - position: fixed; - transform: translateX(-50%); - } - - .filters-button svg { - height: 14px; - margin-right: 8px; - width: 16px; - } - - .container { - padding: 4rem 2rem; - } - - .container-options { - display: none; - } - - .header { - background: linear-gradient(to bottom, #ae8600, #885b01); - background-image: url('./assets/cover-mobile.png'); - background-image: url('./assets/cover-mobile.png'), - linear-gradient(to bottom, #ae8600, #885b01); - background-position: bottom; - background-size: cover; - min-height: 300px; - position: relative; - transition: transform 200ms ease-out; - } - - /* SearchBox */ - - .header .ais-SearchBox { - bottom: 0; - left: 0; - position: absolute; - transform: translateY(50%); - width: 100vw; - } - - .header .ais-SearchBox .ais-SearchBox-form { - margin: auto; - max-width: 90%; - } - - .ais-SearchBox .ais-SearchBox-input, - .ais-RefinementList .ais-SearchBox-input { - font-size: 1rem; - } - - .ais-RefinementList .ais-SearchBox-input { - min-height: 48px; - } - - /* RefinementList */ - - .ais-RefinementList-list { - display: grid; - grid-auto-flow: column; - grid-gap: 0 2rem; - grid-template-rows: repeat(5, 1fr); - } - - .ais-RefinementList-item { - flex: 50%; - } - - .ais-RefinementList-checkbox { - height: 1.5rem; - min-width: 1.5rem; - } - - .ais-RefinementList-item--selected .ais-RefinementList-checkbox::after { - align-items: center; - background: none; - content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='12' height='9'%3E%3Cdefs%3E%3Cpath id='a' d='M0 0h24v24H0z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(-6 -8)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cpath fill='%23fff' fill-rule='nonzero' d='M16.5 8.5L18 10l-6.99 7-4.51-4.5L8 11l3.01 3z' mask='url(%23b)'/%3E%3C/g%3E%3C/svg%3E"); - display: flex; - height: 100%; - justify-content: center; - left: initial; - position: relative; - top: initial; - transform: initial; - width: initial; - } - - /* HierarchicalMenu */ - - .ais-HierarchicalMenu-link::before { - background-color: rgba(65, 66, 71, 0.08); - border-radius: 50%; - padding: 8px; - } - - /* ToggleRefinement */ - - .ais-ToggleRefinement-checkbox { - min-width: 47px; - position: relative; - } - - .ais-ToggleRefinement-checkbox { - margin-left: 2rem; - } - - .ais-ToggleRefinement-checkbox::after { - height: 1.5rem; - top: -4px; - width: 1.5rem; - } - - .ais-ToggleRefinement-checkbox::before { - right: 54px; - } - - /* RatingMenu */ - - .ais-RatingMenu-starIcon { - height: 1.5rem; - margin-right: 1rem; - width: 1.5rem; - } - - /* Hits */ - - .ais-Hits-list { - grid-gap: 1rem; - } - - .hit { - display: flex; - } - - .hit-image-container { - flex: 1; - margin-right: 2rem; - } - - .hit-info-container { - flex: 2; - } - - .hits-empty-state-image { - display: none; - } - - /* Hide all desktop-specific design on mobile */ - - [data-layout='desktop'] { - display: none; - } -} diff --git a/examples/react-hooks/e-commerce/Theme.css b/examples/react-hooks/e-commerce/Theme.css deleted file mode 100644 index 263d67f06d..0000000000 --- a/examples/react-hooks/e-commerce/Theme.css +++ /dev/null @@ -1,544 +0,0 @@ -/* Global */ - -[class^='ais-'] { - box-sizing: border-box; - font-size: 0.9rem; -} - -a[class^='ais-'] { - color: #21243d; - text-decoration: none; -} - -/* - We need to target the root element because Angular InstantSearch - creates web components which are not targetable with the `*` selector. -*/ -[class^='ais-'][class$='--disabled'], -/* - We need to target all elements for widgets containing - multiple sub elements (e.g. RangeSlider) -*/ -[class^='ais-'][class$='--disabled'] * { - cursor: not-allowed; -} - -.ais-Breadcrumb, -.ais-ClearRefinements, -.ais-CurrentRefinements, -.ais-ClearRefinements-button, -.ais-GeoSearch, -.ais-HierarchicalMenu, -.ais-Hits, -.ais-Results, -.ais-HitsPerPage, -.ais-ResultsPerPage, -.ais-InfiniteHits, -.ais-InfiniteResults, -.ais-Menu, -.ais-MenuSelect, -.ais-NumericMenu, -.ais-NumericSelector, -.ais-Pagination, -.ais-Panel, -.ais-PoweredBy, -.ais-RangeInput, -.ais-RangeSlider, -.ais-RatingMenu, -.ais-RefinementList, -.ais-SearchBox, -.ais-SortBy, -.ais-SortBy-select, -.ais-HitsPerPage-select, -.ais-Stats, -.ais-ToggleRefinement { - color: #21243d; - font-size: 0.75rem; -} - -/* Highlighting */ - -.ais-Highlight-highlighted, -.ais-Snippet-highlighted { - background: rgba(226, 164, 0, 0.4); -} - -/* Hits */ - -.ais-Hits-list { - display: grid; - grid-gap: 40px; - grid-template-columns: 1fr; -} - -@media (min-width: 680px) { - .ais-Hits-list { - grid-template-columns: 1fr 1fr; - } -} - -@media (min-width: 900px) { - .ais-Hits-list { - grid-template-columns: 1fr 1fr 1fr; - } -} - -@media (min-width: 1200px) { - .ais-Hits-list { - grid-template-columns: 1fr 1fr 1fr 1fr; - } -} - -/* Menus */ - -.ais-RefinementList-item, -.ais-Menu-item, -.ais-HierarchicalMenu-item, -.ais-RatingMenu-item { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.ais-RefinementList-item, -/* - The refinement list item in InstantSearch.js contains a wrapping `div` because of - the template behavior. We therefore need to apply the styles to all the elements - in a refinement list. -*/ -.ais-RefinementList-item *, -.ais-RatingMenu-item { - cursor: pointer; -} - -.ais-HierarchicalMenu-link, -.ais-RatingMenu-item, -.ais-RefinementList-item { - padding-bottom: 1rem; -} - -.ais-Breadcrumb-item--selected, -.ais-HierarchicalMenu-item--selected, -.ais-Menu-item--selected { - font-weight: bold; -} - -.ais-RatingMenu-starIcon--full { - fill: #e2a400; -} - -.ais-RatingMenu-starIcon--empty { - fill: rgba(0, 0, 0, 0.08); -} - -/* Panel */ - -.ais-Panel--collapsible { - position: relative; -} - -.ais-Panel--collapsed .ais-Panel-body, -.ais-Panel--collapsed .ais-Panel-footer { - display: none; -} - -.ais-Panel-collapseButton { - background: none; - border: none; - cursor: pointer; - padding: 0; - position: absolute; - right: 0; -} - -.ais-Panel-header { - border: none; - color: #21243d; - font-size: 0.678rem; - font-weight: 600; - letter-spacing: 0.08rem; - line-height: 1.6; - padding-bottom: 1rem; - text-transform: uppercase; -} - -/* Search box */ - -.ais-SearchBox-form { - position: relative; -} - -.ais-SearchBox-input { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: rgba(65, 66, 71, 0.06); - border: none; - border-radius: 3px; - color: rgba(33, 36, 61, 0.8); - font-family: inherit; - min-height: 54px; - outline: none; - padding-left: 56px; - width: 100%; -} - -.ais-SearchBox-input::placeholder { - color: rgba(33, 36, 61, 0.5); - opacity: 1; /* Firefox */ -} - -.ais-SearchBox-input:-ms-input-placeholder { - color: rgba(33, 36, 61, 0.5); -} - -.ais-SearchBox-input::-ms-input-placeholder { - color: rgba(33, 36, 61, 0.5); -} - -.ais-SearchBox-reset, -.ais-SearchBox-loadingIndicator, -.ais-SearchBox-submit { - align-items: center; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - display: flex; - height: 100%; - justify-content: center; - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 48px; -} - -.ais-SearchBox-reset[hidden], -.ais-SearchBox-loadingIndicator[hidden] { - display: none; -} - -.ais-SearchBox-submit { - left: 0; -} - -.ais-SearchBox-reset, -.ais-SearchBox-loadingIndicator { - right: 0; -} - -.ais-SearchBox-resetIcon { - height: 10px; - width: 10px; -} - -/* SFFV search box */ - -.ais-RefinementList .ais-SearchBox-input { - border-radius: 3px; - color: rgba(33, 36, 61, 0.8); - font-size: 0.8rem; - min-height: 40px; - padding: 0 44px; -} - -.ais-RefinementList .ais-SearchBox-form { - margin-bottom: 1rem; -} - -/* Menus */ - -.ais-HierarchicalMenu-link, -.ais-RatingMenu-link, -.ais-RefinementList-label { - align-items: center; - display: flex; -} - -.ais-RefinementList-checkbox { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: rgba(65, 66, 71, 0.08); - border: none; - border-radius: 2px; - height: 1rem; - margin: 0; - margin-right: 1rem; - position: relative; - width: 1rem; -} - -.ais-RefinementList-item--selected { - font-weight: bold; -} - -.ais-RefinementList-item--selected .ais-RefinementList-checkbox { - background-color: #e2a400; -} - -.ais-RefinementList-item--selected .ais-RefinementList-checkbox::after { - background-color: #fff; - border-radius: 4px; - content: ''; - height: 4px; - left: 50%; - position: absolute; - top: 50%; - transform: translateX(-2px) translateY(-2px); - width: 4px; -} - -.ais-HierarchicalMenu-count, -.ais-Menu-count, -.ais-RefinementList-count, -.ais-ToggleRefinement-count, -.ais-RatingMenu-count { - align-items: center; - background-color: rgba(65, 66, 71, 0.08); - border-radius: 4px; - color: rgba(33, 36, 61, 0.8); - display: flex; - font-size: 0.64rem; - font-weight: 600; - letter-spacing: 1.1px; - margin-left: 8px; - padding: 0 4px; -} - -.ais-HierarchicalMenu-showMore, -.ais-Menu-showMore, -.ais-RefinementList-showMore { - margin-top: 1rem; -} - -.ais-HierarchicalMenu-list { - font-weight: normal; -} - -.ais-HierarchicalMenu-link::before { - align-items: center; - content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%2390919E' fill-rule='nonzero' d='M0 4l4-4 4 4z'/%3E%3C/svg%3E%0A"); - display: flex; - height: 8px; - justify-content: center; - margin-right: 1rem; - width: 8px; -} - -.ais-HierarchicalMenu-item--selected .ais-HierarchicalMenu-link::before { - transform: rotate(180deg); -} - -.ais-HierarchicalMenu-item--selected - .ais-HierarchicalMenu-item:not(.ais-HierarchicalMenu-item--selected) - .ais-HierarchicalMenu-link::before { - transform: rotate(0); -} - -/* ClearRefinements */ - -.ais-ClearRefinements, -.ais-ClearRefinements-button { - color: rgba(33, 36, 61, 0.7); -} - -.ais-ClearRefinements-button--disabled { - color: rgba(33, 36, 61, 0.5); -} - -/* ToggleRefinement */ - -.ais-ToggleRefinement-label { - cursor: pointer; - display: flex; -} - -.ais-ToggleRefinement-checkbox { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: rgba(65, 66, 71, 0.08); - border: none; - border-radius: 8px; - cursor: pointer; - height: 16px; - margin-right: 16px; - min-width: 30px; - transition: background 150ms ease-out; -} - -.ais-ToggleRefinement-checkbox:checked { - background: #e2a400; -} - -.ais-ToggleRefinement-checkbox::after { - background-image: linear-gradient(to top, #f5f5fa, #fff); - border-radius: 100%; - box-shadow: 0 4px 11px 0 rgba(37, 44, 97, 0.15), - 0 2px 3px 0 rgba(93, 100, 148, 0.2); - content: ''; - height: 16px; - position: absolute; - transition: transform 150ms ease-out; - width: 16px; -} - -.ais-ToggleRefinement-checkbox:checked::after { - transform: translateX(100%); -} - -/* Selectors */ - -.ais-SortBy, -.ais-HitsPerPage { - position: relative; -} - -.ais-SortBy::after, -.ais-HitsPerPage::after { - content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='7' viewBox='0 0 12 7'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M14-5v16H-2V-5z'/%3E%3Cpath fill='%23000' stroke='%23FFF' stroke-width='.5' d='M2.228 1.332a.664.664 0 0 0-.942.001.665.665 0 0 0-.002.941l4.247 4.247c.259.26.679.26.938 0l4.247-4.247a.664.664 0 0 0-.002-.94.666.666 0 0 0-.942-.002L6 5.105 2.228 1.332z'/%3E%3C/g%3E%3C/svg%3E%0A"); - display: inline-block; -} - -.ais-SortBy-select, -.ais-HitsPerPage-select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: none; - border: none; - color: #21243d; - font-family: inherit; -} - -/* Sliders */ - -.ais-RangeSlider .rheostat-horizontal { - cursor: pointer; - width: calc(100% - 10px); -} - -.ais-RangeSlider .rheostat-background { - background-color: rgba(65, 66, 71, 0.08); - border: none; - border-radius: 3px; - height: 3px; -} - -.ais-RangeSlider .rheostat-progress { - background-color: #e2a400; - border-radius: 3px; - height: 3px; - top: 0; -} - -.ais-RangeSlider .rheostat-tooltip { - font-weight: bold; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.ais-RangeSlider .rheostat-handle { - background-image: linear-gradient(to top, #f5f5fa, #fff); - border: none; - box-shadow: 0 4px 11px 0 rgba(37, 44, 97, 0.15), - 0 2px 3px 0 rgba(93, 100, 148, 0.2); - margin-left: -5px; - top: -9px; -} - -.ais-RangeSlider .rheostat-marker { - background-color: rgba(65, 66, 71, 0.08); -} - -/* RangeInput */ - -.ais-RangeInput-input { - background: none; - border: none; - border-bottom: 1px solid #ebecf3; - color: #21243d; - font-family: inherit; - font-size: 0.875rem; - font-weight: 600; - min-width: none; - padding: 0; - padding-bottom: 3px; -} - -.ais-RangeInput-label:first-of-type { - margin-right: 6px; -} - -.ais-RangeInput-label:last-of-type { - margin-left: 6px; -} - -/* Pagination */ - -.ais-Pagination-list { - justify-content: center; -} - -.ais-Pagination-item, -.ais-Pagination-link { - align-items: center; - display: flex; - justify-content: center; -} - -.ais-Pagination-item { - height: 38px; - width: 38px; -} - -.ais-Pagination-item { - background-color: rgba(65, 66, 71, 0.08); - border-radius: 4px; - color: #414247; -} - -.ais-Pagination-item--selected { - background-color: #e2a400; - background-image: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.34), - rgba(255, 255, 255, 0) - ); - font-weight: bold; -} - -.ais-Pagination-item--firstPage, -.ais-Pagination-item--previousPage, -.ais-Pagination-item--nextPage, -.ais-Pagination-item--lastPage { - background: none; -} - -.ais-Pagination-item--disabled { - opacity: 0.33; -} - -.ais-Pagination-item--selected a { - color: #fff; -} - -.ais-Pagination-item.ais-Pagination-item--page { - margin-right: 4px; -} - -.ais-Pagination-item.ais-Pagination-item--previousPage { - margin-right: 1rem; -} - -.ais-Pagination-item.ais-Pagination-item--nextPage { - margin-left: calc(1rem - 4px); -} - -.ais-Pagination-link { - height: 100%; - width: 100%; -} diff --git a/examples/react-hooks/e-commerce/assets/cover-mobile.png b/examples/react-hooks/e-commerce/assets/cover-mobile.png deleted file mode 100755 index 069221945832ac39d2121f762eb6020b9b64cd98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 808641 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

We*d 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


Q}vN`oz{p--WEz4 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�nstfN0p_ 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%0A3BVuepy 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

*qXx$*@ zn453ov6Axum8+8;+mfB%#_6j}ZiD`nFW(+MzOy=f>rK9JWz*fqQF8Z_VH-E$C-;_z zmpEwG19_QS^Ocj8;r!w{uQEMm)633qiotP>!S&IdPiWXkWQMT7Ug$W`(tIcSA=BXN zN8UWQy>pr7+@Rcac~$59`LlGE6%MFf8GfU2|B zKrzLe#-E}EP=j9K6_2zzJ2-c~CRW+)rZnYKbR%p=$0V)L4qxn#!5mcm;#CWaoSwhV zlZ01>D;sBqKYjm)Jof+3hcB+O%7ji^X+PVqlP9g%&ML5DIMipl6;{o5=5vnFU-)qN z<999&_nvSH93#*+UnE)D(J{v7vKgS3*)nsE?*(seZVmtJPZx);BLC9Td*i_5?xdV0 z1LsU86=zvKMJO`$`@BNQ_|*K5V{lyl5rUs(v)(L1c@*AMY{ieYqlgovqI*ikBQs83 z*Hab~kxz*_4nQjhg}>YZ)84qpy0Z&VMi$$28b8;sd|`ihbbT1U{}$uj2TQ}vt9!$_ zFEaT$x4=_2&WGV#`1jsvuO*#XXP4mlJ)TlIk!iQ?aO~qz_^KRo>|5@}a7ReCyaHfe z$+l(=kmdJ)T{Avi@iiRJJjue+KFJ{E+{lT#H+O0Ae`f6RJ^FMU7r=x#52$z-pIwDy zTmQUO${+iA)M*n0;7&9gGlE0fC;7e~ov{%T@g+_9n5bJ8gOd@<=Mz?6>2h+R6U4>* zI-62=wtdGR9PG^S9)0TIW6$V0#jYC5s;&-Kjz5n*6q&O4+EQLWf|t8iB=(6@*;840 z&5Qg;kFgOpvApg^a^e$t=%+63##XLvAbrAu8sVA?bWZZzC6g~ZL7%cAc8|u=CY0bJ z94WDrwy@!37ml`QDk3|v*h1d^v{zmUAf6pb(F0z6HYQH^fod?ViiS+$h@)cj&xuRL zczYB@xItFprf!Lc+&|zaaE&hNFOg|8dcJwKF-f$U;M6~nMkg}u&Cd) z`Nqjtf1QVI0gx8f%0gZ%_JIRSnNC{=z}No<=^>u->!SAe7j1m zuE61cKxW*oxkG#}7WeFifoi4hAP`U|^Y_2|RD`!sXOs3Aw$g@n^&4wj!X$+a$ zh~Uov+&r^ueidg?$M+D|I&(<$BFprs65v*yHH=1_9gyLCAj4#l$*=Wy?ZenXouwUv zkYSm8b#uH=VTfxZxyvI@(v6tS#gMXe8Q~q%w7bGddnpG&?5$Ik3%Ysq29-JFyy≷Gvd5ril{p@@G13~4v8r_# zm8Tft!jhkgZ8#$?vZIr<#KoNk&vjYj(6NnIzA)w~Q^&zsoG=nOp6aOafZSD_7djMf zk|)14wzulkFqBh5$Ab`RpZE*2;K3fL0Dr4N-JN_>~^bND9Q;f`dIp9((121 zmY0z-I@kuHbN96F(g1e$ISUgbYLX{>AVa!lBkqNK!qNLu>9xidQ_K6r1JDSgy=(&{ zl;?*$EML|R9YJ`lafO$IsN(W+rf=AGODjRgj^!Hl3G}c`?P|z<(Rcv@os$+mgJbyu zM$)rFwV$JK!gk8L^BwWY^PeFO`NFNM_!FFR`ZWBIN!fUS>ub@z6=2_5^oxbO7wtnh z#}!wZkC>0dzSKQUEO|Qb)BhD^v12oNbiO6#1zjy#nW5^xSU0OzYX0@lz z@W%ngCkxJ_TpRprH}4Pc-&z|!dEl-T4*X?6^)qaH%e?Ng{Rpr0>TsG_|LzVai$8h- z%qMNshx|Q5fcHiB^x8n4HasNo_{2PwQst1ESGJS)ymlE0BF$rz%=v$&yq=|Z=}ICV*KN23OiKX(R^lof!C(mZk!Ev(P#&8ysz&IGJ2#*NSpn;HZwTCFxx8szZj4;>B^}nKj}|>yumM zrA}ndPdpbUI(>A!?C^T4wL7S*~UBK>Xdx4ly%|aDW0ruhkx_~9s@|nPuK`IOZ^FC9hzsf!_=GX=n|}_{}fN@ zwSCx;kjC<8k7>J7@sb&2^|7o5^&=j6qn>gGW<3Vh)15B7h)a`p3=`v{42j9#r40+L zJoD2gx#}2^t8LQuWZslMeA^9&3>hM;;{;cl$#2@$Q-1KHHGjNDSMUwmFC8BB;`j@W zz^(&w$co(bh0@KdGR*_M?3VHgCnN2ZG6e;g&cn)YWgNe@+faY&$o&D97$$1bmyPu`I!h_rbntM~~wE273yMXjQ#y!qp6JV;>AyHUP^M_qYYZV$SI0FE zto7IlX6`hSUE;Y1aPtxF7lV9F=LrM3J2l;nk}n)Vn^%u0PyYhMQEa@a8ppwEL&D zO^24&Mylsk+nC5#q=<)KuE3xZF2Cpqo(>m->Ch(C_!%`nx{IE zPJ;T(gyJXIxFKcjxYZt#QF&e`tRi;;t8~#keeJ|D2_x;%_2%mM&4sLH4>c=2rezLV z9(arI)2b)K(!Jjqc6{$Vw6qCc?~5ZguX@|AK;vg$+R-TssrNrMvR|FWk>mKMT6R0( zv!Ti-juxx*EMC$g%wdvZ&m$23m~gyP<`^ck*|W+xh&(>~*M^ee3( z8!JD3c43cE{3O}xHpgBe6@FCSNqC=dhu}XBrcCc<&x)kT?lQJhBw860Y z#_M;6KY544d!Mji=S9?<>3}P7ci$7rmxr@CfR{S}Et6rEn*t6T==Z#vFbH}mReM_I zzyVY6yyX3I;_kp{GYNQ`p=1pW{T@$kH!f{((^we3@Y>76D`%dt8|MKF8}CZ|&)rEH zcoJ{)NeWDuc1{8Kjrr>Ur7+4VQRH&zaR;dyppxXW?dw~i*u@AhD~mR`7UdAM=ymEjDlz3=?tKN`Nc_1_F?~w{V3Y!hRg&x zo8ru>`z#&9XO>{fT;5aWvw5Uv$!F6uBv0W#gMCOxenf(M@TajZgt`g^=QP92D=*Wh zXs~a-%?*|b=oNOgoPVh~58Q0y0kxc&H$2>A63fEge(%?X9LnV3Zi|dx)#!nnrA<}C zPd^)6ym=U~P6a+Y2f`U&w&ra_n9xs~ux%S#~ogs*8jT80FME@Anhl=z{M zJv_1)m7jPHbA&7qU|0*tJTxS4i7`|MhZ7L_1(!b=`zT+Z>qfuQT^#M=M=AWmlH;0j zoll-YlTU=h+gH1^g$^ibwO__cWT}VwtE)_xfO{T~hXngz&s{azB7yR&yJL*y671_t z+)_s8jbog7F@gx4#z|9?+4ImzytG~T$>YLBzaYqi2Fb_+9J`f#*=a-NR~bk)MLI8W zEmx@!RQ`mQSD8p+Tp&MrSeK?7{efO_HjpPj(fuQj(#d#@^F=ROl`Hj*rLx07a_PNx zp3$T8GtbwFTriZJ+%s&$fW-A@CHa8H3NRUan@2(XMYb~Fqn^>B%K+TU3uVdyUBr0> zCzUD^A^FOf;`F%rwUa@8A`_hsWQ!41bA@aS^3{Q-tZFO0$fLf6tV-gWZ@(axEagOB zl8y-##8WTxM_sfP?JKqum7k!1`6FCA*W#NsVdLFU+f+z=;an%;5|=h?^QXt{ND;u* z|0baqX5299NV!H`3y=J)P7|5pp-XUugE!i~)5}l!UX>%7Au_>F93Iui)ISEK@s6+j36d6`)>&|bQJ(v} zp37VRiEDTD@m95kqt)}d&fuWY$XX7>Po_Uz`-?SmL$jxUiedo)u^ADB=WErvg{P% z=2@>zg`WclIAs@7BaQJ|d&skkB_fiSre90-{GJeZ)T0XLwP#ehiq>m6KjiDbSzKu< zj5q*BIpI+ljl4t$TJq~Eu!w80+4$OSNTqVC1dH|{k7rmRpN8GB6#cU-{duyQ`xSz!_0SM=eLLT zWp@pb`8kC$=;y@#c(yM}f#Xb+%6IokYm^TD-+2A*@CR>Q7#{Dk{cqa?VR>H@Z}tw4 zFMYDG#^Jwyk}h6pMtz@7Z?SXc^qF&fBAc^lwjNM7dTnXlFm$W1%`nyF3|epX0?r)V z`_;Iae$)E-QVIueE%BwNUwq>dhYf$2g8)ZbDD0Sqs{6VIX1?W^{0l9anT&QiX{sYj zSK%pm8ohc?f8N1Mhl9903NLXaBrwx=-;m{O6XGz99W#O9PAfNG=UT}@VK?HCSy*^H zeCs>^VEEeNKaP_`ZvLvCI(Hxkp=ouHHYO~u9C5AgBJ9go9}d6sEO1e$ z-8`t)uP`|#j)m<7$c<5P$jAgSSLyg-6V4x<5lneWGafli$ip8npVA3)LT^YNK`$@) zMjrWrn=idrW%F~QU^%lWCQp2`q_3a6&393tF5|+z^D$ptef1}keRb*lWv`=2M~%A( zJRH`=mAhng&gg9NDXnsw9PQL-gx8`&y>$w$a^{Q~E$^3~z8vU;%ivJ5)FJy%A1ImuGS#mjDw_sQ&mT;bZw z>u6ILKo&TvB*eY|#cg)$bS;}2Z6Q2}rk;u?3F*DRvdR;>p06F`oIGue5lUw9 z(JsoAefV@kNmFIW*(37me2Ilm?2K~bSu)Tb(1F}}NtgEIHN;J((Xro%>kmcL#XshG zen9jf9y=8;(TV$$=`=6(CD=TJSLu^zaQjo~s+_WWWRliC;_CzqANA?7!iIS9h)sK* zmsZGr0Z?+yYWlXhifMxjKj%#0(?Ey*3P+{Z{=1y9+`y|GMHD?vOa+q+4cAs4bgNHf7kQ*ZOon_5maF(gT<9k)j`aS4nYOJ(Pn?Sw-?k)N934hFbR4e| zM*u9_P8K|K>Sms}@-JH$Pw0Fi8(4Vuddvd67g|0tmV4@3IvMas2VglJMbB54#1%mR ze_DAOOmO6xB_8zdP<;H52u|*FKjDI9_L7j^Z^xuRQz47>tA4c8GLSP!`kS zA^*--J^eR=j6Gw+DT_FUCG@Lx z$r3jyaTd9`byV(sG&=EogP`-UOtxXqW%LVGevaE+L7qJ$&Nkk&)2BI7fV^d1?b2a$ ziad2vKk1%^)2j%$clDtK$J~F>rU}8|oNzkT&3FnvcPV$XLhYkz=O#U=EWj~FJ zV><2aunGuJ0`*?HbcQ8sdNm$wD~}-0;%i=T<;U?1dwY+EQ#fUoy%{T$pBoZjkOxeS zu-$_N8`?b;1pYgotn<}K<8@HWIOeZyUb0R)UW?52Rr*E8D|jIm-3TW#t>e$# zDZ_MbczhnG%pNCr(@?i~ba#3GU$A&&CU=r>oV8|3cpUmkM1E?Vsqkt1Sq$laV+~uD z+23(|pm(9~jm*YiBVNLZ^3^Zg9e(eJ7kPJ*0f!NOd6joK86cM#ST3ADmqC4>8Mo=J zhmUEzY$J4_Vzl47dyhutyXOv2{9wLsN8t8Z=5AjcBFzBAf8Mv;<_wiHICj>C>o>0t zuWsxP4{m>y1AgrUqqnvXYB5PMOQPwXPFU&mmlMl&d%IXRW_8JSgG%VrvX7TXsne!iG4 z;3WDy3uij0&lfZYs_c37HZAlte+n;l!k+aj`dQj}aA7mQ*8X{YiBDqRytzMYvRMD) zw-<(&n3!JUHKSF|P+41b64PGc*u1|m+{Z!l00#|Q0ozj(a2e*P^EL@=wj%DTow3b1 zPzAcpaE{$L%5}03n-h0pZk|&J7bkHrK-4z@#<>t4vILJKwZ={rUeG(4cNb#HBv16K zMSZs20&_@FmU3(2Z(8R_^puVXz30L>?&s6U%r;!O$bX0JygCM?F*xs3*IjnEcwUV> z_Sn|z!k~?d4Nv)&FOC*K*+vgchmbtH`r1?UbpL$kFWl%7cGND}5-&@D>g@eN2aWQi zbwJFkBq%7~3OaM7jV!M6@@IQ+m(ckOJmtbEqbzyNws#jPW@^9IX1E#`KYv=BWJ1BU zhttRK6-Wp#;D$ICfM=)&sp!NvQRyT}RGHK-^5GL1RP?C9scHTx2a-=mo*H#D0w{48=leBda zy*h5{y2r&;M`g-Oz2%>)jyK_!fuIeXN<=;dU`Kj`0goQ!neR_sz1oh_PkLIIxVH7S zgCfb4!TO3#=x3s(4q&9+tcOb5&UJV=w-II#Cs)(bba?fNBs;wFipKkL`l~YJ10E_i z$4%#JdMpQVgF2P0;#uij;ga%nxgqL*3SYgn=QQ0GkHCPZZkw1WHKwiCNq!WW3Q%E! z_^CIp${OcVJ*{J|+C~c9X_^5JXv2#?%CzmvEV~Lx8YDnMg@~iDk7n*6O;AvaywefPiXk%_{4!zgdNnC!i z4DbE~Y^)o{Dfs9hf6QyRULCK~X3*XHlqU$-=GSnh+t2caG??z$!BI#ZloiMNOMhip z-?)TLn8T~%v~*oBBqmJrr8kUvskF2~;@T1N5RnSVE)a3Z*J)t7-9K8MabebhuP=F6 zZ=4Pc{=TAQzD@}p?m8bFlXQyc=qP7}c+24XGw9IptQwsf@|A~QKJq*_dB`hx@F~8^ z)H&jpm(-zj%oqs0p&T~8ZndQ00ZZdopp@+ckap;A!0`ziHAZx~gYSR-XDAyf^2slPcTzC*K+N`Esy*HFF8)KJ=m72hwk-PwqLs z;gHv&K@-=X>#j4*o8)Kcrs=7GPW(qMhPt*CawdQj-!#8|Il%9DY7%WG2(9%O$Is)s z$X9;&?e5~qC2Vut`05g$I3r$@r(_)QXHu_6U^H+u^D|LiT;@^2j)Ff+vWEC9L7BZm z^|V)m+u;5C|M0K>c=-GO^o z8!Ot#D6r_zM-EYjfBtf_2Ui8|HbgN^|vI8I9a8$ z_OuQ!AABUgc z-r?>DlA%v!ois`t$B?q%yMp6l+cUx#a5-M8l@S|gJBH?Ab%QZJhxvkw9Sm-XfB3LX zd*tmHCvSCivE%`H%xkkiaNE2=`5uOhuQzy3h}dX}Z1pF?z~*aUd77*qF!t8b7GX@L zdGAbX`TA4p6sBujuohUNe*pJUHH11|me{WYV zmd#aw@BJH6A4IFq$;*(sG1Ak0UTjVT=3YgC9Q(yVOoA7A6rb3zV9;s;SyuL(K@ z3>}H!m8>Zdl+Avvj;r28mDaMkQ_Xf-!MxHx_vM}^pXdOMSH)PCI8R7u18HmA$TXxI zrR-pK=;UEu57LGw@G_!((ef6)sNvsfyriiJRK9>hs4H-BM*iW}V3yx@m&Ks8hOUyj z7-Bp6SsOjZ?6JOrkF2U~s(n@f06+jqL_t(P<=c)VFBNjtHUN{y)nd}pqno$}c|^}{ zgUBY+erg%4tH@CXzmzM?nd4E1+RJ=(cZaoQYfje#cwQ|hzsdvp*uJAXX?fdyS+^!Q z-jrUxPMP3XXHb50nCgQr=xTeHm$WJOq+B&dh6mbI{gfRM|8xLYLB zJ?0=|A^e1i=Y3DueV&Tc=ctA2ypYB(Es67xufpo!@asvY#Wn8kk@w$!KL-QLUmAmP zzpCW@_wBdej?+V#9w4kv>SvIbKZEt5Y#lAefBxrxKA*Ps^VZTSUtLdeqrJ-)1a4fv z!7I!=!-uy%j8jRwoabyz9XNhTO5JtbST^IA_)5Tg@4YviftPwe;U=c+x8Hhu_~L6{ zg#XU)-n;K|@c!A9&ph>4w&j(NWuStFFTCD*w>F*j>h{M(+DUekQV1I@)I>lnAZg|woH*4 z>WTl3MO2S2pvH%zm`rY$2Po2(9`h7Ez&so)nkjT|kuNWCo{8JMG9lW%gOPZp1|$uJ ziJlprNB%Pz9hLc61sp+Nl`-2Gk5c1P!>M6!z*e{m_sGMjbc^BI#fQV+`8R$%{KMaR zeb~iIyU0tb_wFq7WO;?t(XaEa>JtVDyvdAxp7rC~N}D)r&b)Mf*k<7L6!aw>Gj79Z zGe)jmh#kP%<3{6I2P>RHw#2^5m5qzTOT2Qhee1!n!d*`DWbmf0rvrnqbWGvKyTO7B zO}ok2O8AjyLp~IGI&=}za+MM9@C3Q>tcq%ciqlrJgVBU6dWmEIx1CD4Pk94N&st=F z=2L;!t`8e$)`vfN>-UF+_kUw}ZS4*!2Rr3w-j(`UaG}!&liY?m^XI8hYW56i5t&oO zo1J9FVQS>tfBx-nd@%gcPtFbZcP{YcWQ$1_m3WStVHUOMXI~&*qh7Xn^!?9%aAElJ zrOU(h(;tl!hZ%8CsT077kUFWj=ruPy51+XV4uI9a4j|8t75=OW1)oPf;*OCySkKB6 z{U}?^V+AkURs~s4k_A5YT6EG*)f?kuWCmd#=PB-|$uFAY_!QpEHH(A5ay!Mz+ngRh z|0SNxQF%Z4U>JV%?mh=<55x6~yyn7rFq`b?@DtkiaL#z9%I0l)8)wh>>dqQBbkCjH zIPb)PNt{j)bjv5YwH6(8i95aXC4at~?1IF2CQTSMPtxG)B;KU+uE3#K!Qqw*0BL!P|g&6|{xHI*wDR=S64KY8lX(X3-( zF4o15P03WW?Kzp&gK_x_G9WAqb7A1u^dhp5?fp`E9i#4$akp2d(!JlwsI@6*;SrYF z4S0B9_lT4~KT%3&6Q-4!NQir!40SZClpXn-;ZNdzDSH+#vsEH?G)_p_l3@bg{Ot2n z{v)5xTazibClfKAvYAI*mbQh&P52|!Sd&C&5dC_-1Ug@xoCMcNBtad_Q$I=&SjIPF z){!L4D;ytM*MdG-4c7%w98%E6vD>&ov82rRhAl=DE{FbUBnnp6cF+9bf5t-Fv1p~YwR3(Fue25 zJDkhN@e4R({27#So`;_A;^^>G+U~|NEkB(jI!4N2BroZd={-;!9XF=^No#&+xIyY< zbT^K&%@;0?b*XLT>(!sKm1}#{A>@}%b+m}DGvW+y3aqmhP~V$3Zw{x~@lp;eak5JY zol`BVeZ;l;d9@SKWHpam z9LJ1|!y(58N>%{<&jdA)5swUc`{iCc(k`!1$9u_>AnVp~#Pe3W zHW?Qj=(7Vd_m6gek*W8+gF-dF$~`VPEY6}imbfmP24N=+Go**erW+X!Wqg{%JV84v z*QvklG<*%PGVn@ES3UzKM*UnuDO1F(^^03RHy9+DpcxfLDckXQtFKy@vX0pv4ZjFKDHUpQFUk8Ql zmo5x<-{(;?`E5cfa|Zt0A(+hfmb$=fbB_a@mpDY&C#bJpy*RAzKO8=Jyv--e+TaUe z3zJ6cpyZ&Q&esd6>wEP4w_co96h4!vo7R*gbGRjbvq_0&S8 zO^q_moMtrC!+(#_jG8y2*R_Dm%v5J@e*!d`kNk6C4)6S9$&D3^3+(ISlyG z!v$Vv+UFg9p1Se7kAvp^T|TGHmpa$h_Sh=STBi(0W8FILdH-|gjIwlS=s*G`{Z^WNUio#U z;G){`Ogt;uF+o`_2(*RqO}<8T>NGl%=T=^wGwN8od~~Roe)NDP4P#Hi{c4kk|Eimd z5-08Mbnz9beRRs=3#W-YThz&250-@okjOxXQU6A>42@5BeOjd*+9=gpfi`j>p@YbL z%d^UPGTO;cYcJzm!n)*xgFJbe=Rxs~;mWoAUS060a~hae;9jPo&K%Ls@)_#@B<*Bc zmd53!EuFXt%eW6b6FG5#aS0!W*2Mi}w2``(WDy3AAzA!!E}43R#;14Umi|uSGqD6e zGW}y1^~D}2r`!bcrC;`@LA&!wpLJ!yU8vQ)M0{mvf6L)wPJNsYEYf(A7hTK;xdece zIa!neU;4<5^zf^N^=NzLNN-c+nx~IN?^U^6oG+lVu{hoIMOYR;tsNVhMaT{e%Pbf| z+suw-pnU`ej;$nd_$|-|V|U}``3GIBBl*TTkje%OP`c7Z{*9>9HIil@;?l|_JdHtH z!Kk?|Z0$3OFnIi)HjRFPbwyfs{+cH=;)z6z07BKF2+FN=WJBjQeYMBMOF5v`zWh%= z$O6Fxc=>TL2$HX0L*`8+qI21!WsPNuz7iU^mOZ55C24r^4|v*6$8nIdX(>oYwN8j8 zoV2*~{{#$Yd33;9$J#MYY;6dQj>I@8wU)BsD}9`EAjckzNea`BmP;O9V^{f*D4mrF zTH_^N@?_lFzIkceT*I5TWJuY-)wwDI^QIxP%rdX%8|Q!YNE}^Kq3WvRnzn5l+vT33 zV`d*m`Z5hj`FWj_!DE5356vQQ-zWb>9sj*GM9;2>hw2*2(ULk*Uj!Sk5dBO-Cxz(> z6|b^+wSCmDoG*@9+2sMDj)ro4=&;ZUV_HXrar?MV2b~={M|4Wm)m<#k(XU*&5{HP+ znOE2~qSHg#$`@Ze`F#EBUr)RoCE~sQ`s;a(==$~RW1c#zhw_Z8yE{;HEXmKED)PU6 z?V6@&cEX14*tvX}gOhd2bjvBb95HQ8!8{yCjk|AbY`%8&{}T6R-<};=Uf;>QL(a2O zQ;n*kC{-2R1%__0SYN;fZRFYDu%$s>L?@fO`-yQqhbAS1}LrTI#=KYT`zg2e*5<;k8ox+&kuyHE{~4~z&L^_vx2T`4r(C5aWTOLDFq6)YMCAL= za40X3`dPwd7qUUqbn;l$A&$Kv-^?K{GW)(+52tMK7P0hbo&oZs{LX6`oo*bdJv+^H zr)O(=&&^ywS{m>!1D*2(>FpPO_0b5Xu5rUL0yTPk>jynLN8`{v5cE{6$KIzh2EZ{^ znTCwF6`gF{v!}u_v`^ul1lYqVqwzeGWS^1v-2Mj{fHn3(Z4NG< zh2VqwkBF1>`*)qctc*{hOFD!%xY0o3gkX@5&1s;T{r<}bCy?^cH<$w#Ket3g`4BWYuBfTeA?vU{jpmHL3t;UmYTstWsxba zgVBZ#n9cLkZC(|4a{tcsgcZP(+%-h3U?=P_^8jE+Jd_746E=(^Md#%Tz`PEfx5}e& z`l^(JcvIJP$wNqU7hetXbtOAF9o+aiR&m#FJ;HoTeg8f^6r2S; z#PxJW+mWyX>(#XbJ6X?QQ7WwcwQX3`J?ba4V0n$#*m#7Uou0q`VEW~+yfgjAk8ZPQ z+L|71?=pGd1b=1_=lQypUqRt^KkZ(+IK6{2?jOAUN2fpi;%~F-fVw_u_>eDM-2a#-b`NUD2y?ROBcD=o2lP|h?jT`ag<}BztX_M^ z3-c?+(wZo{pv~l)er~z^t5rnR%@fB$K_gG6Kt0U_Fl_Guj}<5bh-aB}KjukY7T09B z0NH&Y7acu3p;JC77X#|T8Z_-(XO5E;2ZV=XSBn!5hg`w#UTNETbvI4E;K%!qE>6QA z{p2H!e||zT<1*P7E+L=%``Aclf=(J~;vaT{UU zOTGrV+Io~Ft&4ffYIfhIA_2r|TSl4=Suz-*qn1bdr8B1GHLiu!QQ-npd&n_#(aR^M zg}J;QIFer$FyT@K#<=Vhd-q zf8agY47$38hqfb%QKXOkie31NlD2J|70Gn_=#S+C@AsX2^>_osGFa z+0Njl9CbI02l&w7%(Kualz%YE22|*6o3@*72R;VLya@V~M@HZ`&n|~$6Rsk00uUFb zY6O!=BfR9-3L|)7>QQ*os31!q`__Z6sgf$wzkj zDmK!N=_5mT+f8;Bqp&mv=b7#-lE02}7r$1$xIUE320PN$qbA9jVGf{%G%U*}vYB-) z(^We*UUY|0zzs!y(qBMI+l7Z^%(#LQ>QZO*jWH(DYHVw}hd=ycbHr>?_C3bReWuT7 zK_SXsI>hbUdz|JA+XSY8Y1`3UUF9?Kn!iBxF+>&^=4nOK<^@iCrUAF(!CB<#9Jr2? z#2q2-zSc1!?mm^EgW@r;4>;;TM}`g%ogap8e)F5TS~-rhIwTBnU_#>q*t55)4n0?9 zmj|3WZglKiy>gYY;t^k4d@g!N9(Aq!YwUtCt)ABZWAtc8O3P5M4xpvX`J}h)1-a5b zcayUiiNv{-t2=M5Q=h&D%&SdSaN5k1IE~u*R1Ozy={=7M(YE1B?E!n@J2J{7zKd?@ zlccpl+otn{Ij1qyr^01d_1DggWUl^bfJI1sBQJ(oEII#XtFLSqF+zRp`+nycgM_UqjOO#|7$<(WQwF*=~ixarCLMl)b9lD)JcXR>5=n z$;R_cO6X7+#DRDF$@>@x3I5MjLygda7uvV-jk0<@B){oJTE|bVE|D)-`4F)F=&*Qc~ z%PjmM1I$APM7PpizIBB~%zJS0pe^q{ItQr7aIgCFMzY0jj57?nml@QqUtsb7{!S*n z<#@0ns*de&4ipYpxuJ43?mq`8|B@b+ZrspxNsONiXx;@!XxsyOX_ejbOU7W?WOW^= z>f!&8m=k2C9l)M2z`85w@|A16Px?HsN!*y;;~j|~{hR-B`sRh-OGmG-Byp3K)IUU5 z6L$cS4;3R!-h?<+EP^Vo39YM+pxm&*2@HJ6vQC00KDg;W`Nemp-+kj62ch!5E{i%% zW1T!XyThkseL}%I!I!sLEbUCc{=Mtdt2b^>x6iQ?>XU>NV3gt$bb=*Gm$9#^v&=t9 zy_0Z{Fzo@`CBib^80%o2_gP-MQ7m{C$lt?CEFsIBIo^>a;)3sKqs`_hd~Lt1w^h88 z@>lUquhwUZfSk;ce5ww>{?$EygL_KGI1y3i%H{p(kN+_i_q=ca=1WHg zc7}-<@3S)j-eE`d-8bjGoP6dgtG z94XtN7Xx}PaFpFnmM1t>9%>^3!&TV~)zw3=lNrN9KDVmF#e*W9SGt2HeAEq`j9K76 z-_`N2PI2PUHg!hH-}k+lH>W&jiSKc?)iylUJNht=J!IwN{9Ih0 zYCU*Z<#AWvE)!Z0^;Ka_uHKt$Q_kx@)`vEQmwDO@jOgke$#&r_O0abzj!&WMz5>wv zDsNF8G70je831R7MU z7fn=Vs~NM9u>KO}jDvKZ$ZBI~mEFPktc!5|Vg~TUH6-7Nv|b5mpCi%y8o$RTZRHeW zRttRVSUlTwD_7i%FJ3KpBtC+rWwi9W_c?(f{2oeTK0<;~aVG!rTujux0C=H0o!tZ?B76o)l^n$h1z5O$H6e zFvm)>e7(j#xlP{7FR=@s8%Z2BgYTY$PMAd;^@{-@^r~}>@93pqqQqGN?3}Mc;RF&# zN6n6}E-iS-DPN4`st$(Gb6U90l*k0O=fK%PM3C|iS-!Gt$k@iUZ6M4;dc9926dF

WsyE!gBvJWtpJFJM~d2Nq@yFPUnwQ9Q?*FL0*(a$Csxph5|ayT}t z(a6#eH~CqG*-mR1p8WMC>+85bndS~Q@N8$`OJaC<_0Ms@4u>1j*&frluRQ*(>C)C6 z^1(PwDKTo89i>C0@T1&+i{UsUu8KXWSZ$kCx|7n!^F5qv%0JDgX{@3buI_T)%Y*5= zZ(N$5aIk_~47V?AO&1sp&)}$WTl+nB#ysL>*9{)?Zu97PpPe;3_Z~8#O7pJ)!$BVI zXE|#|?{!X9$AkXD%eSVlJjd2Rwjt^e@ShVT>$?A;3Y2ylAnBwB@n}2X;vEk1;cs+) zJDusdrI{8eBv<40*IVC1GjIyep%M#;CSQ7YbY>FljYEB$@H%1eCUO1NOVf+bzl04r zKj!uCPY-_b-%qccdqY+UQW(4@J|Y!ATt5d$A<56tZES@W>@KH))Sf-ECdr==%`Az)n*P}$p>66soY1_xC zcxwy(mOLHTmSLda48Re?>q{@bjKhUF)!Upa^D#SRZmallDgKu;IBg`HDy;A;9TUnM{Bahm80|mPvy7@{p(9 z&#Q7>koN-82aYbD#nmCB49i^y0hd!nI=`xvb0J{p*(^F4vLivh4E(;DB@cJF7#s)k zbQhSfB({K|pGv@$ow%1yKSN$d+8V(wkQJj;})VI;^-khgzM#C($O zMA@>6pLP>Cajp_se;${{|L`~iviAzp@{pGczNS~6bNaSlw?**`vXzl_FU_(|t0TpV zoILRd=TJTfmQ$XkOkGG-dm_&TL&_J$$WS-Yhy2imhc+hz8HUsYSL;D|$f@6aor$z{ ztIpA#wDG)NV%$9cg3ncY{`yNxF1Y0{Pw&l{h%1!fPF!(aP+Gs@7}VFl=s~_^makDm zbe9g}XR$N*r%#bDn>aSz*=1b)2C+Ea|JBKIiK`vdSD0l<+ZG=s+@%mE1K+rGfe{xi zW#eiH4{)=nA~$wJO4_(|38u-DnBdhv^_0GYu<%=4XPW@aoGAr>!mF?}GNSSsu)v9X z|DM~B=%mF-TOMf_=!TSus8>NkMfWf1AxC&J$0aP&=mx+FHgJhNuIhNG959*lK%Mhs zt}W#kK?m2AFLo~4l#BM@&OXOO%Mt%ABvt2wcF2-l%BViX4eC(1&OIwXaDK#b+)6o2 zcRn=g=*1AY<*Q`i!g@?lPjQnk58~FbIEI-oZQ;RP%7Zt?EXQ9x+m5#!!N~_W7>{vO zd#;RnX(Q{`-9vVIRoyY~)}PAPm!3RkOCec3dka(L`LBhO)&`3t5Q20nVKniPC{PNN z_DtGEezk211)wBRh0{1QBHO7&!A(iVigxp-krCkGu}25 zryX8}4E+!@SQj{~DkIdffn{$gsw+~BjUih6#2(lpV|AA)*kN3l!WG(r<-I05$c;N> zEb#(b0J?nY=fAn^gZBazc~xKg4A@l%bPzs?edeQo#Lg)m^PVucT-QoJ9>92opYx;156U_Nk_IZx8K6jq; zNXWm)xdhvsm*Q@jM~^vQ=GM(PWghcbQ;&I`ZlSTx3jY2E{u1@3qTlWytbS~Lr*G@NTSfwZ>jEU<1~N~!okLs>puHBX%*6HfD> zV2qocp8!|t8D{F-`XkFuz0KiWH*dZ?y>RUY6U;NyZ~o5zJAL8d-(UxdGrCU}P~(}x zQRv>I?Mz<2aQ*T0C%^pm^c$~zfs@BQn}OQr>r0!gpdPY#b9?E33nb(-~E@#I9clxsy7-lY(lsPPYq^{3ej#WlSm1`C6r1U|JAI3byf0IDd&OgOMxBFXLd(I!PIPt9I8Nyh zR>uAwW0ErqqfM=X2OJ|3A#xB^9|7n9R@U35U^`_$0@jLP@W+a<=5YisPui*PkP6A`* zrk?(I7s=CmlCX(DcT4yrIQasx*?t58gMoJoiZb+2YZfCzbaW)oWZJ_hm+{DS207xw zoFPdgd*@Et+;|*v!ZZxQuOBK?zP<|Qxf3nKMIc%E3PrUQ(gF;$JI{MgR6O9Z0TiV( z!fiMHk-scQ;njVTLf0Y?y6hx~L|`!PIuewxE?GYavaW-m@Ky+>s@8|(#~kSAU90ZT zMO*jo5LTIK>tUfDg1z!*(d=iIL0$&oRZEdnKz=L5T!l6lUH}SrprEjKx^_+~$9zh{ zwRUVNOPI`ZCF)pUT>F_J?}}I2yzak34CXIPGV>BvWhXL1#m29fk>wB|e#t|3bs!z7N_p@gCd;LkqQRYXk11HbrWpo|*}STxADXHx2a!xj z`08l6C~9|@G`4jeI8`S55rpuyEgQQ06)%`HbB7@#Bq$?p0)RO#fOK|R5N+E?)BVi6 z@IYog#2h^3HyAbRVh3?+XtaoD+@uRJSaDv1H|vm!7G0eklf28SvxX`*#y4mzel_V4 zHzV_PKr|2S)B1aL!0Nm(;b+UQ$im;(lYGmg59$?9xDPu$*x1*teErGSrp${YiG0hU zZo;e=b%79l_`)r5^Ag5YGwkxTqivXR4c(S?DSphgux<3GVdgsIrjLnCy6KZIjL^85 zYZPeyZGLi0kmm?9%yf8zv&awHI=DD0P8gSi2zgy5ugX8r3)}JUEFOJXSQcMpJ;yvJ z4on{cx%Tu1={$?zEVc=gruXB#ipVl_Z@+yK`r7z{voC0iSH#X0gs^m-pBojxiq7@GhKV_G>Vo zzI39}OZm%qaXI$)B!eAG{EV5;sM^f^lhVx$PKa174p}zUG*IHd@Z}Gu_a41H{o0#b zoQTe6O3t32F7bHs34_@UK40?w`yaAHW``#c?6TmMq{}yUrVrlcvvz*AgnG0eI1!NZ z7CUG><7AUZvezz=nG`a`tnsmU(o!jzjv3CR;5DV^*dg;AcKG1q52hb{_kWpQ-TY_ZGk}~z{Ij&d zg}G+V-9A=JJHv@so-AOPiT}A@d~f=lH+Vny!8x2|`y6b#L!YCLD35q<^SsFSfp;!F zp8nzQZBJk4UEb^4AJ8_)rSqoR;OXRu;`{&KO8Bh#9?9>hj5g>I`A2|{v_-RPgwxWm z(uH2)WyUft!jkzi9^z;FS+ZWoYPJ2YrlZavwEZ3x*3qrQJuP0-9*3_!dBtxayA$M( z{Go#V zsp~=KP7GWyd()3Sh{xgL!~k0J4NkPh5y@9~Jp01kF}+~V?i&jC7>CIAd3kX-FZ3SW zkSU)d$6{nPoOZjO>%W&?3lU;Cd zxCqPQ-z)RV+7VQ|92=RJMvr}*_nUmuTAlW|KX^_;Ew*c%Htu-h>rUBz?uwoO+f0`5 zd1nWXPky&?FsXNRLgy?z&?gR)P#GrzjuUWsSY~Mw03=PqJhVz)#6OE4Ow_6u1%`Gs$NS+>)qRx_-*eH!b}}xSPKFoU2hc?xr0OS5EYX z-njXO!6jda|q9ul2?K@lM%&=w>U}Keh`-G(Vk@Z z*SZ;(hoSS9JdGc;m1?85GO(kgdEF)gCQIGz0;*g2;E=XIYH!#MIA?_Q)lg* zkVN+n?{}=@aB?U{r-VCW&?NXe9R@lKie4S$>3@UzwT?Xw>a=hNzK#w5e0|B_>r8U- zT*Ncr=*aMaq9JF%B<7fgGX%Q4S_MIzILMKHmz&)M*DFK*fkTD4Fg(>kef-R|P8WH} zTet@f8~los>G$7%Kk_~7S4Yi9oVDqnY5zPhcu6m#gC^`c>@ro($m|40PoLNL9Xc)(Le&BJq~|&TZgx9?Du>@93EliN_<2eG5~*d) z$ZH~l@UZM^>#Nx9kXh;_%w03=@1?9L!PA|bXU9Vz$7)Q6Gt+xUJvh=)R;nC&x}@h?ChIiLqkCBk?}-j87^UBRbj7 z&ifej$@FLb&}-8_ee*Wn+Y9*&w)5o8t;f^5?>vY{_wp4_0cTOqr2agoh@ZQ|p}!7b z+svSC;yzl;OwM;rJtXt;bb%*(z8Z7J0|jUR)_HfZwnc8RR8U`qTPz2u!nFYz>^d|Q z|Bps6J8aBe!bg4$baitPr9;c|>c1W!l2V+8Q41bNI{Egg@Fpvn8#kZl{FoP}8yr6N zoj>@!Y444{JiWU0Cc4tO84YvLX5YkG*r&MKZq>v<@eYNC2iOU(m`VGmzj4)S|IMHJ z@${E}{X7SS^4bzNvz>eQIk?$fKHRXVvrXPZxOVyC^x^w=r~m8wzc~H3zwm3JlZ}JC zgP-liu&PIZi=UJ7Th#^0vqe?vn9w(}*>J14s|uc=&4(E$Wl0zw>TegiKAX37)A^4bgd{ocEZxZ5drC7D7T{*hU97D1j-P5$ad z$QN78Eb%j4ADkSu!1ozRoPl8=KFo%G;#wHpTk@4%LU3&f})|))%Uu9q0X^ktQv_mrJ1IKvTq1r;1 z)oXM~zF1Lj{!P;Z=TM*?1ofZv&BKMci&oo-f#IvhPLRhsJ@BRcjwgek)~R_2x9)|@ zPa6y~Lw@J-bmwBWWPQLNfJK(@Xt=Q&w^-eA;9GOI+OLLL*qUU3-dg{*S00>t+(FFP7|FIeui4-hfWPY zTdniO&tZ!rKjZF5@i1X&J=E7=o6s5ZF+v9-1oaGDi!%`|i7}Y3@%iz4X%b z0Vn*+TRu8Kl;_Os~=U-&}g|Kuk> zVQp|LpY;}?Zu0c&PC9IqWm%+CPn|b9UM!b#I_SJnk5^uKW%BD${R!~SU)JNH`Vm0@ z{Q2L}TsvW3n=_5_F98CZ{c31F!V?#N$e3}>=&&L`MCLV)`TmX2Wr-f(@aKu-TNihO#4 zlQ7X?HI(~t75dC+4P4x{SL)sZZ+$+4pEn%6tmmG*70wQbhBf+B#wYw;)c>&@K5Mya zg{>13b{VhydAXL#;lw$6X}7~TpYYQ-5qkqUq)7J7`oBmj2D{&nZlG`c$U6yH0jj-JRDaa!%1zk zI2P{OUlEo_^5xGPzKbO1#+iFjh%epToBsGKKb?O2jqPcJ$I#BZy-9duJ=cb>$|6uwjZ~c+!mtXo$7&XFy$09Yj9~{gP(Y6y~kM4y0Ayl75-j@iEkP@~P{(o3ltKwb!Q@4M5X%yFUr< zsQk|gu0x>?;AWwLvDnm+!u1nL<$U#vXQo%axIex7b3QG7pYZ`4*Ka;;af|#WV_lpx z()+$L@C}|o*pIR)5F=-MELoPD`5EKJ;K9|2_XfB=-S;Vv{GFG1;I5s+30*CVi&mYa zan5Tk`-QvkgI?Z#c3U1UN=#=Apm6SFaW~94J~Qpv3;X=JbHz!rzI4wi(9wQ@T|owp z3(J@%KlQ;6lkfnijbC5#k*tSFp5s+W-cZf18;zJNvT*%@C=vivpS8IBwP zzB=i|{v0xNcxhLiNglwwgAFWOEtqRJkd5B*F<)h3t_4&KUAP!yj-97p!t%r%pktgN z_CQ7gvNM*M)=pfqm{$iuz+`A#dFt+8hww5_cz|ev5{e2fK+7+ahFNdon+{Co-V?PI zaGx+sYg^1N00wS(h+B@pL^M)htY_j30y>Zq8;KOFeJq_3JoV5f+DwOxb1tg00*dHx z6URT}(OLYY2YVEYUC>1RQm-i9HtMJ9kOVJ=RG?wVihYa^-c2jQ86AHEJ##|vk`GK9 znH4(`vAvj9rtQA$ZW`bS!lbthEvI3k&;$>K<`JiDxZ6L_$#y}=oS~6OFQSR(erwtI z>xWNqt_Ty%bxC7ddp69OL>I8zQ&ASJ<%rP(EW0mx$Y+^WY&&_imHniu!xVY`4f)b7 zLv*U2^u_Q6LHg{<06+4yQ!0f^7#82O8*v6xWw(BU2J0YeH`BQe{27Nx*|ORk8l?SO z&#k+!_;lPh*K+#0U|BjY04djyKZDUly}Qi9mDl`)8(+4o(1vKyALJv9U|$YC@i^*X z7(VcIOidF5*E2|T@}I%U?XBf30q&t_`%y zAwri$Aex7!&hJQc{wcoRr*K%{qK!gbQX>-gS;Gy`AOjHgopX`oE$`e`ErR2L;}o|AXrX5ef1)#&-89~M%?$5h!xX#Y z=09W5r1JYj0os#-<@j_#gHX!cTp2(z+IaW(roa7Vb{o}}H$VG%ar(e9CMdbVqu@z8 zcAGlR2#vF@ASdx(N3(=!`&08c3DNwMD)S^BOXW@xcM|FrE;~rw2y{QN*r0x1eer|o zrPuFFZ|!bQ?=dKCoZ02D*qdytd^p|V1jkJt4R2rYDaAg!Wv)&;47eYDcsB!qJO%> zM85onZPg`hCd$E^4(|kcgR)$?dW%CPUzncb6M%2yW&hFl{@V1-t$&gpZ-+<2DofYp z3H7zEZ5~C^Hj-v7P9rQV;Q*B__vD$K`9BQ)vv0pQ{qXHu)4fNWV*fEZb7y~Y;XJ38 zQzsi2xDoD8S6I9~*m^Ym`tP5gUVZNE>Ds1W+9JRx7Iz^0l)=f(99H#TP0xsn$VoV# zQpHb6zEJejN7bczioNx?U@JTxqp45oIgQuXb?w!>qu@=qdTsngH=a>CZw9Q3oUVB5 zC<`{)P5LVWW#>J|vCu4cf@zzwPxa~Bw>V?wIp6@DP^T{?ZvTM}LKi&-U#IcNrhGAp zdz+K!Y@4;c6L(P(d}y*b0-moI0q6Ny#w!ouRZhML5jq)%935CV)BoI|o3g;h9<_VNa+I@QN0`CIO%|FAB&ZRYS>zJu_zJyf29MZ}xV)n$af6re970l;7JwYvTGHn6*JTlwFs}1>OnS}} zL3snmi*pg@%i)9a=0*O%Y5QedkbL0C%8>kF&yb8VL+s>4-A6;g*-p^N*9Y8~IJnIuuj>et2W?r0 zx1se3D|IEmgD}%IZ>)?1Sm%xW*tGgZjlaT+(`=W)xMbA$X1vGsmfPcnX?e8F8IGMC z({g_pG9pX;DVMMQw@kC?_A!cOwc-0v;Sm+H#aL4?g&i-6ZeuMZE`c zXgubL#unau9VqX;_gx@=Ey2$DmWhKX=mTC{ib#LgI@f%=M3YnC^AP zSHJqzxg6%JqgNd;%_{=!Plf;6rh6{8#fy zs%ON#rgA}E=E?FR6?+-PI0Jh(VhCRJHzWCt)Qaq3okv}?@O*5BO3B$PO4Vy8QeN&M2wZn;b~&U}xBKxv`D6mz@`nAAE!(M?YWHZ=U>>m$C!bU22QCBNQ%HYs;C%@7FME3j`9-p7PaCy4+-0kVb zo42P++!=qHuPg1p@jp-BIQM25kDFtjepnvtk--8M&GxW1N2f~5gZ!EPC|)8+WSEf; zqJrXh?u@%)6u&mw-M2^}dhP3XwnXU|x@QLNW_ zjI*#`%0I2n&!W{+JXaO!`aOc_Q}jpRE!*}{D4zu0H{Up9>?5O9J1*LMnE!X}O!W-U5obgFng559H?4Z57WOsSCZ5wF#|AW}D-DVF&hXVrLBW){BSISH~n zajuZ(kl}7ZcM%D5XN>36C_{Pb??JX64D72ue#Pk_pT)ME*$t8X$d(!B5RPMMbOz}d zkygi&i$@P!cRX%8WRaqw2f(^AsU6kXp#9>6A@1%e7k_aoz$=am2R?L?g_F8Wrfo46 za)hv#A0YLJ9pI}B$KNbKMH(c^wCx4|fK9S(54HDL!QrZ0h!?mM;Rz=KcvRq6W{DoA`VMl`2X9315D4 ztCo(xm;m`PoiOxLMFVWAEJ+FN0v6BEiq!Hzu|K8%!K>PQm0MXl!WvgH%Oejik*kfB z(Db6Tt(*(wRl)%4M_S8f==$*tChOLFZ5(eh1gFc?Z7f8CRzBjWqaipNb%c}?K5egn z2X5npc95h^FY@VQ}jYBor!m_9_RK* zJZ%6%b;zIjr024S5P%H<1pBZ&W_wAb4unMM|MoZa^KW*@jC9TYYCKLEiZr?#%^VW` z2ZgDqd4;cX{x5x$X;9z9xpojgY!ebuwmtuvBwwzpEY;~5up!-4*l_BZ7j z$|iq|ZL73R;b)?^ajbmqbDv`@ejI0o4v1SfZ*gS7rR;)vz@b%FafrAJ)R1{d(guZ5<{V{$jMMfjk7#h-VqP4}js?u)JV(!)h;KKm zl0$5^g6j(UQ-`u6Ml+&PT+f_&0n2c5;)8p+arpiD%iAAisW;<&0?F$Nv5w8DZF2%* zvb&)y1)V^*MExw{r!DuYEY-9lc;Bm=1Zf+pHx7QDyYP7W6Q6r?`seRoo%T7|dc*zr z)Y2BC{-sM7rbiz!P(5VOU`D^i?vabv?@k~7?0s%1ml&Y-7+e@IFKsfAZE?!=BPIke z_mMH&mFtrpL-gR9?V4Q}8^XvBlKCUD94rK709#xKnvg_$kZAZSz5}F#Wd^DdudU>~ zn#EzaH#j@yxvSTwk9O`(|NOiE&-BWZzsDBM-Pn!w1y{hKaOZ#g)~nN>eEEBW4~3QC8rh0Db@;UOrxsL*pGI6a%V!jMEV@nR zxR5$K4&jKrZg(f?oU!%@9)ef8-;BAI?>gfOn;YXyesW|Tual{s!Z=d=;BQ|D4i)i) zzT2v57Z(>9Cv;S|K4j>QG_6A>^cW)xGOk%9KwFCna#=)_^SQ&36pIJtIlvBy~} zIyU0qK>Ns?D_vxq9!$m`@!Hfjll09kCx029!LGOt6dfZ5ccVPQiBW}uFaP?y8=Wo> zcMc}!>bl1hR>$0JUP1B(?Q2aJc`g4w4>#2{e96+e;A>Bgb06Iy4;{}V*Bvt6H=N`! z-%R;k19NtGUhDGwEkBEWj$Jf~E1jNS!zvS^V zFCvj?Tt#9ra4aYP^A#KEg$H0+L?vl|(dN0Yp`fF#yQXXp(z|uqkO`tR$jB>e1&&_1 z|AMO>MX1SrsweGH{mrsYY%earyrxgePcnYgFFOeY@L-{K{UC^f#ZiU~onlUALkpTA zjw1@9!@+XZ=fkVb_p3}n)VAm%PJV{HOxkvQElNF51pZh>V6@|+_aZk;bJYT6*b*aDdNsUzb2PdqZT<*DKdQwM`rZ6ohacjhXcWyu%;k$J}j zKSv<0!R`EQ`T{A{zV8@8;77Zq(DEg&O_U)IXGSKZ6)ivJ5B}1MA29Z@W?t+6(nqj#%O_}msoJlWF1I4rpu zZ`$ZTDZMAI2oYp7j)2Oui01wV6)1x>xv}ZYWq8m2N*M2nv`b+9pk4*T_gMqz@8(yU* zW#L!`^z-bWhYx&jtb+8)4WlRvT-_Z2M%p?ySS~f)#}?`Dl)dHXK+VpMQQsl(XV7LX z0)6YGjSb2-m^41XhvG9(#3hnxf;>a-0M&e>Y2~+$l{)5kD4_^x+Ceo_w6Xw{S=Oky zI7zSQm*lYTA;(Y43Uh6p|BS=TA&~D;&|GAiKl_6HH?Z*qOKwS$kwJgKo0+q_=tDk# z6Vcm~IRAf!=#!tLrP|*=Rj_AUI%e1^wkstpLeh*16xWIpt8MbyBgGoGU-(Jr&jhSFlcl1IdYKL!q|7 z65syyE5G>G^qrq_$k~0IFx&*s;yl}+ULW&Fe$xY)c~pIV@8a~B*O-3eN1vPi@U5Rt zw{V_nGba&-L-T6{DbjJ&PiY^gp}L5%3`_l%@J}00{o1020QEPFBGh7Pz-An9mt)W} z9%Axae)kM~ zLp`I1!a5$^4%(JWg0wrCwV{@o#7j=qjtP0hCbpv$hD)8A-3%krwp<5Oth&!Q#4We_ z$tyJc*ls(Bn=t`c%X9MULRLjLc`_-!H+vUlV!6oC;h=*-d6}Sc7u69HM})HjCU#E1 zTrlfAa4ytC6j@o&Wx?qpjsL#Zq%OY3w9ka%46}xLL2ACQ{Ae$gB;Til2G5*v5}$=5 zX?M%GRpL$|4VF`Cs)&M0@&V z&ti*XUfKucbYiAlVg3Y{tLc;#{5bl!)+7!aK^IvRTzo}Zo*eSawN24&UIbz8wlHMp z&d^s-oQ>dMw(6@arftvB6>rxgnGZRAPo2jPBEDACyI545|EUw5F0D(}HptR`>g;{l zwi?;ds&q73I#KmZus!?5wxa!Gn-yG>%J8obUv7Fe0UUl+av_v6l|Qa#0PKEi-?nV> zo4;6PdkiD41b2w3zc}ghWcnF!+qcfH;DQ&D@FE6N^-7l}8CvXMm>s#`CP+xgeMdQ! z2rMX}E1oKEaUB1HhJ9_*U0z|zvdxz`kR1nPN>rU3KXWK;tRsO`z={BR+AesuFCATu zg_c#g^(mvQ1C6Q6pcl`y$^;HSlL_ENuaW2B(q>v_Uq!SbN@JZT5U)C_`Q0o7`jQQe z@f24Cksn|1mN@IE$FUlVv zTQ6c4cq>H5k_^Vp>}7 zSuWDe2sT$`+0G0p6MT|K8<9RKXv%q0w9*)I;YV914Yu;yu@JW8_bZUmVbHdtU%HNK z=NzgW7Mx`yRIh<)Hu1zT_kl1d%UtEBf; zds5{^lOeBVF3IIuHFa2wIMI6$wWwTEuByYLA7$-wLfmrA4~xO4WecD6C(m)!vCHP* zCV+h&O8F%=A10);IL2v4`i}FN4L&YltJ)`?=0!*HHEIV6@5c@!30y}{7=WiQb9<5h zQQ^3}RhnaPyZmlmO=vxvN5eaP)K$6zTw?qew!e8R1N37KN_~u_HqX3OEHbcGRUgnD z#l7IC#aqkeQxUJ_cqFimdKAKFdxx_R8}O+)|7me&?Hhd=wZ z?@zz_qi;+f^Q6-ihr0+p)N7NTkgxINksDk2dCYqGIZi}>{SDxHw|Re)S^7C{G7dbB z_dNTp9+ZKKL10}WkSlyu|G*C6KZOy+8-*naX$Ko^QUicoTV`%_^eiVIzMs3t8AoSX z#a!n#rR&#ToNiycGTqy~JN?7o{`=GCcK`Nt>)icx+8hw-MCw^WqxV(XRrQ-+!mwKmPKxd-le(&4O(2{9X>&^zhC-9?@)`+n6r!HKu!a_ou)4 z{clfy=@qI`gRpMaa-p>Tu zsm)pLo_oH>G{zr!WHGN$c{7yiuz1XCNE>WLw=Q;>P`I#l5-zR}8=Z)|(DXCZ&f47# z;GYW{byU6&`?BzXw+qB=7p6Erq_rQ*$C0oWNazKQJat59iJg0lluYm+KH!xg%E8;3 zITYCP`FhqvWV_hR6KVAVM< zDyGv|;JVO}NZ3jT)UMi8f?D+U#GLje4Vl(sa7aKyr_+5cG~qqma5nW4C*Y7 z_})t^LAg^f5V%GJIJR+Z6nbg>gD_VdIME-N_U=%!q19&a_ns#|MWxz?>{$ok>e35G zWtrBdwrNB3;hHmPEQq?2mobW{Wm5m*0Wlg{S4Ay4I>5M}_G??QTf$-^WLkzuP)8#M7>BpzHffqa z^=k)B;3GdsF?M31Rl76ney|dhE1h*|KglAEeBr)|r?fbxtV4p3n#0&4%q?KrLKLyS zq^)u~M#w9CiM#kR?zv5(8?va6DB5}h%>x!a%!5{8rfrArgfqy$gMA{-vjIq3fJpM# z&xThTryURR!8X4|OJKTTi6bW}40*PEEs)>t6hp3jr0LLng{g1yrEMAFsti{zi3%f7 z+(W#Q2`poR_$_PjXj#oKs(~}Z!Al*CJ`p`5u;l7k53c2U`s1`&+ z={?6?FjW9?K#sq0akhZddK|!uW>xQu=jgQL9~Fn3@IjVW;XV-TofV@frIHmfl#Rp1 zPGU&;TWxuOCm(P#cLbs5kY>qZlYznWYC;Yl|H(6bjB+~ueXcpfV60_5ZAP=E~^Ab1bL`;2kWKFC#>ZdZ#I!L zY|zL{*^SqE01!abRLgE1xvXU;y% zLmuZ!4!Iet95-ot`#j)sr|uTJjy+4yPh{tQ3OqX%5$v2nV>&)BhHOpnQ~>kouoie2 zEhwkJYpU6%0Oni!Gtf7mTpc4uT^v+}%+9}*CpY^Z{>qcDOVdx8kYF_oS+Z;>pH%80 z6`oFfqqTDc2dm$<7m3pDKfS%%tz`pchxXZo$5etG(U*If1Q(rp~70ds>uf@Q7T7zMMlRIEZreM z;jLR=2m4u0rw?5v93jPAq;m&EPvtL6P}qm?RzRzHqoAwcAvWb&au9EBGbEf0nI1)M zvbnr7?kMT%SR!3ihypcXk%@FJ*7kJ7WE{c9*x9nyf|#)m;b-VeCBq3DV_-cr%tXN9 zBRf%`(diIp0RojLP1s^U9X>9eoLpq{q2=PWp58H{9xiUD^EIMl>G5Welf3{ILe^A*GE9!?v-@}w?0l+JT_@FugPnkWNfrwp6C z`9dS-eSoJJ!y&_j_$aWIB=xzJ3yxHMkU88`q>^~_MwAxr( z>yQvevSg~OeFb<9m&Q)fe{|SnOCv#K6(5rZ?J3V*RJXvi4~VVvwCPda(1cHE*6ks- zfoau4SW`ZGQUyd*U5%^fwwZ6a;%l+*z(kn!on0i#B%X4E=WeMmL9aaB4W6KatAl@N z0bgy%oCdz+ru}Gd|D5R{DZHHPlbsZ}WbjPgs^rk6QBwCq4+&BR?H4_4Q;EbL*7qpBsHtPG zGcT0!;YGIz>r`rW6ecS*h|cMpWtXN8ED}NkUZO0cCagB5vpYXP$E?|@0ELDj!qsE={%#pX3A-i_Lpvy33w4#7p3`e?SY@3^4jai;$mX`y$r*h^Eb(OD| zsuTDd;Kvz*G9C;pu3wLm)?Gq6WIR(w{@XmkN#BQ7K8}HXjV&^OrJ8~-UvZ*aO3ROh zRJK>b93ANZ{pqv_{4H&2d^Kn%>(0=mUTYMA;%Sn@um#a5#sQ-R-)J&;Nz!#!`c-*a zw?i^XFsms9JHEj_#r>n^UNtURG#?`QaLETK_MuJiGHlRi{F<3g&-Awlu)Wt@u9*H? z_+4YWqohQoMJC1jgkcC>G92Y);s)ETuQ0KCn8DohWUg<%kL@t%OjjN1nWR^nTu*XJ zBP{h@&*-y*+5k_Ix5POLceQA%=})I|HBOuVul(WfOn>EH{;}yJPFg?5%>C-ca}1i? zFqmoYayabX{(Z0*Y&eJH(sQ?_-8bLOQ&&C8Tl_s?6_PD_%m$d&gKHhz#n*&o!AG{Z z9V%|Rcoh5~zwz`+d!w}rrrQQ(bYKkX4Q-E^&IY@AF7ukw?HkWcx32P<7N5-fhyU_# zO}8HW{pr>7o*wVbZ_!y{ku8cR8G9vvCAqfj%Tlc3ce(m{RGaAhlddjoe7gBx{(Emt z-}_rvc~XC#h0sG5P8{mFeS-lEk3UYeoKDXp_lJ0w|LNObpT70{yVJ$Z59{E$)MWM7 z`T`YFxiMQu8E{Whg8ZY(aTNR%EQ8YYb8{L7?ev&i9by;zlabvej@$yITUN}VVD6i& z@W}+5bVmDuu&hAhEqCOuG3eI@G9m;gWY zm^6GkeZaC}Bi~71CJ!S&d_#kt;4Bv_t1L_}O*MLD%F|^lrHj&X7JR7`<(lC`-PTR{ z&2&|G3!YgOpH;MFI(7!%1%h?#L|M~1AvSDsxAy@<=7z$Z(}Cyi5uadonS3ujBNBN! zJGR&*BMtKcCV2-Eo!g$nF}#aY#Y25Jx1EetzONx!H_FffVKH5Zoo9zhCKvEF-_KY3 z8Eg-Mv_JY4qtpwESq|kpnb+yzD<2#WkX7Pg-MGV`QjOT zonaH*bON~;ch`_xzB&IrJ9gCRW1KVY-qG3Om0LzVtwN#LmNNP3)gF^2=CDj)xm#eL z9e4=$$yUrjQ6BQt%U8pq3#-Je2guYpV_c)^*pRPHUp^MdiJNUxGo_7a7XMIixqBvy zA>fuTWi&1eep~=m%1!?uZaps8K|?)K9{ZC{zqB{B zvtE?1;*TB<9Qbr_+_n^o-q`X9Vk|{UB-)SqgAx)3f6LMRS9ojyP8@Df7}PUmF#+SG ziHh&j;I4bK)zdOr_w8Jpp$p8>ZF=BH-FlL@%9!J z9dkMJ2)3(yc3j!o68f+h`Qb@@c{SSrYJ%knuCxQ_y~KVPd|-pkppdrm1o#T3I#zj# zZ_|d|;5%3c!FH0mRQ6!5Y}?mTm(siBWpM^loK$6=(ii&h7<{}`J1n?zgw1~oJoQhG z{Dv;62hszrFi6=Jh+)E+w)+Q;W|X8h+luYbm=m${bD@rjb7?|k1s_T_sdK=QhxsP zpVKDz3Mmd1|NIoTW2L;!|M0^PbC|F^b-ws%Y!5s(E-yc2{lNzx-m52XWFyKTCQpD#g4dEl-+7TwaD5XPLQ(D{m=l$zS@Q4*a=2(q2f>+vi-nJzswYuJh9Q*<(8nI9un7h*2=g=e|?Yj{aR!?xT=LQI5(! zHN5-u=?K;-NJFxa7PrIWvz&Il!8h!8?qC?d0C9J^a^YQDga2a?tLvG=Cm?AVM;YuS zmW!s}td6}d`K`)a$7>a*nJ@V?EGApcSjTiGI62bfHs~*0zc+pT=G)Vcb}wWwVY)G$ z;Zv+z?3Ovt7nbhhY;k*-cgRaQt$bs8_wA49A$WJA9GxMv6WT-eQVUfAf3u*{$pLE# z7AmYKkOu@!XnxW5Z+M`02ExpGZG|-y*opU#t8=JNH8lFb1DHtM%Yy$Kb^Yc1_CZ)z1)CRFqq&mxI$u?2c*^C(*6VM|?YG{?fny z#`Hh^qf65Ull?swiVtwKZBwUbef)|2HaS=48i!Ea;as8r{ky+7{l#zndKRX-*c>ah zgv(7Qo6a3iD${3Gj$`>)Wt1^E9S4-jJy+KV>jN2`w|ZR==xCguc!@V~b5SlvS++~{ zs2VRObL$y&3)V!d?d1+KGO7vE9&NwE7Ig9r?*(j-1utW;+r4PxqAXv*YdzW?htf-h zWw_vFDW;IqL(J4Ubq$nz*vDJ#~pe#5$=}rBvGB!=#=(2Ne(a*_h z7YE5Tu~IoVH8At1dJ;pQae?4d85f8yO7r>+og<6s@leN!rFwXlg>zqb zr`dC+4ZqA#3)qZxOwz3>Up;biD2=0+i-*{Ww&!b4>SkWbYuye{f=!co9lAh;Q^tjI zc93ASExwebRCm{GbM%)obj*15Oc>eOM|7%K9(>ds)~h@+qLq`=+WDgD4|n)+rUT`@ zyYM3Ka?PdLeN%bvlJSg0%a}z!xiw{Qmp^IY1aXw;QL>O1oh*htZG_j9^`4}!}s2>8+wLSv60XC z%k7r9B5j9;{xrJf)(&wxke3NG`Ar*JQ(x3{n1c?oIuxCK#Fh;BQDiNwFihz=_)q(&mRu&*73++J~@#>!oQe zw{XkSFl8vuHk!JJuT`jNEx)fGh+A!^aHUDRRifpDw)rWboA}6{^OS2p@H#6<`l$dv zL+~nE@`jMWo6o>&hi{dEdIjz)o(hcf6lcoJ~$J|BGHrlG^ZFCIO>1Ha1+$wPH@ zPWW}+9h@D`<#mwAule4^S)#+`)^pEIZ@>K(ZG?3Q%K5P09Wg#o^g+lrdi?z7@5LE& z>(;F}HLhH{3eDSH?0jWO2g;jozM0)DI#|?G*iU})lQ?YrthP>-J9q9x5AVM^NyL5W zrI+$)?;rf&2XV#-Z|96o9eD|pRz5m@OluR%=ARB9VO}rboH9>C3%h*D2ivw9LSHLI zeRxl=JmWzbY2((Hb?LQv7;iSqHqm?<@1V0R`jEscL0+riCEq3b65cv2^AjHobFVet zh(mGDld)Z$^=3|oaR>1e<`|p4noU{hGR21elzR>zGszf%d9pLv@ri$q|BvwCxC_^N zh3zMzo-X3xc>2q#J(iTm6w~pgfG%DaW0|*%H{KHVEb-Iue;Q|v(yNL*O|)LERk{@{ z?-$xA4V1iJdG$xrfA?$8O&6X!Ki$9k@w9#RDhu@up3qW%ysC8P!N>GpCy?j(YSI35 z_s(6myzg++tO5B6HyED|;PXvKsHa2nFNJGic933CXW$(auJu~2!{+A=%nr?RVhltq zCYTYNLD$Qy8ZNUl=JvJcr>iVXe*E4C)9b(Ycc(Az{{~+lx(7vdPEADLg%O<{m^L|^ zwt0f_R+L|ly&k$ky0(q3;;c)r+GQ0d6B-N0&HQsW?oHq3HMQ@&&8vkxp}+qTi^Pi; zc(q}hEwi3J?<-8$?aAfowRi7K|N6aeP2ax#yV!<75qfK-ZUyo)bx^jG^Z zt&1FpFI7-U-m0c{h68;`kmmsH4UMrrVa&TKvF3L_t32qIvWA@E2a$O#aD_*&1y;Z# zSvHlt^(@^>*gUCRA#U6Quzk*yH?W#~o6Kt`1F6+3&ZTa{7S%Yg7e#6UcxMD3lK~O6 zVe@!eC-Ry$cn280<$B(!G)sLtGV^#kE07k^^*Jka1is+I?XNgE(KW;&L!7zQSXeSP zIC1Vpf#~FW@uu%u(aC@zi!+>J*#QBLlWOxE^YT;-WX+dFw+(fY}&NGy!Z*U4ea(&6GFVb9lk zXwR0-;SDI0PH1o%7<4B2aIs(UVFxx7(qT}0-? zgEt{l_Du@<#GNkMfO77tBjxw?D2F5$U_J`YfRzybq^%E;lpEwiF_{H7*O-N$ zv=MNEPuhAAR(2~q>(#Q!%0B}rkz;ui=c;a*2O%hql`t=UI=8|D`zpinTE05jF5&dj zDPx<=oD`1XD;{#0-$$@xlPr@~d}OK!0eI`C?*7%G)#}CHpzECmzVae3+vrfUuE>&) z4l?I;I)iNQep!mOL}Xj{@;oSm@@d=Dk-Yd{wt|=U>?~ZZ9{$4H@(^H8g{UWR6zsle z|Ii-(eZu*r16+pb5SDrw-;19D&x4^^gEO z{716Shfxo|5+c53D82exwzIxc*Wk)NfAS`h`!+%xOR{Tj@ITN4K(`&Dy@M(?qVmv^ zz6ekL3~giWr3rdb(rre*s@pnm>NV#`Ixm?K2ikl@0}A3!w5k zDAT?j9S*=%{^S+rStkLsqh^%DlW7y@JVc91%=1gzHA-4G%jMkEaXm*8$xDNPu;-Xc zWp2>#WVssDC*uWrRzFm%HO|-%A9`I#I-Y)5pjkf=l8?wOZUXPK8|3xZUW>D2XJ;oc zoh0tQy6??}pc6&sNXOk#;*OLb|M9+ zcX9lLuI?r&q%&wSy^byZpGnX%4W+%(X zk>5J??iC$AI$*@n>7$N1VBF>M98RBxd42K47wK0U^KKW*Wcj82>Q}!SIvpye)yG$s zIxd|&bl4bIzICHMI$wIX&CTmK0ykfyU*cE+dv7~xV$emK%kB>F{Ab*MB~*@c@%sN>QkWVMS?9{D`@)@zJMq!;)DQ%L%e)@N?Ih3)aS(o3) z;x+DJJy)n!lA&){Giau(*Aw#7IDOP*2j+5tC#?VBSN>r7$M0O7HgIa}Fe%u1`@QMK z=Wp^Q4JO|l3hUke<9m;I^0|pO_}p}tN2fb%8@tJOQSaZ~$$S1jvdjY5GIk{ykHF)V zZ1kl~8g_`6){7k+7MO%EL-a=BSCy`DX3X{L&rLV^T;I>{-kE;$hrctO`QUF)Uq8>; zMv&R|{8p5~Hw9G7TURFdjE}BoyfMxVMZ`5>jAv_d)`OO1T8~lC)8ZY+!@Sz=T6VI` z|LM2io_^lY^mTvDW@|w3bWp$>;tVn{3**qbX*1RB9X2z_fgL>#^kQYok z>1{ETd6}m@J3PfNG}WdWXYF+ip2bUTZGqK3#b3%>M<2S(D8Z#doP(!5Q;&P{9%ZaD z6o>9h%};r4ilw~3y%|z&$LuHuu4ATohQ3PQJOb~Gs*$t->naoD-0;C(5=eAR=yCc` zIlyl*OSo(f@D zm$!)60A31mQLHr2d~mnTW1m7%e!unQzny<^<;ehf8vizD39kUYx z`xvBGZFyRsqq>u)GbD`9P#xOM;(oyXNho540<{kF!l3~4v>Yn2ms7?4yNq6uK-I? z<*6V-+ehBUZPRA9JP%G3-CaVK$3?l0C9#xS`e`lY`R9bX@&P4S4~EF1jp-l}Kf7MI z+I~%^-e?2GXS4@t)oYa9tGL#=WsrC54$!OPfI7)TyiuGKH@Lu-0ki~hja%yy_IM&A z^YSNO^v^<_c+X9gm4Gg`ndlE-hMNh_9(|&AS`BGl2(b$`u^prg;7XfviC|pWMD1(B zGwx^`fR5JDPuyXr(32Nkh8*Kv-=c=kXorzIGdYl*GD4_q|5D#Xq8s?OZ^yBm1p{1r z``7JTJVYD>aBLrQuncpMurj;8BQz~=Aj)5SuE`r$snnx7q6if%v-+fb5R~(a@esKE z!hD1Ig;VMJmARpi%6`^U;qnVTw&?@Z&?F1q)IZ3O1UfB1Rm zyLA(%Ep1GDx{F4BY0uD@ud~KF@n34ia`+Eb)z53)u+1GnLBE>6!s|9l)LIy-ber2Gih8KDEht4$nh&W zKY#D%>}bCMzF&OZiA;CoT;Yqb?yTAN21;RG{l+(v_wIY|O<(-dmw>b5#K8%fj(5sb zPCGd~SLSP9`&v|2UOP)}-nt8@|w+3-psRk zwTuOFml=!DjF}VZltHZi-yTyuGsc2!@vz5Nu!TD=(vl;>dBnt-db48UhsA!-!w%5};(WtVj&!Nmt)O8g# zqE~^3Nls3+{xLEWeU$$*f9TEWU;XsO>7AeLv1Rb)^x_LQS;XJZQ_rhcu1$B|$H_*^ z-)BW|@v`rR@_AIekuNa-Ke>CCB5(1>LZ96#3|WVC=)p-~4OY3O24}TTF4Ilt;5CQn z%%qP&%>47`FL4Ow4Ls?$rkmGq#v%AGe)Rq6-W&h-^!4rUOi$P?(-YD%8v`Nj&pLKk zHg3opAY8p!9%yII(_yJ&=N_v62A=ZzX=vAq;3j^?MHppN*Rwo^`mg@PkEXx+TdzzH zIKW|po4^x1$`|p_yEydb!v<7tF6@eW|NiuMe)vn%pZ~+Zo_=pldS9rU23O3rTK=W} z<}K4t>GvtxZu4y!md^?3CAN%u+sfxjxiXibT#m{d{I@4 zP)=@~Ny(5&k!9RB4WG=~hy&ZkQMI*k4_nj_S~Rwx@t3m6xA3L0HPw;^-lJPQW|p;m z<^f-x`4EXLT=}AzZSe_>l@v5?W>gpWqHO-cQy5QF_&e+FjI-=aB;VHtH#hE}A7?=A zox?HXM9DI#*N9Vzr3WnHkWGcOzjDu?%`LsI1`nTPrKYKozr_7NfZgjsJrLD#m7GqHVL-Vd&x zxvT1GWS$E^=676cM@$x+q)A4GUqo_ns`hLPpvAhgL>|C<>$^^q$9#4gN?_#Cv}Y#_ zyxe8ugv2tK&m|%9$O|v^0Ov9MmA8qqPGpp^g`>uakWX86HhCtD4v>5I6h_a2p$Gap zDe?(+&s{JjtLuBxP=qV|*LV$i}55g-?S_{1vYp@3*2 zaW_U0>Yl6TmiX>`Y-%?7)#%Qma99jJmfa*l)S(Z8as(k z<=J1PCz%DRJj@Rb^s=;V3yWQ$kGz3r1GHUeCuzMd%WvCj$5ZFS628(L!B$uE(GA$pNyys}MqnMH^$ zgO_q*KW%2e2kt_o=2Mhpw2RaYaK~HaX(#@d&4@W3N*qT&SA#T9(#RuB7`4TVdA9EX-m`SX|(uOpAUTKLRQKaJ;2vK21%tccrh*?gK?c5 z2JK{hX53dV{`C$W+gIo)g#5ZbO<3OOYdc%y$5}?Ej%x3`-lIsBeZcG8i1uM$KlL-d zd0VKlBTWXA6HQZ+Cc2`)wm?Sa(Z60F!^kY@Q2SbFLVuTln*lP2D*75>N{{lwWlGX9yM1%u!)&DGldYvj9 zCD3YcB5QdQ-+G?cmu|CNh))Y+5f6B}b>SVbs!`e4gNcC?uX;SzILDQBkf#i5@zw#C zuqA_VdB&e5uXUuKHh&$@0XvDcMHA=PBHBo4sSc@L6CnDJzxl)Ium0{YrGekQdk>HA zh3Why&YJmvLx7p3OR@WKmxB8O)uQjoA=GtW%|NFEDUWH)+c*X&Xfup*RoQ)gk zt|(=vt3ky<(@v}oEF7JwFta%0V>l%(ZfH&l&a&XLTy|KWEnK>Ib-H-<`gE0ZV{Ws% z$9G+S_q88S@BP8APG6gTb9&i@LmMM9>IdoJLkd32xVjb0yyL=A{o*sDIjNFckmXww zoS?<9Uf|^%V?qtu zK67vG!7Vrk8Je}AU)EFjBg?W0cSx9VNzd{R#GVi@lG^$0SE*(+*!@a=1JweoBe${0g zUOUM4l~4}{-UUymlFq41yaMHz?}i(8zJt%?n^%@)D9{6NaRf-CmPSYSgGmrD;|asD~5fn0|KlPMa1Gafu8 z6+SK`Mu1;0bgh>-%fMG>|E!w?X|aR#md*r2ZEPLs$Wm{cS&d!jLOo_D2|V0M5;z3f z#PQ4B78Ph)M4}gPuYLQC&LYW!y2!m&IUqS%wch070!4YfxRhR|>ME|gvaCCub(Sx> z0Qb&m869inC0rY*gynX}X^$Z(xB5}geCAxa(4rOPS0Q=)f84$4vu87RYopt1pP|6KZ8~@OUNTgfWO?G?>N;xW?g*{2O34c0ea4c{JWEovq zIL2u_(!e0Sl&j+uEpgKaq|=E-=1IF0#;i&*9(f?B{8112Y0uJ+ETs0Z;Gs!8ZKf+h zkM0>)##%t4-*IIGqQz;?xK<8{Sw>-%-F(}81i_bPQaHa3PbJS?sg5^wIF!m~+5RS37_0&CS4$EB8I9z^?G5sd> zh$7vktn=|Nzh%cs?HDdH=@%B8F4r>g4tc=4bJ=F!`fn?UebK%&I+fF1j;fe9Ni)l6 zE%2Q?l#Zsx1ILCkHpt`Yt8M3uM}Zpd5udiklkVa06i1CbQsiZ}GV-5p$3Y(#QTU@V zMt&ZHDyJN_wW-V9HI{))4L}-dVjM7ue^q`BEUjR`O&bFJM0jP(cuhmmA9t%(;bB?^ zmmDSPLS&4T6L6JpR(9s#kH(B%PV->Jm{@$#FEW@GCmrN#O=;EWf&aJ4NvEbyKpqMf&B{25cI7{Y|EQ#@E>3GSMXUTpV)#&F zE}TK%d?!mzgkO7lYk2k2d&8e?aaRkIYtLRDVD-ItX)_b9`}ej3tCe!)aA)}Rvzx>4 zwO5AA7d{=f?qMy~R0*y2IAP{Mh+(OTV0stI!RY8y>9iMS6)V2wM#qA*J87gthfAVw zr{9^{dRDC=*iDt>C4GW zAIq9dv%e+ZEctUsKNr0y;0%_+l$(Zonn^z7RcVX}z3u+$Prp6<>ECBp33me>@(Fa0 z7x#ZgFx`*n+}WC<`xc zr&&4}q09@Tp7IdD2-yPm9YR&cQLds<10b+Y#e|iHW4^n0 z_Vzg{dvTX7=G(&`Ucbif%saz(f9%uY5{sJ~TS$feQCW0&s1CVr#}&PI+OVmsW#Tyo zq|YO}O{wjC0;V^*iaU`@n@0}YkUGmkv*VF`tevJ`0%JX6H$wjQNpmOv8pNu8I-B$< z|0s*~ZT++zUzFa-XXmHEz&@Zp^o3WBe38ormw~U9kE|w>0(Y0Ui3*F${T~#%7CO>Y zE}giRS3OuSF(MJq8UcFQS;dh#4Sg%p;OkrNsBkBfWw#!E72~}?I(I7il6aqbw)~1} z*^8fZ49lWx>yWk}CKk9Xdf?+CRr)&XE3z{vYrxshfZm8mJCk(z02@ApvxCR}g!AY} z>fiDKq+v+|C14Fnc!D+7RdS3FrYi>zJr_eU#*pA5N`C$QQ5%wbVWpL0bjZ6yzlf2A z#JVF!Fc~U)VDaUpj2df~UA;@a!?@0DV?4SO)hrB?-tl8O^L7{+tvhA492)NgmWRIM zsBO}sq%ha+lmGxg07*naR6LzFG0J7%Gu`=_2+fr~vXhF2Y5NmS2z}!cROd+}Y+4yi zBxM`O; zapWPHFn{PH-h-vVTE5!y-I%N4^PKhIf|GQskv+b&4|o!`NW$n_-t16m%7iruQ`aj{ zxVWlNBEq)vH#!|6Gz(Td=N|rZY__hpT?KbHZ7U`j&!U>JFK$&*8IHsc4779+g+Fx! zsf-%QG@pOr40wrMa+FM=)8Wd<9DEVLn;I#ks4*pFRs>*5-YQ27Q0d#P%(WY*WU*}N zBh+7&vll09r+>sz40RSc6iL4RF#{)zJA&jdTwKpjRJcm-@HL+lzLN=BKoLforME_K zWH%)hm?^)4Eq}(klHyVLgoDY>~C?^IEj(+1#On02=e1@qNlA{CbRr--35D))3E$?CIFDJ#mex{oW z)|*T#G?*4pJ8=q&CJxqLWPsRV67~QEZ4c*$_haEzO|3Ic*s07(^z#IRKPO_1q*n0f zd7Vim7uaL*F675TS`7TSq^5Nt#c6!nmKSqdkV*wwhYn_bDa);rwGjQ~Z~f`;U;e|Z z!xj(xwN=iBay;qsC3aA-^4L9KR}5D2-3ND>P@W&&=kwSf{KzxIk8(NJAHDuV9v*Bx zbT-eE`Dh{KBc!<|s{@MpNWeobpVS7$j$+(C<7C6hYOLKnV6~{9|81-IwJv{^_Fw?S{SM&;P z&amiKdc{jWNe8*oN4N@{;5+X<)y)Mu4So(gA?&2wI><2^gp_Y&?Ksh6Zx@a!`9v*9 zeqAZX3XBUbin7?IUVS6$B244L63AI3!%sR+#%G~f&v?l#xJU_JUK)Nyo|Lb36c`)6PX zuCN-4+_|F>sJ-!Pa(jfy32Tl<0b52XjSu6*2v**=qpsv}fKiwrbo|@Kt6job$nYHT z)8BvfTi1s--aOaLPy| zY|*{-Wah-Fhma;cD8!|K#xq`8FWD^>{i_2KqzhseyPc*qnogCa(X6^n{ZS7t%!P>q zPg2&a^4V5&%I&1oUGvs|7v_jIX=#&eUvbf>EBTARY33RCr#$)|J1X|k1L(F#-zHn< zdEY{NaA)}*>ApCL&Hz@0hHWn{pq&V-bLCb?E--C#`Wh^3ciBZ2hG457^T1agVzdw2 zR%_dW*|szOg6rhmIf8N5$>lN*O3O6mkQQ{apb{3`8Y?YxT=_(lRaf+_b9!Rx+H~tk zpukbKAx8me*#NquOx0&* zy;6=7{@`gzo=qiG$sa&$WKRfzn76Xoqe z*+ilGmNj^$;SBq0<+W`!S=G6G^(`0VB5%sCPWgqtRu!}GLPsX#CEju(i};TBewLSP z)f4z78odSb zarXp;?P=MSL9%tEP$rjAqmAWC+*}zIrbd8}v2dW5;UnaUXBUUcQZFXzMCjpa8!e-Y zG|Hu(8}E{_p_2xF{!yXIT5>Cwci(tHuq3 z-@c*v6%Nf_2JfozUd48#id{ZbR2!pu9rUEUe{NfATc)rX10{1bDoMS6G*vv5eJlXxN2gCpMgSUrQ zfAL$xOE0`Uyz}9^G0LrSvm?V&2A5bS=_+}^p+E-{-uc3veB$Y(nJFj{f-r_UjIJTn zxy_5i=Eck08*_2E!o4<6Uc4}D?>-p*#RBsO=xFg0`gC~f z_F9glc>~aWE(kiu=Ti=xDf7^C0)C0xIIrK>9e()P%fpXdc_Vd|V=Ip#>qB~-mE)}F ziTE@o)#k5Pb&DWd2450OKT{f+Y=L7D%r|eEPjYi1(%_N+Y|%4bbPNX{I+br+xnxp) z3z*p83)c8tj|Sxv<+$l$(D|xw(fwzeU*ob| z4FX=Gt1(0)K;JDMnjZDBqVv@w@HK8&DeBn!z~qZPHaI)P`AR2z+t-woXB`ARE@e$H>DXBFC2)Q#`I?8Qp2&EdE=Ai?AH=LP*JIlz{4R^VTptePw6(Pk-$PDYrX$w!AR76)$Zw1|Ahl$57|F-vxQ{ zCXIN2fRC|{xcW@HR9xbYk#WT1$PbS7h$~oq>%*0Or3Iffjbi+bo(}rmD?U?o*0XhK z`7Nu9K^GLt;Tl@`Hb=CV#1r99MI8#0G#y})ptEgQA3HlX8f9=h8>Cm zKbPLLUhu-TloL6TZyZ#C>pbKehp$L!BOn7E!%wRwV$jGgCxIgXe$iJx*rP)5lcj?0!UyvnwZ=mgOS8UOgsqHah zbUsZYk}NOu^veZL@cxTV(OHYP(zN#J!ngGm>4_%{OX#NR&MS!$CvH+}YfZ<(M}Hl^ zg9T8Ug*$%XtkX;$a`V5{o3OaC%)pi2O;0-pN&VXIl)*lbx~7vlzGlHJ9zU1v!r5#c zSNKn$<=LMvP(Qw(yvc4v>mxe~0a(WRvP9kZGSp*u8acTJE<3t%{4wJ?q|`e^bH@#I zGPDA(VaN0;hh$;R-aK}8(zhjILK zjp1jqZ{EC_d8|9U3x18xQAdu}__C<-*8MczJ7c*ttmw?!=7FXR%RCe))B5iID1|v-(W`KW_I55v<5l; zwT_fw>N@#pQj@pKKFyzorwGN?D-_zo6p=E1`n-c2zq}Nnddem4jx_tHV;%5&_$%}& z`_n3C1P{~EAgRc<$BulG97X#t((jMFlUbI~LOdjR>Vit4b!e3uxKHMAhR`!pA5hE`n$uw`t={7)2t7l-28|)vbU&rdJeOKgD}^g4^#fT zHQj-^MU5eV9f~vurFCv!KhJ$J8|#}Gfu7(?N*HB$x_x@*=I~E{_`z`Nd7NnD6}7Sc(?W_U&y(x2cz zRG*W)1R83ne3V)@kSF-hR(Fkf7HVor6A)eBU>@@GGOj#F7 zuc03QJK;Ai?+7&E83IM5{1K1e#;yy1i;V2WY?M2!RyrTrmOo%)E-32WWxil*UeSK=EE2spNJUFMIW;R2mF zVJDEugQz&FH8e>#&!Y|+#jFq0&6l!^d>0AcD2 zS%i zJ;5`0pva>1r(g0!77&uwSS6hH9pMVL3ebSe$}+F8qwocf2 z#(Lf4#b(SGqYe@${1H3aq_Qd6n?=@X-vqCV z5O&OfYNw@Lfm#SRJN^N6VchIIUf2-e80rgx4f@)?2581R%i^;_z>FF6#T*gC_Zg|9 zi)OrQv43&?IzKwqm2wsWQ8F1%M>@+f4*x0^gH+94V zKLYg38QeU3O5)21TYvL9o7U+}oJV3grDxyM5EDLNS6_5Mb7#~BmbhQ}_PfLX@CWCZ z6f6%LT9R4y>@wrEit+^jW(%7aHij)OdH#p*{Pgf|eeM5*4g_%HK%UsqpDM#t7IQ1T zAm{N)e^kFoVM~H`RhDuSm$$zukJ9IuHo=i+CZEy@0RyC&(fx$|Q|W~-Om`#gF`?5S z9M%*GOY3-;~%$v;s$d@i|ICwvIa`m;6d7e}Xin zGln}I>6vy6t>iOt0w>A96>#?pyjYGrDq%#NCd|SQ5t-#LicWEmMR8Jhr2Bk}y)t+L zl3U6vIP=8gXCKQ%)tLp(`Jr?1TC8P3MH|sDvxDKog?8b(Q-!&2$ggvx$YcI49M)Kf zudVYXD8f$knImQ_cNdMH#y)p}i42UhfW~OzagyB8krgs9Ol&m5=$z2Fu$RU*1Wosg zOY&La4EN^w1MZHY%+6P#>Egk**S?xXobqV!@XnDOtD;~UdcC~Zx8xqd(rNesg2|{} zGcLJ%!-bK%ZdTWwX|b>a+y(v;1}EP>$LIu#>HUQ#u}0HHk_H2ZD)q+j))v~r`$NvA z^@eX)%-kVhfqPuP;Hn_f$OQ3x?$30pTh({pzTz%G3Knu|uA#=q$>_m05BOPZ~F|H;ptAeKm4G0Z3% z|J?1YbN7d`O4o+wn`!-8V2kd~D|x!BrR~CU(FPqW2StqXQHH$0)8_}9{H!D08WU`v z*|v4FEGN|*SU9)xkfA)5H^;)@zsin*Wh&Y7rabtqKFG8`LG8ffA(A9sZ8XMCKrDOd zQnsz?A!5bB-*%I9{sdNC;M}p`al_1Ntpk3lXN8@;7B5cvDNXA$juYXJ@34RuxedG9 zNT)|MdC@yzMbV|6#O(}}&8(soApS_#W8Ie7x^aNoM+X{Et32Yx7)f@uRgyxm+Gpet zoj_owKD>K3NnX=o11`MUwIcpVqZd#;l%C2jIoj$VVz=^H5J=}2;^d)}7z31_tg6E@ z5M<)afeYaP&q-Es}wV%NQcO4q22n_z}7#n;>2sLMPVYGCv2_BS9J++yQch z-5`!t8Xj(8Jh{O9++8Rd68u?ajSCksz`Xzd`!R^9D|fVf%)qWM7mo*BWT(4EhzAV( z8a*_INcV#eJ_rqsE2g=VMBehfa^*_sYOru;i3XRKUV3SGi*OoQUVQPz&^CQ*%MVQ~ z#aQFc8DTUoDSM18@X@cGGJY1@pRx-toO#Nr%yFqWn_a8Ws%z^=V~b7@*p6-tkAWln z0GZzDirBkG!=Li&;jx{J9FBo5BAK*eQE0{|BDKgI2;kCHE1#X;#5UMD#=3S{*<2dV zJxL$%=qMe*e&feb9WxzUw4trgf81$ne%?-n4lv!X=5O@Nbn_w|EAttQJX>*r^MyNd+?lx!SE+4+1iBB6iB;Yil2pZG1zt!N9^acLRm9dD_qrep2 zB4s`fe$kowUle~zYaTv}_PjiHMsfUpyCb~$)j_SdW%|R9KYL^Nr*FMKT>lg+C4 zt5;YwV40-hZ(=dObw^9I7g1hdF@JaX2mkE)aBF8}_=OiQ4=?lSyE{w%(go z-W*oW(OM$7@-{2IW0UhpFZ*jPs#?}@4O!N>0;>3NurZdlJH{Rkq;o%*HAF=ZsVtQ#JhW7z{^Tn;<`|sZvUf+5K0~(8+O=^fwDsS?j z*kIq^eHwvpA3Wpy2XEXRUU~9s!#6I!d8%~ta+OS{B0XZ}FFFeyDr}*Tj z@lRyS!^rdn@{t-B+C4m_Yf{BSLK+5iMUQyFw2awb3o*y3q$xeo3eIqMC(@qqFWArJ zFW`A59dU|(2d**Gs8x0Z0$@doSCSk9$zqbdgHgkUk_!apwi$ z5>VbK23Ym2Q{U3|Xjuyrp;Qr3`OuGb>kgQo-`u46`WDjB7xK}BSBuVKQ)cMNan zK688WtnVDh5kV(!JkzJNGSSL>}j&>J} z?bgqfdr_*pqvR#ek%sX6+K!uMK#@o43B3ZIdR7NIC*6J`T)HtjS~;+f9wnr(v@7G; ztxg$&Cn0D=ucAl~S|X?X;^G`LL!w1vBvQaNj6a%NqEGTHzU^07NteMTa*|-1_vUY? zr(HHr#4{k>safnOK(kgscdD@`x(UC_BAQGCSg*|_i*|78p zj(Q>3WHlg+K-3xHBchyIy@j?g-3b?1l*8zWUPC?gmUQ5(!^mY;lLW|C2g0n5-Sb)HUtOIdqiV3zRmNH0ufiSiDYWY)b@=p?PceYp%_9Zp>rn11azt2AXF-4wVIP3umQ>f@M$P^DFy zF@J&<0c=0hv?;4avg8>&VU#(KNDRh~k4rrJV_3B>_+!lT0|19MKSicKcGwl@y-doj zYVt@V?8_>kndTRskB!q^)zJds3sDe; z{o&yJOT+#>K7-Ay>jAIjUOe}1%ABJPD8$QpRFqn@6XdwWBCQnRNAMRFbc%LX{uFjm z_$2&A$}IdTj&Rd>!JO)!qU&RLRX4{HPmeq#3#~O|d|*;J=G&m#n_AMSAMG{2%B8>m z)$cqr+~yGfMLv0Pp8H%lUj#6N6&=L4rk)wz++gv3^Y-w+{`&6^um8kXhrj%duMN*# zS|6T%LPN_%9wu&E^ic_4+XFpJe4oA#*iCbjTb$nd_|xImmcKW7lklg*OB;VSTwc95 zEPENIp!}3mG$*`HRB}$;PDfbUQK1vZV2}`Jj&6M9wCE{+3*!`!G3z1SjyWd>PQt15 z)7sg|R9lWTYOqsp(#kW1K|XiFObYk!eEa(FpZ#x_hRr804)<$XM}Gc2jB<>Cnedz?$(NH~I+;?V%F`CCZ^Kpji(t*O$W1Rz z9~CZ|!IKUy`qGz&2|Yav2Ew7$;S!m^7>w&WUNjC=nts5f-+3fY0>rB<<(u5e=HVSM zGmB0V{L{z6e>^$20@LD9*9`qEmoAFe^7WIVZ<;|`a3+xp-hdqSv(6?VtCh;U&pbtnDa=dsHU8*w!V>|)@^8WwnW z)Hv4rCdp$BPF@JcXrkQG&kh#^_L#nW-R0uq*>OZ3?nZfV&o_Tu_R9iwm#-uE9B@Zu z78UN;fX)UxV%E6~SUl!WaULsCR^wT0$g?koXf2W1MTuXzbld(O`~00$doRHfuf&`v z`x)ib1{34N5<+W=?;51OUGesRJw=N469=z_la7O8FC7pGk zOIa9*q+ew=-Lj;gQQwCBDTj;m^i6Pb_lbGZfnO%qz{}HnNV1~>o;kizG)i~iWJic{ zSU&1UwfWq2=SlP)L0UPT<5hdjCDWDy||vjV`t+=}F75mpdj8VLaj!{SaP(uIWzPbusF6-Yvmgh0_C8hLjO<;io=EXOKCQ z_u}MqHhnGC39jXgtjaRMvEQ_#E=~8T0O&}{nN|-ZGmGvdNXxouoU7lt6HTLA^bKkp zysK}g%&BnoD+kJBS)fzxMck>AwoaIinsLQ8R!&^oRN=<3h4e@807w^FE>7DHF2)0J zYOIr5bSn|cU{gw*bjKoH#)B$P=NG&Xr=0Zbv>RY-56b02F2*bIyS|hw{g8ZNO!w%X zWwN|61O}e*1tX$q!t4Q@g&};@sdQ8W|J#p1$!wjX+Q^Z6pn#ADC-;YsZ*B}1&OI2e zV$A;d&iUcbu3sKr_|7Lo4%9%vxv)n$9cpaL?gC^O&bYgVQRk3xRlRxU(>wn%meJ-m zVmSnG>JU;nE$8T(V#eq^pdO-^6p&mZL7{H^r;*?ShLD$Ed70dCvnU^r`5VeDNq|cX5ar8gG7AXt+vty0MfA#=y9RqWZi}H4!!ZU#Q zsFNe{JE7(kdt)>4j=?=jF0(*M|?k_ru}8{ll*hue|)k@WRtq^4aPu=jqhU4p!NJX2;Tcymfz%oium2 zRr>?Z+I}!RxqN+iar5Kh8_UdK+jy&3aghgsS~qKHO>Por>9opYohlEvPG@1g^gKN$ zc)HR_b)uGf^!GG6<`tgw3f8oudqn9t&xLqy-^*+V5868GnMY$nC3Bzan4R&=NlS{; zahyFEbTZ_G&;J`N0DtYL-yHs@@4w0dV3RL3-saP;S6FoHvpA%FIjHGZU@meb?cVJ> z!*9Ix?ctYR{=IMxBmS^%YKAh2OIERPDXVGZSGzR5XCF%wHV-`kAYwZdwWIgc7C)9q zj^lE4PB`U-fn^_Er&aN)598{);uQ`p8FyFd%2>%soA5ARPJH3=U^l?8@{?C_{NgQc z!IXX~Zr;OA!p)7LF+jTI>iN=nh*o8Gn9=LyhzCq`VWfwJla}boL)gSkAU&XjD=q#^ zPerqQY3hvY#2JM**y)*9;qpx@wTlTr2Vf_t?qKlFkt`a8kFMc^7>)_* zR_B;0Z_8$)x^@njh1|sGp$x+Kwk`$~;aM2D8AKNr(lQ;VjP6o${F0ZK89Nya4dQ+Y z!lOQ^cMWmCS{@g&8ckeCXf)u_I@|?LIb*bupY(uRp(57rah-eH-sink97XV$il>-MQN->mT4!AJCM9N1p1!T^CttQ}C8&`U{NqC}irz z@*W5l?s!BCkBc*90>B-+z8Z%(}ClbY;Yg9*HYo(~Csf5@?S;r%qPf;e#O37B1}o zA#6{zD@Z-d%Uy38swXMw6LHdN(|GL(b1WwfucGbgaT4Zg`4 zg;@sp*@e>&`Oi z?8QwXou+Bh=8wjJ{D^BnSYhzEdf{oZ9Qzp6p?3j;N7^He>YTU4lje?=gYz0x_t@b` z0lF)mWn|E>;rJ#mUdiTVu=tkmxo4iuj+UVIv@&7{zw1DWJJ6DallCr#AVGazY;)0> z7x=mZ|Ng!ENh`RiVa8p48g9hbxT3KkpYIM;|2RiRH&kZYGbxroIaYdirew!pkJoOP z%HM4dZ;(j=zi62s*?I6z4IQ>A;o|U9PZTb)kZinm>x^KeRlax?Rwq$R0VW}okEfd? zOy$cLTvHQ^#f-JF6^iDB%SuOcu zhv%UN%zZwDkbV!o7y9NaTGS1ona|k#eGU!uca5F*w`jXp6k6^+$&Tt#8K&|W|GX}B z*pa8>#gyKh*HmQ;Az#cDv4e}>VF1~qgK%_-j`7s$I~4M$!%w-)1)Ao4-ta>-AGKhg z7mm7pEO!E*hvc3U?R*}GU1&ZQ!kp<*+@a6Ot_$*i~dCECj7 z>dtU!<>v6r^6v1X?1b6eU~px!?{G-J`dpplG<`10tM+?T;>DpwxO2!UjCB1~+O+zU zHcjY+#-uTj=M)ci9}EmO=tZY)bXIW-xAfw5V0{TKKutaf)&Sw4;UMTyujU9t`%g-@aHJ@736hBQ`KON@p)40PXrYjybMQ(qlky7V?_NP&Fh z;zJPz4vZc*$fxYAPi4V1d5tvQH7p(*$drr!!YaNKoR;4P@`l;UFX)t7XCo^`dSo}M zLcC(I0K4i`Ul;z9@EkYgE~E}0#1sBZ=`I49=T}_V37dpXvnW%TlJBfEadq?ZwbUb; zQ#$5VT-{fA#8DX#WbTzZbAeRKp7IgT_%zM%A~)x;2&Qo7u=DsL+_Ta`BeM2FEkfw+ zBUy&4ETSy=UX->@W1nubcVVl+!^QOKIVSi#soOE6zAyv#F306v$k?c# z8zj$hd{o1MhLwB7HPD~qlHo%J{w(m3A#bvgkyEP~xAr~G1T8+?mvU_I9Pq1p8Vxj@ z#3(^@J;$qjgNkmbI4|{eHMY0EPP@6!gqtnx+|{6dZf!9cqnv)K+g(1Z7!}+Npn_e* zs%m*UJlWLLQ+u<2)HbuQwt!}nZ+&Vg5qTN;gcsj*c~<2aw_P2wOQ*K6n_IZahi>i4 za2mMCNzw(e*=?@h>MlxGAOaVthi&ej5N?$;7Z)xpDOt91=d9Che zKCWjF%(k@3?~vz&N6^%bZ@>LSuTr}B9i@eCQc2JtV7;nac}BsM(FMQ8pz8DL#dfIP z(tfE!!%|Iql@|Hg?L(LlVqt`WhI$i5qnZqs;AdItB4nX8D(PB%5D=%QCBr}HN_x4k z15%bjBa#miY)q|u$evv-z)3^>`z-TWTlMEyC2$rEsqn^28DnJQ0cA~qnsKek!qXSn zlz@!v2)8_wXK`(G5KmtIbe5^rEIyf4t}-~7Z+&U#QXHLC?;<^P;G-&{8v?dR=Wfw0 zaobR>Dbp89Z_t$P*lq~hm-v57&m^}UH|hl3v0ss{JQ^BGuE--k>47uMPact>r{l#5 zBd#y&iiSoFWhhttRIa>i@w@`m5zQ#E!uY02G*U)}3=`tCfRH>doC#hmL11N-2wCBf{va>_-WNl!<(^$TQtB?2hKAlzhtqnBA zg$HufqP#>Hbw+|ljcK}dSa^nvOIsTHI>XWsPI>oPTbMQFBVJpyH=OY3^n`7FzRTI^ z2BwECtH0?k9rI)CoXQA^$kO?@5!E#70QtKwu3VzBXO@N70 z)M-+}{7Dh29JYB}8Q!XGjAW(+gmWxZcawC3rj^X%PO?W*ivq$;@s7cC8OQRG*^+4K zb`+d_*13t!3$qb}Mq_!T+g_^daQcExHH_^T{8)pm(wW!T^?$`aw{#O-T9>S+x=usv zOZ-#oEJ;?8iu|MK$;Z(bat;h(eDT|{aPHjSVe8iY;hDAfLw7FJ7h+btP9yRdiWamr z(US?D2=f?x7Eym3y)2Tk*iZ8o(Oi@^m1l}SO`nD}2RJC$*nWvifB)7W|M+l|Z#_PB zb&ZF}DsSe_4Hwzs`2Gi<@YWQQB?IdxTg$`mU;LMbU%2#J!;@dV&6^2!UVy((tMtf` ze4V4LK-8U7mPh&yv}w5V<9AjgKC67wax4m;h~LW_!8+n;vdrns)9u^^-}bQs12`9DJH8T! zj)sGEFGQ!aY~94D!fqJdjs_Cy&qakVHa&Xe!kiu7d7JKqzb;JNlJDid?t1W&U>Enw zr%c&J!Gvj-V?-W}a%YT-Di?pkD5pm)Hh6=o;YCA^Z-Kpt*qt2@Fb28fC(gEnjLz}B zq|b%^HZV@0oa|@U2*)V@`@jDJ;pO39|LJ#-VQ<)jrkDOYR#WbsB` zMj0Ch|CNn9>-EM7nno#MrIWUeuQQzT7zV-iZuyrn1Rb?;MlhdkS??O|gr6=%HQdSDHYN=v@tI&9xjRckp#!xr>dbMx24&+u<9GVk zx>8sGSCMa6Y)1HB^}uEkj_HY=YTQ@LlQE}l>h30bla04rp3@j(6DLp zYKtHNikC($Gm=EVGSHqK8gQZ)AHX=b$7@JZKQSmt7wLWZQBDI?>WCZ-TecZ||39>LTkH_?oVw;}f(^+78s4 zGPWl#?IN-uYbTU=;DNCc;BMbCU869HJx8@ zfs=i?up=Ul?X6&BYM$|eH_rd0>E}nL21J1z1~V=Yc3gIE_xyC z9abKP%TYJtt&Ia{TB%Ft7WAKS5GnY7AyQfs+X0v!o8J_VG#+b?zRLbq??d@kQmIqE+#ya;#1xQ{YDq+W^kJ%;0*7 zhx*NYooD;O&hYfJA3PF?FU-HVD96M-GTqj`CYZAzbZ4Ynqn?4C6@6rRW`xooPLXjg z+YJ58=$UfCd+dyirr&t_)8W~TTf;}UR))_&zHsj9uz_Xr*4BfZkKEzT8MmD8^7Q!r zC(jPwy!;#&K7SaUIqsD<4NBKE2Wg$M_%ogV^mzO>9vFLU7h0$2?ks{;?t+uv=XGP5 zATXzU8ckVNImC+&>B-Fy)1DtrY}A-jM2GXWO}3Vve|lKC__bk~`tcwpj|X&#Leb~p z&K=$?e?&5$wkF-Jpv(LB=)-%%MFzM3;9tHs{4c+cm3fz2?hhYuF85p}1l#P@KV&xU z0&aiz(s29kz2Ud6e|PxTe&lz?y7a}<0T0x?^@m-R)p97CgHRiY7)U9M;Z{B$-h|D^ z&kuO3-{Og{Zy_-U2k|l~VTU&pJ4Oc=oxlmdO8m|p zCyg#f!KYK_t2#2K^LZR8J7J);=7J27Jf7sS8Rgb6V1L!HV4nASxvA>$%s$NOSDBg7cS2Rnwp`y0;W&}I12zRCvI6_cG{sO0_E1Y zps(kcl&Nh7W0AuqJB>1CC8g;qLH_`j(F%H5Kw3E@$WLQY97(lEZeLA;ZWWcmx1l5jJ5Zmbxxn=&uCT4$S&Hs@ zAPd@rHEv43y)8|cc#b_;2%OKP(1n+BDXT_HFH;uz0lZxVt53C~@i4|Uz%=l!(dKNY zYBMDHV_26e!S_y{3cFzOfJA#U8;rnf1jynZUOIDvqjC_1?iW2pH^5ZBh9Zq&rZ;#1 z(X(|CnW#t0rPC88_eKdH9OXB?$+Ak~5`Ery7K`Y^F-(2iMy%Jg2jbXBn|{|TyjC+E*tkJ=tEHh4?&X(< zG@J5(V{CDUm2F-e<+Q9z)Ymd!{fd6bFWG_zdcGnDtaV}<8qE0Rf=_3=7FVJ*&ZG?i z&lN06Km5uselq-x-A@2s8b0QpnRUj~J?K2(Vp9J(PD{e>?*vFwhKo&ESQqyHecGn} z0frvi_=@*ruzOCIawhcfpJ`s{r24(k%{ZczZ9Fh<#1WxfPaKUUz{Nm7x_&-Y?4Uv1 ze@%}XkSz#hAkSSU?Whn2d4GFgRwE6 z<10-!Kf9Svd0&0{XOxsJP6}{qJ@#I?_ z1PtqhnZh|nCLP)%P=_@IL+2pg zWk>D4^Bm>{-br)7nCIA{ac(M?a(q!YE!&h<@iFZrn1XMuvtpmam)YK>pW|Wg<-Yg0 z(CF@Pe(CMu;xgytq1TjoMEER$jvyZ)WQ8wqkHh0}V2jn~!71Fie-^E2WiG;<=NG+E zHDh3o{fKe!i-f#f*Z=CbelYwu-}`y$`~L9phaZ!8nZa#66A%Zvb?zP6<;cq}ZQ(cH z{_gNAU;Vp2U+597BN=Sn!ZbR1F)#`bqbUbK^Y%m5W4Y2tA3?3n<*f3x;pa%7 z&X>&HMb}-bd5IkIH%)JtsM@Ez`IcMn2L)c-~`-TmU$VJTRiTQ}hW zj-onA(qLm*T=crbMi~9HpYH7NNRkVbRqnRBeSc~A!#B?j&tAESVfftef4{yl z{PfGx;%%+t7>icv$|vI{WzWtKb<^8k6opjL2Z`#-&v`RHiwq7@V|x(@r;eRKJ3-Vr z4rVaxgHl(&MNO`M!vHWr{RxvaXsYH+dPy_CYiqniM z?qblg=&SF@8NSHFLX9MdAsO%{d#tE%%yJ>z!Sfd0Nvv`28&O~Gn(!!8j7-Gkn|p%f zhaB1IgHALEp$OU*^t`JCJ$omO3*BvuG0GLb;?oAy58zQrJ`rs`ywu|^i%|=gT~@5# zQ&$#)MBBD_(#m}$mO+{EUEk&Ck$KQb-I_<-9ZT9+ltbE~k@jp9nLEx@SMp_EkEkxd z2zUHk4oqBKXr)u8)VBs1=^%%`$H;8II@#*#J|Ah7@k2w6{ZU@2Kk(fUr<|!9d|mXQ z4E})i`I(DjL#)Xo-@x+6g{gg8!QIK!c0!>i>Si?FU{M2PeAhWoytO z8GUqiDSuGG%i0?<(rJ24FsO+e0xq6(322CNmyC7h4y4MrPOa;5X>;mIowUjsiJO+) z1ZJ6^^3Y~nq~X%u44S8GcxI(tqEK}$N#_*iS#H~$^%UCFciIGS(T_mZEOBunXC^g- zdmojihCk^_)IO!*LPCBqX8S%n7dfsZk!5xZUVMU(lOp;6(X!^lgkS;^uxC^2>ms?`{wc8yY3Nz}K*CxQs5sXsq!|pQgu%GZ9mR zhK3G*<_W*agGgQpPkviI%dBDM)t6u4Ik+*r@#Y%=*AR2<`D^GB9pjGGSJh|g+IHJugzH&j z9iEgX0GJ9DB^KXD+YtY5qwxzDRnWH8y-^Jb8f8 zNuIi}GkoW{w}(I2dWoZID>VEA20>v{f3@i~3r7V${4xBMp& z>gh*X$WWREkGfK6=DA>4zVh<0^7Jb_M0e2p-C>z){||VGuK4B)f0?H^OEvP`^y=;y zKTYUBw*@_4pY!x*2F$H7F{H92<6(ATxUjK4Tx0_9;-v>n5H=XRiEy1cOGV(h9PN-# z2>M`Hel7S*-c-p9#RuDlD;++Me!xuPfb#{1+jqFg5bO12i)|TNV~^ z%heTP*<7H+H{C#YF8apwp4MQQ92~vt4yg%RAm{aSS|6&sMK}^OU2*3;!swJg;#K=9 z9&_;IcU{c$=J7d=ikB~Z=yk+td8XkhO!26+DXq#&IjH`ku?VJMr+&dr@uvBPQ}l_R zD!=5Zc=?fKK}AgAr{Ti;()={YN31ZJ0-EyUdtZos%Hw{uU~v}$s%7G7AE%2l8PVwB z%QJVXIZ0Y)l5fuu#!0=qPrO*uq1UO3i_08ifk$@PknTm2G0uSJB6gEibsdND@=zY% zOhkPPNG34MBcZfBY+y7Z&=$?)Xee2^zleW|+h|R{CpomLbm~nnRkf2}2`(aC%s^ zkv>UkEFtVjk?H%W(Y}M{X zcl{&{k4N~1#e0abD@N@y%w=nK@QGQ+HL7KYkeAUM)dh(c) zZ6#$OPknf~wFWi&MhJ(9xGo#o+gi)gs4E*_`L#N=y{3I4n+7W3gg4;vvG8KINER;r z6Wp?h!A3gj6uj&v0Y}`npyT)&>5bUvi5g>uJD*|@QyKEZc~4c+I~|P`sr%3m2yu2* z!Nf9X6tSPlA8KhAuIZa=Fv^oMkeGG@UUZJ{*wJ)BCUL{+B6AM!v)o_h}25A(@u%e}#a^COHf8b_4Fg5SP# zdwBWfmuaW_ky#*waAJA{8dZMu)mLeI%fnl5y_GLhMGvI! zX~5w)le)`}8syh_CEqeam0?GkG1Sn2_RslY4R-AGK}NY#7go~585PE|R=uU3i5MTj z4J=BV(yI2+302)s>6AQ$HpR= z(VxAQXqsl@4AzI1O<`KCN03-ZjJ=FijJ z0;DOz;l@{(=si0;xOr>1!_J5uK81btvKd`&ojmFNF_A?RiQFTyov*r>? z^Ukt>&DdcxA)N4^2P5M=vB#BR5r4DCboFr(KIzE=xcKmVrY=ZDhfAy?mR|fx3@;cy z?)_0FmMgp_+g(1-wp{kPE?uC$&M|;7dTCzPsN$#VF}g6U@q-LT7H>DVzy}Ans5>sy zq!JGHZs0RnNnJ4z?(-&O`4V;NE*KgB4?oO4%qtoA4zS3j9Kb_!#Zi;j)yv#;$B;;! z5(j#puwUL%{sUHYhpqzHr+?tgQezD=?{9Bmz+nsLgAci7{bL@M+@N0%4X8wKU16<` znJkamr2PQkSDj#uR4CZxQXnoztoGDJ{bVmvoij&x*&5yiCI#gYEHT@Mnargr0AD>D8=~c@AH+ zO)r=tRcTYd@(QowU&!y}Y&1|p;%!u&%QW(_aD_)d+OL%Y-B5#z69{*?xSPR#W`3La zJHhLm+!+8IbIM#`Y8Q7g=+~S446mP}b&~Eb_r742uU}W%-d^Ghe#7w9r|%B0zxxcvqut@>zj2cZ zGW6LEfSMwsw3({1z-Q71A(As`)`-#WkN{S>9RA4HAmMHh7hpb{Tmb0=@sDMTuE^8Z zCEopBn81p|Xb77B1Zeuq{MvAaS@J1VqXka@V<0a+D8tFMMgR*G<4fo3*jD{ay@ZKY z4_w}AhhNiccz0QB2gV}@biGGK!-&Vi+!f>E)OuDw9x1a8xPbMzj5603z|?V!GSFI~ zPW?)c_`1NVYzlVhgc&{NUK4bi9W4TZXM0ii*$qH2O+P+$K)L1Hj`vUw=MwOb0kGr; zR(eFE!x-KO>x6Y78-pK3j=?7Jt}5hney_{UABbr9Q+G#qKUn_8+rFlne#+k6MSe&!a|!a@ffh#xjLb6OC*9PmnZZXN_?E+TVWpq8 zZa+ppZpYUkx8$Q;-CEc9p-WoH3|d^`gq9dTR3cRz2N_)J2ZqwtD6GaU%hK>|yE18V zz-g3s)QO1{rh^e~?NNDbD;}ZtrH%TtEE-cC$I=eW1JUuywCGyKii+>Nq0Jqd4C*s5 zL_N9~8RSo*ZP0qq@EIc+mF1$^F-Yfbv|ZYNhAD8{rmBtu-CU54AKj}Qn1v2O_2EKq zoy*FXD2p^Ty2V&7ah&bg%@X3Tc`p{`D@RL9Jlf%~5R*V!HAw069Gfg~=DH>S(Y_&!i zVchj1uEr0YMj7!mUTAnRPa}{&X=s$uNyo1-Y246|VO_g3#yb%0W9KiNrya2)hKPKn zzsZ{ef5PvA|J=1}p>>-tie9_+e0V(hB)aXE!>9RNwuThTt5YV|HOisU#|~!JPkieq z+PIAjH*Va>4w{sQxN?eTnVxv!iImqeOUIot@_OavSHi=3QpcvZwo4m^zUjJY{Uo9P zsr0cvD`A$OC_w4Qz>ICejE-ZP{ESL(4pZ%>TSZ`@B`-ZFy!sDCrFR z6A6FLjFSrbIm!Kh7OnK~h1*oGM<3?&2gZ0NS#(&3+JEzv>%-r9>+9TWz#w^HbGXDH zyUdL0=_juYpM3o3aD#^)HlV zBe*K>BY>XR8K$_8+Zi8+|CG;Zcosb>;SXaG1NRtjR6H2 zmoVHcWuGD9sfa- zx2>H-zsfTJhd_A0dJjteE`i|LaO*xjtsI8So1pmXuX+&pqf!=o@RW|fI!_PMy&@xq-iK10mQkr~P3ye`r$gRa$Y(FuC=Bpq32yy-OX#yrFFi=hbL;a5G1 z)SiHZ(Sc#d=C*?n`Vs|B{p*a2>rkgisVaPRzA!q7UmjrlWfJ5w4bv}@i!1CoX_zwDRYHRw+RjYGa~EV-Txxsi)>X^6)u)_g5S zgO3YspphS$Aic*C*wnYlIO|wC?)qwmmc~1e2=$^4;FasX-~GJWC(x@eG*b$;%ToKI@+$0a0y(5HADd{qh!}mcE&=8<}>1?MOh~P-r(Q_Ph2GGKocN9jI%R0idTs67JoTckb=I{FqQSb@Y%YBudT#~|kAga$j>pU} zj(E3_2Ed|aAo3$$vGzWtygMR6+O`kd%s#slm-(to#yrxkvm6au7|Z2x zhhv<65zJ3)-)Gva;WBeE&Rp0AJbt0gVG~2!-aTm0uU7cloJaYve+;*8Ee-cD;ChV7M?{ zeQ0da7@+}2Lx{Ng&p-csU^Gq$r!O6SaW!0MT+!g7q34AcUP!um^3a*DAxE0eKKpF) zZAbFEc;RB`W^A`{*tozGRap{%#>jNltBb0CtKl?PaHqm_B8_fq%rf|i=RO(!$-CEvPrS5| zNys__%{ouqRaOTNctdpI98V6u%%he6pS^Q=c;(!c;pufg|3TxhElt}1{VT2>jHk)< zICb6o{EwZFu11$LCIKFM6( zk%p)FG8g<}cww6flc5WQU9PZ;X`hRId8#k1zQDr*&>YrxR>A0Xz&ToXqqu`cqfXwP z=|c}=%ps%O^3s#kS37QmIVL=~LKK=9QgTe`1``A>F7uew3ZK$uroq}Qlk`C}o45>57bT3YmPJ|5Uu4q{GZHFgY55A}^!OEn zF#isqwStj{_iGrJDCgGSi@vOadSgm(p&RRXVeV1l9Bv*zf;=9wju*ks)28uhSZXKH zC{E_r1biNrmT4!slW0%VrJ*jJr4zk?KQ^%?4!_h zg6J16ol@(d6AN^3S>tKNN!4a4f6k zPnKl|Lfv~Ivd4aPeLkMBf8^)SO@)%moD)4-M^5fdlc#~IBjuACEE>K%GbwKJS$6^G z`PPGr>t1wtxOhM&%9Jk|(SY1B;m(uVoe-lN_(h0EI`!D-I}|Td5M^+ouROAxepXye z>(ue08F>1l7dYF3h1t(@h7lXtgbP85QeJiGq}nujOUu5Yi;jhnjCfGAJ~c#D85=Q3 zr9CwqA?;UT(eZ3+F66xz$4twZz6(^%8Pu~CDSy^%k;fx$RhMZm@UMkj+JWTM4am6% z4x-+7qk&52PLaKPEYg`Td6$UdSoe@mS)?7hU0!KfMq!dC5uC>hy`x8-mMlgmcw67m zDXAWH)QK-m$0v<(_65sBa{5-I!T3^oR8jSlzGdR4jva6G{YCf+ON~NX?tP7*xYUB5@6|$|OyX(J5PgA|Y4Wy*M3??9eK`3+B`z@{nmcQfU;>vPnzVq#&SZc+!=x zhG&mpS+6S|e*nhp+Ns6xn3Z zOQ~~*xCidjSD&VR6H7ZL10dyCc1KPxK=nt{lcYj=UJUfogMIEIB3)W*bZm_?K1X%R zReCb5(G@RcYIJ!T=fByBpLRdW`XsZ8~K>t>JWqx>4-qdBb?#*dZCLX#V0wC zP)D?nG2KArsgh2k%24!=`Jw6asNqUN8RaScsSBSDSLvhSMhcC}7 zQsckHlV_+lUj0VwAV<9I$LPak4GKt3D4%PwEXI$wDBOt3HNj!WLs7X3X@ zwne}_4b}_Yl-8sw9)isW1g5|hF5khU!j2_wp|eDc55sWjn-rGDeCrOE_uZyZ-ydFl z<~v;28}#ZMDnn6vPU&X?&w4$ry#kAthsbNqQZ9+%!6e;R)jT61}R z`RlI_|K0C@n=P03hYvpfgaQ1?VQqh%#Q=jhPoTSZ8R+On4>0Th&PP8x{PI_RojUVj z(H>SOHQ=dSj~!mr$){4~;j7A-XPh{L14m<|MY-q_> zvhIjE*lH6{C0e~mnJE|a52OQM)#)?XfIRuj?=RNbN9;@0pQ_KC6#t^qrOf`pO~b~E zr{PC+a~x?-(<-cAWi9_vc|0my@tVR{TJblp(hN`aUE$MUdUFvZxP?=GGCt=YX-s9C zk})sF$vz0Wjk(^o#W=yF+D~26U1PBD?QV7*&=q%m>qA5L6VwlwsCdN0#f5hg2ycGf z4U*Lb;JXhzUrjo1_IFu0`Nr5S_wLMb;kfQG2<7Ep=K2gJzUj^J6_k_(1^G^jT_9*= ziNOW~eC|{b-eJNYCG#!0W0#8=7aLBF?Ad;u?*O{;InUO9*KnrkJXWyI`S1bx_dw2~ z4Z2Rs+{JL8`^#LYD8qd(oh46Wgnk=r@R3m$iYZTvJBNgFoZ;mA$6v?kVx-P$G3o9+ z72;yCpw4m|f5A7c!Wgvw*KXjb?Dn6Zes&PATC+C$)(CFTnyFiSclBonyiMKlc* zNpl>vyuhq@e~9>ngMJN*AT8|;?rgC^#7JS`5GWJkFhiHZ1LE;B4Sg;?H56HPSWXQV?sl;pE>?Bw zuGKXWvgDUzQRYM67eTo*h3MEO5T+*KBu9_ZaiXuGQ(X|v&IQxg(ul0WCL_g3IU=7& zYtW{#YcPMowjZSVqdzVT4Oi zUg#-wLa6J4{L)Jni{?@0fI!=KMrAM|l={x`Hvrs4XdCnsyAGb}f8Q@TIUt$Dk8dR8 zUYkS4I9f&I@*4`-Sx6i33te}iv&7vp`;4!)C|@@@_9`nU_BqOeF`Bx*&D7}T4e}_5 z^?36}+o`lpTacw?-eIHi4s>j0!zEsT1E?|7T|{@dXU0!xuVU!YpxJcW=2f<79UA9v z3nzi}mX?@=qC})idgbYx)+;#SfGQN7@EXT_yx3Zpun<0aMj3al)UFqIpJ?b1 z$2(i{p0D$9467X>wSz=j*+D|P@mSL6TBA$Uao>6t-=FfDEzb#9?>{4jqrfP)YCs1d znH0lSCs65#9cR3p#xdu-CJC-a7CNf|*S)~1@gOEcz8V_ZKXp`L%NOHm!4o&H5fO66 zQ$c`#twc&IFMxf?i$ z75MV~u9{h z-r@nWHau~l`O<#kEh20;^Xa>}HoUpDKm6(aSBF>n38i* zoi2heau4|@&@URc^)KYQ>~lFw{!$R#jefws>nSN1Tx?XUl!<8od@27LZ)l+B zLc&Xfhdp;;5%$L)%ke1VevrPzKV#i>nrFpNV?Dj&kcM;HcceohW9SgoyU*V{d1{oKA<(#LwWj$|USxx)ZB=cS9Ip;mc1$VrV zY?nmR@NN6K$dehrh7lJ^=C?Opm~P>*!{X6N*P1JMTs}>WOb1ME+^yock?}w|8DdyK#0AI0gbTU9g4*3m18~Q1(uo93>z> zV=J^>fOss^`GoxT;9tAoYQmL)5HR)Wc%ZZVE_ReTI`4Cm$OSLUtu9<3#pod};Vc&V zz%FhbU6O{nckUA!$kitBVHYMY&ZX=@Q1_^In$=^)UZO`6`WWIGBcrz($cD6uR9`ZCV z{P3$?WdQ{}>vJ@g@!SNf=^R|0ceN<5JUn`oGBRnGu7;p2gvmc-Dr0-KP3kO@JkoBJ z8(48OH@4z%_94rnanck10On z>95Etef8!2gfX@i+{pU~U-2Ltxm8ifGM{h(TL#g{0Zsn*e32Rt@pbY{KVsL3{U~Fm zbwT?7!`_=c*_P$^T{}-^-g&O6Yv`_O$=zy8t!^Y_i*P_-LnsIn5Dvc(JOIH15ByO? z@CNU^@rJRmLKqor3r&sG)m>fFty_1Vay)!K-?e@_ckVnV^WN&Jc1x~x@5#M}-?V;f z*lYdv8XRY&?_uJm@k}ZjyPe!+bp`#4mDqYHkr;g`Q|VdG8nMq^z({PKq)C6hL*MPS z+xC6iz-sWZ4qn9I^H2V7w(~!@KKuti{CK!}@p%l*F-Wm>N*%d!$ok^S7~SSBhHwls z2Nhi=Rj$$tUwU(zN%M~h{aM~) zG>Zt4p;P5@45$Y>Y;qsc3doN#vCpwR#2B68psmv8GA%NV_H;64qD{ekb$M7xxiYpg@r=l`aL-CVgZy8wyxg%tnbSKc4~{EyfJN~e1N z9@|{#kT0@zY3Y95<#txh3{37Hv;O>lfB$a{?|t_J-ZQ+lisqcx%V?_Ot}B~$@x{VA zdYbEf@M$0`)7E8a9XsVLS*9|x^HeiQ`noMvjcmX5+r#eT+q|rA(kc0M0OJX7^Mz-$ zfasQ`1*31zc}lM&QSU*;Ee_oStlphny-i0(8PU6KaEPy_9Nqo_HzTE`8|S$%Y>(Px z&j2{mUSh-v{*rA(jyn7j%gE#qBZ{*4wUi+XaBMf)xWYh+IE_2>kc;it2)m^XJj#EQ zxI?!zxvIv_l@6C7E3!XM+}0+amvRs>D}LCLwZ!PNbnU$Zrt@!OnBlyd-G>b1St)by zRIcE+e#)m^@Gr`xuY;W&*Ol>?Ez8Jrmd$DXS;e#PR;5kjgflNyH+_~rlaL8;@f7DQ zSFimJ8a3Q@k$f_m&^Y3to%0McfZSn;{HGsv;|5LK!TveA zhYVn#tXvO<9&x2q>Ot8vL6---J%hm! z`B?Al($tyb44%<9Ag(%baIBopz?`IdsGmgh_0V{e#51kJ(AF; znZCQ+N)^+Dd4!p1>Q^~MbHeOa9(C>6E=@)uab2jCH|ZJw8BN(JpWzNkxiV;`j+OyO zd0YNLn2?i?>?NHoL8R$d7s@4$PL$LM%4-i|#&0sxb{oQId;`yRpdoB58{zmhmXs%S ztb36a`tmmKWFhY!VN=CSOPSFYI=`9(NF*N{(Usd1GGBv0%1;@n-@bNTVdS+bpTb72{Bc6q@@xBwfed-%<$>7K32@Z`NrVt| zTruN#Ao)$W=~ob<<5HeoM;e+e6Xg&m`Zv&lQEkKFuk}lvwB**0v{dRld52`mRF8Pk zk2(d%G;%-6pSDY$kd`>=Z0Bu>nI?{_`mC>haB>@s>QM($QU+bsr>gV9s80oujw`{m z6D-h64tb{UJkMD$8`M{Ci_>T*$y`BB-{);{F7vbBsdKa>nP>>wuUYr~7G!-nM<4B0 zKn*2sov=RLWZ3l2{^j-IfBav*%T}^;!^6jyhX3Xt{=cnEz)gEoM(U9H2p)B2ix=~};2a!t{u3kI)z%;N)Kwm%sO zk*t&vrt|Spa%fE1a!C@-!+v8l!Jn(WsU)qBcQS9CvYoiP$t$~UlO5hV;ZmxeS7|BV z7UN#w-R>qI&(`rU;tLlp0MD2iL0q{btk-S(l!Skj!&O6uOFtesEYC6+l>y7KLGmLo zzo`zDMQ5H(j_Z1u`p2{mQozzpy=X<0)S_QHoT5xg8z0^G$UM3~{QP6s-&BAO6MSX1 zZFc@sFw@KB9i7j#l2Wqt0_k7v&s~+d`y67lH=1isJg`9>qGO`kI|4msm7^VeceV>z zAH`J4n&%v_%l+a6*3d0FljK%wClPZm?;i>v`b#E~FA3fF_vsS%xB~mh@bCfK7`W#4 z#>G1g-F9W~wNBG>CSO8$&T1b15?HJzQH8ZaxvLR=31ce{r-`--hCEx;=V=}P(c6D9 z{I4JV7L8?Zcuc4D>RW7cp%9)t^YCA)1A~-vm$q|V_CLS(Tf@J1>ksovwNo!Q@mADV z{`?|5k0gG1@us-*Dzi$KSomQh?B?}vv1N$mpv+!&pRp>0txH>5?A3MutuHxyI9@V` z9Q13@PD?9^HyyQ3Sfd6_9z)9(BVjwZ!!2H3o8RDtz*l;6f`1`)TbOUu;`P7vJw=~fMI=wQ4zaNB&+oUC5udT0mjI?rm^VQW*i8fgT{ zd%3NUk>5T`;Z4id3FL56p+j^wk5{te%)cME+9~s5-XTFSY^oT$wy3pk*yt*?z@Bljr94F9$hDMhyFyb3e zTvtsqP7u}djfWS7bXnw_GER4@PUJ(fvg=}SfTuxmmDh4i-4KcL-i40!A*&96tFM`s zUshHSr*3j(GH`eqFo%5JS^7!`a;>i1_>_Z@6u7c!q!b|eCpL7XPqeV9C-`BLe3))p z$^bZP6_kho7Z~A{mtb7zCIQ&s@rTTpK8kNs5=quyGL$dC%MVZ>{yUD=XtoCx8h zI4iplSedet1@hvhZ`1ffnk)9q<2DEJy5-3u@KPs$l{547;Sc`!CR-_<58rz8!SKgF zx;FgrcW(~A{k@07gC|^qK)&dnVT^IWgR}A52FI;Qgtz#xxo;n20mENojd^zKs8;i| zEBHD7vp(pe3!bJ$2jtbX)B*k!T-7o2n{g&jVRR!5cric(Mk9r1z1V-#w+m!m5vW#v z=r=y+$4Cs2)bU2P(t&A5MHQXAdtmpagxKCj#hVdFbTAk#5!;8@EO&T`pkYUrN zFs3QrCZ{Ra)z|0QJLYXk8b==9dzd_UK8|otDI{rVPH4*JI~eYtn`HAmGq{@|3M>7gC=zylA|+$XPp(LO#Z>#K|Mu zzR0)nmY;frKy;!S!yjc<*;ZVh9Zs0c0|G3e6i%nv!=|lp;o0fyFZz+E^Di{h%$xd{ zr?)oOSfAiu1bl)Ig%kJznK1X_m}T{4wgW7eHia1BtwSK3W13?BQfS-&lIcJ@pUw8% z+LrxDIbp4*N}(HZJAnB*)y;HrD(q>AE9gwbT#c!))#z+om5IeI&b4^Hk3r^1wlBSV z?MJzh#|;eT6Wbqs27VM$Zl3Xq|1zIn8IPlEtQL1&`dNj$n%Yt9I=V*?Uz%@~?oO?o z$mY1wj^O*>xHJ5-k3Jio3~vnAZrmIm-e)`8&1;+ze|h-ylTX@L3MSfHnEn6xN3RXP zcJ0P+bMt<05?@*tzA(LcF}b%g8z)d1wNTwbNU%T?E2 zv%TQln+*&ybjQ#kO#=+QtX;*SBVs<&`lV%9yh4~4>>gXIws>)3pmBxG_W3v1$9;{Z zy&o_s{FFF&xZ=mHT*#Up3SzCxN2I^OZd&)|b!%O=r#S0irtt+|&q|YD#ZCPcS8(f| z1vizu{L?i3X(yA$QgG5P*y3Y&o^IIRD%iq1L8}8#X%+08PQ~XrNt}3(fwT;@sO{~2 zRw7GJ{RW?r_Gl>RXo~qZ$B&>M0u#=SX%iD>zlMz~HCNSpDiG&|&^qc0& z5OMt4t6?R#K>_c?$9C;Cv<{YJNb}C{;5v)y3_rx~tD(p*&UOWSxTMMZ)sZkDsAnw4 z4dVTZ%{VP-=CAGdZ?K3Pc=43i{zex)fTX+*AOi;-yPR^C>9jcH(@#Ph#EM&73I_V>ANeJtyyYD9ir2|EKXAh90uMd=$=qCnKd?L_OY2m# zs>!+)+08dnSuc|LFrBR#@rb)5XVGcTs=rst+fXX?yjj2OTwvtb2@Zkr^n z43MRhBN4yl1rudWHpMqe{LUkfBoc?Sy_)7BxN)@c7M!}E+{`U~jP)|c9%Pk= z{gNyAa{d)%cR;^t2O}+YVt<=5B9A$b3mk8m)i|T9!dd6C(u%&)o1R>qeC-Nrj+qhe zV|aM*h>I{>UZ}w8jHiYx@k@uQSv{uzWCfzS@$8!PzsQ(A7TOygh>eqP^4o$(f1?)? zan?2e&Ee#mMpPLHbo&#Tz|jy>#uyLswf<hCI%`HlMuQ0e zW#~$`wvt3chBqpC>ym~R(}cI*`~36IW2`Xzm=oRa-@l(*oP?KNJ4eQCzD)+9md zhYw5ZgAYC!u5#m&@Y3{ZZs9eWNJlyA)+evBzH#G5XngkBXL-uAxFs@*2IbzZUjEUv3Xc0R=JKEH2o8D0k17yBhL;LHig`uBkR%A zI2PSw{HHMZHAbp;N{?!)Q4C>dm*Go%-a9mBJ9$4a*=BTp3kj@k+yM&ia38?lm)M`! zcGbZuHx;AOD_a#7W*%2*KhIO6{pk(>zoOe2)gI>kE5AVxah0W+_o;|?hG$gDUwQ3k z={&4_ysz!^QsApHC&P8|b75L1I*&aWUQ2I{(oa%2J7z793F?VLI*WhnGga0~=YZlg zS4PAWeAghoNFJuBXTd9&$fRC|O|H03`Kl~@}{r=iUF_qyL;a{Wvk@Z=62%tnPuPL!f`ORs(NSWC_`7RN< z`@6gl`7FNSb|tiKN43e>G8xbqlg>%wh!Y5w@L`16;N`c0F~xDnIQZoNUrwjN0OJ}M zzSv+yu?#ipxWw8m1^c|*7H)Oo%Y~g`wgq6&$)Mwa+mT_V<1|%wFti1 zsubgo%RKj3=<*Q;l)JoOZ!rL2dlKBTZ_?YJpiNh+aNq+^FS*!op9R+w4!jUOYX-%! z2%Ng}DubpQyi7k~5dM%ALT}PHiO<&=Ze`jGPe@q+rT1xBB#(dd{#cLZb*pk&b!=UF z!M*HrPVY1srnIMUDH@Bam(`qRg{vs%bShl<6+Y`J-NGq(>M3reokgc`O1_t^YAt`X zQop!52ycxrY#rD##!Q+^@6$GjbAsjy0Vg6E{4;5|biEBmvTcH{!pRQ{Sjk75VAw6V z>P7kF^O&tfuD#wt`xD)a6*xR6v|4U#vk8Zw%>hkD*?tqRJIXxYmNO|zH%vsT>0Z5O8oXU zzs13YG-7*&5w^;Naa)UaQeuM2D5nmk2dZX@Hc{r8axf20V$ijNI{PJLwJ$0s%O+Wi zF)79oK@h;O^s@@8_?BSAm&itq(c4KzA z6K|5bO4u~zYUrJ3gC`bPd0YMfgpU0^2@)hOlXsGoLlWw->43p90na3I2Z3d59WuWq zw-hv1B}D=n#&}3mI&zQ7!b=`)vMmkkW*ljTb>fXHEX-#Vqx`(#x;})FhlFW1EiXuJ z9#hURj@|(g%%7-Lt8BND)ptfs$7o4<2^hY#qPMv1qmfC)N?G3$5$3yjfO`36DzN)0iY#91ih+XK)K=nLg(9itY8LW5ekL$jH(dhHzAOARXjF+x-G`fiQ(MKP}V54lVRFS4MG`Pq|`n7^Z zgO7$6X-L0llySyYDjH#=FJIxMp}%AoMrXd1*=x3?=PDQBN`B#utCcdp`m4Vh!$?4D%LX~9?7`EHFdGn0VTWjd_`_F;xY2VT=O zSr^?EVkAlt12dHse?I>5Ou7|2p8lNr*AcGx4!hxHAsDM{j|a>VoUd4&H2UXV3%ui! ztWw0E{)IG$5UJh>sXIBX0);In!gSuU;j`xQ94;~J$od1|xAej8AC_5FE!o%NLf_MGQxsKtvBF2QXJZBN^ z_(gxNm?B^46+Y{E7W@f5Yve3CYm!GXsyqvROz57o^go4P$E$cuVav~Z1cMAOJYEuS zUU@cr=i2+jpWb-E0 zd&<6Q4%20_#n3W6*&!RKml%Z(*ygmq{|I~<`h`~+;6T@k;6cAU9PsAa0aoj{AAINW zPodzsFMdMgB@W%)eU}C>((u+K3_-qBl1I`Ota@28YC2?l)Fxk*3`3AxIj)l@=rC~F zyznk8kqe$0WHvT2`0RcJ9Qi`AcFGymq!+^f*VIkhuWGei=Wbm+n&QrJ46jOG6*q-7 zT_?|}zs0t{>*7oPN}m>uajWu?sx(V0-UVOJl3`A((u=R*iW6rFU;gP+m?^yB6K$VDezDC24j62rnLM~J-sWZn@XKne-qdy4os$dkp3$64 zXIz@eg}Avknp-;@=Q~l@;48fYdIvcg9i-`G)w4j(F=;u#kaGD-Go<(~_f{pHAMxlR zf@j{^b-RA0G#xx;RyO3(SmY`j4_{Rlub|bK z06+jqL_t)sIx&w4iL4w6;E%_22B1`m2G=HIRj))5F8aos0i@J`F2}WP{fE zrmP!A{x&{cE@==OImxSO`eURcY@R|tM3IyCM2s4809O6ytGo<#B`awfxYdO^bSkHS z@`}SFUD3>2{7eF(bfL05v}9(s&JY zOW%PYyvM_}!Tz={Tg#}(eaDI$chp8K-8{$*1h*!Rha|PC4 zG9XX6v@rl(IewlS#^R*k@wLv-5)FQG{idWh}=bL!wOI~A)koH%~6lWeyH>?*tUFF3ISkQ=bA+I;cIdLn4 zi3m#?rpMbw7lMPMEY6R8w*HK;-??BiISR=1=?>5GbzdVnFnerkatmlyA0d0r zoPptzFW$;_;1;HdgF;-MqoT}LkMB=)WFKgGl|nU~>Y_Hhg?<5J#D^b#n4hapG(@<< zL?eQ$Tr{X?fY4A`G8RXJS{8Z ztK5p!2v3T+tMtes!o*dY&vK~)1f(|uPh{1L;+=K{@c351AESFZ9w>Jm7yWvQKg!&h zjbVU}r5BRVeC&08;F&|rR+tCLV$$t61rRXpWRDJN$(V$b44A9*a`v1vnC;WS6dvH| zS1SLFf`5rOVS7~R-Q*ia1<5&EPK3DNORpuFCVNZjPn(35M=G2hi`<0&zKYH-i@aWFn z;mvQoLnGyYGc3=K?mrlw(|GnU$}F%pKWC%v|M|)HhIcT^n8&>A!MDD}`G9x@Tja^V ztMWHONp~#ZG4`cAr_z^F7xz@6y)G7Pi6l<7mBOF%OZoH0&k7Qu5ANl(|BY=7Gpst< z(t51Hh0eAas~l3Imk|XV7>^mp4Q$SV^;FYtjaJ@$8(< zEmp}q{|p#T&JP3=Q`cX8c0idv1y&}Fa0`WimiBqpS=Q)-|$|aoJ5RmA|t!(9_6A+CYAZRp6 zoH8po`mM=?I&?yte4y`6I(#B3f5KCF6^#F5J(sx-7|YT3g8oDc2cr244zH6oD2F$3 z#!I)<=R7N`L5Mf5#amX*A9y8zIZ@y@zp|7QC;TdcXN5yEh(t;s-Z7w2ACyBrswOl# zen|5t?@i@39+*s$rD2*han|9`#NWmrq?s4OkwJKQ8s=w0=B^z^xai3;m^kT2|MH>D zI0gw%5Gb}@zgk_;5E1!-8S@>XB}kj>{7de@Mi|qrUkcUu32#2lR}@Z?^4F^{=YRk! z4Gq8Yv!AJUVSThtm{~v)&x0(qNllC>vV(@MV5Ap5%1VBVo^{`N@#JM*jSxMwc#e9O zE^yK!HXgn5N?XHk8I}ISoVXY#OamBJp(oEc;FZC}0~)#A_O|r=F3(~CD9wsImo5zZ zRUIzq3`Ei_J83JMJms%nBeU^j7no`v^l?O5W@eS$EW=xG><|ClfA+72zj*)h@H^jU zm9sK|W79Wpo0NlFNck3({`Vq>59>4*c|(+WNnHm)(?T6;svsUl>}i43pFz6mF9MUt zXj-uwP3cD8-2?mRRB(SSLsIFE$bFVrzWU0mnde>SALzi* z;BxJX+v+!RR*S|DovQ(@?>B%8Kjc$SWB!4$UouXdEthh3IfU`8n>1gPM98b?dUKjO z1$EfrL+A)E3<1NY>2@$ymAk;%Hv7zbtUEe+RM|uaUB1EiSMZalvDhr}(RCqW zFga2wvb73b@)zyU>p1d{HxeGnSiHldfB=lV9e1ApzzSnOB}~DMg5rTdXjI%328(yb z_-M-9B|pwj@cSXiMsL5e;fEiK-u}z{(pb`;kX>yTy4(@ciqYmnJ3OYbz)b%%tZB@b z@2`BDpK|sMr#d?w^JK?vzqIk%KcbQ!aP8*(;q$u>xu)&$@alQa1_9szWiIlPXdRQf zUFQ^Ls(ax|38Iy-g+J0NpfRC&=J1t#;yJ^g1y6vV6(RoGY?4@YaY|D7WAO8CA^f`yJay+)H9d6v>TENd)DRW_SxPImOaPRJe z;S|*s7!!cP7Ak)TEru@JfU^n0Xl8EYY^) z3$fhVx(i;!x3fVphg#nmCG0a}+1&q}0SS9+d&4~B6Z8T@g~keoA5171UC>T7)U?C0 zJp30z8D;FyQSl9rPt|Q36exU}(TK7)T5|_h(UB$EE?28w*Jhm~)?Csq_K6~PRYm}1EX&)f(9!8@D2Mef<)J5x($~#sN zg#RsYYwOd$;k=*d?`Q4es}^srm%LL>k>NCXD@~fAGZ+38x>HbEbtVhj3|P-`D~ku> zdJc;N4SNY!n`mqGu+KdYrerkXK+Waz4lLZJW49G0Q%%}?>}66O&Z0FY1J8dYT%*8DNevXWK?nG!^&8UG7}^Gki*)Ds1?=rND2 zeo|J@P(KP~Hmm|fwJ(h(K+pIQj<~jU3mEgz(!O4-m@7f4IU;(cOwFHiF2}Z0O8>qzHen+T`653sbuQ7Ct5&2f4d~my#UY%!==1*6aaTzh z&fL9Qbz5B~&*1Sra`@KUhr@Ti%`LSY$j&!Oj0eEOcbn_F4>ZVQRM#M(RXcr#h9UU2 zJar`!6}_8Z`x;j$VzenY>YyTc(mO@ol!YO1b*X0{rhG-v_@aTPR*%HE(i%LKq49a+_18zBJxeZu%h7T#2dd>C=eMp~F4B3ElFb zZ2p@%z($!PVLm_QU-r~uEKf`U%6x2hpd;m^Xi zYmr6ebCZJqci#SF_@mqJa?&{ybtcOFo~}*hhKq{{HY!-+y!X z&h_`%(y~WAbA<)}XiN?e(!orH`8VvJmRqTH8aEZM;tOt?W`(hBL~lF`_83f0r&|F) zgN1_uLl`~WZp5ubt}0q`H9f74fi<%;2yL+9#l8AsB!aDC8_a-s#nE}OwFx7O#t*Ls zbS80-8=O2u*B1qc`U11ZDj8ph=e8~~5a3W@&a&~!Y4>z$h|z#^xPKd7uLHyK&CV^` z3>?uz-`6X>05Ei@7-Kdy+%OVz^-lZL4J~s8Sn8l74M%FO+x4s&>Vq4&H@Rkelhrer z!Z8YI_}OIa!nP|9B&JvP%$oh(2QkceWw+|eyBF@Zw{^ND`D^zJ)WFO3TQ4Kp*su1& zo`zEgzCa}?8mr`}v^>|)K9#RHBY?}N{Q}AWj6u9t<~kG5cwsBf#LhZRdu&D#+PrOs zpUdj)N7St}oE$uR+6)|722aCs;F9xPKypIjSXo-xHU;7ZR>+v{LBrB;lEJr2TV`&K z@hloA3c|3$Jd+tG%^G`bT*{<@Xb;-T;Y#sbE9)SMEfP*-oz$2Qs58jKOpITfv3YPo zv0c%5CQb6!u(R_V{(aV2^~SNJ?HUrKHQwsMD{eFSB(BDr8OVxHoZ}FAw}Br|aCA{W zPxBHdO)_)@hhGxFYXHiCPag6VlC;qg=?4XDOvpmIY4QP3KKXTj(Kk-L)}+`nsk|mm z#`O2X>5LowUF5NILov?jbUvhGpPGqC5hcFxBO~c$2nro(Di8eh`b*{F5fO-JMkw^{ zmtq*$w>uyfr)XOr)V~uS%PaCot6%~gElOQAIR4A(F6 zKu@`|st8>=fi&G~gdJD-&(&b@Re3cQt;_*VzWJ;1#^OkRdzsk2EK3LXqHkFMVL+b0 z*E_zClG6$kfAnQaq>8?(E+p0hGc`;bj_EoN%9XhFT=al+Slg>O)^nfYxPl_(f)36j zmj)NxKnyQzUsS&fw!oMt>$81CYoFwyXCmbbkJJ@lbhhKvd+^8hjNFQ;k;D3|%k~Vk zB5uz7CtVoHDDUWq)5XxrQ+Rl^L;_XFl}Di%CZGqwlBeNi6JXz&U9hbi!!%A(xu;XSVDrj&e=>7S8iP7YTo#T zaY@WW?E()QZS!WA6FDsqoLju?@08CfoxagLAY-Hq+rSH%K6aVQHTbFiE zoZ3)^N;l28!1Zzqd_x!7U~>QkpEwVo)fnS8nm$YjX^GExu; zhaqWEhImkczyLJOM0CSw7#uD6O;h8Lenk3h{IV+Zld)b!J6 z_@>J|1F)7Sp4}gHbRqNTZB(|pWQRXRyV`4tL0%w1>X@umNajy|I^m)lXc;R^lSM%q z%oJW6qXQEL#&p-=D_D9eV@&i)V(}i~W}Z2jBpaDb6RPJlZc1ZSd=V|&4sTlxy*bVp zZaRp}6psCXZM-!ybWEFa?Bn)X`{VQ#v>nlNzOAcmf%k*daIu$n=iMmxfRA~^JUju7 zcdq|=`aHaj-h|H9vB3odX8Ov2M7nN4UB338G9lU<9z1?B-1cCk;o0!c<=ZGiicZaX zC*VPZz9kf(B}rnXD;euqKoc1&>csN|%$&#xjFUP)Q3xw=#;6tatDcQ8GtX4UvtVba zUrum^%*P0yk$#rU^&+xc%u7C=Vfcr?`X|Hx{4alf*x^mHbAieK-eYcMy1>DZyL|oJ zWyvOo{_-~7**zHkpL@SK{M}dnMP7ROvPNrDtfOx0n8Ys}Xf#fOpONKb2JzCdnZ)F(+>fxA;)ZY8BqpizOosjanEhJa2_hD5>b4 zBjff6uf)y^gH+sx+mW|0Y4i}+r-qY@o|#SiiqVI7udzPIz{E2aa$XH| zcDTW5la($9Fnjt~KZYIR9qZZ^#HeDk#~J6~CI|j{P&n<`iHeO*U3iv^lNnWV;F$<~ z&2=(SgC?TmSEj}%8chG>Z6}_KV~pbs2VNR#ws^5DkdH-|`N}SBVbT$h_jEm-N6My; zJ@hL`^Z`Phi)S932wH|3T;lR%$SN)_-4Qf1*#p+6IyNDbW4sa<&A!zx47Ftf$S%-0 z(OPE(T_-`LOQR;f$~AQnfw;&m?e6!>YF5kGw}Bt|ec1w|NJQt|D=ots3>=HqB*FOP z2NY>r9vKKDP{sQ+&8K=#*k@qLmxN*N1VXPgGU=G$1y?;upu48C7BE^oZRhgxr&AVj zrC;&k-_uDTu?5U~98tdeYO-Nm^jUOy$~!BWzztvcPYoN%3vu$SRXXObq|X;^LdlnO zqlCuZ?Ss_HCO0q|0Fnj%RK7y9=P7MM8YHIZBCztcU*)^04#4h=PkB%;qd)5{X|^Zp z5z_3_flK|Em-Iy` zT;qWQUOthR2=(o++M28agCJWyFQF-aJpR<6;}eH4DKDO`Kx=Zhz9#ZQx0S0%jX)mA zL6U^XBa%uKLK-}{^fR&u;1*H)Mu#$oeN{{Z04MDWI`VWv7umo~-7=nE>WG;kQMmfK zh*w>xH_JyCgKl7?)XNYbJGq0eJXvK2yk&F%E=TcY^;+sW8Djx*41R8D+U5Xh>mH)E zv0er|y&cPecz=F6)E@W?-|$;~GGI6d(SlHPQrCn<4`nQgF+}(t zZ+J9zxqw_J_1sO6cam*|-0PgvSa!$U zEO;z!UEqu0tfDocW4C?G&2*AZ&aCWjzc!qI<=@Sr63DHz@h-^A4MJer?v5n#F%a4Y4U%5Yg@a!C4SRV|RZoQrf_0E$A z!{tkttud)vPnlKx@$HMlZ(P4RT;6(=Nr>hE^=95DtWL=OA`xfdS2?bubt=AzrN9~B zEEw`tZ;$bs(jZ|fPj$YoLi0;(c(Gq$>7f(od&yv9si6bB7&&s~nIjD}h+!lEPOFVOM?*WJEa<#TS-QgZ9Rs52+xZDOQ;$Q^IERjvuhx``K z)EpUT`01sYGG*kj^kyauMa$(=$dS(HvIR4zWeh71VnkZ7&B>+J_LB!3=$n}py{cQG zHo1MtGier#-Vc|YF@vzK=<#LaY8y5@Ko>bOy7yceHR{WbQ492{NSfYE``Z07Xu$ek z^}C*5wOm$>st;e3*HqTZL&b%DXOL}h-(tcH*_=yagr#L*@04$^HJJFKE{ zzlIYLCwdMXpR*$7Ip@7NSaZVQ$8;B=IaqO0zE7L+Tp8)@?X*U*!)h3DT^*8hW1PIh z>+)3%IIK`%@bA)n2RB@l7rMf^vc(lJ8Pkv%G;=eQEdQCfH?J% z?N(jqbPO#XbQ#Li2z70KmA3&9Aqi;`m;MXj46Knu+JZWPazX@~Y{zu6=Hyjp7o<+@ zqwIs#pJz;jr!+d8jJ)dGl^VfA7$>nh<$-j5M)@jj`NVHRq#%yO8dqw(Gsy!-Yl7NI z)1P+!uu2!rGii-A0coN$^D~XZA9m_P5QQFVO|a51D1C!RXcg*Js&v|1#8#R1uLo$2Z~aGjoIJpzvKO=kv+<}rg$ z5;|XCiog8QPCLKw6&4q{M}Ndhd(|ggWV4P;o%GOwX~I~Qop_}R5NB9^*4bpEGi+N2 zNyM4H=xy8W&O%CW)EzPEzI}+949fhZ7BkkblaG4wp(fW>+Ay)TWQC{)g;1xrIlCXtvun$Q`#-Z4LrsTBgvCE%45Y% z`E+@SX?+w%EDZ`e`%GPQZQfLw&Wa8#Ny}Z;dhMXiW(2> zwf|5g`*7RnGj2}`EQ(4S?sWmMl!=|X@eHxDJt{oh;v^kp$xd!yb@ENtmDO;5{)JyM zU>tb~ujUsgx|%){X}r9fyh1znijjgkkoqWLobktuEoL$aT@Z+bB!7gB>pT`H(N#xY z8e?75A6m$0+5r`_$>sZl4m*Lv%G}*q^wi$q&HBdpnZ!Jxti! zlX3_zGI*eG;aaBBEhin}J)E~?z_!r}$fv6ey?IJGJxfPEx}xC?PV%g+Rnk)q-81IU zX?yrxVPqaHo2Ts6)d1x6*cafNa)XAcQzxt{Bq=WH%%4ekiqQR1Dpx$m@KI7lRkp{v zFy|A%V?q#fLa>os^UUFmE!n4O-JgeZ3g6iXb=X#V{I!jCsOljC}j!tHVXH2K?BkT)$&cZz{z1NS`oThTG zhFBSU8o!kYXGG3abq4f`Wb(0KN$jC3A-eu^=qwfB_uhMd_#giD4ZgrQ3ilw(-Qm^O zUt^F@Jz}-hCLPZOn#%pVpAG-&{;vC@*Nzrs^Iv^qEKv#! zxwlWqF1|#z8jX&iCV?^1l7h8qX%jt8?e7rzJc?(!woCUcH4paXgFp0NVPiTuUO zZ3({PPjMZbJ$uRof%6W$Y3&@_nOsfd_}P5;_1qOIjz_niW>*28KdlKd zl8~ZNq6Qn%RIMp9i6}2)krrnk*!T)JPV}P(K;awuZQLEbfgLByDlWjITUd8TzVs2^ zN1y&9i!rQZyNEI*M~#2;8+YWy&qoLpl)jp9<-<(GNJJdNQFEMp#W0nDC4I95;A2^r zD|uyKv;-U9H(eJ6SI;TF`YK%G^@kUJb!B?{QAWsOdc}oa(k&1W>n&!+F&^ipssFGXTc%e zZ%JA8X1<`8gMHzh;S#jWzpzrNZEF<{W%-$Ae_=%DZ~tMuQRYY8NA5l@9(95&87Be9 zJiVRggyO7E)%V%=OQ=>sxCN?x+~so=C!O0b%1l1#XkvK7-(5|(OHUQ=v)8^>j?;vx zKY|0B45OMp`>pd^G&E?;&}~woG`3`g6LIG;ddS1D#weBGd%}9JF+&;}M*Ml5^}|Qc zIQ;h(diSY_Yr0&hUXprf`*3>C(~7!=0D;bGUQ+)8XpX>%+4hHoZOF;koPf zF#sIW0si^NmxdqQxXcuv$v)Utr9S>3yxyIVe;%Q-=W*-Ftxq|F#=L%1A0yf*!kosK z(AcJ4;0@RW)_B$IfKOw_664m!CBG`Lm5D=d+q8f^zZfQX9RaBI-K{~qy$>IQYe&VG zhRs)?fqtRmDwK^wwh38y_z(T;f)Qlvkd-N09HgsZH60?!o>+(BZDao~g9s#H<;&*g zCCbQ=2rpcPgM%wo_L;?bI=x$;mW&=W>_8Ba9+Sq_C`UrT@3X>Z!?I`l(3OE zW+`unTAaHHeQ@25<=S0FDV{yCAdS0f2>aE^0Sc>sG!VhpvueD~+rf`zq0T1bGX?zG zeXW6G8c02zXm#V?Kt}q*1p*_ z!HcFB2b$;plC$LPr0R!Hzeq2%{VE?Fu`t^g;s|WvaR`eLCp( zfaA!dfRzdS<}LXUKcyK~(Yrbe4jFV3w*|9t^47rY(c5YUJXLeSduvZh!0Ez^?|bTu?9Mc7ep9-DZ}m$9)3m-B+%CTY6E3=C0*$U^ti$` z&~z;W^I#uBq(2-a^GlhflG}%pC*mEVv~mQe1LuPhO3}!yyq=E~0}gpsS5@}vK-{X^ z8nmY}nPwi%hO&!mScB1MxQ3})OS6?!IHZu5by@kM2N>ALDNEWoPw7=2^E5B?m(HVv zJ#eKR{jg)~*wezl*Ex7PS4(-LnU?>iEaSqq1xJ6j=T+e)w*TqyhU(lYue@PUDTn2aOo69=Uq;D&@3=afKDrWLRT{Fw*c>cp5V< z!)uq9hUwq?-mjqpKL+d!PoF$x1#7o%gFTBTW{q$ zZ;dkYYxM{{p?~vaoJNz^UVAP1GhH6at6$k`wae$9e;(tGh8Sh?Xa4;O>$40zKBX&t zY23KM%~j~g-|HBZie`*h6p!+F0J4WFyZT35&+=L4tRI%QEp$>`4V;M7e4cqX0M{xO z$)>?C_>=k|V$o|v8f*XyAwVD**44B85}n2ou0xeaIzjAzrm*_O*VU7_$nSiT_G@fG!};@9 zhkJaU{i8d-HT=ETem_}JxZ}8F;7Be37(Oe2*GKr!bWP|121w z(CG;^SlGCKZ`io-8VHZTaf^`4KQW}(=qwGk4sEaoDO-Rjn2ilyq}(|yqpgX?la+d!WZw?*oMZ+7>p)PIu;l_oR}Q4g~&qj7A3k!=wZ~! zsD#^+xO4K*dmXLN+{ngsA>IKTuihRo2KnMzc;EsrudIAwB~P}hc>u8-7`1F*fH~wg zr6qft`}&?_%Cuaie>i>r5dkT3r5`&U>BHMUvv zO0&=Eoyfw=BFm1=3yd$Z^+~7xb~uy9@y9lbEZwTp4bD|?=C$uYjg><=>{lJACX$4W z?YK7##Z8w$SQ&KY1FGoV0P#HBB8r!O4ZpDXb=#DO2BzS`#4apL%T3HXk0G$Y$()%q z0r*ZJlV{4!up&u2FG3LUs}qm;W;ZZs2h^jsr%6BVOKjiL6dN{VZh#Ww^xM^ za6LIRDVi}~l_0z>@cn6A{qi-|etwcKy=EhFf@t4f&Ul?smWg?c{2fc) zc9bY)bCHJx=@iYR5U6rY*mUCv`17yf#1UcJV3^UIPvVjt7&rKqRQYX{pu;!t=v~yB z;8tqnkVvPS*N`}tt9<&rDs=z`Q+`Eb{;aZAe%o^#0o%FxQ1&X1&Z?YR~nKd0majO7BCjbpfV9T6%iP1t6 zHc@&pjKNFRm1ol=E2_FK9+hrd+=Q2_^F@dlc&Yl4E1VkqWsBZ$aZ!6Gh%M*jF+;g|fAmnvr)ggMPB8>z@k#*oXmEuM#du~#Pjp1%o@ zM&Q&_`dbf34?PSr*(wICTPU;jkwVj$YyC>wl79b1(b0uDK62IvfvWD->WUcGJJ^`7 zBeeK5cKqyTKN~*y;DZdjFDZhpl^`o&!!--*yGQ#*1Mmjq2HToDYyv{W7mAhaK z%THIUfK01jt(Xab6@;{Mx3Dm{I%p3=PHp#s-RLHuV|gZyKUqCe038Xg!JS5zzle_X zNwBR}J(hwJ-|KB!WW!0462h620!i8A~<9WqO&@uFU0Tr z$Nnm9D5;$s225MA<0Dt%+g4m@TCB{cxVnmKp`*X+;*TJ{nC~pQSt+9;x_t!?!qY=# z872uESH3rFUH`S=F;>zKKl*sM`|#fIyKlXp965+->NAHyjg*;`<1^1qjd&Vh(x-n` z38*~f)}W1wT$f(>k~$5h3Z~%a&(>y+1geU41a}(s26b8Ly-q# zu=-__RWp1Sg06dqKPJu>qvzDPP3bM<@I}Z=n-w{{8trj8@LfpLm0{l11 zomHoPSu^{~D)*{3xGHX)UQ_?boD&|~LA}dX(MUQI6$fjc36V)k2JmgAi31U~muEmC zKM#3yH5lVHjNH_6i26a&1&8@s4$pm7v~4pW$wxNoKVwJ+nF`Vdjeg}<7Q5%d$rmSh zIWch(AUsVyd|mO9YphATz-k=FUmA<#>9|t^kp`s4b~nIW5eC}N-QI*e4wyVt*onY4 zD{(XyxVq*E1|C=gf%bfK27(3lrW9 zz@VFf9Q5R2|BwQ)cK}8l%SO}<20so&l{c{9*mvkRzku+Q6MxC8@6LSr_6bZ5Ks7J! zTbelaBCLnN>X)y-&;>5rD+m)C-i4~pB1bgY{f&JX_A2I0$6t~n>oMG0WZRp3=_FU*O?@bkn3=XG%gwMY2eDENEULfsuE`^t7RuS z;gwON|+)B>t|OJVLO)(|}D`cfQi_!%1h+_0fd@#`vHUrANPR zDvNq-ko;Aa5NmuS5Pk|GT+*8?Res7l)noCHOw9%G#+l|%p7seERdx0y^05t;kb#je zztA@czx7j5qAUFBSQ(5LCZ+Zqs>rwu=4BwX4@+xF|!+ zCE`8HMWe*ED_4hym#@Uwa*ov=!n^82!-XqeUc)#d9an?+^Wa;JE5d1{xykmPOP4Oj zDB_taWCZzN54ah0)FzUcR-tUj|+L?+@}$#LQtF8aoW z2TzTvaR_Lfa!%#$sRa_4!X3*^gmOiMaKTlQ&q*?VbbXpzB?xhjUm~Qq6M``+NwUim zxTz35eq>2nCFlJRRCI{G3|o<t}r~WXKU*j5I{SmI*#9L7#y)k0qvBN##CPGeWpMYBv zkqR+STg9itm^89n1Ggr6&ZQg6fL@2NE`B0Qyfe62@C4sVAM98?kKcLwv*BOd z`6j38v!#Yt(8jsV;R-7^UU`MikN)Y=!$(*_cZU0%MES4pd}sK3uVR#ee7$^MjMHi; z7vK*TSm7CYE^@OHv`~^OX*Mx-9AGdx7+4ZIa0*=x5vIJLs39jW6KE~` zZ0sz9_ zzH#|jH$1}t;za-a1-1@hH1TWi7FRVn2)u~VTURSx*liTrPJT45cw4E1l}s4zM_h3u zE1I(h0lR{6+Z&UR(eq%0kLxh(OlmZEYMjY-0B|(+I9Yhksv56xmA(fz)|mjA2r%Gm z6D8gPrpq7t4n)tfw0P6L40)e-IJZIh)kt+Y7t}znot^=4RomFG0(pB(5OMR4er&;OJ z6~?e+h0}#M&)}N?jQ*@PAZ&V%&NKNDfd9IYCO}4o4o}OZ{7I0GFzUWfxTWhYX(?ZE z_{qfhI6c63R_%GjItC!=jYb~J5?AoaYXywg&(Cd*w)=!(nu&U^E55OHuDCEg|>)Qt@*bcFGjwB|{E2`i(~hOH~1Yd?|<6DD4l$HXt5IHp8iDp%@&G^J>MNw4~6 zebt4J4PK_*>D6FX%2?;9?T?%ufa&50@7XTbZ(JX4VB|1Cn@-2QEY_49r8bgQuw)4qcL_wPLq~ofTsZqpLFgj(?AfqwG33v=2z2lRQ z{FP0*waO)Uz(x*f;NSB_S`L0WP*+@N#5h2ja+${pA8@j@GHEQ5htBlMpRyVDYHF~t{J$xCC^2z8#uhad7xjU|vj-8I1)jQ;M_SLJDp>$Ijc z8_yI`z(#TfG@)rZ<|kyOPyAz)o{><1wvLU;7F=DMhh=oa>@^jxtxo%RZt1!fp-hmhYAfGN`a^5|i2%p|{@n zkC{NfIy`xBZ}{N-4{{au-~Z;1gW#OY%vZm7njq7R9{IaOYXnpRX}+9r889+1tUB zVUJZbi=|60*M_6!N=$auLyFfU4n3=ZzN9{W&ZN7m|I=H+*lK|Y_^UG0J~NBnl? z3@;?kfH~lRVaR~$nJ%o{;j_+?)j6B4z~N=Z#s&HZzLt1a&Gzf4!M(T4CKs>J4%teD z(Pe>=#h!G5ab~&6OH>0(R>GX;1%DkH}#BV%QcX?W+TN@?S9&h*Q=)u!X30{sV&V#BgLglHZIJm)N9Xr zvF-12b+ucFv^~0mXBUf$Mv;q`_}a+lSSOKAM8&VkIInfm+E-d(J#g0ne!(!V$)wVQ zdi7u7AYi)=4KhyjTAOp=Lo$ zYcP_@H2R88;XjrMFu_e>F|0$@Hh@Oh4(C{CEI{ZQ-Bzwxu+4yxj)esE)ky)>;S!Nx z%b#fcB%9fpeuPktL@GlA?|;omlT(-sD1{A?P=OKg3Z`q|c}>~{(iKK#i6#kHakHA` z2)yfo`~f6s^)Jcb5OxC1gS>Y_JR&o3)~P0Y<5!}Y9zHzV&BOYZmPX%v1R5!W|$V~vDWJ|p1#F?KFvBMSd z$lAaHV{&-#M;---Z`7Bo!*o?&B1=ci5Qp@9Tp8tSI*z&tu z>A@BMDl_BsE3ZHQRGApgY9v|oJXN_08rWXemUHlo2GL)_;!fg3i=0O4I;`-@CQi|* zw7`W(r`_><*xyP7^jMK;0rZXhr61JbGV07ulK=MmmV@ouG>1&u6mn*kZCz^i={((n zVa%Z`14uh28!1zlasog}lK)UT%1f89)fEwYCi6%GYYJ)gz;vf4+&+s#I(l?UA6vucCF&YozI$+{@ zrB$BV{u2_C;a@7Kw7}_TjRt=cT{I2Li*Uh-pS*~xzq7Bh8VMDxN{3y+bk-xyG1HN! z=W`vQNqm)4MU7z@OkFYTaqS(TE}UNM|jVWF`^uO95{ z@d~}jLq}b-GM_Rx?s)ixLN9?cr$5jBl;4&wWvxu+ECN$37UW{<>ahK-|B|I-&xcPx z`f&K@laGeCuG}A9zj!ADE>(J}Os6JJMQHvx_^ilQ0}+e($J|Q14pEh}5av89e*O&H z8sNFIr?}I4mO_BWF*0kExT?VBd@7&E=uMfg@K0$Jej0x#qArW~6m|uv7mtFGDCfZ_^}-BbD-C?TQ`PHM(Gb&!ua{!2g6@H{@cTEU;hI{ zW6=LH8GLc6M###q5$p22E`B7^JyTkz(VOF~3K!3MO4}&{ud@uKt@+El9}fqYt|WHA zfWi@F6rjr|d4hO&=-D!^PGTSJq1RjUw$IFfb68|bgII9h3{xB6SV=S7$FIQy8ocqh zxN&KFlWkA=499_Ihn(etVaJh%9pQpAf`%oAlYI;(jyN!qXh6xWPn;ps4!(5d40}Q` zvS>{4piH+cIk?Cnz0lg^Oc<{`E`Gs7rxJ_qF?417C#(tYXzo&8u7LVI|7E{3Q6M)kx}~%z*~g zIffO_b=krgvBSXI+m;-h{X3VS002M$Nkl|wz)BZy(<>gr8Q_@@T=FVoT#Oh=M^2FB(Vj_eu+5vl!NnK) z;n@i^P4P&q+Us%3p}qkyKZ!3id3Apt zgq4M=I?80Y>SJ`)5q!k!A7ENNugI1rFB7e6I%PK>ku?!P4ISf5FI{N7GVdKMa>ox` zlhLC z35~vR4O5=UgF&LjGj-HKk2mUAeJ}zSdeE}I`yt9U6ekh=6~-&RhXGsvOFr@a8ICiI zr|DT(>jiZ_fDOc=a%`iH{gQ7xO&`ULcU&HsILW0>;E4eMMdSu1`&BxC=$dzc&W}(F z`p%Lic^L(_Z4d(;ZI-50Ts(=h9{A4mLJY^d zO~$PL0YO<>9a1M!fd+Ea0rEvu+C@_s!^vZ(mpEnW{|xI|{+eIeM)`=+ubaXYzhi*F zDB}qip_E18*1)WaFCoM$e<-v+ZKbH=i>4dP&FM&1%$V=w9a!0-GW&G0UFnSU35@;%`gxxb&=1D3H+dRijOq{5``}k>IV1M%A_VDXhFAf(sGytr3k-rqN9_-YVRry^NcPf#dIK`Wn z>ojdj>nymu)JOXEIt0+EUk5kY$FTS4;c(Fj6b6q0Bh29j#uWzd`+jbw1KVe74{rfX zG3@(Ah}L;SBz-=ezgwFSVzD&c7oRUE(s`-xZ*s8ih9}~)3dhwd-ncZddWMxbiw%q- zSgw}~(tzDeZ^xN43s=do`o)fdYqAgF%j^lh7<@FcI6&dk`Jq>N!*`3%--nwXRD79? zaRU>pZ5B)nm(W>y6Vt-8a1@r8JlmL-o}CjV0J~sC4_mjq4t#-J>cDwRA4`4(l^NDw zyRS8{Mg!~G_zCddX9=H!TA2hn_|Q<|HXh@=MafBuh764wfd$Xig$o9U&!31ajGX~3 z!mqG2xtuS<$RvYi=x%dx@GeFd2mKeczv8_9TDq=MaI2B4Rjy)?@hWW3mvQp&=%Irk zdJD+!GI?;KY`i!caW2wa9r*9FqD7-mCREV&W7Z+FaAL9oXP*@=5|O`MLIw>)IUe^* z?;bN&CyWj%G%^_-0~0WtY@;&GgM+u>;eo}O;Mysg-+so8V-aO*<5&VX{l&vPEPF>A zBZK(@#vg1{BO*3A)2~9y_~a>r^Hvrb%>;A8WSRrO>@(0WF9f_h zDXe*wof&lg<#x{OZz%;-{8(T>C_~kJ24+ zP77l>!f7Y)N|SZv;3zOaC68VOtGrY=kp#LMgWc20v?CdphzW2MDPibS;FUar_H^rC z)pdait|2xfNLH!z!qXt5s}&bJ=qV2m$dokiWOJ>ETHL@t7dFbKHi0Fv+^L~bDf`Cr99}@v`d#5cOsW@y7S;_ zjIv*M%dYR5ub*e>CH__u zlh(SYE(1vBwOmIP<(Mn76&bjqS&=@AK#!}u89c7{7H(~>15Y0%fb@!HkDJoWJVFrX z1IWDdq!}f13z=$=FFy^)sH}hNX&-p(TioiWvzr*lAmDwKuCn$rJf=9~LBAUeqz)Vy zF;v*N{{3O&=J$uEEJ1t!{XZXWe|B&9;cFic7nnRzC!-wm5ITmVJx$` zdr^AAS!h$&%PCRwv?<&d^-pD6CHs`_6m}YamfuSD^SI(WeX8a*Kzwn^g})9XZO%a@ z&Eos7-x>a|j~_C&eKmu%$6WWZ&vvH6=X=902mhX9FX`r$%UqrJc=*%1?+!n_^=IqI zj`3&l9|3<3&&%+r0nwQ_D040mt zHI(qe@g?LJo=puP&VVq6?6U=FNuS`J0!om`iJmGOkJ1_hk{E6$Tvt3}EAZ*eRc_{BW3?tI=<|Y}satEv& z7VKCd=SU!D@qtY(MdY;kw(r(Noou0ZG*oS8C-F6y>C`$mW}))BS|$rc1wz1Q$1MMG?%U;TK^PmfBz z^&3%jAvF*li8g*JH<9W(I1PjV?^b%LY6-rgt!K8nA{{h{gaqUv?4Xv z#t-q6E=cq<$$ZJt;9DCN&~i|JPSB$dDz0G%Z|OJ4c9@pNV-+;S~`D=SrvQ%34q^!;)|%Ez&MQ<x)^EEc^926UO2A2(Ll1z3GW&qG`#qfjz*N{&v(+!Jxtfzo-FqV z4<8KIIgs}O7tnYUlD9Q!#4z1koOFimo3aYqJfM7eDNpq?!g#}!S8!_}@pdJzzV_Pd zx;e=_X^^>h?_PK*Tb-*Tox18Aps8GA_5v4_>(kFjYFiYSIG7$dk9 zP_HYGQ|CJCe1wrnsK;~k2lSV9)9kInos;ZhtOCV|ODE&k54wj8(EQ|d^*jH44)=A- z(ocW#0hYj>;n!}x-_j)mMJI>YDaq4OBVEbiONcxx!!OGs9i`b&Q>SU?1qq)f-!$!% zy7+>eP0F zZr5siGTnkJvlrVQ&Si%Rp@YvbwlKrXWCO#|-ks!0{=ACQ^K1m)gMj%2k1@uBe-95` zUBfvwUbT%o+~@jk%s(3o&dK3rzTD@gC?~aS-8#e|waEx4ALc1=2Viji+I_8oU#JFb zqQ{(-@I>ShM3zbS0jy)LeO9XQ@h`>%q3j!XF_Pqb7TdgobOsuZk+(FO5Ntz1)ZkHZ#5)vCp92CO^<}tCi0DIOxj?74l*~sz3Yzv7F`QFYm@q+pxIO zbMDjnWWSt3RCpetArqY3DlYVl6rY5a*EZ2^bPBi0D4ac{vKUkEt&&SpOFDeh$!3$6 zXXG;u!J`A=_$Qu&V@Hv~sIRQ}DP)A`*(4opqmS_J;)GwP1F1OWRu}w77Rp(=9bSIT z-}EYi^h!@o7;_Vq@ceufUA*Y06NnBk9tD|@K?ia;qAQ;1i7q0_ zJs}ww%37F$bzH;)EWU0G)FKY9TS_{~p#w){WYfFBGP0%QzE+ypRKFH^{g*U&?ciwp?29i%d z{d9Q${r88jzT$&6e4<;!&X-?)89bKPp9UWdK&F5G`RBuX@4Xih`N?n%I2w1vDG$O+ zTLX*q<*5uaX42>*jAc`P&awSj4)YWa*luEzhp|$rLOLcX8Ij)&Oh3~BOl@@DOkW6Hz#Ta;#?3^FLeddOFzP26feN? zF(qT|6uzC@tbXv{p(s~|#}B?9KKj*1!xqO%{^=k7GU>wUVwBe7Dy^^^?k>XWes%13 z>89xCu<}CEzYA~E3Yq#PXk(k_rf>x_jhmOp_9x6NuP@72xP+@dR8P~!I=Vn;l|DK=f>T~QW#@^DA-;Iz=dTTWUbRiW+Cb9IKl7#n}{+Ux}k<4hk zblAGqYHmhlSM69dZ`>ORd{+`kI|aE-Z`1{y*r<0+)OFxAQ#&`#$%Ds~+}6F!1-f-) z$-d+LEl$X7<_8*9T*;$YPq=enb&Xvqd>-3lNWy4D@YC7$Y3IorVmxY;$w}z6NsD*Q zcp%XgrZO$I-l0z)ENf|fBM^Vy*-b{8(nSF4q(Tn0@Q$1u)$+d zaF#K3BrHGH4RN90^J~zO=Opzwfifmpe&Q0%pU5qV0`Mb~yQFL{wYYADXyM|sc=Kt# zkw_kpFEo$SMc(oAD1-Jvx5o2EBmVRlna>1tHW2m=`(OeL!{#OIdLW@jz6|< zU1&?JhkeLF$LA)^j zbOmp}R7j?4Nc5lgm`F=m#c0y9=P*q za?=naKFgGHnWw{wS7XZx)t?hq=CiqsmBd#Sn;R9#5AA&q@%G#JjUm%QpiA9%VNAT z@hN}CYdn%xjT7?I#tU|!Iw*E0Y->yT+hXA-^<)UY!FXwW{wtS$JP8-vm=4O>IP=>! zrfwGITqep?N6aeTa#&V*9J$s%rPq-b?%6yEcP1Du84p>zAQttKW0LcGz7vyqh`Upz zZJV?|doNw-WW*P`_Djdq7-iaMMgRhKCS^2J<|=>jSK)8+ltQ^8W85^p((9Sy#CB|f zmTv!(VeQu6i8b=qzxoxH!n?zd`8@GkYrL@rqdL%qnzu$&<;`Qfh=<6zKw@6_$I8>q z!&_<7^eOr@hUA^b3VpGgN9SVbS4)}V)Ia6BFci$Zw1V+jZ~Dq0R%y0HUEyh==l_bo zU~Hu2qRcDsqUS}TtoY8yITV#fq-XiFYk|0m{`>EKIsB`gKOJ_q{FL$HaA^}o#~%Gv z77sg5xvvDkz2p7iFMoS$_`wGsjs3@65uU51@1;zVE}ZA+&jels^P)7-%q!j$z3HmX zbBcsWMeU2z>i5EVTZdbhx+k zBjC8Cm!mw3oWpdtipEn7HH$p+MjK-c!yj>SuC`-l{_NFdy@Zn}o%q(!lBCEYwUm1A8&t!u7XGc`Kkk{3jJ6$-9&rY4ZU0G&_ zi>oaRK8M^FV{1Wj;DhecWqgD!>?v(Ue)-CjpTAx@eaym)NrJ{5Kf~>X#gs4N>T>Z& zW@fHHy>V|O@SR8?ZPRhFVXyw!+AMLTpX=KlPH3|-Rii)9hKW}vudcR<$C2Mm#Xzmj zcIS*ZG|1Quoy7ZG2G%&=ss{#y8bn+Tdh}ow2I^=Gdl?^hX@4W-Y zOMv(J;)q{c0*oV8-jmbhq;6C5Sl|W9$j;&zZY=BmffxDl?KT!6POjtw->~dmJE#SZ zOliblrA!1Pm&jLK!nfi#Ba9K!G7_0dGB|;CCr*-?$vN@Um4U<0(yF|3FAVWVec;Rk zhFU($FG?7cNBrhDElw1s!(`zlCrJ(79Zu@HCCVSe1JO?2TM5MDL{`MQX2|jGF7dba z)W#g;dlMnpjsD#2M{@MWSc?-HCm@g`IFFo}pPPb6M&mGx5{Upv3%&EW=Nn1p*UlVwhRJI zorJ+7-ccsIk! zg^N7k&8P9&e)*Px;DaBrw9Oy0voL+wIA9|e{7jFcS8VG!)X_{A z=>?rlSI8xvhLIT}mZGQ6p7DZyoAJpbL@0IguWzi!$YK%QLGl!1j7AA@?sAK~m&Y&k z?L#XLAi%An*fmNVW39Sz-H$ggPH8BC?j^1+upQkwc_#*p)zzDnU8DcXaGzZq}7foAxj$sE@gwJ_2V4XJbudCpK{Ouq9MrVt?cYM95&e@^VW@< zk*Nk6clg}&bKpAgWX_7$IFA-d<0`l6D=T-i+E?Rvrr(kt$1_G2D)sW_l9_&k->){3 zOj;{zb^+VJ3@1rSc;>i~-#KZo5;Y%d%lV=-A=)Rg@tkhl9yUJsFX(W)!)JVL>6ah< zde~Us8~)C%Uy<5`V^hytk4UWxfVkhqRb3R07YSot+BB{PhQj5u@K52U>EEXRA{x{9 z;-~0N(<*NItn}H6toSMHJpXfWvxw$pdY!lz;FGrZ5HNm>PVk@Hx;y-ffBnPZDYN}e zl=&LUeT_Z#D!>JL+mRQ;GHC7YuM7_l?hH3q?`2?0=Ms|xRQ3H4^O>hQbq765nCE)| z|AN=xJTGg~lfa-Z9+d;Xl$&76O}wnaGB5=A-wbL^E-8k2ql)6ehXThN14^IC)20Pb5@nbMT|2`hxi%be1ib93mj8AVq{;)y)*P> zcx$}tXV;7j^XCX6e!ppv)sTxTrq>K))DM>moBvqC)Z-~{E__>if`KKIgUn=El<^jS zflD8k__@ejWRy@sJ_Kog`R!+fZaYqtZnpNIpA^!jm8lbq`lfctIl!ZK`zpofy#dD z-6-m$=p__K3^JK$k_YF)C9?MkK2Q)=&rvo3+B{uf%Htd9z+eDU4k~ML1P|0*=vXJR zM05w%)4YizPmSk($q9<&YjP0I$)bk&anT09^61Yx^~+V#lM!LGHg%1~O zNfQp}loh{sHE2{*cJ*d8a%yPX_$6>-FZn_<>C{EG34t5=m`{4eFCZhYjzwkgsrYs- zzj1|jMox`q(+fw*Cwai%#=eF(WiWf^&1a7DxtmIsBcqmH z2gWyS*4~!3D-Xm6cgo-Joyn`q-g*>v(hr5Po&%dg zim3gG`OKsY zIF%D!D{c+p8oWJvr~X)AQdN4P`mOoo$DN8*9MO$|#$Zw1^rqTiowUI34jWQPch+6MJ|l*jtM{r2q`Z)~$~ zVH7dlyf-j-G~Eq>pjilRjW_-@jF|2&73pMmH~73WM4pRxpH3dh_N+cF%;7@Y2NGcERAMK93y~yQ_X*;qlpJF)9WUq^T?8P09~kc;=~B zS8&fqwDdE89irHjBe-<$2ero+$7gYLdw2lKeAsqo+qAkN#&FAl+857;73dj*;zdBB1_kF90`;1 z8$ZLEQQydwBxcaR60S^DPAJ9YKmDK5Q15+pfB4nUf5v2Qd-#t&{6&yBIZ-#NB>beU z4Xd3&b|FP)D-Pz6{9OvEaDSKlvQzo%Ifun>H)>ujOyu+0d~|#^r9v~0#H$wKwdubq z;tAZ8KmGKl)<)7XQKqz+pIo^&{A}+%Iu_PvRv!Dj;$24hFEi6W=Caxac0WAX=F4B( zKN_wr-^;-6NGJae@^&W2ymOtZ3usFJT>6B^SN5Jm_I$3)@A@P_V}`pmY7$oboxpHD zYX>t9b)xjI$AapuO_ZNCOy-cptK~UGwDSAlUK}WgHYaJ5;Z}5%763n7|77@l_tx-a zZ~rj**BJSUZ-3vuJ}YYJ7S#pGb+M@7X&-lV8~&im$#f~wzRU-GHnj`;$ah8K_VxCP!Z)-Ah!LCL=F z#k|feaz6|(QG~>6U}Lv)OJ6cOqtju=t%zw1sH*F;$?jWpU4_#YkECUWK`9f0O*&n%i- zCK5^Ezd;Q@ZGO=v;5Du&JJcRBcJTxtbaR9W#xjv7pL$pOmvoh>vCrg&n6r6yzN@trUk%1xD1jlzj6?wcoaV6 zGFpF>dO(6lngtsW(lrE@AtLZX`oBH!;*obA;!J)SGJN)Ebs6I1AWe_}HBU4A;ZK=J zZ@jngSdV6z@SGrF`5Iw5KINpFZObjT{morG5Gq>rG*$m>9n6NOyryW-w#~M@jfQ^H zstqYDWz=X19BKi!T?7xIwEY=56ketPX+y$lIBvK=Wj!R0r!SB2M_yGw1k1@o^#S9= z75r(3M&y5icRtV9Ek0#`>_aJwxFpTi(zrI_4Kack(1}qtOyn>NMLKS~2Zk z9F|L65m?KjPIos9`T3}2^pOkBOmTR)!+gPAGTq?nOlX<7amQIs;hbZtaa6)>p|iin zkA*sm9xJ2Lfb(A*aqj+D#^7@6*4r>;-L*w}b{%vy8eTL`tZ*bpw?qRE7UKM;;l_EH zJ5n^LXn^t9%Qm;yYZP%DbKz~Aeb-$y9*3%bnmE-xqjfVpSE%(iv; zX!R=Fjc4QZTc&dIXImf3hF^L*@67Qxb(XW_C_m$;xjRtNswe0>?uIk{3LZ4X*brMk z6Oa0whM^-}8vW7YD&FUux=I;3q-C1t#5Z+?Kk*%BYRocrS6{-aaA^08_L{f};<4P7 zR?i7cMb*=Ks(e0mXLW}-_-9{IUfwC=_?vkNV~U2)NR52{Tjnl38dQtJ>pf&1cebup zvD}E>qWVi+hT7}>X&k;@p4Wk1hS%ZpPyYuNM;mMrdocV0gUsjm?hgOp-LHqsD_%xK zC*$Yx&_dH>`D>Vh#m6)0uS50>w%5T~gm`;x&r2)77o`=<+^5X{zTFFGTGSULpW}4W zSI))1PT*B(y$;S7!a`6(-qa{ziT|y)?hpUwH}4P2oY!1t+x2D6mF{ypq;Cl{5;)&i zA%6T|bNK1ib#B~d^&s;IEE8f48Z?4xE8$^I;l6P@3BvP#&e7^gIU7u8XyyYHIDFYd z!Ycms8ai26b2f&;$8M8FmK_I>lq*Wwd{qXX$xXVG02dcYBU~WLA&QFUNjC`Pj3w~j z-*_X$><8XZ^gjSLb7C?shkR z<6;=U7j%2SfJT`^j`wVBIT~wd;V&~3zs@8+U)cG^NTuRut6kVHF|l`7%o@j-9P8IF zH6w}_@@jl=e!9+4CcDeCXY|ExblK-Pj32SkAhf^hgqjIY76Pqgm*|QvEDk{+4(G1w z6Lq8pBv%_6XuM3=#l3}gL1wyYu-k0$6GvWGxgO=wFswl;Mg{Wwp3G!%X?)N~CH~-n zZ866nLVDu(qji)w z-teHoL3*ZH5B}uKx)9cQo%Lb9>;`f10o}rByt#6hhF|h( zukEci=qYzH=0Z~B;}OtYrcS&^`Q5oDk9pCB zu4{{83JZ@-Cm~aeJJL$o1fF$c0a`-is{@)g#|5S**|&8XTe6D-VSAJ({>oo8>S+cC z3tZdP+>O|kt_+ZlZTpZHus-c43OfsMp8jk9c|x-cGa7C@dK7r@m!Y5*^3oKC^Uc8E z*H|-67e*&s#pg*`D{s=rvN!n_`(=!gN1F?o_}@C_IeZ2oGy%1V0bvct{Bm7Jz%;)h-< zX-i4DDu$P}j(Iey;C6oEn36ew(dZ+G9`!+Vy^A7sHZProar|%xKL-OCUq~>IqSX`G zbwy%$#?2A=2vHo%>$mNeH{SnGhqbrH3Z+R7?12?@Lovs1Fxmzh_HX-tbVeg1f zjIZ2;D4(%rmy2hB$u%55`E0nvtWKlq=_yBlPPcgT!dV3kT%1ktE|+6|M|{obWaS#W zYAz24C)|3E20P|Hm(x|gjK#ulALX^nCjqwkborCLonhy}=fmpfe>?o(!CwvU@=5Cr z7k<*@a$n1^j3Q)%DU%imv&!PY6BB$EHw>;UyV4d;!RZb?Y$`FpowA5qSn&87#vRWX zVZH5ij@TTz2rgqHvWje;}`{-?%pt_{8V};LN zN5_#zV@`HGhzc^^{UWXGnju{{!#)?>E=*^1A}TLB=RwB0pz#u3^@Woux9CgTcoEB+ zUgcoc%AZK-kQkZC1TOl3XHERvA<{CMXoCNdu?jU=#7Ycl-jQQIQTY?MseB-i2_SsQ z2oXseS7$7`o#QzH)HnvNT7V(e z5aN`Xd{tQmGo1D%Z?uKRUCS+Bt^DSty;VRI0_u8(!s}CGe!{{df02yfdTew4cYFrW zGAN!JrmFn%-kW%vM4OfT#yNsoeizHS?06w8-IT}3&RclJAN-^nA2{mWHWSx*5!NW# z4!ksahLnl?e(*xn`C~c!^`Oxe)-PP~Gr}0z^Hib7rc&_0jPlTL=I&CS(GM?)=m5Z0 zyAULc$W&y@plk)V3QA@K&pBxYB?;~lo4QIX9;9j=I__3og;!KW98;Vn2YJwMnosLLbn(X#5&9MG(aB%= zi~}fecnfq4o5n9~N#O%fFlG&X+M{uPvOBw6km)iy)5YW=`rEho?rwBi=3Rd3c8_1$ zPMpG^J);DDfh7t1h$79p%P{RA<&t1|B-~v^RruU949a)w+Zth7Sqw|1(W~-Fdvrx( zf+NNSc?ww1UorvC7qj84=T-MaY=q08V!Mk`GrV+a#-TWhlmr4FMc8<%d_GNgcaJ+_ zG|rS;M$pF$RL2~}H$6vVqQo17bj@#hz1+PWWg;YDbrR2fghIoae%iFu-2o)5&fPAK z;o>n~I6r-zof^z*4I6(p&O?Vc8V78fk}d$jqpS^EoYD0(!&N_(7WgS_PcVOFJBO<> zUi37R8D=xhh9~(WC&QI@l4hJGOa1it5gwe8yD-Vy0F1Lp#yO5zm-Z3ISI57xPvdcS zR{98VjvuSMXv*}1F_w2d=`+Xd@QtptNq{rD(qeXgUgyy`dz~EfVdsf+dGr1MkZojJ z!*4$Mc=+Ydem1Nv91Q>HonN%`jMhn4Dw$;vc4vT7DLaKy7U?%B)RaZ*A#NU`N}Fbw z2RBWh_~#XRlKeaKKE+k6z6N{K?B9%+ml3;&sh8zD$Jz6NlB?*;|2$OdI8loXLe3)= zGn>Ri@A&EUkB0yCvrR4$bCGc=+ZIpR!?|+lG7ABARdBi34woK$b@ct=U5qkq!hwP& zJM*pdw4j?uxZ-}tat2M8;rw#;iEZa6nukosyc&_W7+7w+Kb&0u>9DhObNKm#!{L+N z)nVfrmzG_-iLPH|5=oEZ=Os8m%Sul{qH+UFu-ukfy*BhMHC(IhfK0PHgF6rWvP;P_YCWYVIRM;_RCF2 z@N+_0yr9ib@Gf&C>dMVO9G?B?{o$i8|K9NN-CqsMhgrE%;SxFBZ2Qy0%>m(Wpqwg zm{@M2j~w?-=r7)zvoc{ph+f5FrnNb##HgqKF9E*+0^_82Y%qW&r@ z(PP5iZRXrRq(uQgWz^Cbw!q|1sfs^zWRagVGI=Dv)(+zCET@i*&*U7QEVC|)SNTM) z<5!C09`?58=y_8fl5pw$xofB~-2m zhIngJRn{|sORoT>DGm7OQ7znQ8b?}WEe-sE?ewgNa-y7|ZPD`4UGY)vH01b`aEFsl zX!#^f|5!&|E=8l#0uN&qKYg||8E9?FG~?RBjYSvyPup(ECF$lOL*Xl2O%L=!HPn+p z!=Gqq@jvuN99FnO?08LYeVe*6pfj1eH&5y!c?>r_;t?gkYNzwPlV8e?`*3zxE6q2(;3g!#!%c&dU=Ve|`EZt6E~60T!eG2jwEMOR?n z(USXID1h8}{73_cb4TmKvZE>EPq|<`bK~NdJQ^x=!f9x#%X$|uo=C@gTb%Pr%em?* zFX0ZccDsvOLr57z#33#}Iqm&3OAcx{`FQ-~alVvv(`n=^H}O_f@RcFW$hj~@ z`vQRKb1k{i>)b{rWBOd==6c>$|24(&&=+0q7A6Ev*G8@WWGH@_YJ>R6YvtmkAoKocW_R?i=8KE@?hr) z3f8v)DB7|vXsAKSef-tx@W+3!iNTQLbomDr+Flgqi-^xK^BSOI&dBKnbyM4~t-lc# z-kgWt`2N2;tpDUcAMUPxfB5BB>_q$Y^WiZDhf9~2hHVDfufDjSuO2B84KiAL*HGdv zSaiy3nE}H^kQemU0B7G*p*RUV#T@LXr`K7v5+8YD3GjR(^LX#Z+QF$v=jee4-G)7U)$5L@)SNxh?R# z_n0jK>qlF|247jae8O?5z`!+#k%3qAfOjD2v{IWx5;EnbP7n#DUC#`t4 zHm4llxHl5`y-6Sn{RIkR!if54F_T8NX49c_9ER><|Fipw#p9gLCe$r{QR#r$#-sbq z*s;m3FemFO;I$3LW%_2iJ$mHOe(la0aCzCOY5wyV)6TZOrmsxbctiJMGF`?H`#)iT zR?yFW70=xsr_6FR809V~7&^f)lpX8A0Sy_49o8A}+b-&L@gVP-aU28cla%8ZHhhW4Unaf6gf{6}Ls){gPzrwlnjYOu{v!PY2HyhPwLN5zr`Os7$Nv~sq* z(rMv_sc5!PP&NJ=+HzLTejq_xpc}(e7W!g4HpiT zztYC1%_PLZOJwRLZKU~)3|?Ry2jxjP7c0iw4wZ-e+P6~Y#!Z|unTZ<9mNJrP9q42w zGSf|FR5YxeieErxiZ$t^72U__ZkMwsJ0wIK7Pc?YH0N?zX2K>)je|-+ntZ@7}%3 z=dAg95_um#ejMYIu+kBSd}s{1b?X*iFa9!drny_@66b-R@uK;QFTQABsRI5Hy!`No zKMF04SlP)rN?3tY$vXJ;pKWxE>+wFOPPpe>uY1vTuh`;02Wz5Ezyh8LkA{{2KL4lE zt1c=$g*46hdQQ`(@YxBb;!+Isp_C0F&>hmY=nCn?hE+3lM4F`m0Q4E=Q{I=S9$4cG zj=puagubv%o;ji-`S<7M&|m)I(@!zT{N?cI@sr`-``|aj`XUQo>jPk!)>&91v%N4y zGTaJh((me~d?|a~v~9GQ_P63IZko5^UhDZgBX})p&jZh)KU>DhSgbhr9va8lHUpaJYHr z&aiOpt>MwL$9#q9z2TjuzwP?S2|l|;z+*doRrea8XDDBi$>cr_lHlosO|pe_l95m_RHbf-odcBinc&gWM@hYF2J)0IXPKokah)ul#rLUdj}bf zAxEryyt~0!mWtoHTjgt2T&;rrg^`+wEe5qV$ie}Ouw1Z)_tu`+5y`<9b~L82kCvn8g62oDY-ZZQ5UOgM|^o@o1;w+h6CVN zeQVC=vDJc3Lagz9_&j3A&mu>o;z-V7jfn|2(12%;MFep0_iX(ppGAK=2B}SU!>pZP zlmVCT&No=ptWfw^&z&9eql8=teV2vnzkr^~9+}$qiqA&z&EGc?`0gYSrK$$#j<%P1 zjlqlBp>3O8E$$}39vOHi@h(QZ7skni#scqP(P*t9M#tQjK1crtF*Z~ulmGxg07*naR4fiV z=o|U4d*u`+dA(HFIP-ao3GZ-;IFC&^`7uHoupYxr#6h%oCAhOzgNVjb7a1C3TyUt? zvP&m<2$Mbw2I;xw}T~RZ7}K{W6b( zn=m4;=5hCnk`!MIzQoIqyH+xZBrS`5(!Fq!ARhjU06k}Yp{U|kSag|J*WUQ4mzw@g z*~#MWB@N>0*W{Ht@ycB%56?UDsN+a5Pkr**d_rfTO~CS4)NzuVM0q7%I1%s*JOO#q zh>*GrZWlRij1af*$&JT`1iWQybo&Ip zqm-QSAtjt`l&8D|K80%KPa7~_GFd(Y&52)wWdzdWQbvUXenI2K6Iw^0reclE4Sn!EqAIA zVYyN_@aiI~T&u4NwS3JG37&w3;nvQmIN^@32qsW)5pPM{}PSrA%+%q@~W$%qtORDPcSf`+mG0Vc#MudMwg-vn=Gs&h^m*= za^AN`Zj>*7Qy9}Q81wU8)L|<)q3&SXfBw$`Um2LM+_XT%HP}{$G&Kv2vsDTj!wK`x zJwI_x1MsN$HlOSBt4ufebhKYg{o>0nFjQ>D;9;6Z5a*W%d^-F3%^TT0;?5LtwYy>5 zrQM7%w{PDbo;-OHoX$1hdFP!NGmP`o)iHc9*nRJN-^-4Zd-v|e!1DFiUk~rT`)+W4 z^2sNGEkljFWHf|4#NZ*$+LfZgNPP0)juj0x8fJu(U&|+b^Ju7%zB14NbI5&{mT`lZ zj=mJU(T{7aeJ^6`n>B^Hd%&SqxpS+o-yL0=_m{)xMOP!ErRY<@6 z>2+nMM7D6?Ga#Mis~s|xzk>&#*&Cf!&%q@iF7RnM5H*t#vq%Ej{!H)onM9A?v}5wu z(}Zy>8llcS1y{N+DU=NiumL~zjs7!e&+t#FG+&I=7j9+WpD-x`MaiYr){`~_w{gR{SgIm+2fRlLh-XeQ3eUMxYwao4Z=L) znsrfxRRGeJ)U6Sc59~jG}8FO+PCb^ zAdjdo`Bg32095wMUjxx1VJ`%>f#)Aam~}1|USXzi!a(F(6bOTZtRQ!H%?kCsC|>ht zfDc>n#a2#?F_AU(uMz2(!`5riSYq4D0%hJ`+Q<7K}u)ZC!ao996q6Y?)jAsIzWsf zAk%p5ye(VwiKodA+fSP@!bJ;RD{0a8W(gNi;xpETc z;x@81i+BJFfIRq*XPgPfg)jLG01!coTpDch6Y1F33@UARk~qzq#g;(AP*3J}ppb{+ z!!pTBOAJKI3xEqW(_M7BnypCJMgyPCC+%M4PC>*W*O5buGNR!ntqfm@G(H78E3fUQ;W%GD#R66$8?uoO zGL@&sW6K-46!R@5FkwOzDQt02^cwE4BR=SeQ~AW1(Bn)f9-E<|w`TxDw(=0xP^N)1 zctr$&$;pSm@Q72s4Od*sHKAqvRDLd4r-sA&aDZ)88qU2`U>XJO7cmZ61`CF>Y__+y z!0K`#UOELMT*;!^rg7fiWPHnS3=X`u39^rTP8V&)>w+5{U|#h_3-c_1#GUn{AT}}S z%(alGdPv;u=o8yFeZ*zv64`h<@Ai=X{` zxXx~wU5q1+b?y@R>Z`A^JEV^DXq?bM;;tHTY0S|@{xl+uBpO#*d-SsPqZm9ia0q{g z7q%KeR`I|8-h1TtqckV%Xt@I{pObJ7iokOODQ(*Na$Ip|ImhPlYlM*x`I9e=Fz#|O zO(To=S5VA%FdQjE^ZwC?A9nq!@Q2~S!v~Rzuo~%OyhrBh;2YPjH#rlQ5ATk#UtA|% z8di&PKXRTobb*Ft*)ZFJZ|D?SIcA7Su zd0yJ9#x>-#7{9&nJU(WzTUO`BwtNS?3_#4jqu^|u#AQc<>IH;?I?=HM*`YG&u|WYC zdUylX0Ee5ad&4H5G=B1Qb$G@obNhR5W%tJs*7hS-5DR$&d@_9c)iC_&dn+vLeK*`P zhfe&UpSW}7k~HpITp|)Jvllw^s3%kYNmq89v}DI9fed^AoHCQ#2lo4a@xKgzIsAYH z5W8ERJ{tb@XP<%}g9W4NE&|LKEkLSV4;-a&4z{dAqLfum;?$VZ-ne?HEyor(^5ffy zg(Kfy$`f}=#7MMHy)eL_aN4w%MGeZx%W#2;(ZY3yVHKVOz$h}p2fUs)e;bw=An@<; z>RM&*oFSL-suOyB$*=Lb<(@XNNEsM{vbOP4+ly-qKDHHP608K;ZWv5H&}(gpMPgaC znayTV0iFy>lzTzBK*RfV@PS*6icyP8h?iH{Aw%$Z*ZVEjk&BBE4rno89<8Hrp{;c% z^%AD#_>Frbf$vrV_Q}>sCUlIm-*netT7a$)opj~uB3qrk7P~EQ={}h}IH_}yl@379 zR&N~NV#f=Ud9{yy%gKpq!Y=Jax#B4O>@g?%ga~3#0WbF0IP)+sZAO;R1B#C3(H$27 z;?T*XNE75wj4E1LiB!J_yB!qg&KwnkcpMONDK}AB=n$1f9cd8>dR}hbm9oqvBYaVY zV|0Z&F$RhtY70&5^-PeV3$GfdoR{#3&ap_22jaE38KVsF)9;Z`$EuWtXkFFA?1a+% zrioj4%O@T`iEX^{jsvGJ9Moqz=>!lyI@?CXxp1wCROlKcFGWw?`O3S+iwiov42`;r z6XHz*UMK$WD3eLxfnP(0?08|io;N-4~Qdo zU{%fKgta+1zUVJ@((Eh;QAW4++3SZ$`7AogCLobv6Tc+n(`d(d+m22;y6AYG8ZLFA z-MqnFe5CFm5IjB`!q!gkhh8}BGYXjjTDam-4l1r8l0N|&!Jef-5m)63rUH{T5y7OV z5l*DlYc-W~2b>@U?|eXJ(k_TB-hxax;!=K|%2_9DwWC5EzK9oIVu9XSrbp40HYzaTxb%oefqM^EYhgW5WyDK9n6dqfPh9e4y!tJDqN7Z? z_1iDO+gvkRY4E{rk$iG@g|FK2ormGEh8n@;1HY3skC9qN4K;k&GCEmY(ze~W=(KP6 zVsq3#y@ahS^qBNz=a~A<-QBqWcv2SY3eUtypt?RhX#`R{raJR@q z3>3nC@Zkr^=kcG_wbd9tJd&glM1zRCUc~Dz8I2zrL;N}C)wrRdhCwZwJmV#X4usC$x$8pSyzMh(F|rf8F2WF{L=*D&5Mcpm7>QG9R8q ziZgZ7rlRdgIf^CZ(_uM^o_Bn~FJ?<$T~}>F1$asF<4O zP29Y~hKYGt8gKJ(O`)eWrtx$A^XSh4!q02roXub1SD^*$w?eCE&Uwm)BEHBqHALga zW9Nv1qceI1mODCT<%h%nbMpRh{oU)_Yj7|; zdB9yOyuw{$U%sC|t0KOq$2gj{H|DT6X*ZyEw*?pR~3m>!9^2+Ll2YC2;k$Y(9T2br{CIo!7 zO!=L}c)70q&f`USGmC(fuE&rx(>QVS&B!T66bBoP4o8@Yd4EjUnH)+bsSWp0D;YbWMR6aWGJnpXX?fN1!4~odKhSt7XE(c7uzfu{i-l4O)KU`XS zHoU#TdEw>l;l}0N;lr!*t?ggH|~uD{y&jG^)cJ!j8g}$bCU0!@6qQ-&%!#d zKkcXXE!uDEz|nOH)3IC-*bB2@M|Y@7boR$A@aT*5wk*(dBuYHu6NW!7mR!Ic@ihYP zi~*F%zxP+!d)p+&{%1e;2oXEfV<0k4=c1u4jyg>yK{CsgQNgv3LCCL&xIqv0QjQp2 zKyXZ-mOH1b`@S*G1zK&_&9YxQq6{9X;sq_n8uSA2>Vz1n$>YRpLH!R7)fBAf#{*`d zhd|MD95Y_xZ6UxD(vA<& z=iRU)ZtGFpF4m@Xm--by^`jw3=Ax$^aC)ia#b4?Z5c8YrAIqZNm7aTCVuWdGn>fP2 zgf=h^JvO);EkrJuA3TFgdU!0q@h%`NkGsvo)1NrnQ zQCpPt`2+x=%d^U(u4=fQC%8B^Y`f$uz4mpyhAYMz(St{Pm#-`iPw#^p9pIf% z8e}v^bDC*b!x*#V?io9?`i{2XOE}CEp?id1UF3%uj+F7p81ThJ71}3p{u9pU)CHU} z7LM6;kL}#Neghmsj^M0wF|K)H`04lztEXQ7~KI30{@{@TedH4E#Ey8+qN@pErt&&g+*Y1omCJ2+4AE{vG z;OzRbUghD$H>KT`p~Xb=>WZfrUP8+q?$WW-@SMsThRYz6G-#v@Gl9UE)AHe@F*+IR znP%%q7DQoHnIuKZUPk-}=(|)Cvhx_zK?qiR(p= z&O+n{T_OSr3o2JT zf^*)?8H+aQDwPcWJ#oHbok)%{o&_(KgQDjXjsF{dD-oK&;VrEy~hQy zSg`l_6!Vw6H-_(T+-(z1vPxT+se6^`n8NoyI*p2diYJo{2w4URS~k~Ceq_Y_-0Co_ z|M@>3K3TdmY(IK9{PMHk4qrauOG6kBl%2a+P7&lWixn?;^Kt~gvb4Y~A#dVB;TqkBw0+`m(+Juu6@CAmMmHu_g z?1a<1a6HfN0?sY`TD|xAqd~~s0&9F#%6l%@Xv+53-QhBCmacAce)rP$aAkdexVFhT z-L?H;bAuc8mv@-lEDdXHVIBCA3@lTQ&RhyE$8X#l3H<+00*7p`+kQfS;vN^jR|9> z?sEC$3ir-vNZEcWGWr|EfgLQ%7C%m<} z9B>)=A&ZE75|(iCfOCg!{Y=&KWhn2wX&3rBIfiHOsxMFhdGobFf0k#<({!eK%}IVp zMkrBHJvdJGBES}D$%Kh9i$LJXFAf)ZO>gvg^&W+F;Q*B^oP_76!BhCfS$+YN!#Mo}#FAVc&PB2p^Sii-Yyb?LbVXoBJZ(sE`fNu3w}q?6 zV`8-E|wFb?rPwUv6ji7wMB*Gk0&w|vsBEHl~T>M1{? z>yat5etyy}3>8;MI|5}|^(9Ejy1?r>g-IR~X7d(o)l=l96rojd!nD&C&=QA#eG%h$fY;r= zs)XoZM6D6C)`S|1O`};*PmNkWk)AQdu)=PSmiqi(+gn$^W8$oc#soa0oF|blf)CiV zjrDxo778*8#;R(8H&edNK1qZ`=H!?VoaZejsy;`Ui%>zo@)T_4osF;bi{g`h5uM81 zpBLen&2GJwo8~DVt=m_1)BN-Dyb7vW93vm|5WXO87SAkb;dPgY&WT?pf7Vx@lvW3@ zg9L_lWRu|$XSDt->={JtaH_xR47pW>;EmO-;nLDh2ChXu-R$SEcb@KWe2EqIx~~$T zh*E!ic#BTjvP-)o53SihRlX`mg=gWy%X5+GvWZQ3EHgbc3IoD+4?DVJ<-+QX;p89x z^Wkrvo(@kQ{bu+rhNXR0GFMpPT<6jTFVNlMmbWGHyJJQJjd!{1qcK(>E`Rdo(>EV@ z+hCzkA>u&j8xrYjd}#^`q+QvFM`vE0yJv*S)=Qo$7F`XXOo1}iCk|rr;@6!VR9f9Y z3r%*2AxAUK36<_qq*i`%g-0e-LBJng#VZfi39!rZMVhkY$^lO%-ON5*Q0=nY$&wiWrefC+!b_*ufkkqTkq{_OBh`C*&fTqa;rNi-UGIk@_EC>EiOq$X1qc= zKHY`h0sLOZfV9S?q?d+M-t=GQbKBPtWYb~rNcoiK8}~*6zYhu6kNx#7cSdri5f+S3 z?;Cjfh#XEdnN-+e?L!(P_%LE771*R>7_rxRyeNwqo-XQmNf?8PZ(O|-#$6^c{B}L7 zam0oE?o;0=Vx(oV;wR)?*laz8HhRn5G@Dm3keYvwUd5Z*UF@5_bd+`Mow-{E8!g+- z$@fg7&H{b(lP)C3dH)5U=Fg0_QIxU3A&C5Z0b#x~+D-N`z>r0wk}ePPOJM?@m7<(#%R(F2*wp6+luYZaJ9b}>tL`94$c?kBlCs{uT_sS zL#aa}1|@(q{|2_`G#Ky@(VKD>9_bem4xq&cVZGtYjNP?1)dGQ)7XiDIT*_uVZAYF1 zXS#7dfln6xIQExZ1(r8uRe7Ao1ZGMjNW@`g<(fzGm`|GJEN9zQOiTk$QYbm6L=Q^O zJi^)+{RL;y0cY@bq$yv=r9n-3D+6()uH{vpc!D0Xtvyzm(jHBl@M)HE1t;G&7hhI+ z-01AM7duBhp2U%+&_K??2|`~`9idOv5hv(w%b~6eE_liA8TygNn`7ovsk$gS{;=%neDjp=<&vBU} z!R-UOmS6tBiEG9bm061`x`i|OTGo-DAOv36f|HJ}!Wwr>_c)YuW}nWLr58APt8$o+ zU-*PUc@)lLPvY|>ru{ySwD^gahWDS8GoN2mm7bsVUiChn-U+-=xp)kGr>zmsc*{9H zJE_8(eACV>AzkrbPva_W*53+e?0J|L)1y1yN!-+1vYp1w`lsWXY4ToiRmN!^*B|mM zd;VHe>-sY56WawpGG%^%Ekl|4ve-!HIQj4hCkV!x$inr4vVhCVE-_6-H|1;UFW7T& z;wg6lsYCf}6NUVsNe@}d! z{ByFOBQZ@sC*Ntpxg4(+cv1G(;{O6fCF2+3GViod6V@xAyQN&@GR^_Zd(A8F4~rY` zFgt&n&l%rD@vwNuV6t%dbXeGX$ZYeK;Z#y^xg-UjR>Gtr85B~e%v!8vA(Fj8VE1(TlFrGPe;qjC``)iD% zmzP&raG-#}b-)b5`&2xppLk92e~SfnzVq`Yiy(U~id`{1_9++CC7YRpKylvjS*n;8~Z zq`OdQZ@J(YG~ztvGbcx^3@8c2s4wX4P=^PH>%&##afjV5mo^^{>kI5|VPSTSH}O|C zxD1VLyqB(Q3>%vaP8%4M7WZOEGQW+^3yl4gH)gG22YGq%@6zcl>Vla|t|jK49TY7R zfplXuR3wf?n@~ax$-T!tW%>74Zo(--;TE1XI@d+ zF&1s}8kn`mu)!aVrXGcIVWPoB1*Or&MT=inQqQ3#b5zOCfG@MfW=kUtxGi%gBftu8 zUZiGh7*h@|{7fPt{P2iVhxQcG9e0|OXJJ)^ao~unFahz7>4ep%kvZ|AEx+p%;o4+! zHc#VcBrF~?cLpUFIw;mmX2sKas7WdyDU&!XkH)yV)YZ74;!mpwq0iupuKA>+lYBCf zCofC@3Qt5AZ9(G14`M$rZl2)a8M*SuHIq_xBCN{hq;XqN0xr%B_g0QLXn)q9{HI>Q z)0p8&yiWR-LsqN&(rCP&%}I+7)x1Al%8$Qrh-J}%EI2{Z3z?yz=OGvHX3BrD1ebAw zgMm4Q{I~2Wccn@$drRt}I!6PZ@I z$P$10fB~E`l5^!BI)vmAl6e{@TjEh>f+s!xp4Z~xHPnt@Xh8G?&gohDmU*Q~nmo2E1qhe3;3lT*6Muw7QayzS#-K*c zshfgyu$C-5%nGhSMuS3jtB`76spCol`w<6sVVB`V!^LtgNYmKP?rWGY<3-)YVm|Al zT;1DJ;WEx743nnIdvpYXmY3{Y={P(RmAZiEMFuX@r0H=i3+{&^G*tU#CF2kF`LF~p zgT3_E%Y#?_v>~o|vW$7T04|L$I%)g!lh_{5@?ITx_;`d$LrpFaw$T8i;KWfE0q-!t z`X#G}9C`F&}X1q>S94^7ykae!P>(Ig#rU(Z;@ls5M~f z++>n3>p_F+ZL)WdOgg(Vr_4cy3;$_}bdPQeSdxV`L zA0uDl>5M&x?;>0m#dk8c3*GVe{^_vy?q3W?tJkp7You{;e8dqRTM3GfICs9-eU=U$ z4<}E5#g~&l8Wx^@%+~yS=@@yd#VVq;D~$-%zW8Dh_k;ZA;Ct+hV7OXAv9ahr;ZoaW z6y@;&J0nnZD#NYeDtF2}44qtdmR=SX=l`jSs?^!=%gPUvB}+l&*#Y+ObaVLM?)*2y z_T8t$4HW7MO59yBewvwOM;0FTtp(l`J5e}bVA^oM9jR+XXw2BtP{YDTd8m{f$W$1% ziMC5k?a|a$-kya%15%$gX!vn(4@a4`DE~GwanW|Hc^Hl4TU6mJy2pmZuc5^qICgY* z#q7D`B?V+bh+#|`e%-^F1=%wosTVZHSkLaB(HP`$D@L{9lA6y8Fxai4Rr@J#l7F!1W06qb3i(i#=*(t5H;Z`>OReCHCN`P>>I_suE_^&bY>P|hcUv5F@~cX2&pl$dik;Sb zRt`A^xXRbZmRB^)up^Bw;e>vjoh%wt{J4e2kOTZr9@3Y91KqIB1-juBec?SWOKkby z-)jqKd31-1i;E+85Z^vuS&IIkXzqM7vwYI60V0i6_|_I*&R}$#Y*JwcX4*0V848sw<}ssjBMg3PHEDRl4`I_o$8-U3KI?c+i)pyx2QIY8 zC%EmVAx$7@Ev0f2>UbR{sw~oUu9?aNfpQkE>A{vql_>hgAB2G^S{ai&3ComWglA2_ zrIWEINLm}R9#!|sTsix*y?VEYWpWTI`Zng=iv&{|7}KW3Nqq}zUE7A`C-MsI?zb$& zJAMHXytal#^6Kj7m+hp+N8_su&PC)gWd-@@wJfw97g^0vXf9o-#3yelOBT942|mqg z*_-P)EiVy@)iUVvxi0KhAJjQ{{T*QZ@Y3K@Cc3tS z%$><=+SWE-hvEh4{Ra=iuU~4Co(2;QAR26*K6{!=g*BjfY4P2=ck^|qXV0F+pkkcx zBcB*qjC%l1pBkn7I+T|gdpYozUw)Y{Nok02w~gg7@7vt7BW`((43Uo)85^fzNGDFe zU?rZnx%}8mjx{2U2D6z;vb+?NvZY3Xql+p27yU`T{OM3+oIxKMnp%BCnU;TwyW&iL znG<#jXBl+$?14P0{U*TRR=bwMD0Liu>B~9(X8#t@e(8_)=`XRQ`BESqtEamYH$BdF zJSqv{n+vW~$kga0C2i7(7V2>)z+(VrJTKi4o^34qXT%;O1n6qV=Uo6WQOGnUG64_tc!HR?50_H zYgpn8>f&2}Mh5}*=)thG_iR|)`E*!%__N{g;a@Y6B!?Bn&rWyi(xYK@4~zE7dKTP= zdnmQVgW+Izn+Xku1Qz@IoXfqxb#u6R^?r&OrPXmvSiaC(WxWt!PD16eTAbYEliURU z=E{G-v6-Xc8n1WVv2(zJSO ztP08uTphaKKfY;kmfoh?h&^S$8`4Quu23Hxl;;g1%@ zmzDO9A0elQ$mEb8H=-`}eZB0qIXJ<Ucq=>dK?f`OG$D;iDC3K#omO4*Az; zqlR+wD$ApZ|f4)d6O*I7Kef&#nv?YYGH@ZJ(BZ)h}rA|5#%p9@lKjZlo(x z!d`6J3e19(Hej16{^|&C(U;$|V<8O}DAGQ7h_1`3?M5>$dBtm@KiToopD6?J%1l_D zX}00wqs4aqflXZppXC-2_$hx!S>O#1>EaPgS{hYr@gANaY9Eoejw<<$3TA~3pyVo_ zj27Pl>zAjrlRy&J@dmdrRXF@~-#*wTaN?1Zp4Pl$*`#G2+ii1g zca>$Cx|Fj=$X9snxS9~{SHwp^K0C0YNXKh=j9+56U$t|=jBD^s<|zmDuY5cquKM$r5LPt}iu4Z*-x zay^YN2g!3N&Sesi-xS__O@9@J7Cu+!B+Z7;pwIgXZi=(YR=b}Z$H#J=&rU-mMLkM6 zMxBY@-Mn^t$B?vGI*IR~(^hD+Wb^9I-20i`Gtocv-;Q6EB~Cy`H}Q^f-_$cJ)7Qbn zdweVUjJFh!u!Tx|=QIVFV160VdFjvL==fTNoEOQw{0;m}nM~M;xAL6FGz-?P9@AkQfd7uNP+e7W(MMXa4gID=yM_YoCoLlzKA)|$+QCk^X<0% zlL6=S?LQlye)wOpeI2W~1_k06M);TId1I1>Vi*<0#9R}D_kK87@P;%Cc6MN_aC`g8 z_2KBs4~COF|7cj-`+7M3^8Xx8?*ApnlOCsYU11l*0*0HcLwLoy;Hrd?=ajwd4!X-L z+$BPZ9=Tv(bA*GDyz82bnp}@wVf6Zk{aW!PyD?+J}pV>MQ$;SBEdI{ump7W-N!wM7~!Uj8h&M zboUWL;6EuW#tt-|h&0jE7qH9DfXVA5Y24M+pkzJAnwG`w1cH3VIS}v;0L=UYuf`u2 zElvtlqL%c8nM3x{_@+=^$eW!|z#Z|rM>yl;dVwQGdn}xmDag_O@o{#*cld&E|G@B;P+G`&GA##d5>8va;6vAT%q-xve@))8_Vc1rjm(bd*j|n z;Jc83eah|?t4C(vQmM|S|IE>C(g%&fOaI$vH=vj9q9O@9f$(y>19zkl-XP7pVqCbp zi-kE|jxIjD>pd`rZc%Oeb~56-)92j z7m*IR_hp5Fpf_aPh*mjdG7=OSQ@&g!ss`8Va{h@(bmd356C4hw(ouuv~%9aDpV zVVP5+>OpkWIDs>4iuwxpj2WTR4$r zYBbQvpS(Dp@;}CI9{C!H{&TYILqh6RoNSm zo}R>Md}ADzzqC<_;M_*Z2?o|W(3wxZo1hZaWT2OL7j@DT zrTmhkm5~U?Qk^mmaC!nh;Kf@m3v1#IXu}Mz?MNPNEl5*J%HH#Y7YJMLI^`~|1bMbC z@U4K&kO4nouFC~Hal*1!3ncbpjrDmuzd~p+M36XYpjLbf5DT z=Tu>tKhkY*wrh|484|7SC7Z>0ix=5Yf4&Iy?yYQ$3_(z%kC%b9Ud&i`j)+O8ZjPM% z6T!FmMz!WoMZ+Ull`1G^i#_;mthabk!v9-08u)9&j{{-WW zJ4?hNZfR(!(V+5}&tO~Jz1=G@glO={@jD7~$N=S$qz(8`WJ?TE*D=ccN(6QlZF?= z%Fj!Zg|QssR906p*oar>(Jo~pF7KB);5=_@=_CTnf7(K?7Xr1zBk_|uG*66aEnDg{ zbt)0vG%)o~5mkJTvkc09p0h1Uv(l%q<nuV6ITllE=K?nn&C-%)OFaUV*16o|=$- zYkY%fihqjN;?%{?(p68mZ_u%hfHN;!8c}AxO(4K7h-2?T4!%P4sVWpnwKgbixpnyI^eK2YJ&q{ngN(BX|HY9n zh9>xT%czoe`ydbKwFv|q509Rm4FBZ)OT&BboeUqHygh6#-{4M(cd57g!`Hk22W5WD z8`GaK;J%9-K7y8}o-4F1c1f{224-n*)*rH2_6Gj9_-)dDITClw<7 z3G=`B_nEXiX<62=NxOf>#k|`XNxaPW$|e|q&#n_Tk!^5KlgE@Y`G>5AlS7UjUE1`L z-^SSn#-C^@y0w=HSDK4Y?B3D;sx7<4v_VE+H70OMdd5BNh^n*9iz3eGllm0aQYx{6S=S#FchM9_v%XWsHA7=LG{L7bfKeg}h9084iDe zH?0>`uY5o^c{>h=)~T!VwKfp&;0qm#!LQXJn0wyhrJkV=T;$Tp#}7JT{7FYwcw4Cb zE!a~hWCp5DQ?{ZJ0|dzxHfP!a<&$LLH?9k**J-P>;?eQa@<^t5OT#QVPI#4uaN;hy z4S#1-D@i)ZNHVy^HW=(RnpT zVM2v6W&VbtTx0q&yB&S{a#Xtc^|r;75(8nE0dlu&?yxGCMLY4{Q)0QY%Z9?*SDHK; zqj$6Y7RKkMj_1`BtUXo&Ht9L*!0ci(+C1{jYAnaABR>IV%r(mIwNKXc!56nG(za>y36!%30JDW`bvy*=a$iS?hKE9{9p4) zr1zMxv7ds*yo{oE{4jYvGbuVOO3J~^$)>-{c8uOowjp?z9WA>|Ca*Hvzd^@X=}cHY zkLz&r2;dAFxBua=eEZLa#jTHrfB5v6qc4n87zuW_{QA;cEY^?sa>!G@P{NOMpwBSc zXiD%t8$aFN22eVxvrBljGoLciwgY?Sys21un{k4?iUTg&kSU83G#3Pq7Ouybp)tTo zk)L=rZNKlmXGlRl9_1mOw^d$@>5%T{Uo{`BB150v_Mgz}2bfxcQ!-~q|PTmi5 z~w`T<>K`*WdTK)QW&pV(SW{jZzS-oOQ5ws)gRsL zyd&-aqsb1J^e(a8`sw}}JLxc3V5HetW#Y&yQs*M-kTopYTTjp+^t(g!@+P|lm$BgN zZ*d3p4$4;5d%ZtCS?#U8H3ns>USlJip zZzmXybKHhkv+9Ir7-NQQjYmL&*s`=^P{iZBgJzXaZa=|bbj+Km*5{l+!9NVdgbObz zD+|nrPh1eTH(XaX(B{0>I-w6g>YIdHyeI zxJ7wc2B)Js7wuhr5h5L(lyQNOJp0pC*?j^QSpk|c&~#;&H2swadg@?9FXS(#NTb$3aH03m3aZa_vOEijWl0A?7* zh7Z6F3}cI-B@2u#rUk}|FThTiSO_tgHI1o})avT)Qg>C>l$kMkc=+tE^ZWgu^IX6F zhDT+V=qj;@6XEYY_nc=w^K;KV*I-)WTw^)XJFg)4g||c0B3=Sbc58c&sVOyk38A>(+lRtFvNoOY65vurgdCSW%vgWI_=S8>q1(jb>#LFa} z3~38*y=l7E3vC~@gH^M>f!jf(h1|8|@tScGA zcG#rqNAn5J_-Q?(1wYzQ+1ozibi8;Wi%*{T6W8ZniY1KY)^#@bqzTjVIB)U)yd#G^ zWp4Uj>j}geR1E+CKmbWZK~#J>T{5*H&D|?)yWk_BDofWzusGdENF#j*=~=XgCVd-y ztgo7;(ZQGh$W2}5>U_^Oo$>`<$F@7)yjO-3Hl7@QIQmc0L>AvZ-W`Z8*f(%w>)_5j zPEmDDC-GiH;3n+M!{3_YLxur>_!8Lzw8Cwo0FYO#aI`GhCx5 zJ7;D$eBnuP!cqczhK~-m5v7AYwZEf+eMqf3pE+FZP z0gZG5GZ#qrK(VT91{>G#`|IdH5`ka8|KxTd4bV68-z4#sQkJ^iBgS1CUkb3Wo3{80@OK&9 zy<69D{r)a=@pj6dfrTo4_80!c>GbihO%L47Au!7NB8$%{dBBClF5EGb^O? z^$BZoDjRP93?agT7hRDJnpZS#2UTt2SCL4?Ie^Rv>YWRLY>llHHw59d`+)>yHjop}yBlOy!c zc-Pj>&978B$x<$@k*Cb?bTD-{lIjVkv!w<@CAJJ4Sm51pV3GrZ&K*n9&er#K9n@^V z&yO#szxe+C^vTDZA;V4><{HyakN@s;boMDa@dc^fUt#yl6P!_>Qz87MwO^xp&B2Y^ zvC*mDg1kClcg@$Z9|xv!4h?TZVJoCy$IRCB0c>6&+bgb21o-1Q1Ox7)IBzyMn`UGB zo(hdHM>^r%^_>O&e_{b!6K%>@ljun<*!{A>D|&2xp`)W+a$d|Uo3hf9c1|a9qc??M ze|^fAj>Oaj(}WxPDnC)56^W$j zBU}L4aoGE(BM_3NZm{_Y=8VUk46AW4?+qvEInj`x0QpjfNH$^a^J!2sy-|2VCT&}6 z*+#LX;8=>hfIMc${0$S@9bRX&2dXE+Tv0IeBmkUePnksD+YnymAjsJ59O)|t&&bi?L5%?LhBI5d&dB&h54S6pKhd=4UHh*FTG%lXa z1m>LIjsRseev3ylqk@Jn6(exmxPXsS1$x%^$gtpNzOn@|f)oxZv3J|Y`xagrZL`=) zJc4{uj>I+7QdaY0y2#-j_jM`$+QvYlIw?{DbmWnAF7o!)x{z|DY+d%I&WWoGfe3c$ zU-dO5TgnRpUj2(R1{)ZpmqhTxbKI7WRR*1&_Ai|wXhb9~uQVNtTSWqLg=a) zLkb%*%4gT}+^Dr1mb1~Wh3M>$23rdz=`7F@^6hVb8=m#5PB}WXBeKNFl39A>kdzJr zo%wN^2!jHa-PQQ=k#k06cK68na7M^ldh+ucT+(Vz%J}9J0fM`0I>eD7>?kOr^d+UE z{jeXSFf8rky|&OHW4gQy_CYa0e!v-a1R(k1EQ7X88p+b|FFXf)!wMs9gE}SdJ$z(k z&G28-(@sr7`ONEt#0TNd^BQ<7)FOm-CpAIxTh3zM01L#19d_uPcE+^5I^FA1@1=}O zn7766rKdfH?zUSWWpkUd^-E?vvm%h-0)u>mAARxP75$@0_8E>Dg8EF?DssgWY^#TP z7O&@;Hh!xut*;V(Uihy`BJ-MnE_@|QBMf#12cB0KO= zMHvk~n$Dhl2S*4a8J{7(p#|6|GAVZEg|VvhVlau}$|2*Yo)5A+Wtx6?hNA*nA0J%u zRU&q@fX{c7Qt(@NH0&pxN4fRdpmTvGc_eTFewm3hPkd2ohnYNY!%VMuVyhG2lCSg} z@rujd_Ty>q!|$+8{~ObvfAxP)H~;9r&l6NTQnbjx={B}7nhh+s3QOB)yXAeu%va=f zi?+L6Y{0(9`?ZrFPCw!mrbD)C+Q2u_)3Kj%C_8&R#8;IMSuOc0lY@hY@%rS%^JyHk z&UtV2nn7x%^MpYwpK%_7xFu7EjIJN;?Z3SGWG^GbA++FjY8i-pOia+WS~i0jaGCho z(euv^U;FrXgRs7SV7jVVk2BFUZRqMw7T^CNCn52oiwuPFZy zro+dd;Ov4IGe7Nfe4!(T&jxOOl&4C&6vFoAaR%`HO?EA@VB6Z*V}bc{lw~c(5VJ&& zr0;&-S>T_q1=5z*MX-|6KU6gtxD#U+zM#Xd2I0KtG%A!u~e02cZbcdrXqTQ@Tyu zyg_*g6kg?Ysf<=n$Fpc^mBk?T%3&$rY=CI`3iFz zpb#15T70~Yq0|)WUFDf>h)hPTknogk#=|0&{Dz4sMW6F-`M8kj1!tF;WN+&(Xz1RD zB9}g+JOqT4d}=;s!ZIK?^z#Y}$!+__QLZIV@1-T2>HZlv-C$n#JH~Aks-N~Lz<^71 z#2eSCkohJzpN5x~>7hy7a&>M*iADx;${z4D*f-4eLPXNN?>ZShm2ZTm>#fyRy23P^ z^7z-bFg?3uYAS`p0XyO_S;B`rKhXu=-WyLuKBO7u_Kl&GaSqDipK`Fe)YlLl(A|$g z$ZYQe=2N`V!Zc4=`RdnlaePbI#fFr0ijCUiu|@Kr`pM zuuV>WjV~|r{B{1@j+rF!)AiMR^St+;6lcc`@#eiG9sNyMh9$rvJ>wPkE&Ch#z`;D* zsecLFmxGK-U?4sA0Ja7tlK9tYWre$Gl4=o`q8Tq82d@QBSw$;=mN<8BgsRXsOgT55 z&RfW**ve~YSqE5^*|dh(+A3RPIb&1>eV9wG-~AAfA8H!o zYxNt^k$$I~H_&N^r#2gA5ym-IV5RYk3WMKU$={Pdx}UeASYPH~K^m56m2&%QUc=p{ z-p203y%&DL_ZGWi_tChM_BKo3L-P*aIwS7ylkE=9Jv8r-d7CT)XhxqScd)0KTRreR{!G zcMgG6jtd+!XLRxpcX>DUV0U`TA-g|#em(u^-oG*ZtH1T}^yDA^chlwBQw9xWaKfu! z#EGi|-OmW>*P&$TySm0k0F{l9iy7EAr++m4;p*R`qqyXpoE^^4;dKKh^0-;hgU`ky zz!}aaUoE<(<@=WdB_U~-bix30ic@BV<7G7lxwv()iJklkFTPGF2SmSU6)WS=@k|K^ zVc$X3xUfYAq*h3sKa@!hw;d{Ew+r@ktA9Ot5Y7@SnkzkK@RN=t>X0;%%P8a|gRJCP zo*?iyoJAsH7|#m2!Z~xWb2feSWN$j%xSBpY{-bn;`#XHS>*gg`yj-+8Mr@ztdq^CJ zevye!2%Pag@{hlN%&QbHr*D4keGZg0sJohtI>k_opWh zo^TlGL$<5#<$?Q^GZTe+nzc^&y)>}%|& zZIgZy+R3zMin!ZQVz%Cbw`UMej66?Dx_yixlb|%-0`Em_$Q0MW)sHlwWt$$Ms6xO_ zbm&lN0Kg)Sjj*zRL!;^-3YLDQg3~nO{5M0Xmdo_s^R z=$P!n*ARY*2@qC6j$<2I#lP%0NQ9Pnn!Fe2&3~rn>W#9JuW8w>Ks@Fh^7}9#PtN5l z3;22VS?u02ONBfamV@#Ldv+9@KvE^;wy)2PZ6T>Um}Xt=i>5*$f-Y6h2!=|Klhn;E~S?ni}+3S=#5ix&R;#Bm+eikl~n z`Bhi6od~3dh|ybKOW5?N$a^_nH%fO0ZrTTq5ZgrbBrT*$^w6$?CSeZ6Lt7cxSpFGW&ZMhfnYhv3dX?7FS=RKbK0}p8NlDXY>{z&WdocFN zSfFE5K4kF>7$_e8^Drn~5b4$q+mY=!4?HYm5|y}BCwe!0MY)ICUs=z)Ot>56yG;MN zcFNqRz#Fn#u1gO0#TSCuY1~Iwv}GMxC-XL(`;=VA{dw;1)ny_7mn(bA?X5E3g8Z4q zz6vj4ocMYSzzzj*^5L(Ce{iYM8LZN|u(N|}p4w8??Ie9aFP$Z$gq7$BBDR+&cw8Ry z=VaM;`TX(&-^QVgT~yH2DAaQ1z)Bc8pPaeEp!I}?cE~e;XIER(k2t5`0zU3K;p^C( zv9LM)xq%!}d!|zMAAI;hW5`lt+8t4yKleQo)m7qfWrIo1Klo8N8rRfsBPqt4Qal z&LCv~1SjXnke#D|?To;8*Nz-K;MX8+1@X19lE}?3zO>`0A2hQQE<2v&k-}iCr;ge6{7}(b@Fki|gs=3qD=Q z*%*5q^mf6Cq(6T8hZ%%7*{S6&Ep_r$v2@zl%&#;ZU%Wp(JosRGyz6_*+c>H&ruVjA z`_@~G68(tUzq||YEbz;)04?)PHdaJ}O{{}$#y&;(Otg)gNC=ownzxQ+=kj?P$}U6C zXc4FIO)oeny@u{DoUYnB?Kk3gg-#ZPAO_cL|Dfq)$9PXFNhO#$=p_<>$Ru6AaA%{; z+$Nn9g7Ci6qcV;De3b^B>fWolMbeUKqqL;0T<7iYr|pzSkA1XqVkird0gd4y?)jBxJq(dO2aAy)K}RxRv>D#?^54jCe@ zYx=2s5P>%>Vd(3SLn;e(ZB4Z8P*JidS9US^8b(PbygHT4+R_8+>V_wbZQ z#EA-MLZ?vTg@VjOOuf|8vg;@JGavJS6C-*==9ng9uX)iK+QIRM5QmKqowNfu(VeJt zsvZl)-H<`r12u9gQ$py=6dMU-;1b)B*ZPj+;7T)OA<}TBbU!L=`p4A8D%;Wn(u1}z z+O#x{3Zdnz=LWE(Snw??>FU`s$vbd`QZiv4^QGfF&-wKh3G#Q>pRZb6<9Ol2M9j&(>Ez4Z^n$MQL4I_Nh6~?&sEILwQ zwP)EtYDHHXhN9t!Tl8};RG}w*#vw83RG{8b=)L+Gl*!<|$X&>|X+TZ}#YF?&PIQx|6bYvnb0uIi0?+_nrKod9zTY=`8BRjM{l~!ic;z-TVdZ zb-erTZ>!6Fx%a2NjsLySUn0-XMeAM$Uoo-eXobhU7c2SAgKte2557T<&-n&)rap)C zRr2}(HqHb`Q_^*mxaiL!nUQ#h*cIo6ILUrMZFEHQR{*&q9@BO#a$+PcZ-S2PzRZQk7ZAB>_ZlZe(yll8*P-a&QkMDfN%MtkuI8Ud)!ucSc=f$*~=`7$= zPYpyS&c7PA7~lc0qs;;fEI*TNm*w0Xv@vK=av0oB$SvqO44HDPt;{}^td7#b&?ga= zX?L!WNIadf@}_d4jBx6wV~X@u&a;tYfvnK)hpmw(&Ga>Q) zVLn~WH+Nqg@l};qC)4qZr_}zl~_3TzqD;}p;2|1d*e-SI7W07cUn(0Qt#yty6YvoUV94f z(#o>0i++gIq>M}ymgbg= zGI?0mM#wV6UR?bOh4m$Tfi&Ge7lAc71xY#%ih&;_NDG{a9p~Edl#O|WTKrV~6Qp^Y zXWf_se3j9^)GLU|SDQvI0FBoIwbF7H5a})`qu;<^K5dRI2hGrire<*crjGdfsWmmv zkhY;G=9Zm2MbL)Y8H+h|RT-IQHUesCpP=T;X2_%CBX)#_YMPfop(%%~lp{76^nEos zO;H?Zwo@SsEs5kmwxs=}{vZ!SZi4zqP}?cD4z;!~n`nvGpLO1aJEW4PBO!W|AWZ0T zRp{tmWcek4mwG5ZN(dh(3R%V_n(`tVIOPORUdlO_1+sn5x=t5c?1@LM+ z1P43`YL4uvKq%>ly%BV+SpTSuu{jyeX6ysp zh3*48&jnLZ=WBj3&ogI~CH*|GD(e@Ta*iUzVhhtpP*)k&E(;f?onvyB{k?r0M$9YO!qpBCKVhB2ccsTaKl$t@ zt^LII`+QDYy6upWf8!ZnhIYu9*5#S_LMLHTccVaID{Gt`7$VLNr9i)SgjA)p&KG=s z`-W>AT;8Lr@^{d_GYu2T7s6jd!)E-lNcb~XMk$*>no%@`Z~58=O{1?%y2e|#rrr2) z!bvt6L*`{%A<`Y?w0=biOyn8}60|PChEX;-^Ch z&1%Q47i}xj3r*UCZ%L%zCc$M&@We&7IOc(OOyKfn7^YfgNcR^RmhH3opn~EesET=2 z0U|3ph#Z8&Ew)1G%D64t0cgG*d9VX&3U@W(%1tGNcol8{3OHnJ6mst{XxoEj)>3&+ zV5@6wII}58aP72|Q)3$!EghK+mDDItx{a)Oc{AX^5nT~vsjB=Bj`Y;UZ<|Zcaa34; z?2N4!+NAh_Z334SMBIv)&Nm1m6|8(>PVf0^dd5vC@Z5E-IpH&#NuU0;SjfxDw2q^Ws$l;)&Ls4YC6PBgDD?kgrDFL215Fc~@|{a4?6IQ_~aKFP9q5^|MhhB?0azr zxw}UjSZ)r;(T}TTr%1~%OqD^n?VPmeU*-(Fc%*%F_50Jo^=H%5>%TmmJ^WS9Ryp9+ zBOFT1^8IwPwo>6;I&ja^IcLD~J=<+ZZF&w5LCje-LkP=<=K<6+Cmxg-P9I<6()LaQ zJZnb#$^Hr&nc$FT0r`YQ-L$*)RJk2EFT>j!9FbF|@~N|7YvZyqC?_0L`Wye^!F2Hc)9L7^oFasZuda_7fUl-kFHWc9=jYSW zvkM$jN`kA&Gh_HW*yB5p9uA6wWs9?6GV$_B3$v{pmTbvUSk@!YA8`9q9A8lJpyW#q z2i~}0=Mk?=tsZa?v=S;N@}@bg>1WQn@Xi8%Ru-_$b-zNhsD9tNXB(t4+Fq9-?P^gc z9ajUggd2@~JcwVSTdhX`fw=uzr%5}Zuj5u(IvCGwcXn%e!Y$#wFPRsBNGyx!NTlJH zx;6BCB@+2YLgCPljPw@Sbak3_As!uz;~VxQdl6V5QW&zxANHT=l4e}Gj)X?+2Q!9q zYuO24|L1EAfrFOgn+r_u1&KqPf=NE~9lIPS<(EL&sYA*>iIvm5z3T;?L^3-W^j%Dr zT>w%}?qzCsm~>=uya=4S0W-r3T81K4>GEnAZj6OnMAm{K5;s><)lK=9tt=c}m|9I_h7yp~r2?3yQPc zI#h1$S~3Ntan(ZvLg`d|(s;>h+w~$p_7GQQSU_jVZ0jMLGSxf@2n6ljU$sjFcyL2U zoA^&Yp*Q4@0>vYAs8_am%Sm0_BvH-|Xj|vNZ3m%+GRrKTfv0FG6RvT5RTjEakD=j0 z#UPBd>g=GPS9R+bIJqIcu`?R4Hx1EcuRm?VwJ+`d&IfM__{t zM>O~SGw@|#xf+5C>@2(eM>6;!vaFx-XJhenjx%`MU63{`&x_$n-WF3Lcgwgq002k0i}bfs$l~>2woHX9-%*1CqG0?cJT}`Sa)bx{#lH zmac8s19$x_wO?oQli4RYTKwd-_x^R>1s~@)U4-yfbgUT}AvKhXR^8D`pY#+gJtkEu+Y4oH#keA(Odc4d zoV*4anLOi6Q z_Xv>SlYhzp!Wm;ZjMsCQa6xd;FPjs|b4Q3BzOOXpy-sj_I;dSD6cx%(Vq1PYWbkD( zRNS1N^X}7A2BinQlJt!y+tbJFkU7C|@ee+GF@1V`KD|J;LuRAym^q~&oB-{Cyn*mf zame^JqgOP-fB0f+`mNvmFQ(~t{+sFM*&iTtG)73s!|Atw{qgkEvwt{! z`uR`!r1@$(=CkJKe8T*K9ZuJL`qv#TdwA~Rcp0<94eOU|+<|1;a6-95C=NYmCnY?B zXoo6MJ69)~_QJGU2-rKY9Tr;3o{$Ek-0#9W3;bd%kh->B#nPrmlbP${J^al}r$i%5 z>%9kNLEApaFxT(8z5QLlrbD~UnPaxvJE7&flX&+gWY^$2t*>Gj_pjx%%MdMH$Kp!# zPB6b=%n-lsrAA0TeIdeUC>*k+pVD+A*!S5x_&A83BB>2ueTDtkkcoWZbp&iV2+BkH zj(vl^_Lg4$r-O!M`a>?x@65sjE_mO|wB-*k$5CMb3f)0lxX5TaG=;VAS_V~1f5tU5 z!O$UNzi-GdLe4q@WdEEzGnJ3O@D{$%k}tCNjXTf=Owova)f3>10mQKR76labH7;o~Dj8cRM6L1c~i} zS2)6()$)6j`XD{ZSdjxjJY`D0`UM^%U!OgFwMjAEg^*V4i2W_?pr8zyEzcBh5!PaH1R@9!_6;@kJajI#_hZ z{NyJ;$!D{5+>xLtP5JOy?Khm2<&zI^#|`tZXKr%yilgx!hkv-d$Ge3U0r zj(GJr=sX0Sbwd0uk7b%#b$V%7x-9ix*RA2c^s0b_=I3=uxAB*0bG!{LemWu*geiTE-i;413GZ^ub~VNn{{v!>Y5D-8QYUvg}~atY3lU zcZRhNEkdWqY2%1`OJ;^@olFq^mOk_RblBCHZC5*d2H5@g_Ai>$b>>DM*5a_CncGM0 z;?Z^oWb^V1OxI`NKwk&CPFL5r9U5-FACZRW>zu!y&>4&C($0ym(e!pnrmcq9iblbc z>tfK}Q}w&^4i+7gL|a2)3juJ_z0E`@#x!1?poVS1Vpjkqlg?TaBo?jencFa4^He;1 zGA#;T;Z{S<=gu-pa__rh${{g2WJ}7&MMmWJMEw;;O{zzLAD z#`_l_J2?v!qG33yj*D$4cH$}%Nb+!Ffmf5%$v)H$Hcux6hh`iW?!e=Q4%alx)GgR9kzG_B|dt&+sX3?jJCF()JH|WFW6g zcF*jQaYb2dv17)C`HCGApPk-Jf3$lzedo9SvuXO@|JHQ=`o|by)ahY!?V}wmTf~ZO zh|WQqSZ7z^PdESE^e3y2r)Ruw zb%m}woRPAPz8-YTkt&#yvcoBnIthhsY{P8@%2@}=)_4@x9a~8}V7NeL$mOD?I=s14 zMFGDYC4m2~$hZ9RIqqmp$3=D)B4kNRTrsqn;aVpWbhJSz5s!%IuT1U;dxit%xBun$ zrmz3Gqv?P7!#{z~G3Cs;F|U2Un4Lk^37!Du9qZ^-rwVN`>xdJGGK3WLfhUrER?wCA zHI6x7m$Sa)X$eB4t~<%HfqAuVDHZi%F1JVv(!21^0>1D#-$L>7bz&q67>1HY0=hV=2kN>h5Hs}D$IB1V@z_xYuyi6dPlk~T@sSd>o^+MOtIXATt?V_x`Dfa;Ngb3i5tFJ);` zbrOsV$2zcV&)Dib#k8g+qWs0t2C9Tj%19M3w`z#0@!lEJAh&g0FsqHMzzrL=Jw`O&K~U ze|Slp+&sYT%v@~~w{1PVArPF%P_heZ`-fcelvAErHxN&qi#WG3NkjgDBVKZZY3Trd z=iRnTWKm}AFnD>N{G>qB^cVsl4*uOje(h%+OnG4^>yl3CT0}reU6gkSkud>0L${sc zn53`tQ#L9ZK_Ee=i;k9$KKdw02L}gn#`sE-;eu@oo)7c!#~QN# zFY$#y@~A!?e#?qDkmR#2hxf51KduLjcfFWzz?a@AvQP^haZ@7&(L}`eepG(eV={77KbTq z+HY8ANYADW$8c^v#8-#78G#cb{;+I0Kx#osN?;xA@+1Iw&nEG!EWUn`EtM?c>TC?8rzi73AnD)u zdWgMPR=M1h>gy7|^XOzCyEu&Viij1!T_XTx;7P}aXtnrL86fbWxWIH25#BtXfEl!r z4hJ%kfqWOT>Vm$y5|l+64q$H8>pIyqT!!A@Sf~L8Tx}hvkoI;+#Q^Zr8pRPgvmm9+ zC{*puFKJxB%^itqJ+jQ^zJiikUhdi$5Yyi4pG>R0Z)K;8uPL2C|1pOpy`}-2@^^s~ zWEV$`8l8cwqvaJma9ZXh>@*G(v#bun+b;)M7nD(Bxa1zMq$EGJAo;J5?fr-Q(<`<; z{@&A5&Wh2&!&jI%qvR1Xc)ra0?67!`gPqQ>%LQ_Cer|S;SbKLY6KwsAt)3S<7dU3n zeB*HX*Z%r{HGTef{&Su(yaJeg6xs$}%A>O*6B~4~{>N(CIQ0^FksYd$FS~rmNGA%Q z?=tH3MGb6-1kjD+0-Aa10?fmUPp1brKbXG7r=b6I_gAL>=jfB^CFi7g$gWQx6orQ$ zaYj{zsJeoos3%n?QyGB?BQI5a`BirRlsom0Os|!43uxzMDW`n}xbjl}xj#+g8kE%w ziaHMYpFmQx=Sl^GM<#99N(YKIIl=BvzJ4_Q;Pg+XV+!VA|3m7z_C2GFv{KaiwujUM4X! zall8^-=>83vh*DIFKawk)Vzymzd-E2r(YWC*G-v z;HX9~H{`LcFGJGhn?HeWZQF3kgOsq+LKvP(Eq-Z|ZL{2=@*O@`c zPLYA1vh0Zld8LQa2v=9rg5OR?h8xWE!$dl9>ql#Z>lD5Od-a6#i zrT)^8ueJ|X1af@i&U9G<<3hR1Lx!d~1r)B^@3Q>VW0X2Z2d>BmPlTK`1Z`<03|i9d zKhHbrxUycs%z2`67@P(gQjX*c8+ph#E?mp3{)X1mxaJf$acS}=et6bx@>Y!F&OZYH zkmW5M=R!9KkyrJ(msS=+KuW{iH1gMWhR#!W=fl#B43$0Wd}z#eOq%7?w1Fk= zeID8>A@p63Y_n6$#iR2`kCX8bY2^wlW#V5WgU|&16!}yt}ofmDf_hZJx>^-5zs7c>G9z29zKF?$dRN)A82b``7}Jo2Tj|zD#IDuHi%3 zcwMJ6(<7(D9Wv2{91_z*b>;vLZE`v^{kgo>;5*%`S<3#UVOCHvB1I4rU*vr$9RTEO zSjt`Nph$ElJ%Hn<;TwNeiwfI1ufuz9UcZj$?gi^cdJM(j&rKNVSL{n6dIe13kw+8F zV|><&I^k`xD;k*1#$ekv6h_5!zm_n|dJFPK>}SEOk$VI84FQ1I{yWou#4pdLp^4zD8ijyp18!VUT&jt_ByJX57RX!joyO9JXN>WegR4S&O9 zh8-5}envPn_IG*T@qAjbvtmOT*>&FrhR=~CyMXz+2$G;DH0tr z>_2e^;h5-lJ>_vrj_1^9l`ZbAd{+!q+sf-=lY8M@hMzT{Oe1{uGI;eln_)G4HolgHPU9Bj@hOLc@oQMHRNo24iuy|JP2E@j?Bszj z9Ee+Id_jvpWJrf#VepvLdTT?^l}SpvzZ-bsc|lUn)Op2gPJSvIJID=1x2S)Zu3&{C zEqG~Rlwk5;O-P3XFOq0`wn@sIyK8{_cTHMsXPKK%+w#ENbA?iV>yxn9(FsR(YnkpN zUIIgAWb)M}EBuCyJ^VE;fA$YZXQ*n1UZhFq=EeT>;{3B|difb=#PEcKtyim!*VJ|E zHFeAe1aIq>4oho*^)VAV?B;RN`y2pw&G|5#I>SBS7^e8~f;F*KUL4hK^h5lW{cVBD zfgYUb{9$T}m%1xqS zk}RAFi;&%-i~F}l;}}`Ty(9FSn4P`UVXU$e(gn zX-|VTk-mB)ZOEi9vQ0P6)!{;Khr0VWFB*v}>=2Qg!fE_u3_UW4kl@_WU^xp{6DEa7 z^6d{vvtHoE?#)O!fXpz6-YFYpAy_AkrjDRo95~4#jr-t$9p@0RhSJM92KexZO-8yp zL{9FzPw+nRFd_q()K|-3gcddQNJ}}8=={(So)t}e<_qoUD$L-Ma!_IZVq4|Z?p&Q7 zM1QioTy1CK=;g&=t$XlN3WFxeF8FuzrlblvXQOpaL4;xfs-waT{4d%G{zyov4e9!@AN?UWF zkhuLBf^;1ytt#6rS>Bfxh#`*^&w1|^4`o|1@B6j-zz$;S@PJpFeDLjIzSU=d3sCDP zeELr+OtYx8AwL}X>Z&wcr0azekQ1of$O?mm$d9((>Jw& z3v2#0Kk!X&&LOg3T>W%qHhr(AFZ1riZ!6T2>Mh;-cnUevEdxW_)VOKO^p?TAWoQ{q zTjG1KjOMT7-f`Z%P4)$T(^5=V9-&K{Ip>VZ9okDq0oyH2fy2^` z7GrtCSWU~f+<~|4L85~67xiJJ=JK}mhB1Gbv6Ir@&a~&$pD(h#^MFs(5^VVU^z?q( zZ0qsCbcthQdzS?eP6F0=D9gDaI5aeZuA0ls^K2o~T!#lNs8IA0gY2>B^EHVJ7Nd_I z9de!ohdSX9Ie6fKo?Fw=i{mW9eG26QcrTB9Hh9{8%sYsC)8oevdGGKQCy%qpgszoj zpB)l>Y#5qX?AFklhtnwsMY)hWgT^_Qe}HH98rdJP<6?Dnz#*`_HnGP^-y969g8(s! zpQFP8gT7NF3xlt}yymPDw$^PaH}YTV{NW^77lqdpvW_6S#_B+sm~C(Fp&w3(gFP12 z@TRZQIbe^;Z-P#dU7qk`&FSP!rxcEdJwESCC8>r*HFK2B5TTRkGR_#?JbOCGpm&WN zTg1QK=Wx&EE#+gL&0vhKx_@>kyNh$2OE@>!R}=@!E{+k+r(@?* z=L58SHRQ(KLUfYnoZq3#LEY_0Pw|d<;O-&sk#6IlI>K26n+y&Y;CTYPuf81PsPJr= ziE~4I^^3Lm^oSa}5B+msSSsVxr`F)W!94>98jeLuA@Cg{CcInI505UeC7+FEMy8X- z-4EJdJ$3XvFfcS@lH^eafFd(ea=D7$m>Aofp6BC)#@DDh(KF>%}M7KM3s*6 z*rf*XdL3oufz!$?1%LhjF1)kAFVX@I%pI0-tLO5*VV2j*cVd^WFj>}LgK18}yAI1H z@X|GXga!LbStKmb8=N*ZM?*W!Xp!``=u($ud^UH1^rA9duv>%&;>D`(x-UqIN$>&Kbc3cuctFg8RV%s1Ceta!v+ro z7|tV`q#~2N=pu#joDywsKjwnfVY%yreZC5~u!$U{VE7reYa499wl)+(L=qz2_mfp7Z)OGKk+-X?3O_6$klmL2j!ieA&Yd$NlRAZX%mBSX=;D3P7!QxmXoml zX^Yfdqr}G)AUMwDUHn*|<(s(@aB-rhvl>Ehk%u;yk96d1+I%fm;b#?-r*M&5cwh~w zQ{3d31L|4INV?`n!GP6|^o{WYxzb7X_$zvp2@&I#UnwktK+JJTkF7#v;E`*+i7w)- zg_O}U-MIK%ZKv{*zxHaOn3B?8Q0eVkc#TQNmi5cIrVoA7XJ~lSv~?>Jvc(K7xErCA z>$_a7e=;sRmCkL4mdUI3sNL1T4?4p6P(4Q?U=Z2o4F<0R#$Ou00 zRhK)mj=c4>$lHiqm-%T+q^!d%z~6`tF=Kl(`~F@)WL(mgckVR=h#6ZX;Qa_AD|gB7 zjApb1=)46zqs_B*UB92vfNYpfyK{X*wp%%c>oQy7ye-W#U6;fAb^d)n!y-fe4gJ?d z3hO)dWe5}SR8s8Qd=P(|!F8K;i#r6(ale9nlMkrvEF8;SLfP&3dT;exB#tvZ^<@;r z(RQU_!dDFU!7kH6>MiuT=)L}|uzhpRVWfZ?lovRc4w2gx^CjCo_SvCx<*OHTRu`wfV#5jKY-P3&bpW<= z)mh~MtUKtWL*Wz~tuP2C!VysNYdfq&d9aveVBEo_fpF|F(XqlVlvF;Q0MOR#(gpUK z*~cymR>K85GTh;@3$HVF_H6PR&=&B1Jx7OtI}LOY?WV`asRCW?zs=b(*Bt$>T^vv| zX{B~wVe6HHf+G_;tgHv%Uo$G$*9z;bz}H(-;7%<706+jqL_t)xq2*_v&u~iYKp>5U zY~R=SFknGtrY=q-ZWxJvfV1o=hvHr_L*8L@-@wE3!9Jgq#!fG=(Pa*3a)%bL1yK=C zuW$e`A{;_*H!YGzitSI2j;1eO9dp1eUV19h*YVu^$q&9i{p}aer~lyBzde19vu0Cg z7j@b#lr~zOQhvYQ39OS5D$4Zo6^4b|4tCqjtPalD*#XC0UPz5TO!_Q8%54t^*frl` z_TB1B8vhf*|YcNxO>?bUUBEu5XS`#vW z*^q662J6ffmHvXV-P=3FnfZ|T#pLG)b^bVm(3$AuXy-uZ3?Am|BMukdr(W4+krTbd zxkP>kNISD!nK9^e@ywn$GszbwgD-G-VulRTQ9jEY&1la!7q?!+`q{L<`5uRAA^8f& zB^~4cIe9JW5*kIgXQ33XW`h;LBqqHVUN6& zBRKQ~z*7gVajqg%?HJQoDD^J9v%oLG0@g>va)7QB6X)0)`S$%bS*mdYwR{f7-=I5a zbvj+j96GHN1XZ48`ao|GyP5y*VZEvK{W|f%yls^17Rb)TFz%PAI%;*KJN)<*-ibui zCYm!g8VU61DtKU9fBPyDLCV3rgLq)IJz2`+Tb8vr7l82z8X5Aqb-{GtRLB5q)>2%h zk?tA-%A&)=zA;1vg{L&T99)o@-HUSjXoEP}!6;9Y_*2Wam2w!Q7Yt|^#sysZU`5hcUTB;cFvVS&Vy8h@30q!;wd} zS-1?uTrysXVj8rCiI)7Rr#D$-5Dy>9-22iEe2in6_JRynnjj&z6Pg6;Po+y;3j9LT zF<6RLi;UH64+zaX25$Hh@2hjo*d17=Y}9#5DFy&&1Z`d1D#2`dDUU&X`MT&g)Gzq} z7*|H;Ol6bGz4q`Wax>S(*4FJVmnP74Ea{+y&Y%}!kdSI#jcz`qIi6c~>e$W<`4~*I z9+s`@J~jeAG`YCm2pn{r?zTA!m%P;ds?#mAvZW29N9vW#2yF{-`zCWT?wvmxV*f>c z68#}NZD{B|r<(e<6kZx1 zuzn)@$I{w8zYw}1&RDZPHe4kkH zPd@iqQ;nO@Tfofx;C)}XSC1tTk(yDW>ToAXh&iP19lYe-yIW>}wMeXjXqMwLw5^wE z_r)7-op0-<=#sWA#sc2cQ`xt<9u?2e*KhM6CA$(y&kC5ns!lxsK}2Y5@iq<1x763} zM_pgny7fNkZqpVfyiFXj(M{&=t`*(@or~gU(GzR?3P(pO7L%THdIYx=+IWc}urj}9 z(fNSRV#TLzpQ9CvNgs>wWcuP1CxnZ?O-48?Dg-Z%kHII#|5JKIPwVz`X?y4OYR?sC zP3U>KWI=ewE*hWQ+wqF+`uFu~cXxPZ46nA}Ad!@>Hyv-X!-gF*M=ZKsjJcTe)sRPz zdD{N+bb5`Z+wVV~&QEXz?HsV%=9m+hRm1_po{mqmu_H#oF3wo|var|dcE$qyHBahY zfLeaepiY1MIg9O!>A|Ci(`%eO&t4vJSk;3pz<>Ia6UKPno8&N+=j^h1&4C*G2M=%- zF_5u%-QL*`ZT-l&hZsogQ8^s<^+`E7kY|fplvjgN*<7ePiZJ*v>)S`B)8iusA}32Y z1rTZj@A=L4LnvaOqm%5yu{_RjBKiKNyJB28AG0vs!>M4mcy@#n174dr5mq9nmm8JH|Oib=%&+dx3-E5_w)(rLeP( z4C{xwNK-HUp=aH&yT&gF9^#d=ejUB|Vp_2)=>k32Xu}Q_et`KV_2lWR*VF&-yWgGu z<|iLcXVeb|KsJ#eGp;eKDznBboMCa$ppUP)UGRA+Vi2myuJ6b0pv#%Zpp4O5M*sq4 z>vr_F{m=<-Q#QVqaK!GJEnT{lpB<1BqP)*f9n(qWi#onyMpYw^JKB*Bnsn405M<}$ z?Bxm)6Y9jV)x0`KU~Ee|1>Mzh!d6(Y5>BbBIz@cd>4vMX&F#>Z z)zrG{tjCT{2chlGI_7RRrvuWk&UjS}<;sN5Nhr18>~ox}e*uRVZPNpK&){3r2rC_U z=)9tSxInRE)e)w3ef7^*53e(!uH8=M=&=wnfE_3JH#i*AiKcI{QRv{qnM3~+7-ZE6 zL{el2DQ$<_cj27{e&H6NX(Xg=SY-*Lxmw{S#%`tmbzn&L~UXnI(iFLIm`53xO7_XL0FfgX{z&Vn~^Av zq3t*;N4jZtx&HM6K1P9|{gom3WAPxavbaD}cYc!)d0I$u$Y&^FEHYarTnD_*qKr!2 zr|~+Ybrcwg((hC}(t1Caka1V#0JER zEiwrMu4AS1tfnVUf_%kA2L2}mmo(Eud(d_L(sGu3>d2VkmH_gUOC zN=0V~Wt<)G(ap+@{(<$!IB=GkbR(}I{7J(v73$zgI`-DVA)sxwZD^j00dumzB<{E$ zd=iy){u3q?>c{ldD|o~<)*;feSOvsj-IliWbZi;C&v<7Y13oq(b;i@)1I}?h@CBAQ zSh+eMX-9$xt(RpYVQEOa%OOrI6j0XGT{6{Q8M0XCs+>AxEDRsq5}SE+y{@SfcE_RM zkpi|)>rChdqxec!=ZjTcS){$f`4jrY#Vc7$MnHlcIx-d>{9Bt>ov@C@-eiO>pz<+T zhpgv@*`6k94`I}wi?XeUw2P6btO5#GdJ%`&5^tshjP*8cnne2_;rznP=B^K63EW>} zSEh#g8fRHD0UkWPSf(Ay`gNBw2d~QBo{oRq8&bE@KyPtP%=j>X8di80>Yn?#=>&R& z)KhR~r*LO*l~?_*d3Eiqk$%}&P~%y64@T+{8AT+lI|mw8JW;%MXk71&TaJMnv>I-y zQ__}1$n>9%?%7a{IIhw*EahwxeYK^%!3Uh+j`kZTva}*3)Kqy?!pI;E{&iUDC+tAG zYlwT9w#0)Hh-%Q|r>RxuBY$y8`pl>@mU5WVGPeB9t##-!^M00_c*8B&B`xfdZ+#p3 zBe0t%e0aX;;o_Q&A2*l9W-ctX3HPoQd;nu`Ctu{tKv>S4ybfoooxmc95xXqXmeCfa z(^?nrhm@UF_LPS*NtCdRS4w3p%VB%9|AvdT?Z$6s#9k?Zs%b4fUidQgyWbU4VR}Z;`Vth z25RRgwdKv%k*-)^AD^DF>qdveAu8#(*`<@`)u-pa3gV82a}I;WnFQ&x7q6ysUTtxJ zxP1AXMIUm&^YnyWE{8f{c#VedIbZPF%X1fl?pG$wb6;+747uj>OqnYN2;U3e*=MH@ zi{0z<*U)7FkAvs*_!V-!o_>PjfUz@y*Q3}CWAAv*>rq$y`6P{vI@1YGsSBJXJLr}H z+J$a<{n|z2LR;^)FJA0WyK-g^GdvvuuQOs$h?Ga3Xj~nsVs`mVt0U?iuPV`CcYyU( zEnh?0!Re)w%kx9FaOh<6K)Pk8&RaN%@+o9JUeR3bsodO5m+vBx{JkEKT$aXu`cvF3yupFA$LCL{zeMXerfzKGFtncQV0355 zId#rg+pgS)3$hLm!wz-uTsw}Luucsv;0gJ5G}ms|1kTs~G8sXJW7Cj3jt=;2<`E48 zgiR>WmVuf$*sC*glBcsI`J)9p>5fB=e0A!MP1GEG2Bz3C5jMw+sdJY-naYWmjY(e#t;Z{xwn zA#?tWFE4rSleo0EM6i`MB8cw!O}n(g4V+QeI<9D!)?KD2nBNXp`@};0U~9@;8xi40 zjdjT=53tC+v8vYz-9=}rLs{%yWJr4#-dW(!-U8OPu3~+q`Y|E65ZFz(<)!uD@< zwuPjpvC$E#W^)DJ^aYvZFMn+BVP1shtRye^C4>9XtrXl+pn;ioFC6!s{f6v=*OGk4 z`#Q)C{p!wU8+pBjhOkQ>NifTd2*8zK8y3;|h+Q(Rx>Uw6O~ajYSByixPAhJ9{gH3i zsuS2?pCf{|_H9xb;LI*w&p69u z5@95#Bf$b4#hkv({w()xWbOqPK9jU@Kf1y)3kZ>09##@%BtF{`T=mpRppK^7zsuro zp#N7k@}=j_P2Y#eB3wC1lVLmE-%FAZ8D$FWNU9-bzQ-9RT|k^i)$ShaP&rjBOS0I>>$obtvX#V~ z@XNbxX8p15xgRt(5RpHJwAcvzlEMsP(UVB)L4M2VFsJO|8gizJ@bYu|P$aT7zvz%@ z;vgWNAkM|KSM!>8rbtT|@?uYAAGoq?z2q$=@k3rb^+r~AK`W5u0-0hB)5OV-h#*N; zdf`Q4oMfPC&s%3=*zi-qJb5t9CsbXFeWgE~d)5_++edm1;Vgs6=|qS`h>(cXS})yv%H)dsk8HAD}TxmFzuE)sZQj_HqnL55xO0ED-8cZ;%^Nu#(}&r zEo=BdJ9RgC{E!*@AyZ^?dc^cw{2OI)}OC12A9SH4&u#c0F z|7K?;NViUa>KgzmB17ru-9|AlN_erpXj}RWDo4npjK?ZCZ%e-11`O20TN&o~f=1HR zQ(N;-N00FY3~gEm^ZLGFy-LF%UhmfEw4{&x0P$0}g%kNlMr0VWG;FT3K$Y5hh}(Rc z_v|)xM*Kx+p(S0C4FStCGEz3Cvl|lfw9Mr3l#IEVrUJ-Nm`%*%^LTjlC*VE*9XYC)4KP2g&__p2VU)B^{9#q4sbb7hVKHqptmX-@(K^11$OYUbMr7$Upw-o5Nnq*ZL%Za zeEU^i3Db_{1apDRi%iIVe)NJeaYqidkKiIT2Nw^XH|&^jW@}?tFJHG=p<607wmLt0 z$+m5DBz|)A2Yf9EC&xEF;hk10J?H)GQ3w1A4(^#aU91DT%HmAA!Jz@|LvYS4BYE@B zWj)ntdO%sdLe(?uw$Cn**Lb6jabV$v$=9QF$ecsZ-4c1N%Rv(TqlxW4#7QmZ$09bA_yEkdpVu6+sZ2HWV@(2 z=|&bjK{`p&RantwG`-?pN{;4AK%%?}Hy#S@%w!R)V>$mP`OqEt?L-MG@);}x@10ax zE2>PYE!0y-R|yp&0|0M8kiW2Y{=#_(b`3<@-Sm>fJcDh^8E$r`keBVq!XRH38Nj$( zO9AW@twVXePFyRrbmZp%$!k?PuSmH`KipYO-#j>)zV(pLAMSIQF=?A@SlK^)H9gzE zo?d-{6O`Amc70_K6#D~RF22si4RYHrP)_s@NPvy};cpSNP;?oqTeEdYrV?OxWmfS_ zRLq13Jqr;N#pPo9yYS8ezbp%k`j+Oi;Avg#upYO4rv6)>slhe{L*`nyT}>sf|60cj z?QYJ3pIw+Vy)Jn-Li6)JFh->5Wzb%bdxOT3{Xi>ZewX}aT1#Rx6*r$ zJhE`*#`}X@=MtAg@+}Ua+Z?^EYd3MD$*nSWsm559Q^sU3OFt<*IriL#He4 zWJNZ3MZj}qz&;Z%`E^q6v{e*epqfW?T9T0|QC9B{GB1cOp`yu0XC8XFWh_|NPg~11W35pSze3KdPRnjzKkzG=jc+NPMp)AzmD&{ z`O2^^UgtHP^=rc~^)xM^6G^asFJ)NLY2KNC131GCI!#8LnfBsl>3|Wy`&k#L&0*yA z{kk2J-VB;4*Obdh_hQyE&4ex|>t4FR!K8I*p7&kb#-TX?mL?2qc%18P;%1A*pgse%^vsV_It~xV^<0>n%mO?F&(~v|ELeP;1;g19 zF)N5Zk=o#z1Ff_wXPEdZgW~HfFl54&JxVabF=6NGD@P9Sjve_bjt&YO7WtAA`FXdT z5^<1qFT={+ITScWz``CU1Ulwi6>wbmXKs+c5_?h8UBm-mWxeM7mhIFO z^wRsy|4nx(KvW&$#9^>q-JIYoQa+qw*S%a|^(#Bla=_Jjb9Ie#i9u&WXNfzM zD7#Hj70@jUY6B`qZ5LypocpnZKA(hWpsE|>)P({a(uRMXO`kBOvs3XzcFz$G{fQj|@K+lbK@=wE4PY)ovgCHl* zU*zjtnUnxvX=Jww^`>^MKpQyiRZ}NmoLQicjkhL6RYu&9?xM>2qG4gFumpFg1U^$6 z6Lf?n2TidLQ5|r~r{fW5;BjV9kGd1PrtXApXu(IU@TAPCr|D2F7kO$2>TqO_D3+IX zl~!qm|abrWuwE8K0;Y7 z4|gnWeExcRdc}dzU!3Ep!)a)b#txVb^%LHDVP%D`PYJOiFlDb4mNrhB^*&&cClZkm zy8y0(E@$)5mR7u?=5D%8&r{-DAEb@#MSmCGS>TsxfuFa|RsZ0)fRt&I)?T93M5^uG zqkwoF23p@%16~LcTI8nt9Z*aBTg0@$x&=RSa9xnP1zqsBfht4matm}Di#qob8tm@L zbNrBdz6fbP{RYnyGDbae;fa8+oK^t0Y5z-CYFCoiNI|8$VbNkfx@XWRU3{I$&Lv&C zN(vMH_Nld)Bc%fA$zI4OUWa^f(Y_huO1A9k+@t7_6+U_O)Ls>seZF@Kin~QZw zrjKXrshhBfbH+ZDppCn%=bastsx+ODX3ij}Ly9;GZgk-1R{NBYAV{!iIu_BEIhg^PfyT;FJ9`4VRI^K@o}kr!EzVV)RO7FCbqY=eE$xH|83 zoL6rh4$5GjjymbpOrz4ZlXb@(QZ)xbLtw2UI?>GE_DM7XST_xliJYP39EDxf%quhH z;759aS7sX}^DoU~op&dfyUi|{9NfUuDSdVwaUVyc<)W;OHJlq0%< zqs%oAGmW_fnd)BpRowF#@bLuVq5Q37lw3~D)y^#fK zPUe+;QkVdnlRtj6p)}l8XPKy}wy#$_iar0zb4;>Z9KD7#{ z>F28P2;w!(&QHe3>$osOolXiPr8c8!7?(~vMCWv{mhm_aVQ$(|_Q<1X(&So>*y16e z|HKI%`NFiWNgMgefXp3u?mCFqFrDT#Wi=84b2oFwsWK%Kpyn&6{0yc!PDrD5N*Xs! zR}t<6)$9b~p)`OSrz{{_>}F#o*p5?Iz`n(ik86Qy3$6(-S#(M#{UvR~S|LBjc#r#( zn!9Fr;U#@`oukSM!M@)!aPc+RFZckb^aOhr7-Yioc2u!DX~IOtHCTa6*H(yjC#>I_ zc`pspw@+W4kXc*MPC3}u#k#Kx@A-7mPgE=Y+E{Jc}j^JU^L?EH3)bH`(<=#}4^?eW}jp*q#o2H6lGI3wvfQ zD|eY7=r$G34t=%BYfc!{-6m)Xe-@G4XUo*&SuWtS(08Ydga0lg%{4l%IK#p(~+N;s8z5oXw+C=D~Z6xzrS18@u3f#};+tz{-a{uZV9s zAiH~m7PF$vIMz9iAwMOK)j0Eq5eC^Dn9ns5b!MM(JLc{T6$S%8~PEU?vJN+GnC89TM9Ry^UuT9BAQFs&_*uhTImXSQC1sd7}ekP}K61(My zZLq8O)|Fjg_h^R)I9~U0a_rI89_^m^tuyYaV0O?Tv$2fVnJo)Tt~wyM8()5(1Bf4f zgoF2|@U`Ch3ZL>&2dOt#)OjcPsdFUT32#uZb)=bQ5vJe4=E|h5D;$iG&iZW~!|?hh zFF17QhBDnjf1eob*nj*>w5|NB4mmpK2g2{dI}7{*EzpyeX0d*sE@9L~`-={?C;!r% zt81~^lUQlDkO2u=@)NTSjYwaX2%WaLz5&0?Bfd^<&b}w3Y4ugud%$KAZ;LGPg>g(P z0}!WQU=+;54pX1(-%Ovr++hsfn7;n_VtPSeH1o_D2oMR?mxw`to;z;ro9&OYz$Q8U zt$mDh1M~PFVs_8Qy(Lb!p0T}BDo&Tqm|G7N7}637Wt1+68;-N&w4+|3+^NHm6JE|2 z@+zAO8 zpftnIaHV7D)}rh^n3+P92d_H6Ov_W0&ijXa8JH}Eb@KY{r;a}`eMh^I=98E*i1d5J%4D;!{(&OB% z@&!;&^~fKUw#(iHhz?3C%QicLEnNPW9z>i2<<*~2XCxpkuif`}jg6JZe6#tnjie05 zy>i4((gz{1!t>{x+gU*20l+wJ+e#e(RtJ{T7ayuR2iA7|r7zEOTsiJb7aOJzw7y82 zMENX3U`WuhR&_yH&{WviinwJz?3Q+;{sw90hkW7rb6z5>^1{B-xaWWLNkrX%RY%h+ zR7{$`WPcA2l$dv z;9SR)HPXR6I-0*0Ob_3};+KCRFaYn|tRXLNfy-R66<+4u6EC?c!?J2V$?if%pV^u9KT`O$ri{G7JXrDa+=u)=fD=y=sTb+LcHB^>>@_bOTIRpS6W z?rtwhTIju`)pEB?jU~)Iq&mBl?K=Sna2znEbKmy#s#I3jlG(~BP=)YD3G+>lotqx1 zgLZ9fRZb63_IQsOdIf-v{fS zpN1oA`{mQ8c}*p+VnA&N$H-i0%pP16;t~NAhsGISF2Z5sIUB3hYZmJ|7+6559;1Zs zbf58LVVA|c=VN3mB%{qGt2iGf2xkHLijkks);Z%VNA7sp#ks<=E;u`!gW`766^po) z4i_w*cS2cI-#CgfsQN08yHFf3{q(O*LCZ&XcF8!I+v6-5KLxyp4i>DtefCJtwp`Lq zaGyQiXNQGfHlidC`C3n^1z&Y?_ky2-K4JSuWM+W(D@=xM7JzvzBv!F;!kQ6NyWY?P zI}oLkddM!dV{`YK&eqp}aHudUSgAH~1l4=oR9prKD~e~#=y1qqiLuExi{ndVOZBI6 z-k?`r&4I@aJ+~7ozar%!v<_PCl8}xLA744~GuHC+;8tHh^C=>Rq0=u1Qk)3b(0eec7GW0t24zHYS5*QTyL;29n(92%P_;o|rL zS$uD=W=&hj&jKII=ukODI6FM+LUz?~jtwuI#r5*oM=EUD^ovfon82ruq3QNm^~#e0 z>}qYqdi>(_g4T+1lbY5BT!QJ#?G2D4}g{M6m7%RJ)hFr`_RT@h?Z^#=gF&NE&s|?eMY? zEi^HaSI+GfIX||=0L;JIinNQf>Er3e^uz0e={W~d=d;$(qs5R5GP-EmJgw7?V5!~~ zZ8&;JgRmJxR$KP$9~U>yJvokutBxaJJ`j-qkhOQ(V0QJv0a5^`>RqPNdl%kW;1_Ly zZW7%lt0phFzL9X7S9O0hFOo+yqi)lBL%3iY?iOO#4F(b8&J7}) zi6iuol;MlCE|>7nPvHtKY_9%5!v|i8sw@9|LNC5znp5cX({$AGb;{GWQH03EHS;bB zDLjOIxF#=Q!^i@~m>Cx$gH8ppNlsxEthOk7ct1FVhB7tBL9(Wyz&ZZQSox(0Y}3wz zGi2vpbjKDI)Q;L*r~eiX8mBpqMLu0ssd`e&6&xS-_0EwU1|IDb8%4j;L788 z%-@*@qNhHj$f`opgJ0@YWCDR;e%o2OEDP&tf;Lj0VP{ROOlAwKtOgxN(oqht0y*bZ zcV`b(H^`{}!}30DO)|wQ_<^&cRD@s3dKgj1Q)C7{yvWTNH^Wfo z#g@pj={cRQU$6(&1UpbqdqL^FHU&h7mai|0m+2Jg&Y^YA8C*QumSqtbQ;0yW5(EnW z`C>|ik>0l<;LTck$qm!Dgjt3d{1zy4W@Q;Nnv*-yoYQt_+56tBIxWMJM)L1w&!FqH z)>Mg#ou<7y-ULi*AbH+5xMpm&AEMmVF#&@c&2GUES(slsmDy}Y|8@)08R{*1)&k(< zA9U}NwQ=rTTb@o_AXa-66?R2EDv!RhwUHi zr#I*zIXblZ3GjnFgqBV=K}MFJ@5frKiDzMqj;h_=MBgtdr&vb9(st~W;Q(2t7Wq^p zSB~nX4!XEu;IPxBSK8o7_x1nF-n;ckwq$u;9_}6yc|GUUtxH{9DmBpEjD*Cc;R%F5 zNPGa^_;5V)33z}P#3LZJcwlB2&9u6i?yf4Ts?RkuBQhe~Bl!LPW^23o4)=)2b54~s znzT%hZI^YkWtVNXwq3Yw$(0+6l@ol8A;8)J@kVj0hi9&MV5^f|S5)|taHWf9pWLwR zB?FKyxkiCCMwnGM!Mk2XRu(j;gnwJGnu3z ze8D!ObIz}DtA@s%w{IJ74+7$~$7&t6PPi(^w;yz#J!i|5rH}s`uKqob5$SI6^*5X! zLSZ?%cVh2L({o===>c!vlP^%j^CpC6uG&!}p=s~v%ZSM*dC$uhT`c*e|J-~^lu~i; z?-jNyobr!&r5tx z*m9M&2Q+(C`&JmDO-eb@LVa1g2E)}$zqtH|#m|56Rmu^uBAZXErDIjwm1S&yqQAGR z{mzCoVFc6JwsnS$@9*ZsC4PVnj6^{SAHCvq==n=WqD4;`jbCVK>+%o>=U-)`i)ab>g?owN>k|a{0t4eEMm@_TZPjvs zm{z~I-3qHN`2#L}Q79bg-ne*ygHK5F0(qJepR zn}_cUL61hAZG&p-?w%v;zKGMmxU;qD)fBwV8n{PPi|NQ0R|NIw!viNuZ>wmde@;;WX z<2(mz@{3G(@&P6~MwZ@(`0Yb)le_gxlmh?w3n5?E(K%u1)GUwF>ymAUZIcf9(h2vV~bftFQa~;UBn64S^nrz85GDyqx zQopxr}T+{**Y) znBMW}RO~w&z6`=-ht*Z*{Bd-xf3_~SSn;#tz!xm_-}Yj|Y<&iEYta4qj<@|gS9ak? z&~0CM?B5J;=#$sxSNN`vmeM5Eyf4^i<7$FI7qA{)LfX^yeSa!g#`ayS2y;->pe^ybYr?C;dwfLnX(}((k06)iiY49>mMo2s%vU5!|yzEK>j8 z-o7W^GhpoGpalJ`Hy^2Ba$%ZhPvqPQ=<~X1KVBG~LE;t8D#H~aON~ODG@|yQ!pi|; z2KaBk`lcyaV*{1IZA=?hmaM?_?2enwo5Y#~t9zb>J?c*jA*td?0~?D3YJI{3|w z?G)q9b7+6f78$o4!HxUQrN@dOR*u-2x=qW9iy4D>4-|IKEn6;-_YQ;4)v3k^YLPDw zBF(ce+^h~DKbtFb2dsMZWu>JLjiDBGOXG*~DYAK!(g|%`BQ-EL8p zU*&Qe6RUzwxe7Xi0OZiPa*Ltkw5;g+7?)jJxA?mRokN}V#}g-pJ@ z9qW>tur^m*!A%{w!jkj5&%ana|IKd}KlsT{xL(||fLM+5obz_xqiblA1L}fCDCjTM zF?u}%PNkFAXMCQzLRQ~HZVTe_^u^1~)#8S8Z%!$XXO!I)c}o|Lj!{bn1RD5I?&7^S zM$xi7fY{X(mmE^;fw!K`a|`}^UUG@Wx=(4P-X@(t#Jj9JtJL%ZV5bupo>pmVH!e#=x60Ff;MA= zRGTc)*fO!SjX2zkBe`_rbI(N}aCn-JmJ4KospTY{HbHHOq2oJ<7lE=HR(8ii$qzJy zw|=y-qcnkM)icAultqja#6}k4f{6BN)sPsiGPNXzH{_{w)%kRO>P5HYt8NMazpT}% zM5_FzLv1M`&?1INJ9x&1mhz zHo`CGayrJRUTdIt(4%^szsSt8v3`~giHx&-bO@g9UkP2X&Ik0D3r;Ewn3Yh^0veue zK8Fm^Z&QSvbcr=<&}$IN!#~5&^q)?4vhb-p#%`qBAP@gNhJP4(h@A;g@(mF2Do3P8 zW}k-274JP#l|g~j+(PfP5*%bm9$f4o3C% z9amSpze1~Y=NU|3OxW;N&r9Bn-LhhXuy?y?*wBpO%9~c#Fi>#u@`_a`x4gABuEg-X z9OPIjC;7f%wZ+W^n`<~9$2Y8%qV7V!e8ctJYE)8Se;d?9p83YNA^zfXgtgc&-leY?rJgp%>ne|7UL1LVM&q9c;=0bZ@TFl5Rz# z9#W|hn*6>!x!`?;v57ohz2^Yia||zZuGbpZfY(@aL0)_jxFSiT)HfF%5bO&WE*gK3 zrCHN8A|a1>l&`Bn9cbLp(Jx)0!V04st{Z>HA=SFVEbO~s<0T%qoY&-c9O-@N&9ad+`Gx_2#=<}%MhX$R$AVjUFk zIRkl$E%oxptl&7m!%$>ZiS)>CSd+eLUzooc+2h zK+4hWrEf|71~+{{B^NEjk` z_d5AC-e39)`AC|uV>Bc4UeHgx{r%QXjy_zRbW=P9-1!@9-YF*GrhXHD`c$^$`W~3J zHFR1N4PNVh0nhtd`ml_gJ=-O-v>mTF5EWN_C}_UI<8wuj6Eveu5l@$)opB%;XSkM( z>o-nvf|pD&RuS3G!jsA_E%gUzq)2?}*7u2d$ktz!moJ@$6$#?msts2Bt{#C<<--BK zx+*7*)Rk||*P^Ua0p9GjmDG)<=Wc`sz>x;N8E+flS9uaf@}&#u=rr`Xvbo-c8X<(W z>?4D9hPD|U5g%iLM3qlCC(+C`*YBLnD-y7?fl5DNXC4@d$}9ATr*Fj8rl zkQYP1d@PH>k2Le@fxV%DJf45z7+8ala#?Sbqpc2-5AdzR^l?sWhZ-Q3$gR#CXVq;? z%BAee-8!n~H!NM{kMY71vp|V==S?weuIEoYhhb;Bd< zqZ|1tOZt?K9+^l_9O>aBFVY2ZP9cmk8k4*jCKQK9{a9{BX7#)Q`m{C)XUvMeZZI;t zddn4d?owDoNE-RU$Ue$?W0@#R==c1F2Ab$+Xyccy|86Sfyf@b$mXQ`MjgiwV{ zYsnYCOl)XhEmN&+2ZFSg;mA+uLDxYGB@{Zz`*;BFv`4Z6&D{FG`-{It0h%&YT08T^ z39yY`J$Vj_dbuItpgpT|HkjJ112>nd(yv~uqT$+T3s@PG7bv+E=8l_>yiz&aa=?Dis*w%n zpop1@N-cC!Z`gBh{A#dTv9$VKi(6~JvNpi5z4U;j_qM{tk?{)*5~)1!R>o}QLXWo? zn>^guGjTLdHKPXj8iDv1!w+(PMY?il1;kX^>RK}edJJ8+d+i1{8*ofd?LZz^wAcXN zVSG}CQw&!gY|KI%mhiq@oWA%JeRFIpZ^jp_p7C1kcW>V=zTj$b--<6^y;%J8CqG@h z;3{*siP`wJ=<1Hqf@ZPB^6SOMo4uf$hRNKF*h=Wn>U>Exjx;7tXg}9QCtz#MWKF<+O7>V7xBf4!rFv zDz2sM{~gn0FKu1L*9$OlNqP8B;T2TEQ@xmC%X9Bpc{9PRR7YF!>9_A;?IN0nE4Zk1ir2ak9)u@~-8ofr1oxO4;J4*2ceJ5U4Y9{mA+fSkPd;HMNk1Ma+U zDzL)uY8p}S`_?w4eQ5ZP{_NL_fA+UOz&n+zF&PGWHN?dw)v5t z4DRU2GWavzbbiWX|Kvi+$t#{rwkLpo0z z(OvjEz99;^T?LzDf{NeDkK7@YbTpjsg(mpIiDx-Cn(Zl`GI&#xV?zxoG^^+;83xb1 zg^zzoQbe{sa$VmS1+ zPKIub?+KGG9jopYAHdYN^`eY3ldQltqX;;1rOf0ju!fnGH#B6a@kYk-O*xa#NhVNJ zXGx0=;T>pk;UQ^p#LvOsyJy^gxs=u9Yw4EIfP! z)diifylcpAy;4Rr8bEWb>?vRJJPj8ciI&$W0C4g`)ogP?m1~>l1ETX9SC`9};jfde z$m1Cp5vI$ejCiMd6c*@Oo#Y%w=4z26NR~))K>o~;LMS|bd6}e(qF_V3li3!JP6Ady zf*~uBZ47L#rD?j8Z$r=*CW`Lx_;ckk@yeiV>0{)JqlSP^3LRNK-bEJ5RElPnhM@6@ z002M$NklFy`PZT#MNI?Hy~MpmDPW=b<+ zHHze&bXTXCcQ5p?Νgae{q~#_cALxXQ)6QQ3%W`B!HD^C$WgsPzxgNY_F9DP+r9 z1l}9HgW7sni+Wj_a)@F8Kz;}s(#*bEDak-FvJL9F#5Cx|gO!?6VIc2`M z^w3GOOqi21n0q!Dn~)d(n8ge10A_1LnJJ%jw!O#9UsIQGcs3B1@;ML*491~bH1Db#!#7-wyTyQF$)xe} zMzEoK28*;Oj6UxUEocbij&>_FUZ zWx>IhK}f#5r~vr;wc&P)aUzWaSaGAID91L89?*b2y)F%gC(5()(&**r!p6L%_E>*U zFit7g2AL#lN22jY1A`rlw_s(9l)agq4h+50JZA$jV6pSp&||7MBe^Q(jM}bINqKZP z7)`tmT^Zaj9D|rEeBfaPb*b{%`6r9#+{oljQSaV-wfO5V|5eV)`Si2T7Uyhja{HTK z1J2zV#t3KWiFBIy)qyP9(Q%)@_2 zBTzv~tLUXF6E6N$XVjH(l`gJfiseC<{-%Tma>N&LPV*sR-oVrbBJ>*woN|2#;bYoI z4g8)oU_G9ywT)*A>@##LfNw3^oi2mc<~7zBqmR0=j?deUNb^a;oRIDBAvfY5qA%_| zew4}d>MN=fsXk+`ZTAM-+ON4rFlB?7uSJdY-|!Ou%Rm1|`uG2vck*iSpZ>*Hi?3M( zYDeAP{Js;K)|w3HkN!LFO}xHN^BSj9g+ZU?m0tM;F+Xucrhn!sj6cH;6wBWs2|J`k7hQP{stad=i4}+U zp@SrN#IS*Y%4*)_t9~>20xA0ECFlr4zc7*?%OxM3I&O3ay%d$O4(f4^5ePNk#>VP>Kq+Ft(HfD5njsgki%^S^#(O~PK*wRzW zZ_|T*e>&5X_Z|^FNtg1CzKJ)_)`2MycXuyl95~>5pEG zho?OJMHj@mvLj<@%Pk14j)29nOb5ZE>lJMP)^Cyl+Tt9CjXHi{kmmc zryfvc(+&O@3v7e@a{{EHMFU0Uz2s!@86J|PFj5Z5vn==t7yXf}UplEi_@(DKAv)1@e*=2k$4>M~mk|P{KI2aY)hL=%C|x}vctH^L@{KQtog)tpPnRyUE!G{dq1FD*WC zHZ1=uOOqkI0tile43@!f?B&^FI%d-Nrc3=zENX6&RC1 znrLx$EPkAWUzB@wX9^4Kq$93$2 z_ymSd5%-x&&QpI1qsOV#Vo8+scjwGBsh@HNOmnT|{3~pn9tcRkco=WPwG!*7=dI(B zUN3y(gQEv0^~PbMj`6=E!*;r7tZvzGmW;PxW$CINvmnsbXXA3I*$d9BS@1JoZ@;=& z{N~rcH3N&&XXhNEe7<=3`UU6qJWrhGznp3)qa)lP&+;8-{rup^IrC-3(b!kdf0QM& zR~T{LaNoX`rvvmjpQuNj#(^W8&az6?Xu;`k;ycXL7UYFX`8IE( zQ?%d_x;^ZpqjczpA0=KvSsTlRj~hkJZW3X zY&BJ$RO?-Koe}fo+Uz#hKO0ZU)_nC6WIXpH~hI*%g;gc`cKYG~XBc?av+X(K+Pf3vunp54e?3(8_z4D2)y3*S^F_s~=Y#&`Q+Z&VD@7 zrg9Qg=TW$tp;me%E5PM_McIY$mnU8TBBs9h<Vq*8Ix&@bQO?GrV}<=hthzQ-hKz4YnBb>BWWmy{<6>VCtj3*~F8 zfboa0nTIC828(y&+v^fCC12F<7=eTZN4-_OEBP!#`Dw6B9Q*=Hd>WzSaO;@mVgbq% zI)-)96b}&6P3$s|X8RUa-CLi{A5fv2x&u9p5y~EUffvX!^_h6mGcPeLI?j0gk;A8K zc}{x9uOm7M(Gy;ErkuKJKP}XF1|IQ3A2{WZra0+N3ZFPdq;-t|KXuy1^CY|iy@{6J-;g3|M?*=%E3z*N)`x8 z3m+qO2}3ms6v>)S{>kgqhLAnWf?@5^;H= zR}Jr$#vLF0mHrMZeCB~7+R!nGD657OjThE)O&KTM8tYLI;^&szHc=$C9rNJ|9Oow| zF`}>#0>g<04{18t<5MBKF6A`DCf6O;Tk-|p|4x;i-pu>f`AwnjiU)1#BIflHvsh02VZF`J|rT)bm*cDz{@D<8;v|(nY43?`1!0z}5QH3G zM8+p=AE;x4Z#p5(E3UxW#_&V<)|E1z)zY?AW%W-wlV)r&jp{=KYF5{)G%gMk249<} zhNElgxg;b4fQM{tH4c8#EGxT;ENSBnZ-1?e%#@84KQ9P+rmEaZ>cPyGWx8IiqTuEQ zE+RCgyJQ4<{qAk&ZRbM+>4lxHIQl@lvC=la=HKmLFQ2`FFM4Mb<@;~)^~Wt^w+uM0qZ@A6 zf?YN$p~EdJWlo57k#!yw_D%sBXWyx=5Q(wUTW9<7Uz z4)Odlm{brV9MIJ_jM|nHAY~%mL8yRPsnCTlI?9m2EfCUiK6U$`-Gq=DX*|3Y(}VD`Wtq6FxJWW1DCBSpQs{~=k4WOf30jj;IpV}qT{!s( z<0L5tJeW!E4B!nDipPTe9PFUX~`SqTV<3de=;$JuQXK>pn=UC4Zkuq z*p{Zw^a-w$cD9iiK(B5QV`n&%8Y9d%@=6y0q@$bY$!u^*2PVTf_{K0odAgFryrk~J z!^xKN$~*G+JX%H`JK+`q%h;Qif&&lRX7r*$kU!;)Pn;NcpeL+)GEO5{@|OI!x(`=m zYxy{JOFjBG3pomb@t-m!4@UCS9~rx!*f?cry~J*OAF)Wa35p}KP}VK!AX4N~dc*$W zgw+{FR$bDFw_h+`nl?Aby@sWqB>q@Mm>x`SbtaJ9aOB5r8JT+lQ#V)H_SMlX6HU-l z9-*B!-ttuW(~J=7x@t)!E_MH zYv~B2-ZCqoyc>dT*?*0Qxg*|VEv%DtvPItfdUlel@!Nb26lWV&5R7)|=Vfjf`LkL> zsu(KnY={^q%$7zEop`r2Bn=y$aCyQ+$>SENIVz0{TW?rQLK+`^bJ%$*qN`Cbf-vLX zvfTcJg#_xw%VO4?Tb30nY`TeaDzrS6jX$2C zQ1#t1twwEJREL^`i@JDv9;;?6*zl2?KR7jSo~Pmz9%sGPSZCeI{1D7$=%Hk(OIA6j z$wT6M*{n7`jS?!NTAbj<)^yw{dI%D_6%-Q$|^>Yb>Ow_mE>Q( z^g|lyGIlV3#6bpnN`f5zaWJvxn(mD9-8|Vp%*@;!h$@gN309Mz#I&gykm>%SpPsfJ z0VD1pfQ5p3qi7W>b}9mZcChA2f^n8c;dTzZ*QR`%WVHB{Z42;7C&sfXfJu!>Vr{qice29aennfG)I%w|P}Il0cj?w=ZkWZ-?WHNoT`q z@LY8rA*-Niut{T;Ng(HcNQ909F9%QN#+c|yPj_KsSh5nt2`ij^s|FnLt*8#zJl7}2 z8&?2{Y^TgW2dFo!@bPIE6<7uo+`*K=U4zxQAAHs8Em-2B!5w)h7cqz6LUsXWJTIoWwSX!A^FJ5R@V4iNNPm9@6Fr;c0QYtMpf z&xTky4OXH(e)8>P8u5M6TA$hr7pze);!37u91x4&5Y@aLHh5#=0UBfS+;2H??-_qD z$UDf%PRr9A_G9>?2EH#19IEbnb=`UlP^#ptI$Gy%8Ew$jeD7{ghJpIMb~dCP!x=z6 z$omkl%q5_94+fo7oG`xI+Zcf%wv#r(d`D>d^kwC!k5!X-Z_^17VnAj(?*d|P82~uw z%z$_57Fr+XyCQuV@fK5%@d#Vpn(qV$#+iu9m%_?xRYoaT`ayJ~XAOGqC4%)b*Ld4-lpBnAZf>ZD0Y ztdm}u^J`$_m$5~T!xK#hVpeVd)7iw+E-}Ch6Xn+VP$$aj*LeG5-{JD`!;ZR?zH%vt z2*O($q21|N4ysd`9Q5ZaECFHt3Tlcl&d1oz%}qqX&VLuFlC-3@wCxzr|2z8si$T$-d*ZE?4>EtEgUEm;P)@=oLTjqS2s({Lh>rm)vH=GlVb!AbI%UI$sL+WF01moW|Iu-; zI8`1#<*)o(R)1?)sWQuUfPDQ!4%%MkwB#pwWm1)K6X9zoczy64J*6J(>WGFMCB3<7 zbPVnCZ@?JKosWn-&413LApVrEYHm-rPROt17GFfi>V>aYr~HAdhHYGdbzaINjK3=L z%yFzP6QPjHAJI}(`4ic}3r!RBTQ~7b!|^5E7vBmUGZU|oi*(zB6V>nmN4=Q7VOyr6 zI$6!+2trjStnczMEeD80C}(DJi>YFYx2tmsW1VqKPXhe2t*9%=nUqMjOSd~Vhm5J5 z!Z)2LU}hG?+ppNx(R9FX?`V9+Ss4-~9gQQd4k3wcF(;%clUmTop`nBmGjmLWh7b{# z7)5S4uXK}}c^S{2Gmd`&4LXaHD58C6_wo+Um2}m-D^~7Ua^wmk=NxWPakY^vDzMu$ z9jZ`x+h4LPC;f$bP8FnJ-2UP@Brya9jbH{!N)IXYf|=vcGnPklPy5kJ5KqPo*mvb?Esq zwh;}n%ALO5I)hTf--=JZ?TeIAgO4(4Fwxond)50124oL&-cY~X4#mcb^rONH6J3h4iXLJK^>8G!w{#r{Fzs>(SADZl(tawm)3WHi@5v;w)vY@ zh!|XSYq-M>NzE&P?i#HYfMRG@+OcPhGBQypWZZLx$xkwn1OgH^-lvJXK29QmYH|3} zsYsw)kSfrg>a-DS;U+_)l|hSo#HC?OfQ?YA2)^_ijD$*I#yMU~Evm=@tEcI}CI4Mc zAB}ngD!-LcHm%dO1L$x7SV;=2lMJ$SZP?{Xzf&Fo2tQw5BH{#3oKXZcXp+NLvCLnm zmprF}Hw8^uB)$1H3fYlk6e3P2<3%r6`E>Q-YtA?$jrXUH?L_iaMrlOA7M}yAAgEmG z#`NM-{w$|WBI&rFed;CEZi}J;((o?m+5Yj($>O)~e!2Mkui3x(@~4Z{AN_d_0d}>J zgna>=*#Xe;ZZX~Za=T?I-{m{@e+=pHul<_WVra5d*qs94c4_8~TPcY+w3*QH;R`T=j0*hnC7b-OiY;f~;_vfL!lq~x&Qh+9pO5ag?*jF-U&o1~b z_w-#rX}5`76Y*gpA5%YS;Cs@*W7W+%ZZ~MXtzX`M1OI9VKKcK>JL&4GtyQ?iKKyr- z?IFmKw(o`A7@=Y19X3~15BttB*}{EdJ<0eqBFyZ+XY_^wNU(P0Kr|(F-VlL#@UwFaa);H}c_^Sc2VgusxI8GmH(& z+1}4>(i#A0=Ntn9vKPkfSNbgr2lM7X@)(H>p+lSr%H+U1!<@tsEiIEX$ihRq5r8&0 zf&#UPd3$HX5aWbiWXr)AxSL#sRzv;lIwhpp%q}(CAV|0I!M8 z{4f)PhX2cdy;!__xmdj7ph)Y1G+RAt1ANKoB9pT<_+%j8`Gs#OoI#l%bfV{AwLgv# zYQmsg;Q_v~MAsF^({KzmFww6pQ|5XmjcRGak{pO8P-q(`E{{x(2sbk5kvR3I;Ut+t zw@zf@I;-%6!Pfe1`GT*k=F6YwH+XJ{Vx9|!tG2F;K%Y5&p_g4v6#&mFjrW|T`N_#k^vVSsbPBF8xZ{>lhs|!` zcRS221(1~@Fx%kQc;Yr1+m^S|dOX3(l^@I%Rv0xj`rWuXos6vd0j-{GLPr>SX3*_d zW^R)K`VIXXVd%Q8Fqe~%Dd{ZjlWb4u*fQ}}RE;V2cj|w|VtlVT=3bD+mdTP^@U*|! zFP;A}ZI*JjKTy9KQ{}n^=jtEf{Q^s7?jj8 z%YVgu!~9Fjb1^Lo>zf7&zkqAJP(HV`$;X?KZPoUB7HgB8C%l9eEpj5S_0D#v!GYYg zVv`r~riq(wJ7tDYwr}8F?Ix8N3^A5SPx>(+f!O3lpTOG}r%FOA1}F6cZ_CbYp6xuJ z1{XR#FRpOQLIhw^N!1RtG_niV&QKE1yu;J>LZEGrB96RfYb?BML-KT$pxZ=TeWpI` zx6WTd1Hb7x_}3>&$^r%yk@qYdVbh$j|}1Rn!nK{zQ-}Q z#mD~!TquBdfZi9hu*rg_))&bEr$>#+R&1SYtX#S&z37=1x}6R2hEo|~n3r*<(M!Z4hmw>itGG)sDqja^Z+)3OS?-I9i`DFuEi!_NhzqFC{__Ns?$#e zgPC;erD(D;jxt2g#sk>$5^oxI2T!(Ci)?x^0&Mrtl@)@m!3$#r(Un})!F$-1&O^vl zkqzG%>U^AyxKwBU?G)?;Yy`Qb2q-z4C!Mh3)9vw3nmzL-0!aH$drYU(3;Iug1fCXm z+|Bm?UHtaq*NcDg)vLwdU;JS4<@Nb`r6| zuYU8_i$A^kr;ArV^TnfF=-WIHkhl3a>=n%}J=Ey3hNc5&(;YB4z|wESH_!S-G(W*t z(5E=@RESRhBTm{DTe-noQTLuB{5(;5AL8!8qX%_f?w))-4X}Goxq8(~+CAFDS~ipG zvi90LMVDukBXN~C6Y$q$%jmp>!+fA$gg!n$YT);v0qc9!-L%PB)k}Mr#T^sFDm}KH z5pHaQhuTzlj|d0+hwu)7P49|IdkWs3d!ajC#=w1abW!kGfKapuKDV!LB@BZ@3;1a z&+o*<@czi4n2tjuje~OgdA;t<;lT6c{{3*1urmw~CCIBPkTjdu0km0&=)Vu=~9?IgD3+2}_XUw}+ zoal+TjC&Bq!L_);X8vfJ|6t^M*8GX1Y)0fbMRY|BR%RjHE>s{$-n=_Rhm=V6+H z>Wtv0OfAj`8CkdTXtaqErRrdyg0q9smvNYK0KewrQpQ@q8L#lsAHrHM!v;brF9{N% z@j+ebGATuPC(~u*aZD5*%7XHud*~S#Lq$VH_BOG~SRMU(TWH%lK^t?68FZQv#x!_Y zR@9SBsE{p_E%Fh)R^BaV|ItWXF{56uSwUhx<#P=m^CZ1Y_L5oT1h5P)>QGt5-FG1R zpLjr8eeg-RWDvfE0mPZ#6eGc^-=+tq>$(Bb&mfEOpJ9;s)$djuNdD>K&wu^}9|zki zD|v6pi(AgprS-aV4&mqxnO)WHq%`FNyxT~eSP~n#&+Ny3`9Chc{^B+L7^_HJfuaSB zTI`uhu0&a`sQ47VXP@}LtEf#-$&B+et4ifZ`JD_ur>w1y*#csm4Tt=sF8DOabAt@6 zJvaTVnGCBx+nDvqJX${MXRR-?zfC^{O;>){CRZ=1BNxaB;u7PGON!WKv{({<@&Y;3 z1=hUxq+MW;QFmw3!$@<6p*1Vy5J)|?HiV7=O5Gyfwqd_wob4dx1#driZ)ps5P^<=b z%QprUXhmP}R%aTq<9JHXG~1D~s5|qlPSVauOA>(?{Rw-tgM3fOjD|b=|AfKW(09iO zmv;sAMq0LIVfetzUFqq^Lu(rrg>DYE_B?mX|sJeXPAq8W5+W9aaA>+u)LojB5kRaZRSZkU_32ui~_lKTUXw}D>nd}COliPfIGJor)pOhg z*At0Um@t@t8fek1%!x6fH;}0#FfBB8WOtdQrYBwePM}~7M;Yeak&G^h-D`T{{6nQO ztcW9pIn5zFPd8-|fj4SWoX3fBVhr#lL>@Z1Lsg zDOa-7xHC%Rj2c?p-3>kUn-_~8yt-TbXRrQl@h89dauOvhAY6^cC39MkTOUFyg4x~3 z+iZ`na7yf!nI3?Xhhv!#Wk`z`8sa!f zNZ%|i5+tqB{M}byF5WUoWK2gq?*K+|o*D3rwd^;ncv-BzSv+HpYTN&Rl>_Fz@Xw*? zIm;g%q%B#Sy=H(*@6S8z1%pG3Jp7i6PZ^6`X5f3mxm1f2w#m8&>h3k^Y=`9LiN)

Y+Y@~3FKJsNy+}^Q7CoP3fnL62Ow9F&4p<})> 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 K-^1Q@e)E6+vWb@f diff --git a/examples/react-hooks/e-commerce/assets/cover.png b/examples/react-hooks/e-commerce/assets/cover.png deleted file mode 100755 index 0487aa31c9161284787ce400a74162cb37183d43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 498063 zcmV(_K-9m9P)Pyg07*naRCodGy=jxN>LW zh>XJz-upgkP|{#DfB;Ckdt?S8=>W|1HiH3tsi_!j{``Mz7awnn?Vu;ZSnf6>tZ~U z&h@5PEVh!dHk!e3P)x2QcQBL<+hR6*DU$nQyV*+jP;`r8GMP%oO7tR8+d_I*@_ja) z6cc?`vPTZeKdhCVk~M!=7a!i<7uPqlVtOsR=#dj^*+Mw5F-R+lpMRVe5APPm%daJG zGEx7qxc#sw=J)I3>RKA*%T|LjnJ9MaZSn5yQg)7t7hg;i_l;zV2ASBavEje%hA}FN z5AWy2&C6*q9t{*Ur)vE`W1ynpCJ-25wLvGeX&hTGp{XO7rcIb9$Vbbl%M%ju~n0n+i>8kN^?4wMQrx#y3Vu{@@hw+CHSVY~QL zn|EhEZrl)Z|3g=M9+0RDYgQLw}ZJr&({K1uLNSgH6XJXYz#6jMlaR>LSW}# zB;&WmcKPoDH%G-<_N`Jmp_{nZ!a;-diTt2MrkWcMb3!}i!A zU5uULHx&Qt!Ik_eiii7!uN}l3fq7ccd$DE$|+|U9Q>1fe-wf3H*Mz9T*?+ znvT>e0Q=!~UEIGH0RM7QeDx3iQvCG855@J}UyIi-7y5A_gmoxm2lA7DpSSiAV4ma_ zhxQWcDK9CzUhC(c=MsUYdBxmda~eMntxxy2)}Htd_yp$F+I0zASonQw@Ee;hHE!-N zW*pNI)4JK`*yr@-bft9AI34@6{~WeGwG4c6AD`NmUi!;9UVX@aoM#7gX1^@Sk*Ynt zJo|+*g*E9p#;5J|jR$;rx2M#S$-c}*0{pP;we{TBm(;&5lk+jh+T-46UwmJzAxv$) z#C5*6HCz)lrkvF5xBVuUp>Ye^(7Qo<`@JREo6^Twi(}2F%+**iOpiBR&S*FsF|>swtc`mAf{%Z*_SKDrIA-_PTHYJk8eRYz!Q2W9D{_2RgEhSF{S~ z6^y9?!7P4H1%bViK>}=7-wDKgAyE2DfyF<{Kp~e{z7}B3unNgUITL1tEM@zS#>#S5 z#&aQ{dN}x^D3p!75fELBe}y4rLv{T4jx;^^j!9h z3`j>Nz%z6Y0L}uuZv-MUOl!Gfna;|>CTkk-d7|v*4?isoyngWpz?f`pf$B10D4Ev+ zp=Cnx_PhJy%Wsw0E#Gg%^B@p=C9s_A=%Hdh6&OFb!Ty23dFc`mPXE>Qs90!RiOJ0? zGSg*=Xx57BNZ@q%MJ)i+V}aPjV52N=ZX5%F&{sDaN7^(t?>`QUFaGHti--5Gi|@Yw zWAXbp?@AC_agkhU7nA+v==1hD0-q!Bh!Lo}+2l-!X*t&Aee!lrIT(|(DYpA$gEInH zzE~p9v7IX(=dh(ohQ~A)?_G0 zkCtb+kDOv}{hAw|@8h`_AEWbFoSXVjvH91)>+`f%3^lR*mRvWz(y=6_xwgK*k=w6X za?#;Btla6!nle8>)6w^@p9*)3$5&tjuiY6YHQbnpu55WWgXg< zYVz|#>zw1VdrQhS)L01aTHxwtvyetLgFITAuwYuUmWL{}A>?$k`m2E2pX8ZL9*hNG z3V?oLZLg zRNyl|09;*-#Uqee#w-O&&y{h!kxk57GP41ZrvmUt0+}D~7M8&bh)k>j-A9U-Wq8XP zXdiAhj`Ec<8rL@?TkHn9I+u%0araT+w%RAsJ(J$mlK3$`inw$F!t(?c5{ zH}PJgFOJhrzCY>g8p46o#&JF7Zz5uUTBBo6aNeV*jByuFJhp9kddEH^{8Ncf#`LFY zV*Vx{p1D46^%2O;ZC_L1Vu|O=1zV0b^gRAw|6en6n%gd8bu#MsyZBF94O{h@hCuSA zGsM#Ov>kpf6K}uQVOyVVZ*w2+%kcQPxNUxpwz$oY8?C4t{D>(A4dRiDNOp%Y?XI!a zflF>h+>6NPLYuf8ghe+e=C*x=QdkTQwvV z70y#>?0Ax>>%{G2l4FW}^*B3S7?%(?Z%g;p$3ue9{9*ly*D$}tBoDBfO2 zYRxIWHTP?wJv)y0LE>*C0LrH)ns2GS)j-|M7p< zOi+0VfZWxs=&uB13NYT>=_W>|@KOM*vYv(NZWrInW4TTZXQ8mlyA=rhPC%`IWm!%6 zjjf&@L&*u`Ed*wkvc_Kv)%{jT?{`8um6|*GQegBy>nZw90IV{q*FT6~0I&Mi0z|3j zJQ(XE+lG^`gk!%K;5k>H&ZTS1hb(L^TV*NRK#_F}AghysP+x#%Kx%3mYkEo#t_o}w zpi9Q_m9nhqvm2A-&^J-G@rZ)WD(OLa66!2(7Ps<6+11iESIG|mg3e%*3mb$yS{VEY|_`K4;$USoxMye&9Ut%^9z2~1LU_c zOcUDeNXaX9ai{Q$H6lF581r)0RHglODS)Z$FS} z1L?gz_L=c&DcHkI2hO^!*)&4+~b)Be|&Gu zXZsUmpG{m#K|4Qd=yUyj{ZHp|PIiqM_04sITaMcSV>#&n9el3T&Cvti6B_ug*nM*g zOJ03vVml_vuPdlsf-xq4po_1Qq}xu>6^~PVoTEqKQJRvmEnQYmL*qH+B8wOWneiCb zMRrfqXI?eger&7=Nqmh}WHn71F}ZA;r=@FM?YCGl{$&U%`WwcJE~hmaJcFj;Ehn|x z36HNszNd}tfHyQ}|HkrmeC|NPk&TB0_=sowBN@=8ZqbeXZ1DTBHFW$_r+?>{$t!hf zSBCUJOP!o5;r@Q*J_X6$;*U)DcObA$;4)4wxT?7Jtm-FZ`?%II?c;r1S$3}MMn7n1 z7rUx8UGEK8$|pR%2vd{eZoRjmA&y7IcJ&b%mjf+ZIv=WFzVx{g(YkDt;RA0{!k?Hn zq^Jh|gX9NYYi(lY`g}|UO)6bjAm*UBG0=9a+Rqz-%3C3kTLGK{mEaJ%C~)?JkXAB^ zp`Qsj)k_Gs_sU*YEoWItwle@O$;AlgrGbJ(CFg_i>fdQGQ%D6e11N8<{vaUpcZ#?& zo*({)JQ9Gd7aNZB(!#++S=6_GvW(?{kkp|BZdQL67%DQ!usY8IL`MRsx5{=NC_5W~ zmpauX>H>shTUfI^N5%72-pc?(cZI!{S zavbV=|Ko#R5Xi%^bZX!p?#ViqT#YJ#E|ZLx7>)qo0eq>~tnwh(2b2!@Jeh_)BgbT= zd}`_R zXHGWB^FFt)k3BBq)V=F|j@Qt!xV!CbV{pQ4<3PnZWgQ5Ah7ED7ck{O8ZOERGT3ouZ zj`N0~YN+4)c^2?v^qGlO(ux_UtZ8-bH_41`bNl$>g^9Z_#T5%Lby8=2H+?RpA;c!I z&phz4Ok>v8!gE~Dl{K_UVBMgXJjAK5A8q|T58ZwwJ^op_KJez3{)jDV%CnTB_Zkk# z;&~DuA5wC?NGHOMDb6vUu`L%TpAJn^rp|=C-}O;DWnTHZAKQZdTzgD&(y@ZVHy+zE zW7lO!`@}c3-M)PRPL!L+KaGLMnSE}|o&VV0xU9x3x8xN-6C`LWM!ViwcmH$1|0^>RTnhy_?~DZe2Pw#!=qrUG2$ zmhxPM&g$iZ!X{;Z95b5{DaD6Lwft1UC7ifDYLv$=ayBNNfd@Wmw@JIkD4~}9L_zC!|s9Ipv zCRMzE5MY>$Tsqczksx4g_#G?QG7>V4U9>SVNo<}7a3(95J^*OX_yuqV*j+07*p_iA zSQB#J>LrNRDg`3OtJw>+y^t*e=LO=90F}kP5SXpg;`+svfyiWb zlYLD7JN`j8x7y8Hy^wGv8pWS(@c{Hngyc~oL$<1RGcOY4<&C@y@j+SBWMBhSlf69_ zpiJoyt6k050NUFR2ciCS@SPAT%XzV~dea-}m`l!3b})_r-Lo6Ay+ipEN_bF`1bgNJ z>;amYERv{LkBu!mT zch_YXrVOq7iah^h$ZDHTw&NgxwW<64S7WsQaYApS1ELfFT0BV5TsWsZ8mh#G(Aoh{nj@9aD0qpW2FHMC zy@*gi?pBB_w(>p?u-4(fL;Simv<(B z*sXw6GIgP){00YWWheu*-l)U`^2zF*3p|~XsVpV@Z_DU{j=IiLCixN|AGfMwJ)T`r zctmOdfCZc{-kZI%s}};L?~BD-YG~_)gRiHi6CjrOt|g1w&C7*3qH}@Js`dQx^~6A4 zy}8jcj;YD~pzLfirLSHHD3|YKT2qUfQXL=ODI1+^XT<{0cw#;k#hqSs$V3FprW6P- zC7cT^Ua!caCVr|ReJ!5>mgVEa1Mv_bE%2Hwb6jL2%N*bvVSxMC%?|+Pht9_uulo-R z^(nhsbkv$=JYIjL7e6k?#pM3AvZBYu^#p)hVgzz4sT$MT%u`WSHy`I}>*JhtT13lv z7hC4GGoOxau*cUiJkEcN_Y(b0zQ=Z6LiedMiCdePpQ`5){B?GpdoG+R@2ThKC1T%2 zW<1AEjiukSD*Ag~WU*Zpc`Z-s=HF3v>@C;8&T}K;&FMKgzB!?7+m6__6Vr3NcKm$~ zTxaIq(?=XUy~n?=!cUN|PmG-BN#}B7tjh;Ln*;R^EcHE&!drKGoSY#&_xPplPValO z)2|P2e1!cmyV{ztAB{tfE;@3wxo5i^9hyDfu@!Z+F-@!EXnYv2oLzi7&22f_935?S z_U3%6lW~rAf5y+*zOym!Zfo@x$JW$&LLU~7Pq>!yv`^LQHlC^xX?$oEpH;|{219TCl>Eu@1CxOl2}0Q zIPyQ+UFT>?j7@Hr^Sgb1-n4hxR7MleIs06HTbVvCX;^$I@_kNTEpOS;)s~>6Nn_56 ze4Fd8jzf@5Uog>i`Th*^FK^C4t81iVXDi1Cl=}wyD*XtF8hkw9NXL`5bUSjXRLF^= z+qu)(LO|7y*lSf~rf##Q_I+XP?q+UUSK%X zj}Z^|8W+`rR<#t#Wz4Bx&17OgBuAkBRDks0>ZbVn{GSA>KNK&vcg5?;&uS;DT$Gw) zngZ=MG5Ezq3^?}lb^@Lc6?RuI!v3~(eXvpJKAEk>T6WYIpCjjt`B9~x0Kqw8`w6)D zC?>{ptWIIwb(+r0I=VW!P3@^&UDli~w6!)c$#6)Uui3S<-f`)PnUi1I=q4Q9M}B2# zb2_qwpBsPe5%BBGuhr)f4_&v~c3&lSx@_{c%jf9=y(Bs!6io6*%cCoEF z|BUbqf7;m9q~=uqob0wTo9LSQaynbLwOlh#8M9-yd;e2ki*85j*quPwSr-?PF6IjO zaDx6*SzAt{wI~;_@fQoW=VRiK+*|mx|D{%el2=ljHj0nDkEA+$O0t=f-hBqW35bUf!$-(O5OK!D2V?6rn?b%g7s$ zHLMaBZ19^djpXdwYEQ^^WeTh2^mr`bb)^>}Dl6Esj0N7BEdrTWDn-JJ8;u7!s-t}W zaamklZ&jv)k`$$G@m{|S05FN))PJVT2DO#RA}2$7crCwg#3R4o{#_a6Dv|L*pz&CC zUMpkzN@Y8i57-2ntL$&hJ~E!y0-6EEr)xlW^m4Aub7h7zK4eW3LrQv#)y~TT0pBNz zH}#?K^$tpEe^W=BSWN|R-wA}Cl)yiArva|XnjXt0UP{Q155%4f^oe3kw)VgLx8F*? z%9+d;#dm-Fd-0pWzZF+g=7r?zk7d(q&kr9m$u@QEx8zRki>?#+q^8}Ez3=3GtlKa8 z2BE&7HIP`B`%q^uvGENW^Gyv=vvtRGPVch)yt?;a`l_wJ^s~A+KPSKWKSykeMRTrs zEUVT>Y}Jm;S&lY0moluO$%!LDK2K|TYC3Xb)5Y%^vHNC%nqD~j-d0CB8)JXHzs_zC zS56r`&Yycxpz%a;j%C#8i)s8gN9%gyK5uirUi%t*kII72E`lm$Y+u|%dnbGQ+T}{y z@Hl&U9P1jV(e?`?`StRy1gA)dy{)ydJ8BSyUD2= zfXs!{(Xb$aacRL=(xkRO_86p=?$^-rekuYYmR=6=vzi~XKl}pa@jf1uG!||f?QvY{ z=$!ss`!;&o(45PjTnua(C>0#W{&da3KOe*6@;n})jAWkPY4#UNUVfcFP&1qMa`Jj| zt?@i&`E(g^Y;%0a+K`i#;cIlMIM9^WLb*F8`?>YwgU8KnV@y1@=CTM<8mGN4}3HLX#gksZgQNta8D>8&lAAl}zT4!o&;*Aeq0vyKk>G~O%ZHJ@kSFea0Naq%eg32Gg_m&EmT{YwBY#Xn$~JGFkRrgz1VOp|MdPsUJker_)cc_N614H_CdR3rGg=UM|K0b(Oho zfU@Y64LuX^KB8W=z}}BPDJEk9#R94)0>W=zjf$THZZAcr-wP}zc!0l@M_CET9*bAe zF`2kTy=eT{Qoh8}$)!);P;z4>FuKr-1+QKzV_M}kto(;`O=ag|F<0r3wSaZSP<;F# zuoS($;89uGq%@PwO>8&H-X5(KbH&Mg5f1+C*LpeO_pgcVuv`e`A>yv5MK|8lu_gFjP&2LI^TitON+BzAhw&O!?Oa02x zbZPsfrhPJ|UHm);ZQYohLw0K!nHacT>D=)4*0O)0ggo=??muVrF^%tYST}O+rFQ0M z&fWGDI{Q5r9bQni^W}(BhmzVwwT>@$3#g-Qr-^5AQI->x!wWr*wKZo)cO%=>j36RoOzj+tXybLhmfnp#!`^-LxfS-2K|OR-W%Us7j9B6^@aHw($7aTK)%N)8 zvd6h;*heFykF2hDt}l(J(WQ;0 zVR5s|@;!b2@wU3S8=Z(;p3}SkzW4Sfx?Nv1j)F;*8N0qyi3ins7LY2KQo!f$1Y`;f zRz~u6qnGgM|7ioL-tvovUKY4kMl_Wu$b2Q^m68>dpr9Tz12nW3555=8Uj%0JV!N42 zODOYJHHWRnvodqHYXRNEYw^Dk08aUhI{}w^!JRU)Nva0S<>h#TwV+{vr<9~%SVlrv z(FdAIoo6zf0d@hNEhAcOTV*p(0lWpaO1M4%oV-AfFKzHq-cxAS^-SuGe5CY; z@M&Y2Udd*T0dCQWK^lg+va8tMrG?DG4%_jW@3*#k-&8#fKl3#mldC%T##{GNETL z0Ka9E`5SDoQ_5D$lnwEc`vBH}<5v5c$|LHdOb0WH7ZGyB-+$1H5LK?@<=2zq)mJLN zA;5X9mkjbgN%&Vo(ZQe?QCeg|Ho9WCm@Dg9Gy&*W3^mrocB**+;JsEB_*SvoPJUZ_ ze7F?|Ex=up7!~S^4=l4 z)~+^o#4}iK_KH`JE)GOob54k$`A`VZ0zac02h> z1&i-h_E;||1cOyZ@Kzq!5-E^TrGN!E3&>Szj`jTKVsiCb;IdvY$O{VvY!{PnEh|@v z&Ba^*_vFQY3LdFpF#3(aUX|%s{9Cbp_)+B?l%Y&{4k@ChvAwWQAPw*?FA1dXG9@D@ zi2?XM;-d`Qjk1n|ab#6XhQL&8r}TskUKv5Mi7oS48lV9H6`4R{N^yu!Ik^jgrlZn7 z09{IS5N3u2{Q#_FQ_rZiEC5;5ri*^`0nm=%vDgSymL6v7yujhLfpIwm2a7RM;~<&-6kfQ9A;X(Y@zq=)zOt|<%7&)Y##}ELyaEtc z207sTNEy$R+F+xOwSFHkn~5Kp*i3A$6w@Wy*h&eI$<=l7^G~nk>rZN9JlPa)tvV#3 zKiADKlGMK4BA8y05usnSQIj3gPMrAE(WE?|&wrf}sGA=?hw7q|>J*Hisny%d!o4TjehWk{bucf@Y2mSChxlzI|qO&Tw3?DO#V7*?mDPO3~${>#6AJ?avz?-#255$~@3GnEl5> zpzE!|7YqF4k6BEP1)#r^b>nbUqQ=J7v7?0j3F^i)ZRfnVAGM2hN4r@w`aZvus!qCA zqczJdufqkrziE#xjvqAJb-B4&XJ0!0b#%G*Q+OSGMy?+ZO<;2jP8o-s525W)CoN!B zhduRD4tx4>69&Ho+DKqkBZgcuO(684eY6~Eh{u&)o*fbDMtKnq~j3k!#vzgsQl)%{;Bvw3{|-^KVwwWR-4tnU9PDOU#GZa}mFcI73& ztObBzO6N-8E*Z=d>Qt{(Kic%EX0h7YMr2vbe#$?rRGI^TdQEn=Y#oqIO~mYj^RiWd z_fnuQ^^8Gf$?8Rx3Sy{TB}oi?R(RCLA)xn68OR%f$Xm6omGMhV(795kGx7k=X$ycY zFH4kd@&yPRJu7847nH6b)_~p;JXG!MA;2%P1?Il{@m^&*rYeas)e8;pMMF*Nz)vk} zL@uSD7bfBlKL{{00GzE<2|%$%bfOmzT9&xLV=}Q7p<<~VJ;Uhj+Z3Q9ddY8lag_0bDCoX8%OBr#AxLAMW%cf$V4828s*g zHh<7d8fDsYK2X;6?~5NFM#Wdx-xm^Pnb0GC03bG8Ezt(gbJ=Xp%b57aVYVeU(}vGB z6jgVOJom@_biN$TCE74uVc%`&XLEbPS@@gM#(kfU+eaW5+a_)wlRBEF{+zBHeU7e9 zAJZkB)?N6K+m*h~md9wv)?Cb7_o==(f0GW^b&35`>BSe1xt3+PSa*?r8GMs(Sgg;>J_zK`f#ty zW86k#7uq^`<}YKPdA><`y2UT$-kYewlmraxcczQ)&7z8yoDaFS zjQJHg@ku5ApYGB^&rr_V_bnPEGrk`CvwFRo7md15NtZ0Ot%th^I>=9!}(YBnAA< zWj4jtX`1%<8cUC9!-!O`X=rJWTTivY43JQZdudG25nEICM@>H7Za?R&{d&UyDONK~RQ*|w^qAi#FLRDEWF%~a|6}EETw|8qcf6o$&yY4)mhI z?N)V@*KgH!Cm>VjiWXpP0Bix7rL(*hQeK1d6)UCP4lV20Z;||(q-Bwfia$_ezs^H?iT`CaX@jAl7%v)8TzqhZ6lxZ8ma}Yc7S7O0B|SD5T=9(r8+35 zLG5O?u>k##?;Zr|E(9nC5cz?6(XyGDfGj0Q7ax9BhBPlNR7^%{xc#U)*Af9(eDz|g z%yaAxB|FGQrnJYsXzt$Y#|Ad}^5)B##eAh$ka0f04>h$%iuskwgxJdsEe5h#Zc(!u z{TM=~b0{N1SOvPPAMoF5a|`$Inb--R}E zA#*4P&a|at7J}nJiuM_O#4)#7%+*oZmx_2st30RW1hj0cAHy2M61l1Ur`lE>Up+6^ z8(8U!>9}Sd_qs`f(&6-6;~1-~#xU+VJciakhrQ3F#TJ|BI?_4cXmg*~;-)y*wDOB9 zX8L^GDFSxgc^n(ARfIx&grt-19C5r3T zwJeo>c%FOq7_U}83o6A@L8haZI+`m}S=r2(vsHq$>OrrU?*$mDMzg@pt?D%MAPLAx zmaUbg;N^USg#gzZ;jt>wF;dx%;R^w+KMJ(`Ng1-?mn%wZNQp9PB}g@&g-D8KA2L$(3a+z!kZGv3vlR0jI|TYL`M+r>af;US%(4S7bpei(8q^_($OEC4&O#$qpY( zuhb{dTwpyqh}}lu@Pe2BN!C;)Gll@Z%F>3P4C18Kmv`3N`}xbJf@0Xo66p1ArPBpW4|=%?-;$*SLMqOBvr>|GxO{r%myXzxh6V zAAypAW)umIHrp|-G$&hp0s67i`FX<9cz!lpJ<<>iMpTNVH%Uzz=JWQGj*7@4r2>>->q|OHQwcA&{}019c^e_ zA+!f^#~pme=N(7oo2T`!?o-Bb~4ig*Kz8a5KZW(_#$)g04{qh&Z)mG z&b!#3qpjFd#+B!h?yFmPFs`mW!LKGaPMM!BcQ3R6Y(AD|tWja_y%_H+>u(Yl>}-A7&FnlJr0SUW_Riz8uinX#-) z1S|4m+RAZq;lw&~HpA<4Z9hLN5-TT88#nr;82ljKVM}Q}CUtgrKiqpMd;3bUwrjE# zdz?RwV`@_x`}Ev(?&RzayeH^#ec*AdEtx64W8m7U>T&LjaO3j+Afi5g;hJ_SO*ZU| zi4M?G)f9D>eTV(~migYCPb8rAS*h6*MQ9OPmu#;rIZF>F4?+6q1Dt~#=hCBQ85wdI~O24xl*6NVZdXy z?M9_DGzz?IQNrweEx?%!X0!vs@=`HXB{9Goxt8UtzNJ8AYB z8P>!Cf2CEiQjKfXlEx!I?c4XtpcY^~R!I(F>Y@v_e)wT7IV%IG0kdZ}gW~pQf$NIh zwQ53RFL9izL<%BeGI$Wz5{l5 z28ILlg!=+fFO?@WoYvWsA+2pP>9vDf+i@VTavzI&|3i+cDkHBq22YDY@8cuoxrO2-I@ zZB6`R0Ozeh-oc2{3xRiAsQ&Tz9|KUi{Ida=bOBZaZVKiVo)}6s=yp@oe3>+4)NmwkiTA(Zwl4D+8 zC$!wW83`a?Y?WCn`DEg1B^RhG8RDfLvv5xxs%EkHDKWwe1`XU6zhxr}I0i%=izV+jxP7q73BiTwibSxzcoVW;Tmz~|#7ZqP2%bx8Z?t!D)M3usUF zPlm0_oN?OJxw`q5qo=KoK9zg!^PD~B)^Q1W$KpkQ>|1;s+f&yb(|jt&9+T83Z*S{I z%HNjnoOC=ypWTjgEns#NQN3MP{_fpy%?e*^b$*>)N z*v~Lkn)Z4$gDO4gNs}EZ?WN8c7p}wDYP&8qFg9jAuc_U5Cn0bUDCfKPo!i%)EjMYONQ4*)J zV_J@h`|kfjPsxiw5Dx1*G6>k4`KzR+-Q=it; z#wH(wHng!d&^vG39v}O>Yl>k^f2rfP>*_H(n|y8`zcByMbu4~O_QZW2=Un_^I{JNm zYGW_+)HjaXTsf&e=VyPYvnnzC-|#U&T)~c=>`ITY(5+H-$v{=gY?zn= zH%GJo)d17YTv^P@gx!Lg3XC1A*27x#w&?QzQ<6n2fHjnS5M-&0=k@AV)=~ChCR*w{ z>vkk72Ws2ue52AE!|@9NuH#}ozYWhY0MwF4mUVa`V3_DU=qwfs1(M%h1QH9_WMVDm zYQto*W(9gq1Rl$5>%$fxTQaA~Dh4cNAjs~;P0S{P8bH{Nkx37LHRU2C3*q)MJuF9bh(r=0Rru5NbM)Cu;$|$`-zwiJVxF5ll9+JR(CG zFj+Eqk>iBYBnv=bGOeXr`bP>j*~q*s5kOcb7&v_;V3@ktl-~g8o!{$+0D<4v*DC)Z z(DzQYo&m{UelfMIaN4WH*#kUe?4~A_f zZYycelZb2N^4kS2!XrjMhxt=yB2y~2hV0rd@F0-JBmH6-)`L2W({{$QdE)GRNm(=J z$7!2&9g2kh@#oy39buf0E+KW#vC%w8$1kVr)V9NMXpZ3_J^m{8`p;%aGkYs@Ed&k2 zZ(R-jt&}_}Q7$OwaNtsMm?izX|TRyAl7J;K{VkdDWh_|1T zow#FS*00>Xux|a$t%0#TKgP7meD#hFdvu*QZo#JLHnX#)Ya&31!)XIh7h^Zd{g0oY zEOE08Y&;g{c&=>&K9_eoH!<3Si{=N+a8glXwtr5GoNz>H{u&S}WOFqBvKY?3Ra);m zA*^?*?|Uy0mx)cbuz&E>n!XqK zdRq*q|1460u`B5?1Q5=F9B+);Y>RNHIn{`>NW!mLq}-~ z08@*O2mqR)r3A!C0Ia=OP`dc#5rC6SWHO=$dYRx>V6B19ma!XT}0B8*!*~`?kUfeGgBeJ9wJLw%uAihu%!$5P%Cvh8kN{WzO zPVMIh@lmqFfO5$rBYLC^^^HLF+q+O_dZDc4xd7-_Z)S=?sBOJo%P+~Y7YYij-spUz zn$P2jf~Eh6&y_&oaFHsON>c`YpS`5c_ChZwd?>C3O5f=7aIZLPqFHvfKxRst3{`ez zJP`n9K$*XwEIIfS>S#-_ZK6%R9IOBUKmbWZK~&a{4P=cIGfJj#VUA=QHM#FUDi-pU zMRKTmA8)=1V3JEa>n|oq*E@QUpc;An;^1NHpUCw zD7$enwBCY@*zPXZMa;KXS(S-3HI~+_5qnAJJn}itK3*$!?G1LG(+-!F#xB^gj}-m& zW3i`md42cbg;GS8FtvqM2pF(;cvEZ_@AdMzGE-NeBek4Ila~T&@06)4U{k}2(N=$% zdeLiMJ}4P_ap8JRX0wjRQ&9;~90=f5z31Whwc1qsdGVvb?H`J@%0cjwyn(WS2h@I+ zUm^1r7&_#^3J(@w%PRo^Z)KjcZEY{zWJGfgj=kdLh`Kuo9}l21$Kv%M0{q%0Unzw_ zZRmyc7kYW#P~fhW{{TdnTwY8F@Jg0)kSYIEB7<@p23P}J3)BTW?}rAl+C)P=t!5E}vc0mOMpBALlc$(pLn2c=4G zzEEvvWkr)6P4+dVO_&?21$C`M$r5~=U6b*w+SBhB*3XL?DdQ4igStm$8E80?~mK+Xk(f_$4w9V z2X^>{k|=6xTaNBPhsowbtX}jn9ddGZyL_kR+>d=uB&I+5X>2#Pxy=; z)v#3fU6%V?VT?}@e&Uw1-Sx(C@1^Fs3|3;iJz$oh^Jk#=o|TZ>X*}M!{>NIFz;{E zCGIlPFyz|PFI82Z`f+OJOQhgJ`BftH@KnltqC40zxbB3$*&1tJ98SGuWr0@ zJn;pVt^u(w*MA_x$3GcJx8;DoLjy@c{k9%_e9<`L)@ck%y>ZNrxz2d__@rYK%RzK{ z-px&7S%3X>lhvL&^$E_{=Q>F_zQB7vAAG9&7@1B#$rwibb8A+xt7LGY*2s70Fo)ZY zUER8oF(=zpov-a;<+1j8=z{mMwDz|U<@^V=SmvIxLm?Tn@Z}-ua;=0W#h2lf+W~JGPYfzpunv-LAbYw0NsiR{SqX zor6BZw!B??r;n!|ZK=zqYbVIH?fLgDA+|sc;hC9d77)Q;tBF7u0Ajfr<`#`b_>vbo zO|K52t~?5ryt$o~G0t=-DOajgQ<*XwfY2BQXuMq93XqfvA-W9gNLkB5nXP{fP-_4R zFth+zp^rm>ki+2%0k6uA#&DIB*z%&nu|R9pksge{k;Tf&o&Qb1^UsnidI6CDzagty z0g*RkEc5a{o1Azml$D3sIbgNH&;Yy;stU5&?4^UU9s2=H0XONV^oEGcWzGj=zGV$i4*K!UTkO)ZmZH8 zfal9QYB`7UC&cVtFCzrJzkYS43}dp4<;!qcy#G-z8dRq9Sg7(qb^~tXphZUXb7eYH zx?`+b-Au_Xr9FtNWHKMIlhPmdQbomMq0%B7%bX4+L>7txhR$w6xej~?i>BHokL>e} z{G^P>O7jGunwK#GX1{zjDL$-~dHqrC;-6l>QCW}wQvCez=i$1cwzc=N8)L{XKMD|TJWAEzs4|RrZ=)0(h6P7a|yfZY>3fU_q(d^ z_vh|1p4OApu|9fx zi{c6! zeC#MR#IN1jL&VxftvXLS8g*fEzm|Yk*~~BIMmVj0C-=sZt|Y15K^{jlJ@>vyY^|(w zbx;*T_#t0VsBsK%`dVqPe=@+4tYmvJgC0CLs_)E;2dUo-Xj^Or5GoUPt+ETl?Y**} zUt8vMF`%7*sl3BT+0N_tI##`kf%^4tt;}b#o2f4hFl?fPbW)arj9#k+4X!T3CR1e| zi~8aA!5)19XUVFi{{*d8fv#Bekb8jPJ{MlQBbR0|s2Yk|G!<{w$tG>)WKglZWn zyLnxx;&$=kOTDmAvlXCxp_ePZ|LMMX{neFh6u;~MbbkNCqIjkELkfZxtul~j_{_2N z$WDuyWK9GD3nf+^>9Kx*pwx$6Oei9Lq`@sv}J) z5XQ^muh{X!0>^9l41x@34*)%%2k@L9C;+c1RWb${SB>h))vIDO|EJ>P{l6ElUMSwu zZ{uMWOZ+q6xSkY_XS21n<=FR%$8_k}jhh%h<<>T)O*Yp3BKR1-9h;-a{5!Qjj!Vwp zWAbDEU&`^P;&aZLeoJhn+RF^nByn!7XPlm*XPWv|T^?RsW zpPQ3D%vh82->(--Hi1U}`fO%zvPi`Kx~;x%(h>7HU9Qdh&b>d36Hn6m*Do}^%u8+X zaq1O6`6|vDY198 z*j$n89WD2#@*nE970R@`Y>(?9EoA$76XJ26i<4`lvW!{0TLz^h!=7}V3->bGPmB9G z#=e_3fjU11?IpGK8$CYmjnXTF)JR#|*_1W+Wmj0H&~RjFSfPsF>*tEVyp7f@bNg6+nblMZ*7j}o{ppL>#g7} zpvc`if;Rie0Wd_i7<{(dOXOTU|E)Rd3*3i)eJmkGc8G1zCpfOxmK%516Go$59LK}y zmjVSp2zdNKfT*&S0hnbZj#3+XDWH+e-{Dv<72K$fuU;~^6=(~}IvB}(0i|q1mD`|3 z^j63){L~%>cpelVg)vg1<2z--iWWdo{TLgvlr^mtWy6Ri2XJ+uY-2W@T#<2XAY`zI z(OL;KwUQ3X)VAzp(W$D2UY-c}Np>y3wj{|mKvXtrG&A^ssbu&9y4nj00d_Si%E49k zt^ShrExPeoIt1d5rf=l8e4qcII5AwBbdrtH;KF3{k~Pgs4=rfwB1@PIYk>Wm8?uH4 z&WdJ8R=AvB3W$c+UTP@csAY{UfUSnrN~HY&SZK&s2=oOpXn8L z3gA_IKfaq6AAWq$3k9bFzzfxM);P(u2W1)Gybi!Qfc%Kk9)Q-j%8r(85BGWr;B0Cc z+I-=_f1au-b264~cX#1skod(55$#6{22!>bs+d7NZ2))5m(aIT-D^S*z>V+3Z6V-U zze~t3%_{-i{0O0URTgvEO?Empu0K8$#Y@?-nE$4jF8?Gy36z>gHQa(^^*- z!=f7oy6Ws`t-B?m-LHE&msgN}S;#9}Nv%SyN_H&>0)iYi% zJ_sb78-Q4hZUhjLu}m2YE$#A{de2)GB;F7KWgr_sN+gthJWyGO@$`kjRI*$jitYSP zw*wYe+*7ve>VtvTR-!|tIErPEO(@CkDc?y7CSO-EbNBS(4aSW(wI@K`{ z=z2Y)elah|6X+{InAhl$#SB0?Q~l}bbtoHQSRE;iKf7HGZJO8}|cd?i2}9DSlavF1SSKh4>ec3hPF&LFdDyj zsbQEWilOX5?r@|GYUx|dmntE$R;K$>x)hT)qvFNQo8sf`kHy!oZ}sB7idmlovfT*+Un?Yvir6*-PF6v?7gseRn(T=Ctc4kV?HjvZ&Ldh zW7VYpvHMQ-*N4Q1sLuYn>!2+^a-EiSjjfIC(8Rji%z+^Nnf^T%=o+)z#Hxn%v}U)r z96TS7(}Xr~`y`RxbFYcWvrRhqdB4v(3C~VW=|&$pbrEp)>-yWYhq)sE+7O?C<}v0b z(=e0}3kJ81`OY@hk_SR2`cfWxg%&R|9{{#*lgHvoYc=jj;P(*<6*i zr>(ZnoazVWO_!Yb|D z%6wzG=bpEr-}wxaZ}95AgKRsbFOuhEr|%KkPPpN;Q?heOMU`m-0h`;6YBdWWT`E&p z(4ri-edyK0^UxqZd5MeN^UhxM}kp@ekwD8DDt2eBy=%H#yQ;LDlNG~fK zD4QDO60n(a6id}J1{@x#a5A7b8M78WA(UGk18(BVNFy*7NIR0PWG0gh3{VU0L?t08 z|A0Jd8m|QKVn4v`S|6J{vJ3vP^pMpH;A&u}Mv;2W97~C1?OMjJGJUstnc`-7D|*$& zmc4{^td|e2rE@HOfU1Dm;pKz^WTj)Sm-yY@DpqQ@I@96`S;0~$A9$%CwVTH(wXs|( z9auu~pAA4AACO7u3*RWq0ig}6Y+MPzwd`%tktNL_uB^O=UJ|Gp&M);s!nI_-|5-l> zD2CT>rp23Y1V&38+1Ci-<%C>JufI}uyY#Dcx3$abkg+UZ0#K~@OUFc6*2I{S8k7@Z-SvO7sfF$Ddb< zt>Sg_&&5w4X6g?Y+EzA&E$scQY<$)`O!v>THngR2z96?Ge>5_WalAiOd8c_6Jp`Sq z_Y(ZM8`dTC#4_zcfyIm1q@S3u!|_gL2cDGFWAV&~EH(R+Id45iPcQ(w_3aA39nNV3n+T7vju6c93Z9 zry~(k+Ul}=?^JE(cFNYe?e6{(baigHlOQ4 zJd+%rC-rq}x25qhA6n>YeNWtJsy}F2o9nr{_n7b9*-8#u@p^Ob8N=+i5JzzibLaTA zo}FWR;jNC&A)}R|3lm+^50{-@AE`@Dm$vsmKRyRsb~JswsrA0SmLL)ohfnP9G5y5O zKTU=mlhef8sV}3*eCisEBTCzfBbLzS=;@1NT~(Rms8ZCO#p6LZG9jsB?cN>g7wvA+Eu1_o0{ zcr0XbEr40juz|rS1-xA;<9VbP>;W)?5L$T(MN5#RmFpM^ARdgBQOy@7GN%_Gb*wg( z*I+QgT!C6A0#rktUuqqL0jmbG07(E?AlH$UQIUgs)b?US=>+fubRCQUr}+ec6wnjE z8UT8wG8hsiFjW-#$qF58fUVew4V0*`n#__-ZDV2S#r@sfWKWfuyB(_Twc@xCNGhB> z_^90Atw3Uc?3prwt^TuK;s|g~`H>lQjH%-+y~rf17}Ya@w`79z>x5-pOCZ_L_sagg zeyI#XzU^X0Jk>HE1}2B=!h;vLEUv1C(5Lq7vpM)XAW zs)q*hOTL1_cn?K4QcY>%!I;1cIF6rk#KvL=_A|Hx<*QS_TflP&gsjz^0odjThL7)7 z21FAB``KYO6PW!E#Sd@)qxkkK{TLF4P;P6i6f4Faf9(&k>#;RGTru|9EqU^5l9A)@ zN}tN;%J1~#Ss#1Ojhv0K{HA{Q z#cBLJ=J#W+o4U>U>-zh)?{(H)P~9c(W!CT}-ct%8S7)sX|tt3Y0MTM+f5}E*u}O6hYxLNXStcU4u&|4!hdQiuKnWhYYyTyYn;AVLH{>WoUR>XxiLQqdGb?#|RE}xvl+U zRn_V88hwfTHm%j!SBBf+{KsNgXGh#_viy4tw#A)Cjv33?29u>fePVCl{oFC^hy`ct zNOfmOjx;rUdZ^y#M7caEQWXi&$uZk8%&!BE(Z%_I+Mfmws%@_hlKj?+=#aI}KHAeY zGUcx0P3h9}nC~#Q`xvBrea>_8>*Tbi^?On&C>_68ewdp~i0ZWv#~`L}K8_}q5$-33 z9VOjOI`DPR9b)df&DyfFNruy>dz*@hEh!D>X}n=E-3f6@=iOW(J14NWyasqTT)f5O z6St?w`>~Q($#XHj)~58t#;*~NQyf$1<7Y- zkzT(Xx4Jpg5p4!0^;;9tspEgBkF1 zqH-5&0lQOxYEerZl9U0i@)&myR)d*IPHp3vGJy?T77gV(0MG%xg;uM6w&Ejjb*LLJUqRJ|bG1iL^U%XbTc>$r06zfof8c-at8(%0vGL$ZUFc`~!vgW4(;n&oOmd&&? zm&lr)sC}u@AXe8|ndI^s7d3SxU-4!75a$;HBEa|(CN|%Pv+7@kz0>A5gHB9GiR+ADmQ*+Q1^?LmJe+m= z3$M%R#`&50ml%QCxllJfGeM}6amqZ+sQKLVD~NzUfcJ5gd3dQgcbR)Y-pf;Bke66( z?Qm&}5$eNxQdQ#VXP3M$?bfIGTS<$Dfb);}<>Pn`n|JX#eOQl52)f$Gg&{mPbjRjW zdVLWy+I)Z1=$-`CxP}KY+IIF3hL#s+I?l%0fj@PDn3{daXtA|b)ihS|<+xMncI+DZ zAME1F`ju5C*)9F;Z$%U5WtIDUvlpu}uUgD4=V`LKZ(q~(mConBIv$J9*9-K8Yq*2v zhKnM7e(`f37u$=!Z@hw>789X^JLe(=P)s+QwmmPCjJUAIrAq3~wPsj_gYw=`M|RHk zzT>*LA?LOC(sw+VAaZO_JDBWk)^X%dg;wfZMiZOQb7+JPn(n$-2o4LjMXne=25;2X zr0*&E#sdL_LqLiQ1eUT zfHD!v${kF`9l~KwUR-MjP*u-sCG2N#mn`m@BAQubs`&uJqx&`+{4mdI5<^_ z2ox=ZG*gBHK=k^Wg^Uu{DnarPGN1v>sbhTe>dH!WYy{Nv3-XYcDsn->K%H!baZuKj z1|+3Cg?0v(127XW?AnOdGP2b+*2@E-MWFtwS0DYc+SkhDo?T;fh#$3?d8wjho2xB= zzQj^YQZ@e=UKBXy#{>Q2rHlaVfW%wL2e5wqMlT{1z5NIv{eZ_-4n)B5wSepSgKA`d z5J;?29@L!Ps)jV+HundB zku6Js$`2pr#r5x$885!gk_>Bs=90S<=#I?*(D0M#&7gk&)4KTmH{TR*zb}gSAOBdq zexW#v+YFLTxXxeVc5EZAUA8)UhWqn&{s`19uBU3M8^>6#(>%r4o~jdmw?Fo&%kil; zbkX&cwC-mbFK_Q^o88(sFD3uJcR>GSVU- zLv60LxV#SRj7;fkGBNJM>+#K9w?1yi)|bfq-C;U*=jeDcn0mMe>;2e`-q%= z-9C_q(miH%av6tXG|rp)`Hdo8uoSi!zH5ciw|3E|9;C`j;&F5}sj;gKh*e~4h z^&ys-ZHozNj+<=2)RNgx#o(@*LZD77;R+*8*|@ZOu^u&07FzK1?QiNI&Fbn%s!4`PEn)}Cx-nI$@_I$P1wi_6%(2>K7g^^Ud*Pn|TuNaW_AeWl4Wd~| z&qOb4#Aa$Y6UPlavXw08d)29?Gzb~YfbxLt3=n##ZH*pkMXMl&#dIQ&oR=bQr03O_ zQ@zYk+2eX?;Os@n{ke&&P}Y4bgE z?HpShIj-{@`Ys{kvFu~cXcAq*g#($43+m$W@wNlvng-`h5IEkBelG;_j{;18lEKRG9lsDLE6`aUaec1^5*7kM2Ll1i%7Wgi zjxXgVsBg?Ro_?#$)}I70J_s}f5T+jTg8~3ZtHDwr4XzcaC#l&e(jE>;SN?$&Tj5hSVVzK)l|lcNrN`DCS2SA=GbDJ2=?*tobh(=@CF3D2?&|CSp5a?`HX?&)$t;S5HIFxBjcJxF( z0*tFFhvbBkAWB{~zX1D{XfD|*t)Z;w*|pkb!UHgn)! z;B@g(KZFQ`M&?)F{=WG5c3a$R|7!j-evF8QwX!KRyUj0@xNX;Yt zyKBzTm7N|p=1h)tC!^&-*STkUeIaV8<5MJK2+k4T=kEKZ*H7ka*E#lBG4pHbxW0Z9 z9qwsl|%2Vg$y3e`3yp;Z}j7x4= zZ$Ag?b+?sKe;w|}-eVGL^RcV6)lHk4#-G>wxYN=a}N^{*eef_b`;grEe%+}O}#x64wN(|8QJ*q65@w7T3T zn!bH1mi(^*O9eg-u2n@sB{BwACM%TYAj?^SrWP|LI0S}zo#(;urRbCuyZRt7^w{cqCAh*WU?%U9ME};rd&Z zc@X$1VGEV?SWzZIyjxyEDBGA68)Yq{Z8e}Q2Vkx0F0YjF4B)MQ{6|*pM8GV-HT8u< zZR_x|ytRA-qy`A4{xP7cmA{ZZyyQ=BUX6|ux~(M09Z^Qe)f_-#p*#mkTFgP zi}hNuR9OtUppDFEfb38o+9o7F4#;m_mI(dudaIZIJy>S)@Qvy@8(6N9k$gl96l|in zR`&S45aAioq)XNbCdh;Sk)ucAV z<>&pa0BdDB|K=aAijO}pi!Z*N8OTmXHS=L4pqv*n!f}5qAX_@f!ngc%{orwXuM#QW z{6q1VKiw9;|N6be%0h@k2z3y)a!sT;T*KaRhCS*|-55h`^gZ%8_Ra10^K>86rhV!8 zFImyMV9gcoiPGwnO6-Hh33&5eo4J#gAJ?O5%=5;V-E z>U^$JRnOcHT61$T_N&S%-*e%!fbh&3Y|LGYrmO7d8J&$fs?K8#J^#3jTrBHEX8oum zF58Jqx^F3wxjA)z)yvFz#fPgy|S$LX{I$3JlaH zmoH=G#%!vSV?eVLmixoD{56xXo&A*9)Wt71hbjgnyiqtRWSs2AAnK`%P1J7myKt0I z%>=hL<~v$Z$O-(Y^D$9tr#V}5bk(-fV118EpVMQyI@xYtIyapcKKr`s4dveXs?FiM zF+7L!Oq9+*%_)Baf{vUZJ=9-zIsX&o$S`rn?;K zSXO*o!+Fpg$ID{es5*4UJV9VECLJTEOWWb`z5|0gHK4a@u+L9#EyGli>TzP;#hbUs zZFVd|5VeFnOyQaZ2U@33<)-JUzTNepj=di)El}@bs_Vy!3Pjy@D7$w4sR5lJOoy9m zIP9r!ah{yKLoTTKFg8B!!G5m&Aj8|?j^o3+*`x)zHA8@?utB6PmZe9jQjajNvJV?r zf`I-4W-6M}KIe^J2I=u-kIl%;eZAiMBZ6`!VG{^SmDd8%PVd z0AqRN1I%79rerDmmLLFJ%Ske23xTEEmC8*3imKN0b}O)P^FkTAZ;HX<&j!e@q=T0S zZUr23FN%uWH}H!sQhmT!`a9=Jtrp!!(xvKT5{CRP2!cdF7G$TYe5g)iEI-Qu-A z(aJ7f%D5g1%-3wDb~72<5}}M`^+`YFIP@EXegM$#6as_^!{kcZ)VET3kTIn-WZ%8A zo@dwU*H~UDOZvmx2VUh^T;Gg~pMTWxjI3&X6z}_6fztrkvgxn?`cdPeG9W7baZ6c_ zZ#DnaGZA>r4;}Y7?ED_&(O@ZB6_sz00iQiRmwKY}ffE1b39_ap=f)K6361(Z*QT zjfszQM1GdTTnsLytGB_56D!)MyrnUX$2nb-+)tk#b^T8F*n7V(Kepa;>C444_*A_f z&!z7yauTCYSwqvgcozG52+7%zYwIEUBo;!=wa1F%Gvbn~kB{a4x^k^mH*#MPsuay< zIlpi)udQB|+lk$NTt|J%czRr7+33r)$23oU+{fpfUzhOHmXTyKr}vrI{`fu>$F;3( zDIGw9U03=p5O3A9xaYFR?rWW&r)c*j+vA&)MW55<`rW>+eJQ>DxGF{QcpQs4w8W*^ zzN(ZadsCY(bsqcZI?NA0d1m`+1kY#Oa~O-%8@u+?#xtJh#=eXAls`T1BCClumCIQ9 z8k8*0O=7Ii$C-I_z^hJ|8w;{^CvG^?vD<>U16wjWj7En08e?l^%t%}^d?sIxrE#nw z?cD5s__}cz+hA2!{m4Zom=;^py&+J9zjZN)Y5dq_3#79qi*0TasMq~;Uf&>aOLdVx%YY!l)EM%4B0E1ss6B<;txEF{S$~kQ2cRJ@dfT>%7 z)yRt&)1WB|~vso|_tT;Eg9gGZ#LUh20{_ViL^Cjg?+t>F?7EU=xA0BT-% zNUN3XV1SK8_KB8kV*p%qh0-2mD+5ephd^5}WC7440jdDZ{Iifr24FK;(JR@&1f9;t z27E)05@O9}bmC*8O$6|whms&$t4mE*IORl$VJPQeS|pe3>abB(B$O5b2ww{<-tbdM z**%gxUIs|1j;XSo@tICCx36xdDzyR5EAU-qJ$M=5TKlnNUkMau0xwibxLV znk&_pex))UWP;D7ld>XPjp>7GR}aPf`Zs?l{{AyRhA=>fTY9l^p0*uwJ+GE7_I9Ct zUS77h=2P_%yK|rRp0}fP9VvCqC6bao{`rZ9u{vDIWif%-w*b46{ z{AV4Lc*&2C&#L6OFnK-@JKO3?{!*#f85p2!PH488<&_l7FRi)UTA; zD**TY2LoOw)2UwO_rd_`_2O1H!(a~s>Jupdy&)|41r2?C2qKplr8F#qdaZ0{T~d}& zE%1?&6o9!~y*zNOI|(n-!{(X5Q!2H<8z!N-+kZI$^;X$}B*;=l&|QZIDGU1-qd@kT%W z*oH{zT4U<$3Sf6=rAO8R?3V)IQ8bl*)WeqRaPUVN$tt&^Y;bBsQv=*$rk6fa28Bs| zEBtw+mm>1=M8IZV21%(CfS;-KD2?7gV`^CAH>EVNe64tlfbb$LZ@49=zgrN zwJz=+JSBD9a`L^6xOds$>&nwb{;9O?7jg(yU9@64)7dk&r`r1%{B`4Xi5S%BdJI2v zI@})i2ma<5F4SgoF}l{=7Ps$0YR@Xx?;U3qdTC*0;QblnKwZc6^r>Qi-cMn>jG57e zQwE_f9<}4#BY1T>dWh=4a_B3}U2S2u@bNDf$ndTA7)93#&$vw+_WD?KwmoB@H$AuY z(sDKw=zTo8U8jCuaQ{4}ZT(F7oWE{gN1wevMrcLXF|5xB~ZJKAz>C%{;pNW91 zFW8y0KjG!{Q~wH(8_KvX7eilXV!BLd&eqiD8abvsJMPDrF4X`3v-hUUkt5BTUXn~E zw_3WtnT~lQ2GwysgDe=cR=e~3AemlPB_w?}tqBeFk(A*fwNMC2O#Yf6H$M^|dv%~Eg z5!Q=&YgZ$$#@CNo)PncB98rez4`nGI`AVSQuL3%il4_s2D32h}bn`c%i~=SV{ulL~ zoc~}yuTOtBKv59lVtMgFlF7qA!V=3Q+W+aZ2(qtkPS`tu8o`s?B#=G zBU>L@$@m4l1WXo)yT(rFWV6anF!?T2ih|M`fTtHKCqX+0fcML50y=H5Y-Q=2e?U|0 zVutIfZdE)a9nkj-OjfqxG84+oM3=IP&j6Jn67MT~iV<gC}l2F-ptvbth3UmUO(V6T6 zRJU;gpyuV1y2q`x0R8KCDt98CPXfM$X&b{wfx}PtWJ>D`3H4dVx}3fjSd4aRTrV`k z*0R?!s5PkR<%{Z7E$$~}GheDq2X&vB3y%Wpzx;Zy+SD(L_dhNdH_EW)g^}0q&K7s7 z6TN&)zTk#>2vRAYsZm>9VP{T?@m5EsK&xyx*e!dm9b^n zwf0v99Iu1@vbf&_o4K57BeXN3c|6BvN-p`Ax8?V!D77WkDGL5PO8a*R>K6OP)|XAe zE&Rvo9$RAmjB=lI8{GS2&JV_8EOuk|W3SuDb+nV1$9wuYO5+bIGj<>cJuc5>_;b+P z$7GE485mlz*ZFE=_Y- zzl=LD;gkb;LoHJ{cc&?hqp!m#b@?jL*IooDu=3^Pi-3wV0f%z(>{@gG3O$ksi8Frg(mm@*m&v?c+Zd=rcelO}K!K-;owO((4au?2RLMN7@$SJ?#sj4hPP zK&hk|4KD%&l(8Dp;->@%HILEzpqj=2w=V)-;Y+1J#4mc}!3ThiG%~FLf|(SmWMUtz zun~%9F;KR{{G|MdT}w7~q#N)Vu$xjFL>fDN@);OiA!BQO|0u$7wJ zWINlp@q+))FU2QNn;4y`R0h6mWY?p>Xc1cm`J=#PRtYa7#2$GseQRY`Yc@17;f0S2 z0mlSXk_@0n$Mri!SALu+EuGRROMQtT7ONAzydZvxi-75;7x|(uK|bBxE`I#*vt(rw}imIKClCXjxpBII$otuzpQ>>9t(h>W2Ry325kl4_T4atC2C^eA0pazzbM6cByO&2pEyNcm*?h8J;N8}aJ@}dq@Saw zMmx6VchObT%t+bFjg5X?;qmU>&b5b7_qk&3yd0l%HGL)PiyKW#Sc+fUx${M5pHGwH=t%~e zhf?Xfb~d}f$ecrN=TRamwH4T_O>uma{mnS_CV67_*!h`Ou50t$>FDR$b@?3he&lpA z8vQhm97#rb-8mcjTvx4)ez(hMy78N4i}U%}$I9>hoZI5<`PprnN}uyr@57kJ*jMS* zK|)ITAoi)Jv$Gs8r*k<;WQn$>%Q?Gq$^MeWj$_7EW=1Ild7IA}+M~a;Z_Qw?m^Pcw zjX<8RqZ`qtin@_zB+ZF#)Qm0T{66111Jd7c-MvIK77XH^sfs+s>ma~owscMWh*g@? zX(}4$jTShc%kn;_P4hJI8s^~y z&Dr_?M&&L^^k#d_2MlS|< zB45^xON>yaI2e z#dT`ms3`LJ=hYcttF5n6`|?gTdzY(ofra>@IV;HW^itrZ>Qf6y2Dqk{Fiu#IEU198l(|bWX^7@o;4vjgR(iQ0Uk;qS@{++P0mv%rp?2y% zQ)imeB`45HKJ~P*0k?QL;xGRoQ(1FIcG&o8@EIFd_? z_8DjhP>#2MAc1Z;a-zW=R!}VMCVR{S-N)Ff*?Z8rg z5_(fs68(cVpX2VWxBLB2TE~qIQLgh2dAb&t>u2i;xE%J04KpRV~06AeC#rZb0W!*9q7aSrahykabKy;Pk0h%Zor&0An(9udc-}T3&VsDr(?$ z^D;nyR~6|{*$CBWRtXLW?Hd@;Q(qY%)LtM6AgkU}0jU73V8|=LScx=04FZJ%KLxqs zqin)=0B6etm!r?hLOxR#@QFZkYB}>FKZQtk1KeUWTUeahNMHZ}KmbWZK~&;P?F|U8 zI2d>@9^ywW=;x+hbMjfmRa7(ggBA|+%T z^Wws#vYz>o0|0Cm5>Iu_3l5P(Kbhti*ODVO&ng%4qXJufPR4Ur$?O`JKd{u zarNP5@$=vQy0|?5e;1c4?2{W3B9p%RBaP8_@5XoI!1ow$=6U$M(BAYP>f?){^np1J zn#aYqM|I@)k#^3t%ZofNJGM=^MwauIo#J)DC#Ic(LuDG@f^~bQ2-O+E`5Bs|OdD`)}77UxLYtC5r zb-Cp7d5pI6lR4jyL6jEhVtal)uB%U7qNS~~V;HE!Gi;BN-PW=@>2r+JM82`>lC3@Z zY;n}&Cpj_B@wz8*(v7d#NyH*5`B7@i{@ZBujpB-7wG(Hr2bri2~^0pYSVcg&W?8Lln%#-p30B1q_8MnC#J=`P7D z_3Yl}CYdkI4JUr4j!8*-oa)gp-yeyu$GXH{YBReEMycpT;qbjRjX@;vb?0`a?B~uN zR8Rus#f|MTOI~y?_D#56UcUp(Ro!DiJ!R^yt_17}oU|-ey-@DtWzhP}T?(}{Fc&aZU?uHop*F%GhQiQm@)v!!F&+EZGF;ZZN7od*alv#Gns=TFKv50~w!B$d;BZ;OuA0K32D`PgNRZEzq171WKlSw(=ix z1K=MJ*Xl&e3VZ`_J{7P{JkV#h!YCUR9INZhnK+q`IxopAmZO)dgZ-#F&U`rmJ!L!2 zH4c>6coaxYJXfm4{Gg0!1D2(W#d9Hj*!ipp$y~+uSCt519CT;0EOVSV%O`B+g^{vA zb|{u)MT?B6lG#qVXXYaO??0|Av;E<$wvB0p)#Rwrs_ zM=9p%58sYv?5f0Hqj$ORl=k8p-yiKE0u#m*YUW7of06Zv=GfRBb9`pW<`0*{Z#5yH z(=I33EIailO!@$#hyGZ6r)hl~D%RyOY^}*;3fK0pew?=ry!xBZdc8>1g!hQbHzjdp z%C0r{lY3(7`yG_t)^YW(`hNHO;@uRwZR0sbM%3BtCjBP6OE`gd%$DnWnU9y>OJE*T z*TFgI*)}&K$F%EhahoQkPSwUIECAYSe;>pcFfaMB{iUvA(BnD z)iayl4S^4tnr0vP+u9SHw^+=UHqL-}kHI+QwB9H$45KjzS0)KFjH{tc(M>nVjo|ax z9NDJGeQ$=XB~R_$DEnL+TLU)!t-ak`X@xcyIr!XZ$tlbC%hlyy1aKh=hvEfCA`bU`x0ANBuR_#jVLKwi*o}4nhg8iQb9J5#i z9}5^gQ~3tUe=H2ZBy*M}&1W2(V~_yMX6;0PUM+Y9f;Pov#f6CDg@4pQMkW)8@)k>#w|KmJRwl9lckwM%PQ}W1XiN;;R!pgvP4+f% z#=d9iK2w2fD+wZd0PrcFK_+zq?v?E>o=53hNcIMhn=b@pyuPnkoYSXiK{&5fPx_aC zJ_xwh7Z$RSFEV(EAwWF@WR*XvuJ-DZ>}|~}#=^d)5eTn);tCMW`C0x0LIbd$t5nJ_ ze_t=&|M^USw}dH{C)Dm%ITe|+xR733%o_A3m2g?E{=B$*_;vB_>QRwYk8Jtl7)F3G z$Pd=d*Nzu<&E42@KYrKug>ju45B3H2lPSkrkJFS7KyPUiVM1SiOblPnX7_Jtf6;)f zvF2k&_WQh@xKU1BPrC%sOyHA!IA~*9@`0A4yJuae#@aj^&gW@+fhn5#i@j$5qw{H- zteQTzVi!;0CBE!UyYO$bxzt|`n@xriN55_C4~{-%cUOyDM^V3uyuLS9eW;6>)pKn5 zvy9n?cQoe=pC@xW4-V>ED%N z+J&r}YejjQmNqlv^z4g&v%8{w(oVZS>z&5WopIBF|1LHice)~0{Xn=LKSQ_A{6o%^ zHI`_m`;_c2{~muXb@LGY)wyN^QBk`uOFP<$E7xKHeEVRVJ&p&WZ%n zi62v{@#I$5)}&u0S#x8MWCTeP5bmmeZ0|_(&&Mc_`}n-tE**-Joy3Nt)R%gOzfTN! zOzqC+ah{v`Je02SYK)rlx3crc_2X@RCN0ztuE9sig03CNGLAsf{v9Hw8td|PDM0i^ zFBX*LD>7g~M^)~CsrY(wqw7b3oT{x$2?Qa7r>9Q>D?h0nG69!uY69d^zge#6#eaaHWEYEDE1hy3taeT-m7TE6WZeVS zVzbpcM!$eyE197<0m_o4oL-=q?4c9|04}khd5TCdfJL73Cd)^ucVhmb1T=eQr5W;WU|8VzxbB{ zins9cb7hQQUSr{EaVyaK{6fIH;FH_W6B~lpc%PVDk>&1xFy(XjlQKdXq(!&=EPhS)}#q&`*NQhsF z{^5=iDCf$C{_EoU_1_gUQ_mFpZ5^y+Ch2!_U}|aeVI7nXTOV?<)?ecJ-L^G>Wv@H> zain=yGM%-{QR+)Q+kd-^?UX}`(wxPXC^KFUCG6Ic>&T@r%1vnUu7lnm!?%MP_?3m} zQv{FF(Sy$_-`0UW$>_Q3sG7;kTB;k>5uPrwF@(xXe zFv_QG%F(wlaky=BqG=ny6AdHaD4f?SQ{Hpdhdrv-O4ga)}MniY_ct!nP z@9@fWH*uiXhDOY^yB$NWa!b#T0ZFpR_E&*kQ|%jwDK;b!y4ba2BeCZPXpbYsLcjGN zU@Uzc`@%5!-{xZKUUhsq?(%Sr2@VtI_AXD0? zUM{F23j$1)wkojsM75Ve9Z#2kw)>aoe=19rQW!!C1v~;S3q}-)fL;NkfQbOWX~P19 zm3V=WY&3>ttHH`i$Uw4$$#7;M&2Ws@51GMfGr%+$mHoTA5fJ)W@&s_JmBB&RtI$!d z5ds8i^`9k=zQ-o(Sn7387B%2_;^lu)10;nNfHN%y&{nsTvJ|?k4;pKfCo;jU@lWJF!X-uu-SCy+c7dZcTE3jJf zsW-jW%lFQe;Y~RYfO0adSq1n*3~lqq0bM_+9L5Da%1{P0zPMDz_r=NL^DoZ=yqAlc z4+%^r^ZG%IXNu|7jcP&*cz=~X7J&czOZ|OOgzpG1u z&-TRy4<^3AI8$adS=K4OE5=uI?oxm|Uv4}|C-FWND9sXBUI;{2ee$Qxl@FoKt@a^= zF&wtK(q-*8bJC796>RwIzTu_6m0J^XtakTfPx-7@cN0+)!ZNB6S;)+fn%p9-4^eZ@ zWqpl%!d-o29%w9P`4s}%&eK@}b|lVx^|@0TN7vvqeqTD5_;bGCa!*8U&Owy-#rt^O z6WH9K&&EB>=aQWzUTG$QF-ThQL=a@_HeA~`Fx$VT&eCl-!rFg%Op;7px3377IPHeXxJDO5k*W179$K6*1 zoMW?i+75}0fAKKIAt1lZjDd#OhAE-AMN$j+wWWw2ikR+vi52jQd_eZZkMOS4z@bE`8Ra zj3&n*elkD#?|zjeg*FdbXyUSDN2@=h^;_?cHpOeFtDj?xMyQ&}U$Qahsy%OVI_~G# zx>ar=uD*BWb|<~vcr_no?Q@K+nanWajQ9?G>%57wT_3$;OCb~A2@6*}n#?!bt$gs& zrF{lF+#`6a`<42fmCuHLaLJE zUpo#`5^ZSIS5Jt7yG7&U z1$}(nw6qfD*Uepb zHfkCj*=E8UXyK0-ax!39TWrBW(rByjK5&2h`m3bj48hgh0%g;}X zE7`kJ#`IcxQxbTZ`1bJe6=7P&e;GX_zMD3@zKH0G*G9_3`3)W#8ar4g@$hzUvvt(a zdfV7Jmp4ZHHT!JnXZUqATf_X#=ibqDzJcI;9?elXHiz;)zju9p_V!$F2)EXH-rK(~ z$`B81^ub@GX~b-k-`7^S(;ly#&N@tA>C7UgL`aQiDIUc1GiB#&~nqX-+!l*EwTv zJI+3tW}4z_uk|j-$;Y=8ALpN`cJx`+`qUGe#v!+&ZQGZ@I(f;Grm&p^!{@l&|gu2$3)A~I;QGZ{fQ}xfe-?0~2%pY9MKM#OWB%iw(HYUsE zI*rGk_6E^$lEK+|1|3bqz)zXvcF&C=BBQ=eHWI^KBx>w9#0d>~{b84j1?=&hM`I=e zk1z6|=f{uEi;fO=A`P_dmn7xL&-j|1SZ49~2?gFIH)YmnYRY z00lOI_6WB{3>eD-1U%;8LBl-*K{hZojLB$Cz33`-XvDcRhVzBmCS20#_) z%*zSU;V(;sAU)K0`C$T(0j`s7*@Bh*hNkV~Gun2G`(~q;Q;Pmf z`Yr`zFUg92cwM|#4ebB;^@qj(^p~FvBqvH~K4Sgu2Qq%oM|7WYD;=fw$XDX|qitRU zjx`VcgCYCp(GJ@*GwH|Fm#He5#fYaH6ize2~jy%HGAmee8jk{UiNBP~SiH(Xq`Zm+kwQ-^1r^+hk1b zuRQ*yvbS^wkW}VwX$`v{(Ki#;tPk3qrd5(%c>h2~Mt7`R^w)IbbLp767nOMTZ9ArJ zlO4ux7hBsg4|`-?P8VPwgO4Dvk5J++$u=IheTtHd2He3*{aDw&1Chy}e>A6PsHn};E-@87}{pV5_()be)yPsq98(t`+j1M7#z!N{%xSrIP@IH;B9AwgnAPUsE_%jv zO^!`P56%7gHbNM>&>LOw_ym&TrTYS}Uk?aNol)6Q|eO!`tE9lR@xk-(>zyB{Pq43eNZ(pAOKjExGB$>7PK7kL-0K{zeWD7Z?mk`YN5|87FXBehJW| zKJn=$(em=dmo;swH>|qUl!A~zC6FUmu~2--1}3tEfKZ=`79g1$z;CjQiKXcQ*w#Nv zXslJ+nK*3}+m*%x|5-vyWjF)kKT(=PHa;m!nwr;?$>61m#E^JUHyOLh@+S6IWMiv= z7ZXwz<3=xITua}TVuGKS0*)V5HiS5!cdZf_=K$K`wGt_|#hgelnaYZ#=r_uYKb0*X z-l_hxfO2%pb418TehG+{uZ*viA+fIl0ilUR0{Z#FK<3LYJ}jIqzI>+k_{rj$n%6qN zDr@?b`6YmRtt@M@uCG)|gsL!40@kTh{qDor;z8;t!2z%?T-$VCDD(UCPm1M+F9SX< z?(cuq7-5h4A<@O`2XONMh4-LG_Fy*I>@45OQ3^+?ZLT{^-$Te5=q<&2K=gGR#(cM! z93e1+_3$0dLE?4H;|SsInR^WRaSp}np5odKYh=p!hqYAN7u$Vys=S&#-Yz*MJ#9CE z)PFV~4m2mm^ts-t=R?Yxs&g7$7pz0rh>WzKbaLH}L&$eo+s-9jw9Q#>W3;~SOj#-= zTL-ekdUE>s=acK>bC`VuH==%wOFp}-(mB!(>}eP}y(I~|whceypa&areKosEnseIj zzSEcHW_Mqblecwp@}AxAdos7~ZNs>fG{^BQ_tTo@Oo^7Z!DoSipcK1&oN}_{UpGHX z`f{Fq@6oA03AO^az1Z)oH1MY-%wKaH{W<3IJCG+Tk(FY}-&FQarJNz$kBwC=-rIeT z^P}%WP~F;|x0Ua^dEos$`=q7@Iy(L2v&&**F6^l_IWT7l#k^fu0j(%Yy;}v6P{XQm*o1O2;EAQ?&V>~*~b6s6e|EjosNu=AA z>+f@y@6tRh_r=yjk6rTFEw?Gyn=RDY5J>o=07bnd?)06I(oZ@I@sqnR&;QU_OYQ7Z zpq@64=f}mX>i9DIcu0CyGax9214@EBQ-cz~5CAtBkbq|2YyqADljD&Vp)z0rpM`2} zl#R_JeWjYnsl3N3>5xFOYoU48OY;0-!#!Z{#RcGNDrv#X>rN;eA@KNFB^(3+3%pgW zYaM2*M#TVavb6ONFclsS!_S+UMff~BnvxXM!YDGo>`yYJ3prNSMDmmW z*sON_v+^ARgTZ*o+FmPrm`rcW5*JplCgA{p&nnR&U1p1R2I^N|O4p0hkD&qcJ<%_G z24n_20LbQ}7`Efar81>&KPkic{#n-oSB&uTAgkr#5d&^SiZ~$vbh3@-?x~+(-Yu{H+gvTuh2U zK7Ny!wh;ZMpRY64Mk!3i^;nzq;UQAwD=FBq?Q86bxz7APJy|Oc#2x)ib8|=LW3YuW zu8H@!-$D(y3y(7QF}ve9&mDDp?E|5eg`p%h1A zL_k9}ws#Y%TtYc+L&`C^Wf{@#@#|dnzVfQtCakL}?*Te;Kad6fv(#q2np?yVfB2XG0ho}HuGxKc z$xO|BZF--d?e8>*n$5!Z3T6;LwPTxH^Qm!6@SMq-0%RiBY=B2M*pT?5HHu4PSZuA? zdW?3T*Vt+{>WsSyK`U#7F1NP{QQGfus&S0=oEE9HgM#gHXePP%K6|5cLSvs~&pVId zHZc~r#zy8+et1)K!=HVT7JbXt(+>ige^DUU0u}$Hvp`+FknrU6qX0qGgI?-ofGUUa z^d#`vUH~X2LuVayP9-rwUJRI3DTzn2kd^A1vY-jGL^fH^7=U5}j}`v&^OXV2UPC(7 zWlp@Qb~Fn<4Y37CywnT^5T@2L3zkgmwZLe~X#mC|gRExi2d~Z~Uv>lFE`^8#`T_)# z5e!lby@BbH!?&@>;#;~j^RPqK+b0G3$^2Hljx z`19#MsADbg`C5Rp=$>UOfc5!O^`0$00^{YVSd@jYSWAXw#G^wMRvuIWgc{Slc!G@R z%?+hkRH{P&_SMyDarfzQv0C0OuBj8E!~VBm=}n6v0qfY|8@v(mi>E_&OkI!}Tluyk z-{uz_h5oW2wU$Pf)=%nbYc#fa3`x3I@*V_gCb%;XBMyOZLcbVu9f z_-iu6+&)wL=IwRoe!IQ0w2|jMQGA@UrGCaS($Bx18W*Qy9bxgFYG)hj`km3E=J%Y( zY?>0MoW|!;)M0vZ!knOR4@*s6fxI}b&fnI~9xSkuB(*V@8~vP#Q^Hhq&V2EVcV9cM zBB!fpn$_Ix`H5|t4mTD1ONS*1s-w1UiJ$RS1h}s@P|YOwn4_;}Zx$enX=7AAH(j0A zL{6f;wP9**+g3+vn{H0-e`wOBe-iI@lCHm%);5)8wB5xETs2AhpNxlr}{=efKq%U!hY?Y_Ac18LUaW1Zv>*F+!T$C5E=KozoD9!>2DzE+dWuI+*Xv*H@K6P`2^e<^QVF-vnst zeks^*^F^7@LPeiH3jkC$@`*}WD7zT2l}&^$HmVI}DJZjnBK@PrFJLU0vV0;yQOZs> z68ptHhn2(-5DE>M$N;SX)%5!Z1Pg`?1;FK+GqkGE5!xh6pd}f&0Kb5tfW0f(bt<6p zMK3m_1jc$TP*t{*T}^omE3qLyaAbmEy38)7hW;Us@FUc~XMl8MCvcUr9Qb*1LN+%e zg?{8HM$#$I)utZF0W9ame6IqT&t%h+fO_Oy3LplY_4?A5`z!H)*``-Amja6KA7!)t zrII8G@MU?ODoYqE<)y}gSSOp%yLiz!st)r?bdg4g!THDAyBd3hY}lKJgy)00?i)sUKi7V=GIhZh z3Qptg{uz(+@|1`#-$}Rt+6TR8kebN=ysuB7ev4-^g+)S{2mo9%PkC7IkGjVRkR=P4 zG8>ei0KF6#skBrkw1dxpgMRpjLCyv?i;vP2TmxQjD6P>Pw2=i2u<7*9Z@f~nkq_7e zp!+Ov^I4!c$Fsn2^H027BZq8VbO_s&t7HtPtY&;U6Cw==j(qH;{0C=%a-EdjtA5b~ zi;@{EZUe(DtGX%O0WZKj^7)uT3>bkIWhPTvg7_+VT5>j4x`J$HYCA6#bFPg+gGP2W zxc5ei&vb#W_qQsOBCz}Foj_j!w7lf6Au@m#0Q%9gu{AnmhpQ|GS>eQnZzNYL>p{5+ z!N#iNOqmY(rGMwjNPgWc7k6I-LQ_v#wC{gdDoc4O8Q6s{Iv9KX05tRGlaiNJ+C!Ph z0<9m#XD@aHy_UU~dU@dOCqQbIDG~pf;&mxt`qzI5loqH>?1{^>{JXO9ASr9xGRTRY zex9U(=m~bprqjJ9$J4THxblTi0v>&9&CTNo6 zbA>4ynh&DpP(r@u7}^pZE~nY9eN7)`8#{-gFln^oO+Uo%b8tG-?v#u}_i;sY{*4F5 z)E!_x?GgbSs_rAwE+V$@%o_P5%f_E)WjQFN;bBe2!#;bI2qVz}uk$&BYD_A7Fkf*c z6s;iiN1t2QBV%D*wgPlfd%k#k?qlRV)U}UeYySkn7Heai7wx2D9#7evaT(O!jKfft zvD>~=8j}={nwGA;WOcTTI^u8@$HvFYySu(5sbrtia`t)Kwbd=~EXIzuf3;0lpL+To z7P(c$tKW(w!?va1sXKJnkJ^dq}UH(kZ+dHo9GCSJx)%HLV zul@Cuwa;nYb*|QI_jb;HoqX-byEI?s*j=J^ee`XUA8GyCKYKZEDK-vd=KB5Fp<)5%-x`(&c`mD_%Q~wKpsXrZZaqWFi zWZT+ZRPFW-4JKoj%VCV2#HDT9*PSk%V|0-|(V@POiFZ-9?_X$4?YVCVb>Fsp)H40X zFHt2q{+#hSn+#5rKDNGIQeXEQBorKQ+=kAdjXx0i7ue8@ZIyP&o>ru>hQyX?33s&`yB3-m+*}uuFlg900+9tPCRh&AtX;#dgz4 zcJwP{NMwU-v2qpSeN+}Q;4<}yEz?<9&XnSyR0zN>GF?WJpMX}p#}9<15+1x5@WRS) zyb5%tj0Ax9s30k$1c-Q1`0~l|hl8fZfQWCiIzXrc?*z zK5Uj~GV(t|MtT_HGcB1nNMjJ;JP&0B%=@R{z3mYdyMaBeX|R`^$Fhoj(m>zme`aW9sY6 z&r^oPX?>5Mqi>4*?}6q}J|9ZXTgD8Vee83+J~#6AL*KK1>=wI@Z+98V8+;`WHJ^90 zFOL)-q|(OWdTO!HIZO9x?DqR`b{M}()*Ys;wDliCavp!b@5v6&sBYwX6q9a>vL|uA zEz!2QVC#eVtGM*ykquH>N1}M6XJkD}acvBZ>5oh2Q67hV)@a?feox<)&==+)t)(@e z+Z(v6m${s6GHU(N?tS9a8>uL?@gO$EYq}$EyasgR00Ai-^jn`XHS>BOCrV1Z&Brks zFTHtO`8@51lcx3Lt(>EmUAxQL>GOCe<|tQq#ZR;x|Dtb#hDMLr9j~3q;??W$&j(#h z6Ri{F?V1njB6+gu&91iQh_XInUms~z~v+#P*LR&$Z8cO zf=>h%k}Zrk39QwnIslMyHvKdJsp!eTh8Lg_8V8yws{zQ3Oxif$Jv$`>ImxFC2AR$D z1Hke?vlsgbSf=y_b_wn@9afq{w1DblJ$q371E&FX4MjrNYPi8V;G36_$|3PLpb^;p9Vgq}{u9pWY3!N6l zgnGy4q9==kIK2v>-KY&Z-Reg^O6`?^Z7^aczJ3dr77YNqP5|I5m8(!7R3BS<6xU1D zO$NXxE`;}#8q)%Vp9H9{)W8OIDIj`t2GF0tflGnNOZoHjPpS)k_peSAewU#7dy_THrS&T>#$A7Jzm1%3Z#!SgthV6boL$NL;Dm{ctBc zl==Pfujh+je^SX9=9PxxRTIfc#Gi!Vf=uT--kVs(R4W>$Y%+ zLFeXR9@*v^77K01I*uv3YIE?drLj5G-W}|MvC5Aa&QgC)|HnN`qCW>qL->1*Qx=-v zYZxZ`9p_T*gCyI*Tg|bmE*aqaL$rCaqZPuUtp4(^Sa-~STt^;K0X#~ zoiz>JxMVRYOd9?VDR|>Lb^eU|D7XF@%!D(ibdh{Hin^lE=zC5;EWDrbsIAGIuXgXU zV*iwQJ71la&uzQS#~s_=|NU(IVXf>VduWUMn0k6T z8{#?ooOXO(mIGPRJ+yazi^-Kr524w%gR;24&9`D2$G|L%ZXS9b+i8-vQ8Dg54&7Sq z#?AX&j??7rslRWxx4ORSye_-Xs0{HSGL`u-HXt?8ZHj-7S69)nKmm|tD|Qk>oH zj(d3JJWk8lxsGled0xyIhh~w~dHTWw zC(0!;-SSh&a% zAANBSI&JA(<1f+f(>A8Z{cF!b5o6GfNo>y=;Z1Wj5P_TNNTeA<>T2g(Z9bluVB+2| zw5M*pw@ni+VjN03OH;;7F#Vko#@pQvG}Fb8*X53L*>wzkN@9H8ao5o%W%RkMCAEr8 zjljV(y%6x^M47p-cLG5L&H_G*8lZMV`3+H0%Q%e*dA$I=2_PjX23`Z83h;eZW-J-2 z7)Tk3mB3m+PQX+S09VXllBc@K3_cRkl1g&WXY@u&o#a%S0}&g&7?B#qL8UkJvy~>8E@NdQUAQTmY{6%s1jIc~;jNyBc}euog(n zf!!Q{tU_K5c;%kD$M(WOaS7`+-HZqEq+T*FSVSoRInXyUk$=8otmKnob*q>5U0f|K zcIUE*F9Kdwo`aVp%0TlIQ2$XtJfJk?Kmg3sSScpDzQ0vQ@}H<>t#-c9AXf4jyQz2m z<&$29s4^L3Ph*#Qlz|Oku1s!aLIa8u=Z!#S%CFqtDwA4eH?FR!Bdxh7fO)xEDC_xb zar=wFWchR|p6hp~i(AE&IIUEUgKX%3{Pzbd&+)JS%a!7Eqgv1(7uVe zt#eX@_8BTSHwg1BCw+Vw8nIPJE_=qkt*ds^8|t1RDv|0z&a>2y-1g+7_l}%95hFz9 zF4xgt!}A5YoU)eGS{&OkhHtPhM#kr2%}kF8vE9dams8X8*6sWFau8d?AjC27&&53* zq7CHzuE^Z$`3`Q1&hBG(7mPIDx~Tbh=lj!iZJq4&B`uauSqmWzBN`Z^u!~~j}$BfL0 z5ZG6h$-p|mMP6(Nn3?J%Q({70l1y0&-`viWs_HeIF%f{;vVp}%35k1U+yaJP-T+7n z$ixoG_l*rO6IXp92-mi(UVuZgkgX(#WIi`wGjssbIC!T1G>a*30dSh!H^5n7IunUf z5&-LTy$bBy06xn$vr)h({0Wdxr8W#`74fTVMvH;H5(E%!c8h^*WwOAjaZI-GlI(J2 z{Q`FyAgn*s0Md~~i3a;94Cs`gUV3cJ*)-rssxE- zF<#dygF$JHWV-xN363WLwTz=eAiMAt#{~}G0@y3NT7WKqF!lp%Q?L3}d;s{bDuF=; zHW|+MUsTgsHMX(m=Dk37#f}mh&&otTS9Y_!WPr_2&wgIwqneoyOTEkxbQ*cY_ewH; zRsH4gZ zgoN_uFDrr9stc`J(C;q)y!ib1W%2$>FC-+!Da?f0#x+fcrct_?AHMM>q6xoENEDoS z*wE}Sxy>aLu5h?co1Zo|&Rig#(bV&A2H#ZDB;Tfzt)HCF`v>hb$i+%Ha`O52@d?rn z1Uk#{cLkBu+qUtvbYmAq!9xb02g4qju3(P!mw0F+#0;gA4`aLY4$fInoM#}nSu|!vXG$wBHhJ9I&XEiB`5KWY z4P|*gxNRfi+AgQTe@Axv9H)SD-(69 zuepgyT;i^XHkM)ExW@jx&1p-sGq)_xiJU*D?ccX^qB2nC%RG^N1%*2wm!VjmbsWv_ zAwerG#yDO(f$Q-5)=#7tGXjhR$qQ<24_bG6izLeb8=v&_B zoV4e1ywA^0V|z!?S)NAqwdnFV5B547m%y(AYAA5M*$AiwFx9`8S6(_OuvZ5lr``-G z1pvEDa>`)vVnV=9Su4xQmZo;`-~auyN=@h`e5(C>FEH^>|E5w7pp_~e!T&n?A|TW< znn6!ZHsC4XEdZ%ZQKqraJlX*wsnbkWv|v=@0aV3Zn-rqKvQwi~)-<3rfVU{96%5TQ zZ30a>41iTTU@3px5J-Li>#4EKFaeSRK4XVjiI2+U#qZ?5K-xzEz)}MEdrJ1SWROLj zKw5Q(*UE<|W%>U0!7`Guh0N$P>46U6XdrvsHajt1Iv6ir z5UES45Wq|v)b%LfA5a}|_eK3w^SJqNy0{Y{y%7L?rZOSozzt;}tK5jnVo-jBv9&hM z2{NY*$d_EcKzLSG^NZ?)15}eezqxxdF#U9;J_YGTN>&1(DcAC{5tuHUo;3D=@BrD2 z)0zA{kxhINz;_<6x+eR2^F{HJ?#s)RN&hGy91xtzvC_*J?*ukq-RR2*WkVAat50o+ z`}yMb=`V{9m;Ww*7-PnJ3T)RhlLa6o_#|hHEDhd0J?N2P` zxko83HgV?)Ivo8jZX8bgAChd(`^s}5jzav965D@k*GJv|CI-u6&G5TD_AD%IEfAI3 z8(l7oTi>Imn8EtDXYUl^5t^O%#1WZOyLx+=_Sig39vf_&dX!?A_&e!IZ+Aac_BOgS zCfIs-+1qpbQ%&jTUBOV8H zeVf9w&9^bT57p;>O|f@OCvvtuHWLlcUHxqYeL-NdXToMs;I$?;2?0^?C-Ao`n&dSUgo|*6K#$>rRyopchzz@rMr49bM$_-t#3_Den-jN8{84VrCw%Kg zm;3H;nyzK$_?!HWJ3b@OgZw4+e^1XYq*z(X>)XZ2xn4r3@)h^HLukM(8xdlG_GHj&HD4h z;!fom{`x;(3(!{0V7(mh_SXm5b7?QZLk>WufxluRE7{6kXl0lj$pUOmnayOuDmxd1 zREK&@A4US=LI)nns>CK_o(a6PjZ5tYNCSWh(>xLKX)hiW1GS{BhO)^_z%}xjT!6v` z_5#Z5+9sUTW7{c$IMgL|iigTc2ynjuoW&6R2jFa()&Rqz(@Eeju{={r4?x^xAK*NG z>p#U^K(_3rzBT|d8Q>p&RQ+RtwD?MSkf#T9NS?~8JSsa{q75Jhu%)yHx-^Q52gRFg zXUbb#T>_Q(#sSd1fDO&$I!e;_C~bW0Wklkf4!1z#DWZPWq9A8EiRTpPzhe4!aXskZIxiwBq<#UKU2uCGWpp%MOl*Tw`jlSz1%9RBDJ#J(}B+1J-n zq4M<>#;`(Z&D-L!>$5lNY~#_|G#rFzN9hl%D6e?k$}aY{jrn}^x!Kw%N^gTHwzopE zix{KmM@F{SZC}SQetQX@$vid>59fdV4{*%Q_2hGgG0ev<=dmf%%$uBcW=Lj}A6g&s z;=G-0&=!V?%WG7MZ%t0rjz?ue9YUw;%FnIt(2=+0b1IM)FFXF>+$%Ed-IxMLT7a7stf>jSU0+(;IyLPPMU%by-L3kJn}Da_f}wciCHM6{o4bm95#< zT3JK`wvNticakYjl>tv+s>Q&@wxJ8;uC_hX#}5cmuBRo+E4qqJG|n^D8{_5@k?v;rS@2Lh zrepGGm*jr{06+jqL_t(yAGW&8*v@qx!?r(LU){4XNlNj~=0X(K3i|pNV`jM@c9zdR zqRdr4xD2<}oX;H^oyW)0d5D8fVm&3hip`8MNE~vMgGJ6-ai~L13 zq6KG)2ar=gEJ~Z533#UdF<>q)$wRKyHde-KO2^ltlV-qaN>be4t8|1)FkC2e_2PQD z`1qF#mElkptCfGiNrAiqGyxXj12pzCI{5)G9%8&3uM;T?a z5nvrZdEwwse|xex0p71vW@Dw79|~<&rZv1s;YWN5`uP=ePW7#IJ{KLC*#P7l{hVpe zetP^1tXmIXEL#a9fnxW^@$Ez)jv?3a{@bx~o+Zcd!DDj_*`wa^Wb&U;!aq9SBm(|P zLCVrIGY!wNbsuWp9ZJrz{XK;IWBaP@vUh~3(QD`Pf`10{j{M%+tLH{|uCq#C>44vV zDkMj5W?X7xledr2x}iCbw;%Jo?>HRFHXqB<`4Bpi+-#x;!KFClZTa}*{XF0JjKAwK zkT*k6ZrcpjgOl3dU5!+(S@LmD;iGfSSNm99AEvFgqA%*-%5X%2p^us$BfcNel!>Ah zJRReI+9Vq2;+S&3%MoP^3G4Rh$yIDHqiZS?Fb0yi(`8c$4dJ9s< zBt?HqQm4j%ErYFh^41Y2UP&>sq`aoT)NXz@a`VqcG?Rnp_OZrd25!ZKt)v zy42pqvEv)(nBR+z(oap2?~9#neM&6#`RQ|+`D=Nk1vjjcM$_)Hk?3P!(wkT>Ei3o^ zO5or-W#6j)^IGK}R@b6VFMHd(+zI48mqV0I&?4NZM2ANGR4C<{+Fz8(OeQWR9RNv} z%7VoafJR}e%BThuZXUMU7FLu^kSWM|_u)eIe;*~kse>#4@trbmAMT$d6A)Z{0)Nkx z-6}*@pryb`0i9$pw}%B=zZ4C!j&T^E^jVp)0OTBYtxqBVo`AH_q>N(G3haCm=t@rt z7LiTc7hvf5S*mlKUM46TaL$&321W8g{*eLLd?qj)P!`no1b`k&fz>DmFeTg9{+9yG z6}?gDn!3v87XZ&Ifu_nn7ay`D+*A=qW`LEpkSwyKpH&uvcuTtg+-J)ee|=GH zXNtw0GNNHmArO_arU9XGZKYbx_tZ<4-7A5@fW?c~Gs}dwmm{j?vnImg)2~m;fL6Q4 zhuHANfJW0YmnofbE>Ku*%CuK2jgjhTD~|GjYqGLm1b{O?&d9!&8e(&*`E#y*#=v4E zA1-b*4g$b8j{=`n!}{p~@IC?Ofd5PCOJAs6nc2LIkqm45vO_*FHY#NLqLB8v59N~G zU$d>|+b%Zcvf};s9;aR6@dr;A0meVhhpy)^S0&kOqwxduVKnV8rL>*Y_O23b-Gb&5 zT92T-S(f6}W834VZ!R`|VZ7sHmIQ_;9 zW8)LBZs6%m>#ictzSqUMX@(?yam~c>qTS+w%~?S5d$%#lO3^2l{`}hRUlURncd&CY z1{LY0k42gL70$WM%%klwaj7L6N^MRb&rTQTC~dACOKcyr6_Yr-af_0M*U^hoHqnq{ zdrN;FZKvNx;q%q!^EP_=i3h%Mc%0JR_TS7mi+O=Dj)6<6&Ck+Ybe>($w&w)9T!-0^ zezl@~-lPVP26ySYi)+g=xaedgoxZ`JehALA061PNkHj&$@9u&|N%QK1peubNMbCJ$6ix ziwO8QdOoSq<+|O8YW%mG2gWt!@OhGSkMX3we4Zv99UuB_>^nnjJv(@r+tl+JUNs=i zeMd|iPwUsO<}h8lj{S5wZ$mabNdY=Ko4~8!({K9HsW%?j1x-9+TQ_(5pLyea?E8uZ zhd){!z5p{7(F<&&5`ni74`P=_AY-)l6q~kZiwi!M#M8~;Y&Lp~C2VcWk!~yRlq;PcXfaLZe?XD0I#pk1%mSiIjm6jHDw!C*aAIH=pP3_E@eZI zXSJRcNw_G(VA;>=10-fr0D=Sl3VSu1&UB6Kl<6Qt)&N|&oPce4B3(9VB%AUdfT+CK z5V@vT@+`Ys`Vy!v8vw(_WbI9gEg(CkFziqa$;>`g#H*uuR6_S4O`r8RmxWB2yNl)E_BOa5Nv3wE#6ek%F! z@xnw}$r^|5QQ6qf%0fO@i4SaCU8s(zp=4 z=6W~^u?)TGa!z0~dG2SXwNqgOr?IO~iH`PbU5!nyQf#sq49wovUy?Zx(lj`2CNsM= zVkaGl@{Y*lZ3E&x8%jE2n~O((sonc)XXlwXpL{P$PtiuGWZ3?wy>rfC>&q9s+mQ(< z&2vci4Y&J|shfODxUhjoV9CZQf-Gi(xYt(xfKVzr+I{_*VsDSe2cR=Sfk;zOAMgMv{q#DqMSIU@j92yz41wCn*K>> zWjgna?fwuMO7-z{8owV>GXGkS)ScEMbp7@cXKJh3T4$Nmvya@KOOv!sC5o}idD7U8 z#%+u}7pHrlQ_&x^_{*MLoa^-QE{N(i@zU`94%e)mTt=ebZI~k0{c-*^au zpDW09ncn8IVTq5JydSz;P2_E_bKMP-%PWPJK7QzP-(B96bI#N1&_y11{O{;9V}mlM z@p#eYZ5>(1pXi4AI@(>Yk`8>bIAMPfmCtFPZ-Hd?rRj36HkjTp@0`cK#yyN}jk4a3 zuH66p-nnDk#%%1*7w1mrL0=(OMx!y~@HLR`blAzyH1Zyq>6sW zYp2;~OyI@8=taCnM^%1fH28)^14b@f0J6E20`sfs&_|={0j<&iyZ%NNpS4X^=)>;z zivY27a=TAi(D>oL#(PU>^o9xNC=YTHOWWsI6R0?6Js9lSoqe4m^C`|pt}TWk2Ix_{ z=`z;NDW`JTm|*M;Ns18y^rJQhk+G-c^$U|-{sjrS3yEJo>z-@NXV4A(CI&m3IJiww zqCdOf_l3rFH~%B`@Y-b$d#1Itc8a_)(-=wc%#TQG!J|ROLds9CUyk8#li!hOGJ(*; z74=$6XfPV$*%NMzDZaW~{`~jH#m&bDfp@CcE6Ovy&<(JW8oaM64Y2{_JV~JEnUK&_ z?^z2-U?-&$c&Xm%YGt6}S|v5exaCE0l#`%D1mG*$>@1G~hcSqS4nVyUkoj=8HsJJG zvK;g#Yx$LIR)LA<&5QMj%Co>)WC1Lac?>8nE(1Wx9tLCvIHpVnpsi(YOCMk}k!0z! z0I2_L1E4mtxouMsiOJP$WZ~IAng66XEF}v-lX4jnym*!j>^K+ju3JP|X7+^udKL;b zuQ^lynv7uj0Y%dy(wP_wJS7&$r32vpLa`(M0Oizv#{T75%8VxK8N0}iCjPt}lFBI{ z`?tS5*!Vqb){qI#OZiUqBEd_=7h1Bl9~C$*J+s-tryDA7uLT#@88b ze;o58;A1n5v)k88^e(G5KfFKkmfGldO>uk~^LQWSd@I+!=V5;>4ntc-t-rtGoZe}v zu(5dHhDs*2l*WTLd~uuH$6TQI=k59ZZ6EM^tqCHs!GN0i2^>nHDDGN_LN1TR&KdA$+9dUq>>_*0RR4=aJhmjd_F%Lf5N4MbG7Dxe=Awa`(^ zNG7Wm(0D1ZQD|QRfu)U1;H3ahvW#)X21)2^$|QajhkoQE9u8B_S16-pWXnGA<1>}VU>Y*+)H)_3m<-uerb7cEuoHljY-PfP)A;p5 z0cC8FsTO(xkRCi}0BKDqG-5ng&1!&k!1l#PIs|TFzu6`I1{jMGT`7|{*+kh1fN;bS z8~WfmRm@rm9Tki7JDj6i)}45$|kF7;wQ!1|RGZ)7jFUf*1(G)2lpCkuQd5EejJ zgaT`&UphDdW4){iF#vFX0-Tqgv&I*)(sgNNK`2{6xsemaZ>gHlPfyB@{&2qd^z);Q zAKA)n{pSXpW=u5#4R|e`8fO4@;t2pw&E{97PThQ%DAJEQljzOqR-YepY!8#^lRqjjzT9@O~w*|3vf5 zVj|}l%Z>bhdICh3nu`m4G4O+Id0-ec7WP|b5x(KkE#9)yZX+xA|Izw=M8I~PW{Q0u z7Wb`WPyANqqVLar3%k2rY#qqdHo#8c+d?P(uvhF$NmE_!F8<`ww(T=Heed2rARc&M zJg~T)y$85S<1$?k^gdS(8Lq*+oNm%=%GQ3H`DOOWkH^V`Wj%RojBnmI_1^uPO5f7i zfVOu>xj2M>M=|Uh6FPTYy7{I3FSao^kiDyX*R${WYw7ppivBt4gl(LUHB)Ag$ zkJ%Rc_O&bSZM)hGA4z1)&_j=T^mQNmj>Eg(nq*JpCqD#HY&{ZcA_!=d68pe%5|Qt# zem~0k)jP|vWj=V^#(n2mGjzh5B$|0z)-dGpmaTJJ`_SC^>h!+u%QIrWT(>!%iN`P` zDGI~($l1S!76>sN!zd10lHR=CeK$GCYh^Z~n^QiH%fJ4o%f+cM-p9KK)$0|&tC!%B znTvwu>b<~BmE%y2VKSOI0B;d;PS&n18G(Q6C;I?%d97N}MnhI{%3cOUbzb|xCxDO_ z6JjtRE@0vNA1{@$`?UC?n#0$sN6j;6d9G}0A*uiPZ=V+*{&Xo@1o{ebv@#kXk@_)k zRWk`&$;zeV1XDV_{0|!hWUGxC$~8K9v7Wy$ki1%e(e#2u$q)@dCiR;Ey8$+_RaQzL zevt94I|00^!Yun$)NTA%wMrA}U z1T0^v{`03_o>UK8f>0uLB2%1*fyTwY(9laP)%7T_9&nvfAJhd0#C}yRYX-tH_@&3b z1W2<|&GL2e!{XujCzX6ropC9!ja)IGb95U_;F|ftsrF%^C_AY6O<&H5A(LW{BW{=$ zb95tfh7D1MoASFJKMz7JhC7Z~DbB8HaGo%Zqw<(m>^sLgEH?eQTeFAu-rPKvO+jv} zGqlaUEXk<(IA3<+uzhiJ`BY?jgGORGX>Z>$-%4_7@nP!6H9gf$8+S2JTRKo-!Eoy= z*=mH}*ss!@Dfw=3X>2!uRBqcdM{Tt@8=Ww!{2-Zi*qT!v^^)vt#l~tzYosY(VlNC-xe4M!Yyf;^ zy7&YTe_30WH1R+-{;=>a1o#5 z`qThwXww%JX}+9UrhEdKUlc3AZsO02EoFx0fb0~Y{pGWOcf~rzQ#7*s+`h1&gos|q z$V&?Ol0ZIEUW5A4Wc8ET{r={3@kufIaQ#JXiak!7be$$1Y3_(vM#0Xw%#y(lDjxXS zA2qacx7lI(wh?_B{T5z}4n~Kcq7TK!c240@8>2EQB)cCrmfHpU9G|Ml*LzNHzHC>Q zv%SeZw)JCu07EB>=m_V=+JaLV$EmX9`K%b1V^*K?HRRvaW=F6=u#*Vo{w4mtZRLplAR6X#N_`%-Fm z;#HUbl&I|4-_Vr$y=`|JT*ACFXb5wuAC0~Y@FydJu$_2`Cb!qeg?Mw#^vu~^L_u7u zxMq*bhmfeJ$hT~j3)h&qx{*p+avv%}D$SB%|H+at$>Oy$Vnxj_r-|pWz0%~kTOLTA znP+}$van4J(A#*>s2c}ILDs5U2(sWeLG@pK{yug8F3D)^z?6o->56f~p&gGnUrdA< zD{ZEDzt17a!*2Ft&WMh&@O%0=oQCV3T}DW2I(mKHn4RqQ2;jC zyVRBdWCVP?R+j7C=f}nUCzYO1Chz6D)5Q<}a=Ey_eO4RoDi0zVKm7S>u~ydZxdFT{ z%GA9Vu!;V2y|`{AyYxrj3z@evK;SOG`KiEB3!KnOfJxfeQphp}87KG##!?=Gdrf}> zy*0pqu7KQDdP88kdJIgbGzLI5ej^j`lLASB%@ck1K)B%2-w#Wjs=O4>2%?I`NW4u8HZRGPoZk zmyBs@Ph-=GGS%&cgp&8=kxc7%i`$2v7w;}r-UAV`#Xl74_K#z31PCHWGal(C{hsz< zjU40G?T7YpvTv$)>;e2u8QK_AdU$>fJNJp(KD=MU*D;;#oVVwvFSap`x9?-lOo_Q! zuYF?I{T4pL5D&Xod)6FDzWM977+Rk>#K~htn_rIwiO(qhe~Q)UeH=PDP)4nO-BAp# z_&6alo*Sk46m=J+dE-{X&N8#RF@*UQSqJv*+29|_?e7NDr1iL zxY~3q%&Ry7qTs?N)$Tvv?67@XhE1`?VjANTjgdY3s;m^)h})aIn` zZRzYDNxuHY&tLTW{N18zw7X` z-e*{8JOoo zJyo`^UR=18;8%gYWX@Wav}^&aH2(yYlKqNK3oAglKy83z7BfEp(C6}*o#IrWuYt@$ zQrFK#u=o%3BL_jt#d*o$-On}R4z3|ZhS^?V1QogV;0`S+iCReWB2`pA@0slA3 zHs(tMfM3q&yf^R}kXISg0RQ5lOvx`lsV=gvc?lpu?u%p~kaF2fda~Tt0?_eGCdqDP zTdM>Hp!&1UfBLs;qgkBlrGR=fB67?o$$*FX01$m~owCUv?oStYpPp561MS5-m-w z8B@y4+p@hhF6?4tf3I$z4aJ~jw7aM)%B?p>jis3R9)tvoQ6B6@K#3O~YZs04vMyLl z?ZY)107K#6%V67n@_011EaSp2mHaUY~!OEa|g6+}T8#CD4hHv4cI%-aD7%98!Ky zS@p*Dfu?s5p@NBowq|7$_MR@2IS`BtHR``(tf#nMd&X35x;cl#$ykqz?@=vlN36g1 zhHD~pw=CU7ArM_>v(JQvBVF1aX|9&-$$ch;$&SJRcM&_Zu{GOu*tSzBX-@iavKEbr;^b(cv(xBFodl}4NFm3@r>*PZa8j-GD&j1m<|KJ@AT zriMV*=-{%shCIcXc9%};)T7ViaV&99`FjLEqeS!D2ZcT#2vcCat*XT5-2J}B>sC5; zI)!r}$gh)LVrpS)b(AjRxX~Z)`J{==mtc1nqoggkXX35$19G?%2~@CSG4qKS@PF>R z{g83kII2IN2krPKb%{9qwms5`LVJG6+|Q4$3BBq?j?NWTIs52+Yf=-tE^Zy0*)C<+ z;aTTNd$O#R(XmXrTD}T|Tnk|4<$-{;uR=rt?ViaT719V;dn!PZ7v2fiHL&koFUX^A zFor$}bhSFlr|K60fH20|;0cK2fx47TXvv@f__VBO-2;FEreZwcu>sZMCBT;gWPkeG zoj&ZnE^dCjSX{`KbD^b_m3UD$FyJuY=So2M`#-7dh59ARz}!~@w*^Q7XbLQ)12C96 z#C9;b6iP~6NQ`K#XovaDUm}3X)M%!3!;59%rVMaV%Qp4N9{|!c5u{Uqv>L^yI4O95 z-PnSkfTGw+*$L$=r?WDdpUJ|OZTPYjI%_X!wD`zQ;zcH~3Rc?ls|(DYfIX1z?1ct5Dz#Je8^>KmYpeHsH@Q|jab5M4e}(t_IBx1ZLU z2Sg^-%9a=Q<-$tkJs2bC-hZS-$m!zzN8nv48)9(<8K0g@e@${ zLK)G3&}2@p1t!B|^}zv@b*(97^XdJM=gNB5jpo#o=HZq6LL6~jKL`w$&49|UubQJ8 zD}1=UeUuIc8}M2>L`x<&8Tsdw_E6IILi6{9xGJOk_U^;t=KL4UDK;%L#dzoZN^@Gw zI{2f6Ie)0b7Cb>O9X9bw9mBRFDRK*<=h*KL(b8r$v|`v&;+vy?DDnGiiSx&;_ZXDo z<21)OyWZl&IHW=*WU~)*D$dVC>f1qmqUmSpqWSsNPo*3VefNUZQN|$w+6Ns zZl0Z!UA?BgZ>Gi^y7<{8SDZqbe&>Dl`?8U!aNaZy zemCtrv?s;geXwy2L?y5q?6GusT>P8@)hBj6Ie+pc(OR-bPO#&>O7tecnU}*^2?l|F zlq&%IOrR*>qn4-uM8Lo^A&BS7EPhsYE$_{vT*Ac%=v8Jy*}=pHKox^6t6KeTJf$tt zL3s;p3r52v@DKL_3IR~{4?z6#a<%yR@Au07Rc&Q~uYk;y-8i`d)ZM5=$GL&bXJqVZ z8zIYl6cnM$n-&tjm;t3r* zfU_IzZe)Zn1^jOWtg586f%beUaDS_M%hG{b%f1${y`rpz#_LfaFylk~9`2-9Uj!&9 zDp%3iW1F-}aL5K?aH$d}WVhc7xPB3k%ZmUnZc=tTUpSl!Nd6*l8z5d7{NhHS^`HKF zsrX2!#?7*dWeKG{p77nmge=)79!3P9T8?Ur1X#;PRl-=js00QjT*wx`yw;r3Jo@sp zvfu@LGyjOK#euO<1~WiEaR+dJ_k&7~2vEP*ix~MLgL>rzy@!@JP3yNoh41#A4WHz7F!mmg-iNgVn%kRS z_NgifKeRsg!~L@TH0Yb^QjEz|p5H2+&6j2$i1Gcc`djmhZJndCCGx2Hst412$4AS- z>@LNi6fZ)>_Tx(v8a~&QXy+K0_}S0WIOX>AKhW+sV7)zUi;=!=;9-pLo6nIxX}0e- zaBZ{K_wjK$kIfd7zsc@=JTcPdPQ~SpzCuv&vg0XqW=)}X_)1SXl3UvAWPW?R9UAo73)ccKPro9rOIkbxw_a&X?cYJd`O@ z1Zo0OQm^=0)D?Zuu z@SxHT^2yL&WhIl8OO`Y>nkn@`CiI15efa{gOL>cxy(ExY%z|Dm+u6!J04@u(C1aX< zY8exAN|B3ksGtHlX`HNCGl%F+|oLFz0Z3n~m4~&4QJw&b+lw@nlYEhKPo=!_f}G?c zvKKI_s(ZP7tB`!q!!tap%+wuz^sTna(e4Nrf*ZE7Z;kkT0rXJ$R zx>vVICB3c~o(P|DlSO{vHdd6LY7Is9D9sc17a!Aq!ju*8b+8zLG`2;;zkvy!S1)CA zQ;u~N+wH-XL{8`Qjg3nx!#+DlIyf}dA{=~6i0)}Qd(fG2$F$b>3p*SBE50`Ro~x-u zJ}qI(cAhj+sU*&IkIOlnBh?tBxp)`IKg;k?cXD*Ou^~>3;^VoJ5o&9i86Pt=W-W)x zZPt5St_+obcsk|17R2sB$t6IHN3GQW<6;%)Jhc2i6H4nn+=gJi{(|XKbHKScTEwc+ z%klPmTHBQ9w%wkH&cd2BdZMY~p_yd~R3v#36WG~YpKX(((6%R@;S8G&tDk$%?uHNk z93T}edx<`_0j$UVCNhR#Tb$3kSewSJ2H+}T@zoRClGoKx$96T>SeOsjU0y=pXe_gz zj#;;bYrIWel#|f4x%$sinZd{11-1(i`gI>;7R%%Gw;0+$JbDbxL1r%3(ipq;n3i12 z(E%yuHuKY53Tx$Yk7d*z*9Vf6ENvREIC!yrIUCZk=|Vkr1BgoXD!?IaHE1It5LD*? zc#|nE0X_rfZd74QRjHiU&qhe6wWdqpQP`|(5|QKw+0vy`LCFR%TTf|K)xP`TesU?F-h4NoeE3-{ zKy)Lcm$Ct+^6UM(r8sc&Y4ZBpg?bRr>}7S}uOHuuUbcStNx-x~Tx|T|_jd-sTBWaA zIxMbuG2)fk!b|b2f?P`MKY_0FWoAL}buM4<9&nr(1Lo2;12ES5Nz0yFy@EalDTPv_0pzdF7TLY@TmaW`MFx7 zP`$1f_>r{qQ4ST<+N&KkzCXw=_*uj7*+9UwKy9;0I$6`T^wZL0V=oF6FfDlt$)*(s zEk)P>z7b$;Oz6+7{Bh7+8U$MNg$FNTlwR3RJa|##`hn{8&?1C(8Gzp8A#u8TwXpTO zRf`ZnZ7Sl=6vGqQ0YH5&K$=+CmnrgvmlV=!_#!W`*8|tq|Us?phx<^*qJ>YoB=s9L;{8Lz@ zK9_H1&4Em+^V~jMhWkdO{<9sL-}3Q5ZhkgDzAFB_5eU!E$_71$a+_a+xw*bE|InCA zQZ&VvvmPhKjk)xBXL;YlgD`x9WXv^Uc0|Vb{l#OzeTc0Uw=gvNx66(h>-$YSsAJck z4A$+j(9tnK0hZZMyssnAe%-uJb5A zZKJuo?Xy14fgZ63nQC(DAmR=uM4yeD80k}d6~uULwq=2@jT_}M>T@Z`txY}OH|a#x zo;;6jOQ!caZRC~=@<{o8BCifPyIhZr{#0(Tr!QZbE8X*AY1n1!#at$~O;Dno=3Qz>R1Z=gJ=}95Lqp5y zncNE)ovRH5E3;5*h=o92z*t`32N+6)FiCMMP;)j{zhMEdQ+yK;Dl?3hp48CHymD7M z0MRk@MBpb?~dTi6fcq)5{7ws)* zo(^DYeW(?zCjsBY1DW_lW$;>ScCL!^x%dF??d5y|!cS#~1y1sb8^hP-U`wCZeRz6_}JlfIknjdScEkCU1x0p38Tf+w^Dxg39wA9NF)el;0MSk3=H3ya7 zddH(}0O8kf)iYZ>2&RHsIJtfCfKROdQ?{ABbSLBKTb&OlrP<@N2;CI zdUfV}@tDiN2l)8m*lB(Kxch#e#_A615xDS&P?y$oxNOgoGDobpOx2#Lb6qyZTTT}L zf(%denvd8baMGDKnV_FhkKdM1J_q#xLz0 zy^&Ss=fawfncPSaRwAE$Y_aqClE7!j`fOR+oVGLX!k9=6U@V%?*BN?k6NBjWcL18u0LC3Xod{= z=L63Nc>EZ9^qAIVsAn~U{3`eepWDx|Z+87iA{a_;!&m3?ecq?an5-E8Y?IRv#<(`( zXHQDG?b=D{x1Be-8iRwc1hTvNwHao@^RvG26Y{#H)sRFn-o^&(I?eHET`dL;X`;0` zP1ncPjdjepCNwNqAo(d@BQYG2)3FFQld(AMQWH(Tlq0{=7M@!e!xQhXfr|R2-~B)0W5`2*rYans!;R*S@|?_p-Se-6VsbfKUO_Z}kF0z{<4% zN!oSb6GVXU76a)5Fc;Km=Dq%QGWpBzZ>4xS`OQDQl3ZO#0$Vq-QKj8#Ndgcr5%@tx zxUwhsr&c8bh&cu@r7D|>-OHj4CmD(b1ca3S>9y&&v|m=c@Ytz!wq9DaQ0d zzDL zx10v_=F1E!((TI>`7AMJ=ac-rS?Xnk>z`Gv4iGL@^GddeCY*(R?zhwA=Jp+9-H7>T z--=H5-*kfQ_#hA54au^H&HD zMrqy5j3noDH3pI5JLVSba#g$Q>ho`m{kZ!QZUJL#J_PNNm-!GTKD!LNg^~{=H1pY` zFP40;J=A8mdC5bh<*YtO)z@vM#IO1K9ug(@j<=H9e3w}dBnSRH^5OUzb-Us@KXw!Q z;^?%8+Bdq!FHpm~-I_zL|8{M|D8|_D5{E{K9IGEK^?^&#+%C!+_RdjiSD$!$w@OFC zn5*5jb$Ryji!k-s9h>YSP3ip_g0}BE4}Wn4i8)!met78K2I_&oUAYU-u$5xf_|%PW zET+T{O(Ub>U@VSLj=#s;Mv%$h9u?$szw=nO1R$dtuzkVRoQoy%z5Lzo8p(3&kdCy zN7j$QGhXi2&N}?$8U}muB1~-eYc2L;mwbu&f5$mgIoGG5vvHN4wHE}cEE+VtT4nS>&r&R58m!FFtlvAo- z^+;0Xvt9sqz7W_qoBa5v8@rGerwaoP4S*FP04Wz4n;=CC2xowyNCE6)Q8)(JoX!Ng zipSb@FsyNZK_NiwsldXMCn1rl_oX@+5Zl^#AX}g{RjY5lyD;$dQQiwWlsv>prUAqN zo2tfD3kQq=6yc?a^lHZO+fQo8ai$&<*dh=aRwhpQ!%TNLEA&;d@7y?fk)Ysg{&3Qj zA#zJPdsvfvVg;l=6F~dsn}w>zmy=IF-c2sl!UN2jN@rexXD`^3JgZg~-oJ#3!Brw5y=y2ENS%DzmrV(Zo*l z^xB@P!g*ykLaMICD|%v0Y}W#Fi7z%%P0V6hb+_p6)$W5zYXEiD$c#}u0QFDw62JR9 zwOx@sTVBi&FKZl@p4KVy#{gs5DbWDORJ+rAnYi*|#S{60{&Rilaeup%9pUAI2GC0$ zFH)S&RbejuPpV|Uqm_uNnwPR|t`-|s0j(Gjj~P9jm9yaSSFiQufWTy``1!Je^&=ht z-mKi6TBls7Z3mUmuFFx zRf2xBEl3Sl6?mT37Ncmkt(92`@B2oAr z;+J4ouhDx*6PI&ln=IC18%-Kq&sCnk;prJD{5tM?E~K)?oDYAd z^xNfjq8neD>#fZ<#o?G$S50WvG=-GTr?Aq6_ zF?N?|8E>wo#K6oc%ah8gpADMiV|sjl$%xoKG$IF5TTFXyaJ$x>DYmH_v(C_lzvS}w zwGiLfRHxhhvwLj6ON%w+j>q_v;~z2>+f}9yes6nz#q&dK_iG|&Lvb$2TU*PDc!*B- zvC+wD4BP5!Fhfqx#F85M8R9KNv79ZWz0+}QoY3Ry=efPx9=Plq zd;uvH0OS#Js(nKLVLe<`fsIc9)(?77A1?(wJ)^>TEj~0Ft09ONGB+M#gc@v@V zOnXtFbgd2X7BDI!7Xp1{f+~KelW+9%>D|5hMpJ1GP)|SNs`6KWu)Sap03MK5;4=U^ zqKM1+1%0+@*Fnn@4ax!LuBcpi<~|^eU{d; z`iN|;NBn8lpQ%IPJP&OF$GHen+>qo7S%pEbTUYq@t^Yw7(5jqYu9!~Jnd zO(@;3oD84yywB0N#Qfe1*5ZfzHYsD`8_OCRue_!4XzVu{f|hM|mFC^9+;x+V#a1|9 zF7H!ae(pRaxjwI}I99dSaeOT|^2duL%GkY+jGR`#UlJqoKDIZdAFDe@&sZFPUmMre z<%!oqHimR-$#CAo?SoF&oZ7gH9+#E!#^df=O2c`&E+tyV2~icMoG6Q7JzpO*%qJ0$%3FA1)S1 zt;|L|jKK>~7~&7jLmj?rrlixw~zVDx8IE8l!rPk#N67Xp`M zgMjN(0k7PV45*Y1R?RD)%?}AM??fxtYzw`r!Ck8gW>t$*z5LT3Z>+lZ?XOjBEiS5o zPX)jl$gb5gFj`f_Aje8>!w^Rj>eh0BqV(fZ4-C z9Ie_~Rm{Ac@KQjn_pqj-+2SUE6fg38;@QKyfOTdGR6f zfydgRh=Z3F>T3gA3tnz$@n?NNsBQfK*NLTkS!mq_kl(BFm{uOx%R|6^WETKwK$gER zEo5V@PW7@&tCrUqZS;iz2seGK@s;ZGHD3s5UHKw_^&t*(fNISzu2?jE$#ARcZp$TA zG$V(65zb6C|Gj|eg!>ita360E+t;bD5$KJ; zw!C8AbJJ=2uKV2e?)<%me5T1hUyf-~{K;eJ-O=NJOuL54KB_Ir=Lu|z*U7;K_VKvU z5zIKa436H0uE=P?HG|t-`dfJ0hX&`b1r9hsmxY#nfj(W=^t`}U4@2K~z4yuE^L%Vs zpPX8uAIPy?Vv@6G7ddW*+Z@aFF{c^3@7R-BKg-x8ZQ=84Wt+~9j%^Ipy!ly5rhzhs zla0T)<8?^&4E5P#n9}EDx5g|k%th-+b)2T2b^F1gJj1goxmj&nDqZY*r`m(Kd=El;E%22NcL zmjJvzXS9_tgBdGA&o!npmyPi|Z_MXS?8dl04O=i7IZ%d8i z8_%F8rz@sOk3CL2tC8!Ub9@~7%ts{C6P}@|MC5eG`bJXv^t_KZozr{tXqTav;R&p} z{kXPv2LO+kuLbf72z*dK-gEUSRxz?wx#BQ@q6M`&24JNngROxjjX+R=k&jE2=L&G< z7$9>kaN4S11zhq{yg4AOz*UQmBFH7N5panPs#xjw3sQ(anP@s`^YN(m9Du|``cdeq zph@{f)orN61ze=MSXKz-6wtU(mA5L1-~IJ&^8T-zE$ew9z=z%Liu{G+|cT3)Yl;5Sn|YN@}X*%{J*H21~u{k+E}C9urq0eIfquh3sDPi^=+0ddO0EMWGzZm?L8L&4QM?(6OGo9@L>SUOr3j ziImdHgfA_iTdDGUp+?m2Z>}fbzy2ustbu4G%u+c>?fmwOjyS%=Eqp+c7diW?J5=e$ zcqo4?Mjog`34LZc+}Nvd4bw~-TG*?5E+htmLLtr3leMTWU!SnT9f>c5V9SfQGnP{~VXcGp6r5 z#&&cE>vZI1_Aw=MW7n!-->$4bwjpXke_K|}m&P!)*J)Ea>YR#3wwSCnL$o%K-StOE*|fA9ek{j+#JO^VjiQY8L^?! z(QxOEoG!IH(E2r;_IEZ7RS;c*@8NW5IgRmHybkp^f>HsI@HmH@$I0zJE?Pei^Wm%$ zFK3SSCOgs<^K%@_XzQqi;848|VJzmUsH$A?vt|!%sy?fyVX5=dhf;m4p~uZ{OjWqpNhK^5vVtcqQ>T@F0863<7zt>NO;#!^TjzR_ z-M7=L$Lam2e5o56qdfx_>W5)#qLD58nCT6(Hi39bZsE(N)@_uRq6t#<2^t$FC07* zs2+S)3}h5_(X597&jMfP0%c(Uus>73jqoy)$H~273DCP(XiV~~4F&PBvk*Uk;zwQ- zsPzJ5o(UMI^#}2%iu+N3H@dwdoP6MmgHRPMUd6}kP?*mzWH(JcT$!qNETe}rUw)uxO(GC`D@#^hswD?8g^;TAOI1g+HczUcracQX z09JjaU z+Rzm((O|W^V^`x4vsyGJVUVG_TiH@L;)Hdv585e@n=-)e^g6Bc zF}LVhWhki>R{DIz_n61$ht}q@lHnFjWkYJC(;%}n=QR87>v-%dXb1kj(Ci@Ys4R`G z994;M0F9maZ^?-RsC@W--xxc#(Yj4;BlJFI$0Gw`>lzE#WX>vbIx2c(gkR5-u5{Z z(H`<}TQ0c^`|E6+OMz!(`#O23e9xR;qB+QjYbM2)&bz%0 zQTZt4jVV((IaoE~@ z*4c9T8()vP`HqleU<#-S*i2RJ;&QGk=%w}kHL#V+QjlJy#A>}X;M70-V<_zvXv+X7 z4M6)O@QxHV;1@0dn=~^32&7`zL1BPH+F)2Uv=GQpg&QiLsZOQg1_1)-<^X|K*j9FCUiH!fX`L3t4UiN#n$|m|GbQl zr~6tqoT_q~s$OKRRsu(9Ng_W0+0pT!bG{I;N@M}v_5wuF-m8LoE;+o&??I8G$MsAg zbm*;2pcQF_h%jL9*_^6mf!;uR?5x9>1bH(81z1B$oQGKm28kl~ns_xr&T3f}!TAnDUKIp}e zd}#qKgxEm!^v%b|$*VVfsqm;ZqIHMXPsRB{+$S4VNzYZae)DiSxl-&ePgnY99HLcR zzSPZcp7xL+6uu>QLZ{ny19bMU`|T0nf&UWYc}$y-?azhn5&Gp)JohbQj6P`NTfwfY zz%9i8SRJF^_4z-s^Ydn(w{HA>xRJwA(5END7Gv#hANkhU)pykBim8ryt;KrcV{?x+ z&YKjIwvJn2iJlLBO}B^#oOm4BlP6}5CrrfO(#v?IJH*bUMAXe;9NYNVM4)e^|BdA4 zsAF3*^@}>Pkoy`HJd0ydify1NuUSrXEH^jb8f)c7Yvu)=gv3j8@^cwDG`{t!)d!8m zB^jB^xnbJl2)WTM$Rurxr+<)EKipT|5z`>yYvt;=B~M(s)j1YG=yJy^k?|;`^ZIpY!MF zT$aaIy}WVlS%=8`!N&c!ifG!Bs;dl-Cgbk!tw zJ00V`QDd6h8b5Qh!^eimfhLYoi6@Q05GzVNc^yrx?TDD^AWz%Xp8CLf!y9BfS85k@ zQ1AoF#um#9BjLB_TYD_l6=u?-~)6NDvbbvfUhQx54Qp|*Q#1xXfA+?v{C@{JyQ>4T4hkhI@K{? zFBQR5;LRUTSVW6L2e#SKzX>-_RTNY(Zs%ReA$%N*+M@T8Qq63^UMGU?*F( zv&!g4y%29IP;{-f7z%^6&9G!4Jn{!Wc}bzHwF+!N;3w=>JkUWq586fC-Ka%}0N^)z zu_0?hOA1<~(DNDJE>vL+uzvSR@U!T7Q6Mr{8|$-8{Pbczs(??_{=_z+#H*!%ZvoT1 zeD6`Q2rWAVMyp4(zCMtBI-Ufe_f{Y+RmOKW0(1eG)mQrUx3kIXZ{`N@0-B%c4rJ>> z3A6a|d#QfQkBRTzX&fs#6NmH12EGr1AX+6 zep2;2AT|~5tmRBV_s1XgMTO!+7P6l9!c657xwv{zD;X-=6^{qmf2IB?v#Hkk5_`f6 z9dFc%jFMC3M&H^GQ2CW)WCTX7Q6zidr5tg@huk2V^C4@lPwL^$?uje| zQodb%;p#|bHP*F{w&|$jBLCb}KiJ0G))b#b!HS3XIJWz=_=oz9s+|2}`1de3tGi^= zK6+wa{LOwWiHS1DrDtj>R;BT-=sKgiQ(H&k9P-s~m}1b%Kl?Sa!;a$o*eQ0nXE85d zkGtA7%!B=?8vmPhGU!ZVyEzfB$9{TC+_45MYOG4GWn#{J%uor<7Lru-CB5-D_rr`> zUiVepnm9Hjxv^&UxJUk(JJmwWV&6)14WF+-;0iwZSVPsiA7q|$HD#AIOlcv(;97EO zru@ON<2%Kel>j`ecoyQxo)^^7iUB|q6clh1fY=`PiY7dOn5TlTX(zD}IJ>_6*{Y4G zAYN_mjThsY131+$ftG;Hl1ZEuKpINWc7g$b&Hx?%q(XsPGnGD>`A50zv0i ziOjOl*5XX1wBcr~D@1w$eo;yLk#I~EvV2!>;!p4I)rw;x5c$$`u{uWeN}18&n2gud76 zdSMmZ#E6P#{JOhgo-YB!qc}f`hf8Ors^t>_=nGYR17e>E=td5e;~##O9iq9sqIHS* zSqy#UK~HBY#BbHY$)UAueWMmFKfQlFxjw%Ov1M_;(2Z-va@BoE zZdS~(LHBjr9sz|f`w-sF*-z0I{%VP80ocH%bp6F{O+0WmIGV;`sPm1iHpi~=+ET(< zNk@+M&||}NS4mUF5*vo`DbJiWGJGg+#L_toPy5dB0S1D>={+rcRq5h0PHD<{ux;4Z zmgD@xc=w|!twB8Q$GJ6h?beSHH>&%I`(V!v7oh3*ITGJ{jLpNKllmT6$+W48p;ECm z_pl+=U)$SK7}hNIFFtmjulptfgpbdDzBOyn zS#sKv=_hG?{F#@IJAJxubaYp6n+)dB7q+mnej4O5=JToMimQ|m6?tP{ncsEhDKewg z4a04Z{q(U6*U^T2&9*6MViCo(y6J*V_-D=#Qc#YNSN(Fq;QA?;jbl_(ix2fyH&_kU zva~4ZcfNYOW{wUjcVe2zLlO1^oo0T-;Bum&9v_`*KoBC6ha9@(lvYqwv;``UQ#4`E zqV{9R*QzC;VsSYxp{qTsqf!u4BYn`{R(2h+I^hRVT#FumwqT~l!D|o-zMGh zYzpW{RWD$zSJP4iYUWiQGKY63S_P|0*53O1%6h7rxs2@Pdj<>(ycPCmEinZ40Xl}z z2xL_iFfZn##RL`025L*jQoVq!Z?n_@HimlH&|esU7Fz%c*=VN`ZeF2XgH>0n!j^^N z2Y}JqFo+JM(ST|$1~I++>#ck}o4oyA=&(RjWK;Po?ACfs)9zxW78j5DdWUiJ0B(kx zO#qx}k+ISIsQ_buZu}(crs`b`&-`3fxN2ts$a*7C_DXFgXhlL*FgG0fM-vmgIhB1+ zss;zdf6^UcBi+2{586BW6icRxvl+BTBJ}NZ@|11 zqWA72aZzGwkUrRV0<7`l`Wx}-1(sB}pU5{rcdDDoty9t7{Csa!=-#LK;!5DZ0B7Pz z{0Qb;IyS7O&h9?}%nJZlT=d&)Su3`z$)#Fp5T`o<$(pFv3E!zE4^`h&#c~4}t``74pnE}e3wjo_ zl+McMMwMq`G}KJ~qK@1{_T1CkaoE?@L(ALOo?ZCy;n-Umr+swt!R1ibPDxhogBmgN z@%WDvAM1;ug}7jMY{L*)eR{^s#cSaAhIq_AAx432%_WLF9=gJ8#?KQnJ5AHguh4qj z2P)?H6g&IuaD5&}-}|`lecbH~``Q90+kbxqk^399p9EauW58*(5OoYkh=s@3U@Myx zmcrycwa;B`BQI`+DZ7u;f=tV2$VSv1b9L<~yy5|GF%pYECmxj4qpE|;5*4Zfe1O&)$R!Dy4g_^Id!o*HXVHeh>9@saMU| z^em6B$7&KDqu058jP!Bi=|-e)-Cci|D;aSEli|{-?y5Ew|8CQc6xe2tWH%FKi4M?m zT?WfKv>LEzXJgo*u9ODiAu)s!Dg1Ek=Q=EJ+lZ3+=ue8W%pGT@^ZDEWJ}Paky~O$B zO4YHqesog7{gNPlm1DZ0|J5SY9XfKA+4*9Y~krkefl|LJ0~xSm>7Ix>kds|l!2 zr8*u^$xM%F19IgX@;|*7zfy&^Etje!=F5o_#fWT@G=NEGC1ss3)X)R#;%)sqa*hgE0q7jQ>meWj8Mi zV%O>+H?}UX!9P0gw?@&T$*!_RBwa!Y-RBtf}3m3l_SZ!t}w1(HC!h zwmDtHlQ4_G=f=r#$=&^~@o+o35}$8moDJPrjyw-|rDlABpH3`X5R@!94~`k8)3O!(3TzZ#OxvseHVA-2bqH z{FEC|ZcB!Z7O{~X1vNn$4AoZaD=;+Ie%@}EQ{5%)p5Z)*^y7xEIVz}Z2)>XbeC+xX z2KAAr-h7Q2@VNVAJm+KVOXD$(ANQ?L*FNV;_E8Sm7c0s1bDx&%PUDi&_2u0meIQ)X zq68d!3b5)%3v5BR%k8A!auiCTWY^=<-jCQ_; zxmNOZ?)&@b3kXP+ifKx;)wa#lnZ@5ntD8$$k4jJ>7vtw!ZM$^Y-&B(4_bEgpkJ3O) ziN~4CZGG1r^kMzFj&Y5*M=PD}!IsJ<7koGkAmiO@#6`kS{MqocE3cYk-b1Qm!W!Kw z0ujGOlZM>+8lrH7xR@53jJDDPmD|ZNTWp_w{FH#Oet`*k3Lcv+5>^lTd~U8LY8^ny z51h`;-akKu7zTNaF-Ku3{@Ah3!>kV2G|@T_qhU%6!B05D7W?R9?CW?$!Z?ghZGBrI zi3GR?=zaa|xRMc%H9~{*cFJA;q7cx^F#v9; z?B&vgA3)q%GRPpRnAH*308s;nVb%|}A_E|IC6Lh;&p<~>q0&{6!?mTVga7dVezJBO z-~47_iGse3s*14`;GCBmlFy6k^YH9LA2 z>h+ATQwc*J_5ub|4ZajWO&r(y_3HIPAiI3i__;;Zg_-p=7EjX@T&8(sv@SR+-7gXwt0OCXM z>dj^@Idl1@Cf*imX1;D;Bk-IN=;mDZ13+R@{eaRq*I2xz=keU3k7GNICHp!3!G~k| zG$cl@|GE7hV%u}lBxzl%4j80YPBR#;n%>ky#SE z?9%JHzTn#|o=5G`!=7!u1wc@3FK2xln;P5vsJ%DJioA&@3(%F}oJ0H!5@Hz~rwi*6 zpK%|p*E4E0RUy9R7&P%eyk*Fauiw$Sw!|%!?;bRI;#}afXgLnf+qaJa*)CmMSAKqX z{e9BiEsr&OAHxnCchy^B8rvPmtMom3Oh4prt(d-a9Bl9N*g1n!?zfK)+Ygc7j^i-7 zhoxe<;Ti zIXLIgjW`XlKfS*CNQ-hH&Cxstvq;4XhKP>l*8Hx(cp`axG9p1|M-fthQnWU2a< z1`JW1tL;fHfLlOlK*qURR0xAKFc44_K$7$UFq}&kfFhtOZ8(s_A^`qU0X?<6 zZvaI2Xh{K(hy&=5HUn%4$`pH4RZDMRDsiba2116HN@xQh1vKBN`uSGXwZHqHt|n6f z;8gblu+mBcK=?#^iIN|Ho@&z|1e&%JRBn5fG(E3rJz@5!WeGgIOv(CGODl_xuK>zV zvUx5r_FVmsm-lC?&|S(#?4i{l`aI87Yr zc?>WPIL{0EHY?&UaNYVuOSWt~!+&KzwlEh!ZvoqClJY6+XMhu z@&JkL<$?m<4Q!YHBFdF2iwPP%mjQT*)k3*KwKD5ZMYz30&{}WE0LBzsY`9lj z<%>%|cvUkik7&{HS}$3oFY~F+*R<~t2v6%2HuT@quUYsuJZrTy`PFyR$+fDcX>Eec zrK;tZYR_?hx0$@r+=J|+od+)u4RgOysu zoSj@w9@Za}tIAD`>DkE1uiMLs0QaIDaF5!7eRyjj0}r@9hiuT1XG7_c={!T^AChK_ zjr-{B^KWRZ+}6Hp*lG5$^^m+T&7MR0HdIzzO%2aJABsvkk?9}0QW`GXU2pq-$&cmF zt$XS>A9#`5C!6Qokj;H}dx!8ijq`H8{R!$^hum+sIv3AFTJUmYvEKdhNb@LXdm694 zduERRbBx7#80#P7Z}(X;?D~oIaqRIsblhUOew^waeC{8feD5EI2HrCU>xl@5tVKjV z=&&7!_IjbO$&37TF~+90u0iGvB5KmUx7Rs4iur}jt zO(p!Y&*K#VgeGOYxkQvnFAzpp^o z6TK+VUY;lLkSb-cL<6G*Vq!Wk#DfoTIJ90MY?{;L3_us~S2O_PfX4>>22RaQO&((a zxV%`83g)%wIo6vLZ5+^%s#@#&EbtMx*!@L4j^BPipIpm+s(();2N0DO5mejqLOoh+ zJg5gVRl@KA@)7|9r3I?;vO>%O{A80Vld%yvI+Ksgtpx@fz^PLr3aC6)74+=y6i@lI zwu)sybz}hWs!fG>sa7VS7B`7lJubyNGu>2y1LR}xLg4w$M;$9@r*nK4m`W=R>bR9< z*!rM09rJUo6F{>7SK6SM{4rGEBLz(m6r2I!$* zHGQA~hXHBNuT-V1D(IB}V*Hsc6g$boHe_-+gtgpQ?`isJ155)b(-H(3bAjEed!Afa z8;+;R&uYUmKR;6(6m!;0_EI^0A|RP|CvU!0Rk9`?^}&fRIsWqRcax8L>EIhxZD0S2 zCfVu-E&u5E{GfIpkBS9V;1t>`V-(u}O5c21Pv&a%F;@|_CPcf|3m$3Jq1cJ{No#() zd^LH!{&Dg&rz!N;?MsTlh|kp>x5&GAQfU~bPsXqHdgSoZQ@Vjm0(; zmZ4=zOwUAo9`iTjVQu5sHgl|fOMLburcVjdxgqjW8S%M&=8Ok8&g5)LlAgD6qvB6# z`pz4_y77}ieXN7#kiToJ8r!<6a&x&cID5n5*SYUSZ#gv7=!g$pc6E-wL}4AZ<$Z0| zt9=~=gK{p#bQqAA8%!JHmfchPgk`*qZE5n_eHy0SZoL%?{+bCkm{FJ+vKQ56#-D4b z6tdFzZgy>tsmHFRH_7!{cSY2G4u;sLIhURzBd`aX;vA^u4?JjcW(b4FpiJw&lX*_< zV`Z-0XKLAsbBIGX=W{+n%hH&g`sKC)!ev3|iXgr$e7%QS?{*Adui#5={c}3&Sk&IM zsfO!q^S#8g#@EAGH#RoA>JK>p?$qR5yj(fCeC{Fr$V=?xm^}16&GE!h30V|Iz$0^8 z3_dgO=jgEfi-qI_)MLT7`gG)h$H&>+x9Lp1mbvRubxKt-$Q(KU+i=1B_Ro_+JG+->CZe z*Z=FKz;so2GpB#P3fRkw>ac_0P{j*iEX~p@*#d^21Y9l!N(Vb+Gd78iN^!t)3##!; zp2n!w6>?1<2mzV`QDqxd#v(DlrNpKe=K@VNMkVqI{?dj-=NrY!bPI6j0k&3uE(NW`fs;ZSq;F;i~q0>yw-YXD>eYvW|(Y2zO_@)IMvuUAC{B1-!8Py0?I{(uNxU= zV6|whQdw(oE?}9;?4^1v(`G{^NteKCt-(^QMD7J<6G!~z<%m?;1IS;f1qXZ&OR1J! z^oYj4Pw&)*a#_QA!w)Z>?g~`lBQW$@%6+ir z8JD{woN6LR|2)oe-^y8~(V@;phB}XZ_KL<2c^I2@tY<*5H@_~i&e)bdABWN*!)aqZ zeX_^Q#j^dl<|jc5TRBS#4AF2W7h*IaqU$^C*xDj4+ub7D@x(s=mJHCPlfQcM%G~uF zkI!W}t4Y;+bT+QJ*v3x0D-ORlI|4ETB^epg;!r7M{>ie>9(cPpG*N40?~|ED(~myE zrIxPusO!B6#nEleDQM!~o-K{HsSbD67qj%b*jH=tE!=DRa<@-UH>dlk!(6=7mhDN9 zlhf@2D~ep~O0uA3&Tl{1k+UUM?lBtba}2)4aySN%^Ld}kT|;5cEYqRyo#XI(11Aw1 zJv-6L51YpzoE`UfZas%+wEK`7EQZ+rA^dTE4xx9Nv9Tfl(8bSK8(n9-kNxiRp=c*Y z@o3}`h$f&=7Ijr{t%bG3m(O;rv1 zN&w|20lBwRx1R7q=3gNhiWx1#prUH2>UE$jUz;EzZz)*pj@=1nN(*6{q#Y8M=f5N9RfcMZcgtZYEE?U;yzKo!9UNjeH z%9*?C$=mB2NiA>Kv+KrM`L>T7moaYNzA=<|-Nxs%?>Tk4u9E+Z=Vk9aIslksfDnH2K;v+D$* z+R)!z3yn~>uNi5Ma`eq?IKrGtMKi?!f;$tzj)t&(RO=I~-H$AAT*nQa->3XDM>BKS zqef3#u2q3($ks^v*fyv1F_+=vyY5|H;E#e432f6u6Pd#tZ5q!U4Wdfp88u-jlLK}^ zM0bx;oX{1rHzz&B!9EjK(*X$0$0wFh=UF_M*qO$oMVipYbA1*=*C)3vr#bVO$8jpj zaa!lL7?y+B9K)0 zHQxBletU~iWZ+O=J(p8@Kll0cU!Jh8KIgP}(7WD|i#a-rNfn$iF-m=p)C@X|!q%cM zjkkyfouql5U;Ugu@;e^)m=E$9UfD-1%Ul0!j?Zn#%?%l|e2E-OqY?Xyyx2m&-cuUa zTN?Ly=jR++kB{lFq}H9LQs~cU=k+~2+>hdUj$=EW*7tngW#joDZ}xeh;>9+mn|obC zTx+PPDw!@Xgp|RQ&nX7(U9Oy>kifSPTc7mN?mk`v;4ik4RYFW!8}!f;7mt|}q_Ioq zosK2I^-yt8us8>GYv)Lelx%k=jfx={~Woq2=(+$$O*;ny+Op)y*OvSePI4WWrIUlyg z5*m*9In<1HkJJM?fR~sEh)9qD7Xc}GLEMF2+ydw@L0T7bD#N0Ump6v#yhylcM3|I^^@*dS{f_}>Yfq>AobbyY^WdKGhUunC+ zA7H1vRZ#p?U{je3UgEIljfY7K#_MKXH0B*BJKm7ZRT4+2@zW?2YRTk6x84y+qES{%| zKL9xZ{gu8XxR-rPfw; z8~j#1p{>1!#)#|E+IxiF(sO`r<;Y4P{`oa=51Gc9wni+w#CaSe9au8@DT!U)@V~A5-_{#aCaa z=eQSs4jo6=Nq!Dl35a@ZfDa}XKsvOhbHWw!&J zIdC;~#Fx#h*E_wmPk^fcSzI_%<*jO1ts?nK zz#Xj*cyS&J`07`ea#U5e6(9+SNb7^p213U{gtepq;yV#Ydh$e-t@w2(IHNZMGy)F| z2p0`)9jGK`qHcqMrKeP2Q_YJl0pO&%cqLiz;K!$T>&fr`&yT8@UC0+z%?f0sf3&^G z5J1>GqAi4s2QZZlo5z(vQ=ys~pe4hZ091NcTLrf40pKJJ0fGTti4_}*O=4psi8vRy z3Aha(u@RQ4QTb@yPDG<~dRGg6Hqa9g^vs%V(+WaBv;c16XuY^4=S*@x2uP=z_xd#z z!$B?~qYw6EBf)^?`a*#ABBxVc>PPzvbcja2P}Po&_v(XutDkRvyReoNyp$2}mI`I6 ziSdQL*b90ii|_WMbm_l^@_;JvlQV#I36~z0=HivsP%#Aj2XubmWr?zLqa1o5PO^!9 z)Ye`@zEIJ;l>NNS5qjdthA&UzA@>InbN=LTxqPzf)gq0mr<2k>1X<)?hu5 zVW7O$kQ}mnl+1JWnWmDO^jImT7ixRL7YeN1TC({9fEPDni_${&ieILz)_QrQzG(UI z>1y)Ls~gdod0*@=*1WmFj(4NPi`>#y;wjNHp7O-!eY*N&=H_C#x%qv^jwjy(u4}xo zjgOPdOa+#8q?AL?cZ*A~&p#;pK0I8R!_Ky?=&_t1YS>r0XNW*4&l=VB2c~AIb>wT` z8tgf7d1Lu{MCu;Z&b2;To9iL3MOB|Bk?l68a);_I(Us(;dTf<4HV0cX6R7~5$2PcL z`|PJVzdyw^84=l`T2@b-vW)eh-!JNc8mz0=qM7O(#We0mhp1;D=I)e@(2^TNc((f3v8+5WL|=*9V_upcGwwdcG7kx? zWNoRI(&Dbq)P8M3yH8xQx{^WnHyMsjhvh=VkUjKxZr7Kxd!C2CrI=+shHU($#lOJy z8)X{H`n)nOmRrvaQ8a)C6@K3yX#UaHet_9V;%mpr`1RY3XQ*u@y4cQmE#q-`-M9Ff zlf{)-dxDo#*mx|vM40M?E;p9)$A2oPmbl%{WBFB?-LhWK3bzs5A(aCyKXLA-vR%iv zJ+hHA1}M$TbSvGHJXJbMOt;#O=y77(YTPu9Mu#mLw;_&cOi&t+Y0~4N9~Yo=_bkUU zM((1&V^5`|F7}Ynd``OoZdI_)X_@tZffA6hpvggYxICXy8{!pk8qf8Z)@8+G$n}`| z=N-n{_kq9>%QV_L!cD9ha-UpJdhX-L+)rl$SphbwWW_uH*|RmR5(F~}JiJ%+?d2P_ zufSkYSSt+ytMv3eRW&SN@`Ot0Ct5@ZpcXm^h)NJn1@HkOo+o5=eJE09b%vUV=zPGL^V%$^6s5 z+?u@a{(fPgBmgY{nn~rx+ml>RMIL${KXg z8iZCB0O7h_+CpOIquNvOpY;mSu!`i3;?9-Anz1~ZqXL=A z;Q#nH;-WShZ`3kFmBG~h^98`;M&PXg+o{@Ph((O7KMw=NShnzm0xu>6;8nKR+C7Tq z{Awo9S#4ZomsOO@UcR*q1?yBaD??aI>AY4S>!1I6Z)*q;y;7Twsro$gAH4Jr5A8M- zXZd37Gh{bkEbt`>?MC#*Nn3|gtp#hq7&@rP4!Op9Y*aZ;ujwp zVs}WVWkGKIFm;`bg$Hjw76(!9$_DM*G3aOMu3bO< zOPekETJo#Bj&0BS`Tc`dE+1k!P8Y^XGGmV89FNVxw%u);m}_#rJ$LA{oSu?xrE#}u z+gdr@uzgs|aGx8$t|eQ>uG27E8>(Z<53>Ca_HIlLrkk7k$j$yRC9 zSo9b(M8~%0w2O~raXggI`FGj-eP$@{khl+(HH0pXNuGXup62B=qn5(W>4A^9W*eKi zW}?$Ux<*`HgXFzLqV4NG-0E#PdnjFFuceX)T=#BMc;2S>`S^T?1d0^9o@mGpOXDeB z={&x7NJJc7?^{u8rP?_VO zxqUx~S46QFW}gNS8IKQ@)u!{$pY741XV!uP;M9OZKt@%W0yY8&zEbt@haZ=di`NeF zse)Bk740poY*Yv^ATU5Bz_3Ah0<#TpR&}rSb_OM7Mu4l9i~?5yuJlS4P%JPL0C8#U zD5zMKFh!+ZSuAi;wD1ESKT-7xKnyslI|pqZ&IBG(4NK4G`%jOPfBD}(7&!Tl|KqF4 z)vJY$b>os1fVnIjRj*5dl=M&rlwIm&fb_CnshU=Zu>r`mpTM7ms$s8Q6O)Mm;iUnu zw76h^1f*J+1*Hm@mn{OsBI8s#araae;|7l7o5tCw+@?wxpnIve>`fuo6fH}5IUs2G zOclIuuPzKc#b$8xwLpJth4@sy19SrpgVfUw10WlK{7m){Yiy>{7}>P?;H86i>ic}I zieh}Ax)|G8JM1?UUONNPrN{a<6EkdH@eUkTs<;8QWNO zq5$5`ODpehWwXAFK<~ABiBp;VUZC{N&npACL;rAIyr{L2eZ13=#ak#IWEi;saQvh- z->5CfOdvF%HTj5Lfa97g<0<_i&f|yF*oV$`{fA46 z+D=4z0(1-*sxL&hmYK*od>j%5M>@@Ys6LXa7b3QQ9ot66S5$i@K4a}mc@fj`*??@b zin4e@$3yrNmO-bj`+$Es<=K?ms(H7i&oAcUoQEeq&xvPj2u+SRmLDHO>-OgON@GLq zAb!PLM@je|wk*cQX^`= z23q6!lU?#EFR4A+^)I%(O*`a_%UPfNyQG<+F43@&uFb@_;^MH zDz(cw`}eNfb2d*1f<8axvNosUEAi`oWiaE$&O=`UNUY=+y^=E>vLXZb{q$n zo!dhn)28<1c-muTu?@vGw`sPW1+BrB%7Fq4wz`jy?5Z=?g7);>$I@kvV^uKGhLHy^ zTkBpH8y}|ax6{VcIbD^=h%+31Oy)(Qr2%dXIlLUt&c0LiSzqbha*%2}ADUWoT+0jxg@Fh5uS z&fD3YCy0klq>=k1v=i#zPZ5Kxj;u&LM^RjFAE_10LHbSYJfjnX%9kN095(V zNp&w^E;H7H^79{Rfx~mPmscO#1!zGLSS+L#8EdMO<%3lnE9Nut(OVgP0J-P} z&|g{p$i91W#0r+RMp`Guc_~|$YS96({YGDI(0T+L!WRP|&8$a=!{dxT)PVY``UVVF zB{Q*F$xrf=>iNlB@iy7STi~f>+2i;#n94T#5Z0Qf@0y7DJ#Xq5r^+QU9&&nD6wC6l zL+l&-W9^OPcr7`$>med_e++!C*rMr>|DL>>Z_W8RgBb@iLzRZ^uCc4qkSc;D5m=Md z2SILf+Ew-pBWh^Cg%p5F?B;w95X^sC2mrwQ8-cb0^yo#*JgqeVbMHQ^C+F%(Y}L&I zPSyEA0BS&$zhLNGa~lI<0dNh>7Cqn|m?i-3`6W69{s~Ct#e>Uxfq{U70u=#N0W9YN z3aJp*Q`y=%tW`bBU53hM@ItFY&@?xy|M>rICV%=@wM&o<-~9G`^6Fc)6%k-7G4{ht z^;ozQwXQh7oSVOZ#`HYq#df^-&%jEJ0d~#=bXrRX8GNRSaGppfQVQS;_zWP+W)-u5 z)S3gpwN@6yL1XAASX3SZd;;8JPiSX>i~#tqRq;$)6Cs`^|5~jvL_N8EuYStX1wcw$ z3|dl9K@8AJOAvcGq=HDy0RHWw0r*#SFrfUMfL`>A)LuaN`+vDHfcWjN=acI63{fvM zL@u_jKIjF4ms9nb)|^ZuhNz|owY?CMn}~FVRvrSSHRoSekTg`;Q*F+=0Q#SZaHf|z zl5$hM$narFjMNWWZ9C+iZLF`BQenl%Fi0MI+yd2*-wpo|>ci4h&g>}^zkM_R2f7sV zIsb0^$bg*$U~q0jZaUZ2)%Y;b>6RMj@x4!0IwicV&F90uemL4`L)qBW=(cV#OQ+Dc4-k2N1^T~EW;r-^$yn{$P;@Vk-e6eXEi?t;`*rG1G567Q-?l%vSRr;-grJe&c;0@ z%AEJ}71k{yr!>?rKjmt!nSRt7-&#{%T*7cGJ2%@uX3?`>4P6ueWTGM2>8NO@0#wJSAUTbHN=CTt&zfigp2inV zRBMvzO6l-A3VkH8eCW&Ve6_umu%hpg$Za%@FD5kN42R8pXKKqp`+nRaMwPZSW(uYS zxArrTb$$~M)v$E7BA*m_3`yEJ_!l&Irz*Fc;c z>+qbb*Dz*C6hg~o012|-A)h#QInLsAex9yPO7C>T_h=3J`+555upg|a*#dp=GWcb_ zIE_x6o^dvdLFFL$Vi*iNm&R~?9H#2qY&#a@QJ2?sRjk>a(V(Z|cOvvM#UJqef6%Z|rs&e(- z`uEZzfu6!s0d)v`P3t8#iSP>*x?s8+QLQot{j!hnNTVJlsy!n1M`ToCLsBMT|Hm8@_VK?CU zjVgbstk&I8FXaosD}XaH=IUw8!+$Lu_zEaXPwcQFy7U2+QA(in);oGzKMHxD&xKkG z(X?Pn6hJB3PCoT+n~?YCcozjfW+ms#3>?XsVWZGiZhs^nk2(w7vf z@&@qTsJ+SLQEOxXui9{khn62FiV6Ai{oh@fF977HlDU$-k6P2q*L)dpHu+g?POb$+ zKS{PdSd7AoIm%c7 z-B~lhaq>+-yp?EeiXAOIR;KAAM?D&Gf|{gv=qveq9t@ z(ccE0El#{>vM+`aS@AH(ze^qz6;u<0@}uD6HrZq5OV`yH8T-%<&TETB`<@u?%ll~0 z^X75@%J>(N}?TX`mbTjv$0kL|0!Hsr_$hp{tW_gyXucw!kxb~+M|)|_UZ zH@@v-T@AWc!pM)o*OVJ@zH0ApTINB?DjN~(q-L#_eNA_QQj%GkhW>0McMtJ)(7D5S6OMs`p= z4HyW>DX9V#B}e18dNJZ(e}6mq@lWf?#nsc~@BfF3$+v%huBu>Fuo@67&`|*B?X4=e zRl!TuEb9c2O69Ayvyeelz*@@;9ZRx$F#~Yod8p3zPbOgDEVR`oQynfM&ZksI3p^DR zs!HyaftpseD}RXHMggNIF@Pw*DH}j3fIYI=E!JlOZvn}XBk)sk#O5V07yP)?%LfH~ z)6PSIAemQcOK~Q!-(HA_ufoF5FGZuZSPA^KzRm_%3w%~zQOns6(p1RR=AAT{h{(hV)mam52edT3cFf09JGP0J5_O+zo9&SU1{- z@KVBc0DXy-t{|$S!-WEPtnY&42LL(!sFAUty^9X1ghoFgGTFD$TO!wM`b-;SKe;&9 zOCOn1yu+6lS_g&ZRO>*6Hfx7fwBMlhi-X1F5$#CME$-qLD9+0(7xI%p+_Ls?R3+@$>yD9ZD9C^}xC%ozAbLsd? zJm-Vo_}I3cEEk@)aXiO zrRT$C7L|1-$oW)HO0;c0r5}2}k3aC{?8=P|wPy@nK5zOZq_*|>@3eh1{o2B;%Q+woj#Uv$m8HoJ6{ed-({8jsN?B`g?;H-N%>t5<0YPgiq3HFvSuc&V!b0Jd>-;S zs`1>$4C!YW%3Wu9Gl*vzZ>RL}T+GL;MfnQfo}<1b`^MLOao>EO^UddcU-P;#KA}AY zIH`YSTXX)lxocgKj@)6N-Iy6ZyW}|2F>GqvkW!S+!{4smYcl=(G3dTnQnF=yAKCFb zrEyQs`PRz*ZZ3qXcRea$&~)T(@vm=sS6O9SbNN}apb11A#}ajJJj4Szo`WU*rSY89 zmopY3wEweV=-=Mmd>E^H7`*|?3bU=8lyVKG*L=DPnOmnlGaaap>7i}1tC_L;4XNNi z&D66Tm9LxO>WAL1gt;1`XW+5*UABKnajF;YJyaipSS;EV(AogtSU|IW*mL){>QXEQ z85Gb*_3a5o#KI}5DCVLy08-Hgd=&o6#~7=YMMkJB6&NVs(pokc$SirHr%DxCgaI1T z3gSQh?RN6RA0AXydph~;f4`Xg>U;GPR)sCJs<9RDEZ|x`QF(3E)Eei8dLkKsuIOjn z+Ix`6`u|aF9_SUkwhCJS$&hG3tpLV=l?F^wT`be6%I1KU7;2(z55bwhb*oes4=)I0 zBSgrjvU??P*J38{)DnvcYcC;iT>w2G=mu0(vaOmKALQem+Mk>Na!VLs@Rh2A0ZktP zhUE;^-VbVFLVS1$;6}1ouccmC`0=NuRrLZqU%Z)FOz{8Aio8QVYKsMHBE7Un;iCBT z;a>67dTC9m++Hly5=1?=Po4zt$_{ejL}SG1K|L^LI>slyJTO3C<8vvos%|Ny>U}AI z9*~{(ExZJhmjeI6CgN z#Bk8Z&$ZS!pNMLm+VWFPvh_7En7=L9C<-}t{A7c zfvk^_!?9bXYXb5hC#DWUj`{pZ*DEr+It+imb;q%{ZEHuC0u{#EbR}6Y;hCMYjj`C( z_}J*Hgh=C2k~L&}=$d0o>2eyPXADip?}5yRiOcpJ8febmWZ)ZYSmd!EYh82iDqmlw zWWcW+Ks$iVNXD&=dpk^e923(MCAB|OV@c4Y_W+L*veNr*%-cE^vE`~SRBN(18Rt#I z_dG{&^zs^~w3Q$QsSGA?fPYrutZGmPrxybEXm4{fd!eE;&_ z?k4a4dO!K?f4ZE!{$A}Q)M5irldFif44bKdSre=Z+$8|C09w6}52pYe?d5^0u>~xq z!WdwBs_JU`Ci5~pLD2$R18`~pC}1{?tz~ye@{nBU05}J@B*ZHz7X&UJ<)8cph_?BQ zxd2lEud1ef z^|-F()fXEAuK~bUOX9)*0M-Ki!Sf7Sp!Ho$C9>f4~*M$^Z?|6I<%$bl^i?At|r z*h2TEOo#i!c58DFD7c35#~S<_+K_|s%yEu)v2j;R_SIG5XPj#{Y<>-Q^VVYvoyWH% zvl6!RZj9W<_C{UG?=<+Z?Soy)+eqE-$nKBv4Q)ShMegU);|65!TRaFxY`#7tSoR{t zx^9N=CGzOZhtA7fD*9M`JIHuN-1uY|Uq`-vjB|6a)sLQKhqfIxY_Yu@^W*m>o}}2? zyG41rkB!djap=`~w8{9ID|sH%j5oynj5&#SNn1?UKGxd!;P{%GfXsaZ!K1pUL7s^6}X>eycX5@Cxj%PiZFXWTZM zbMt=R%gx1xU}etRbi7}V$2KP^))ww*+hPJooTK=X*B|t@HH<%t?{)DSqSNDuRG*9K z$7?%0M?N+fjIQq5q<$UJqS%)B_>eksvYt~{s=u6{Ddl0$-Dl5HKSv8=th|po%}@(M z)`Y*I{00&Htp3Kb+!}Vh57oIVo%>kg_v_WK-x8gVbARIg!{Op^X*}Et)D*x7z-m>_sywAC*eXZ02NZ+{K$42nGxbacRMirhsaEkSAP@l4s$R)d zP0whbK<{1wjB1H2IjU5Z9_xiHP?-MAAAVd;-v4E3!0bQ%^R@asUx-$Kt$0G<1Xfc0 zE53;UXL>73NewJpTW8a~5y-i)L;w^v+hm5lSP+nUF}HRYw84Pq^i;oOqgBuX7|T{- z0I*KnXh#81OKTCrh%dNI%LrPFkfBukQYB3lH9e=f>Nf&O0e`Jxm@fbTgcWpRYt`Zc zy=iMf-{mun-|N_X&_ZFWRq(2>bm)gn^|;nf@`GLh*td_9fB(NeX-!Wj-~HX(zBph_ z0c`Kpl$o|E*b5*F(2m{o?xsBosX+X(|4Q~kyHtx1K;a9$^l!CPyAXl)q?flVp`W#l zY4Bc^*ALSF(;x3AfBd)m$t$X_UvonOxIWX`=>>+$6-ibuD7+G4eZFM}FSd|aGS9xq%8DOn ze^x|280;HjTg_5D=-TyY*N(0|P4(%>&xH>^x|HiJPghUdL5l|7Ov;?_EUC8U$VZ0n z(bcTkaJ~{{%k~11)A%2_ZL;8SpxMW>w$${@ZAiz<=eyW4?3yX-96!?rA_KnxGebn_ zImM%@o47c>o?kwep66satt z%*H6B^&zD7NhL8i7AxC&oCKS1O3!($zgcIS zY0c$$8T0#BSnswxlOhF9!`}Vz_?6`LjW;i!>!S*DjXuxIuy(}C?hUpWVa}yFkDs%T z<76L3Y%E^MgeGQ&HaX`M{b;sWL?)oC6d#vE-i0r=G?FZQV`~t~-)1KysXF&dNJF5 zMti|Dt(t1sZInPiHk%~7wyPy|>9LW|d2So)hIGH?$x+i5 zIm@}xg$bi2HlD`rH=2xu1K%VcTK>%B8aR>MkSpPO2=`-Ui!DUO4@0$?9`V)B5RJ2@ zV>g!==RoA5i8Hw}jT_B0_XR;%LZ#N6^cqK+0F= z7X~1osOl8JlV1Br{lkQW2o1uk;RmKW96oK{H1!!ee?p0;Wi{SVV zV3Adk1cBcIZqEg#-mKMfLoYlONc!LX^J}YAok;f?D6BRE#YIcCG?myFRPV}W+EB?4&#a@ z1cDm?FCLBZay{Bt;2tt)TS5gcRmW6m+sp9OxA}~Q*#b!)Mfmoc3$@W$t8%!CsU!o) z(;5Vjo0Oy!_)JDn$^0OYm#S+XVDyVNAX%oIs+#v+@%qajJ{fTPcmMffvQSkqiDMU+ zY(3Kf_JDf_5FBuv7$_cq?5Y%|bqe@7FB7C1`(D-Q7pkPb*Ow5)fhz5lfOq^}3lKLj zTWd!vlplY%pZwwfxzP(E*OPDljut6%1Ky{qFkVQ8eJLTkdAT7=2sqEnDygW}cNPjP zK1sEJUwf&c>}2h&?T5f=?xYrX#bmB8J8nOzjfRdF0;I`!Rh4v?bYPFHy@>!3s4lgK4jv+Z7`wau?BxT zTlz=+OYwXcyW$U|$MnM=hTC=I@~)d-{4uRdCbRiqPELk=>kx8~*_Fpg-iEwTf2h~7 z;Cie`>#;m*ag_VT=i1z#W9WC;?YJ}_2oI?W$C7j@+F^3hxBKttetZw z7o1EG;~3;vurs&LK34L>?GB%78W|;eA9s2`AF?m;xD7*OjHTH}_Ab2J;yuRxLHd;| zV{coBv~in1ht?HhGTP&UneCg(p=~WF-D}ux4P)KkOJiW>?=s)|;xs5{UC?LWmjaph zHRpHxnOb%z$6KZzHbXA(EMQ;9-4dkB@1F9vQEoGS@rKzvjq~Mvsr5V2Gw#vNvn915 zJ>SKoRPNCAc3s3O=E=p*X?NR)&v8x0Hb;H(+%Jn!AyB2~HFC3Q#yUiE+bnS#j81u3 zqIWv?$5Pk^eWcmjwV+dE`H#;Q-1O-$ZckqAn@@hF6RR{n^{qmaxjA@-Y;pb%p&vri z*yA3AdqBgxJvKvP=P_vhx@&RKvMlMVgx`j+xwEM;cZx&va|nES(Xy7l!0GNVCjW*9rAP zR+X(lSU_H3mI92=Cf9Gy1(I&`0ziSm=Kyv>4;8h*r;23(lGr#Eq>Vxrju#R#2-u8V zMggtQr1ezjBJC)wido|)0wN#Px`9?34|)L}KrOwF|Knfpt@Xk`{co>S=?f@{$*NGL z+L&rsRbtNs=+Z-)ga_aRh{hLw8=x)#GW3Mjx#$5q0jwW&%nu6*kc|yitxRRG2C2-& z4l2O`*#XvR*AZGh1kjrJP@z0mA7o^lt4f%vXRGoC98oN==S*=xwrx5Fyp`Lsl1K?i zRojdQ^+x{H_vZp~dGVmqOzk2_WY(1Rpv?q;z1@HSV&$*3^w3hA>UgELBFnp#s+A{` z-~Q)w>&=Y+RIHz=Y8hi5mEp*Pp0xvb1#n#%KmO8IgLW0FI#%4!0k~`JMnsQ257IHS z_8(erwLt+SUkG6S>CX?7M^$%UT%S$ee5bY_T9eCH(kF$N>Oswy9HBy4Yo}`Dd&#-K z(R$OjS~k(&`AT0BP>~#L6PPTJo|i>ZRZSZXD(U3~9#~a4w%&YL>KykTCzsc>M!`2- zs8lv9hiN;)##)C~CR!hX>#U2KtVp(6d&rNud|n7+P z5{c)u^^m&!x*ihC@S&>8p#p=)rZ8Q{ES?Wfuz2Kn$M*R!5%2f3XSv*!EdCNO%&4y- zPbS8Az>eH@U>T|5rICji9r9Yh!$ZqMrmsyazwBbF7M- zIo6Tu_I+7Tzen-2GhVapa?2Wh26?;wT@c^yU0!$ZU|V|)p^JTq`F8QEj*;#^xdI)_ zBbmTo%xqn2!g&092u)1SvGEj=6C3c2X?{4j+hu_sd4J2BhsF1Xl_?#AVhUy@`M95=rv9b~H zIbm@c0J&PJqr=C?*OPav#pLbT`^l@*Tl?D_Zp(WPL<>JZ70SqHXJdZs=5ojKK_ACH z=J)ZbRfNiHz)Mnw;L)>JSe`&Ssn-7hg0y+K5QqtI2oMd>JQpCm(H&_m;B+cL+1ee5 zffoW!rx&W8U7Eb`PbL6<0kow{mAf~Hi(rhdQU@VocXZqRDMniOhELE**sHFjpBozIh z0V26b08-CX0SnlT-3ES&h87gzrFsCsvRxoN-$I}RJBbz5)4VuwE-+khjbbKB#cU-& z6MUZPZ>oHuo;(V07bfV`pLQA-P#pe)$9#PNnjb(A)X>{Qqa~&3Ju}F$64w_nR%ZA#p)dnoMrB(xeuno9@e(|#(`^kTRpZwH@VHhyrhHcqs z3$oDzAjoJ*Ewu)UB3UJl#Tr;6?|h~l&Pjj2wPQu>h>VDg%#-KdOOa7^PDJdz_L}zE zk$JvYckMvkZYwdtO8`K883wS+W`lsh;;PaaBZ2hDkxpbnYdoMdo!G@(Aes#j0kZ+U z$t2#<%L(dTvq|J^A_G(sWdwk&G5D;n#dM;Q9+WVVYG!9&yH=82ZyQO<)DwZt=yRsY z5|!1WCSb8~BLDBpKIRU)DB)o>tL57*%Vdr(At-4QOUOikAK;r(9L(L(eS!3{2l?he z`OO|0`qXFs41m|%o=LB*f!=OFC3(aSzJX))sCz&;eM;N(aLPaAQ)0Vbl(j&zasyV> z9T$_8`oL{(l2j-CVYcxF4{Ke|^<=xhqLs{H)BLW-zZlgV9|cYO)#6NGd;O8wLq7}e zg2!d~q)9E;c+ujf5trZYKDf*nxqK_)da09B=WFJnuDv=Q-_*r7W9wqneOcvm8TWPa zd2o1)r82wy(HKX>%!hPz!?g+Gle6p#J!Np9UUg^^A4Wv<0t@eIgx zs$2X8`9`l;Z-q}b%DNM?WxiUj8$R2-!*y#mX+V|n5xQv zU)hc#exa?(OYKRomo7=MEk5g$s3?mw^^)GIx{ddB&$R{J8%HlnEZL@Ub}zE1F25JY zG_wCm?8to;jmKN5$Lu-R(J#cO)2HDAh62*Z;r$(1Zs;?xhy!%2#v8ium^9n<3u@wY z`B4z1Jh8Fnv757#29i z2}vDp#hT2PJu9#hK=JVISU~b1>|Ldv0f3=^UxAEDn^oQ14D2LhRsW5TcxVc|6#p}U z&w#{a`EmfTDlsEUz54Wiw?cg33`a z17yz5RCfcAp6qFFFg^ET$^?(Gw0K2xtb)}7XH1Iy(N(_<=N8hL=v zWM}uOS1qFB2Lgz%jwF)oY34!xlK&=}2UTw|egMC`;E?)RX18KM;4zFTMPeEAm}q54 zh!wrukiXFJ;EI5HscaiG%Fm;nxB+4W|H+~z4q_&L{Ii{TC!=1VIpf4>YvU_!%C=%P?%~1%^UAPMgIu^H)9U-V9f1(uY&Eulb%u-1U&s3rDx{ zKC&D2f{@aKwu>(oIb2>#3XET#lpjCNX*`y{)J9u~1&@n$tV{W?GyWoD_~7Tc{J0&D z(kJ+WwP0&pzOnNf4(d9tD;+8{`K2x{*HF23h(8yIlO|p3+MB}4+v~cTeJv~-_ptj@J(tNYue)N!+Er#?!&EI}y;6Vn6@H~LZN!_JpQUk}E3qkw zIm4ANy6d_c>xcC{gIHXMwGNA{h}sqHx-5sSrp5yGDr!~Ndi8#R`A-|{3rEvuJMCZ} zZAbQJawAE)Ui`TR%QQ}zW0RG&m-_e%JZ)%{L`O;(uZe+8erBfe*yJ}fN$I$MoQ~?> z>*(s=jnT91`*GC{_igI=f@VqZ6ojR#hv_?$l_}pvTj=p0v}s%#3;k;2!MLFqFz!L` zO1K@K4&Od|CA=QK7+#;h8g5Mw!qIpu9N&H~%pZId2K&3=Pwsst{O3=ox_}DLmX1v4 zR)M7f>)s%!&8o;Z&RLl*ww2`ymIe2d6ZGZwOm#Ab+IY>fcvV(mi&6#xqRAHC-P^XB z%ri2j1?Fw<3$(kdOkDwo+X8*b=2b$ifzW~rg+rsLGO2~$u`wZ~GtLAQlR-=^WgZN^eNKm8#JAi8kv)-yQi4=K|cG$p&(zc^dJy1N7fE zU=*kfAWto0{>ET4Mf^j_j^W-&;IoQHo1SVH59$=3DoeNzs4tFS<`IUq?!;HJr=FQnfF}CQbqlIpvoi_dOVH1!1!W$~*qMs|jE5=vIAK`7A zPU(C_6&G6y>za&k>ox!KA;*G$v!Ss+(p${PbN1q|?&+*C&MzCDoXUDO$x8;bUi$dj z*wDzjcC#W=qs_+%uA_C^Tz0F14La0~F_k3HJ~DJJd#Y~4PgWaq@#Er?y4a|T#dT(I zY0O&C)^LssH}5bq9P0FbhQftpWBfKB+~EQr_*#Ly&LvH01EY{5px@8{=#xT9E>RsJfFy&YHyT1@Jx$VT0u)n8Q{CkYe*IcWi4(~9l@$Tt>dw7TfOYXSV{S>8aE3v zs!kVk=B&*}srd)tUIF$fR=f3`<{&Q)z1m-047`W2CYtdFt!&wOIU#R&JC zk1L-GIYoQ-ImaIJVKG<+FurIgG%4EJu9Bf8GPi?&1@1@6JH7V)>Y2EK4i@JAjDTx@ z_R!SE%jjgZ$6tQV237fJN=yELe=0krkEWT1kR?a6MyWsCHD;fUE_~&BI@2guQ6tto z$;Ov3v-+&0-E7Ylihdkg?Phcq9PcG4Mk8gJs_j%DA&mBvkxRz# zOrRa$CEnc?U`qLp6M=VEZU~$vTU9_cjyK`BUx2TD#%c(q5)2F!_U)i4pbd7^At z>Iv_uX7JUUyLKjPnFr5~Hrq3$H4ac1?b(=*jr^4JYalbAF%NTi4+UV7xk@RGkus)1 z9{^+r=q2DW#!lQ+udj^ak!8g4^+BMpo~n^g1S!erwg*S%-I;90UZKANhLH)N3}~x* z#RB93>H${C-kr-&b15QH)Av;6CYUrNks%(-M#R~e3M3y1d{&q7rGHBfJ2_N25;AkI zjI;q@1c}LNo@nzyHYNlt27sr`2(_#kPx%S_0O&iBOyq1U^V%}GwV@v%FQ7K~GUKUn zjf`#i2p}4R&SZ=ERD8(}KRHsF6u^7g%F6|@*E9&k1>BXc)Q@I!KzI`i$W)UMVf>+! z_}f1#(A+_T7s!T|k@AhoY3L;mFA$h-N`&lalR^TBI^dK8;Uxk%zs$4ofavHq5cqti zO#-RUO&hX$QjixKWOq{@hB@m=Ddv_Z1SLmK1&~jrJ8`KBFp4qNk3N*I)K6x)%dz_3 zD!{`To$cj^bTB93jGDuIRMWn>CbVlmmOUKWN3Y-Onq9sR*3;4TTW|Dbj6x$mzzFxp zOS`5RYHZKk)5QwxU3}Y4Yqz6Ghq}?x!*xABZNoQR=Ie3w)GBe%X2T|Z;8D!?pJm5I z9E|=pP7DKY0#g2mHF~9Qpw!e)UboKaX=w6}OJ=r{@?%5XOvh3tKx@NAAXOG!X zHfurADiD+tw0vh=?&X|I>Eg%Br~JL%Y-#bFYNm1HTi7_sM$em;SNDAbt6M`$gTOuf z$YZf+@~)gpeTbd&5Yf~wz_#tsr}l|i`o}9q&hCriG%(J!{e`Z1C(t+6sw5aKG+h33V?GKrj%tH zyuQxk-Z0z^H^STVPlWFVfzIPs!}m|04EH8G;dCZ^PJr`Zei(Lo25@SSurMW1f+hBa zGxjJ2D==(&z8zjW{$%*# zZo0EsA*D7{=OXl#sjIwUfkbg5JhEyT1ZV_64&Vv!Oi)t#VOwL#UY@IMSN*2~XLpqC zZ3rknX0nJYZFmO&kIrh5|CFgMAQ*H8aJ4_yJV?eAz!e{eYA%45@*$MFm@`%YF!iT* zDNiB4PgJ@?ZnAPDLzSf%Nj{q)_64Leo@`_4cv~42CRqm{@|gf_G6e^qz@X-$-cfCD z>>-<*F_f%;2(Xy)D3qxHJdR+0ltCS9dr4tg(3%Uq2#Dn@l-Yc&OnEIbd`1oMQ*AOi zQmtrW2?OO8*(E-b2VhR^ZsumnT9mE^+KZ+y`o6M}@eyq{iQ;D&?z-Qm{?$X>#*=ov)KY%fNrx2fBjAISsFV{Hf75ucS%=2t-O(;Ab6I~aiD`=KKpZUzY!~^!(SA#|=u1h1qBj~tfx3pSR_Y8ZK z`m`#$W+yhTI`7d3z9N^$ZJi&R)aCp3!#;coM~k9-Z`-sk9zTBGH2)GmGh)%7o0GlB zyCgsR2GlnHZGB~4fAj9DG1hfnQlBkbyD9C%_Fz|g3cdKdY*|bd)P7Yr#MW}f!OnZx z=b-a#I@igIwEj$N8o7GA>%43vHo0E*49(ogpZ@7lXFFQgJu0kSeVkJ^u(m#z!A)|w z9!XQ_(sfAxX7IKW;nJHzu=|Pg;+jZfYH>_eOq>2~eN{f|-n%Vb#)I#)9q!lkZ}?)2 z57rsKiIKOYJn?5)r$|N2SiRzuc*%8>|Eld}d1a;rEqbJLTc+c#h_Mzrv9VqmM1Crm z8@M1IK~`q1eScPqC0ZT#cEbV26)_3hW1S0Ab>DgvIK!mhQQ1n%1D@&OpxdW zMxF}Eq*?_T-emRyI-ec^f(i&mt~`gVE%^e#cK}K(TamqrRqp-Y@+ z0ZFNGJl5`)qbL8ieDh@moT|SZzY%(ee;BsHdzOI=08b6>$q8PHwVvT0Gn4`4WrM)@ zY|1PuiEV5p=!N^5&yECQ?;dO^9<(tbK(YW`%3GipaUm8uvP^8tJZ9D4JIM#ERVKC8 zRv1b)KsVap+g$y+yVWUDa)5j_!GSEv04oZqBr`}?O5+h`~6AxrAPXx%5 z)lBIWN`Ig&I%(zT!dM&G64#swWS-?Px0mU{nWc16WDsvJyC)}r| zxdNSRaRB^t-bC5Tf~RD2(y{mf7TY)fI}?~O_Jw;9Ih){d5&4@LTlNb)iRtutypEjW zgSQyX+Fg`Z^sjd%p4q3vy7RH_IP~RNY+=2g@-_;exD7P8@6t9Sgw8K?Mz$Xp(ZT0C zpGtu zKGt}yymoU9a#dZ2lyBZyxhSvEWbsz$E_&PM>#`1oq{T7O)@aw|=~`$|zFO2L_80$( zs!U(Kdzcy*y1LJZ`Fahy#EIJy`FhN;hO`D6`7)|rrt>SE3#CiV59~3NGvZ~?umZrq*Qrea@Qu)^@PTJpm9;=qOYALbKj&9{6%~U0DbkS>hAaG4Or`%#J{B%d3v1sIpts1miAY#3;H@u3u=0k$8FKkauvO= zkrILxfW?B3EF1Z>w;yiJZ-yVtKO4S!{+008r=JQRobHAD!tc(;O2Sl;-O>D3*y{;X z6Ye*+f4Yw_@v?h8r@`{LvA06P&qTxhfNBfc2ab}!KNvq5?wmgnuJwLYl;O~#$bD`c z-Q)0&N8uN5>2^-XS^Slz&wVT#O6@YiyD~A{JB>eQho>jhDh8Y-!&Mh@4h?{sfXV=X zY%oWu388U%Xe+B(05IpiqV`N2cMo<1mQKTW{^GW>S9{^9=SZfd#KtV#et#^0avH8Z zu@m;LXiK*PY9&Xo*#KeWN)G-WY7;+zOq&dSJuom@;4{E8nbl+t`v!aPBmw|EImj;F zG8;4=Lj^D%1BOdCv9j!01283vKMJ=6uv0^sOk`5(Vjd~HYZfy&YpCWU(ji=wQV|5&SGQ$I^{0_fh8W7VxN%SiL#={0>s&{a5xm`D)1Ro z9(*)bP3no2`k>Y_wY8~19JZegqo@Dtuzl@U1<)c^HMhn0@bI_7(T{#7Y#seG)mVNI zl#Fee-Il$b`D;2BFfLmFU}-0VnOetI_ChO($`)>C{`@%S|C1I&}21Fv)a?t_1@MCh=H=QV}`Zra39I{ zD#s=bPmHo7C{}<+bXENu4E^;dy`9A}^g^t{=tWV*O0~uXFH-OnvTOV(P3~Q$(#= z#vj?Wy{k7~w!W!G4^vhu_iysG$K57vWNoyq3!lejF=%uU@dahg<1(KU>9c7~_0}5Z z-f;iW4qYA7$|UmZ{urL|Iy)k=5mZ6UM!*!AY5 zR{Tnhm{;gq^K59~X7QQL9jES=HZW{xj&mJ5o2G&(^ygs%n@edn-5NO8h4m^vY8zh{ zD^6c4!#itq_3z905HfxQ@#q{mA*;^sPRo0+oCo~Dh%zN?J<{g9jaSwQsiYvQ+p+rd zhxv&|lv(GuoFb?7mWFdqrzA|fc29+HYw)}P=by3c=I@<-HoSK7GvU^W{-ymy8P4Yu zWlJB1t^P?E;TRHPag=bZ=x{(O)*neK!kg582L38ci)2r$G+_ooFWgQ;g*b)}CQqtf z^poMno&acdMJ%k)DI%f`wj|AkuVQ2I+=D5{D*pF5_OVi{xQy(w-?V$Y47ZhS3m_Q* zI)Ti2cu;FtOyxN0^8)Ji1@z@ld%Ph=OfWyE%AN&`y?SFe9Ns(CkJ81Jk$U5)Q5fy* zSf=xi09^PGz-$=EH5s;j?TFWxEJ>0sdOM(cDYkK;a6if=ASl*7#vz(S$)@}S*}FY~ zxNO!(4Pbz4fNwxjfa#rmWkZVwa2an0NDsk z_od@l8vx?4ZDn`kHLFvsi8)us^i-%XB}yi`9xGcKfEnP@Hg!Z#D_IdiP9~E#u9OZD zYASkdfGGNz{DxlGa{Z|tSt|Vm++yb}Tnqgh|4lfae?I)1Klp?2_B-DXH*f5RPkrju z@Ua(P40|8@hvD7_uY~En|5bth*YcTcoXS?|(3d|bX(F-2y#dcNWoaJ?l(#Y^@;7?2 zc_bO;1pJ^cxzcN-0?m{!>1(1yWg8O?RW}(hS{8(n!26>^)rZzo=v?wBeL=Q7G1OPV z^yx%pPPTX`;S1j0P_l%Kc5R-h3~wv3AzoyDlSvK`{_dMc;laIGc;e|Y;-gX_E? z=!N@=qk+zp9hrOQRxoS;QPa2{2q34T;sH9nEo-La^>OKJ49;BO;{gckC z>s&JB9uGby&YOZ_$+3yKT$KlFrs<%MHR18>UoINy)d8&qe+^&$rA6K0exeU~94xA*@22cD zeqFz1l~viu9rBq=4nJd?+ZlT<<2T=b;%C@Jxko;65i<8}e6g!Kck8U%RA(gfJ+R6* zrCD~34v))`>XKf2P&BP2Z?9W_o3;&~KMa?}*gz5zIqIcGKRHdLZ2i580f^L(e=cl# zZTcMN+UFOo7BqJH^TYWz5;k=B_=fKMbJplIFCKSJUzVF1lV0c4e$nAvjIYYny<5fG%eH?=S3P-0yWM{;X|(4G*4x@eNAKP+l$sZUe2@bjOyrC_uZ)+ zUv;CK+KO1M(Qy->P%qo%Q@m6Xro{N>{*4uOrv6LU$Kndy&Y$SOtJyIS;yKyRR8?HRQ>bjfk zT;^x*_s@ws-rg2#TL`AI-sI}uT7(6ZgP69 zGy6|%8sl`S6jLlk z0URkoVMF!HYLKX3P_6(z{RlH<*^xd^jpOrkftI=_!*`%;V1QLJRIfa_W56N6Cz-C> zD!qU-1CKqURA&%Ei~|_#gX>Cj09YJthv}x$87ij$NIDWfCJitXIzYk*EQ*6bVblxZ$Kz|t6hNSK!EpL6FJN_y_>4!#AtV9c1|e^AsM6nk!AE! zq5=JR${PSHd*&`h6x4EP2F zR{QA6wgItL8yi4fW45%B;&}Hz3lGjeAHMn*e-gg+`tOCGIrz8XNu}Z6`p##<-CO@j z`1ngdA8vf?SHk_hm%>Ls_}y^zs#aS+_ls2@%bObG>Em(|l6%(&a`P1;D_q17~-hZ6zo>?vC zGi6ry6;ssy21rI#C2w1{Ioas=Fv=28FQ#WI@gb?21L8>6%1UP?l3h;R@G^y%!*0rn z*d~Pg0+|)l%#q#&$QJPg*d|sj&LvNv{mFEsf9^mC=ogWQGN_I**-`!y5c|Eadaf?~ zYCreR+NQ{F>Ig5`X1WmD;#2pbvo3op)Nh03ufk31vdhL5eyu(neNJ2eQ{`go?ebn9 zQSkN_KUZaWyW5p=s-vxIa~|uQSLK(5ZL25?F&Tmv^bcK12W{Ajo$xY$wg7#FQM#9oW^;u@-MVwzDx|rsX*+5{zwXf_%YnFy@LpG^VjQzu zKGHR{YRsjaI!^$rfOrRAr8=rxlE5dX0p$Wv~SgG=Dz@6&5*@yR=OW({S>>k6 zyV7m9{yif7jI5)x^;zk#@5fS!^#A?kA?|}Vy?ng!gf9%Tz_tz7Qij^z}lWz!FfaMsWF1X0WBjv$Mw*O;m&FPfIz0ZkPTMWE zvd;tLGPlfOqfBUE(sCHj+;Dy%1-ge8iU&2eUE%a;si2Fq<9PO$=OU z<3{x0d71!hW*!d1)$ox3=kN7i3Ew*VO!)fvbK$MyeeEYFyzxx6oX;umdnfE*1Y<4m zG6trhIIF9t%+_-)29t&;;Y|GNd_N3J5`Z|DaA<7x8gUj40NCt%cX}*La~NJbeM$J7 zFh@YM{D?N0CGM!K4=&YM!Y^^HnInq0uZ(r}kv85nkT|K%k*7DL^d9f@u`8SfP>+rJ zY zP`)j5Wk9WU$hiQyhsusUP?-*tm5;-NI}>Hto`+{Yc_8^b?4_Y}nFwIsQ+6{M*8qs| z0Y`0U*`RyMfK1qMP=GVp(Y7%law+klHiAtUtg4L=Yy^UM>sqP`9M!$fWOM^kZ|{v_ zon>vLh)?MQY#yla3z^h&{Qx}UZZe)J7arOskNzjUP{06$Qu-NSI_>hhM(03LNCkNrH zKYStl(FdOj-+%B#xP3HIMz{D3~Ylr7O*!;kV8WV+w^Fm70QOO=gs=0Nx#nOJobdl#VX#eewj$Wj66zXnW-YKR@Y>exZ%r5u`X7Z z_2W0<(X9hMzq`(jJ`EnKYxx5ze!4FgTU|HHN32HSUjSEK13u2B;p_OF$y*-xbC)?M ze!I}EXX`34Y7*jKH}R4$6CICL=cR7lIFGtM)F`Xt>%O~=9Zf+}EomX&WusG>)@`U| zutKB#$!B5|^ZM^m9+>EBv10Cp)yl&yzlc6v=EtfY)p;;>B8q&gTRwnz?o9 zvBz|Y5V@%yi!oLrU-Jh4*!QclsGJ-9RUhY_KfPG+z$SdPu*Q-#u^_Oap}KCW>w;mM z(6vFlMeeGf^HpA5zmL?}mhwvN)Suz;NPL>=39b7-Csm{Q(+M@!WwLAdweDEijnLD3jpGWVjo{{0S)xLG^lnv=GxTi8aSXKPV=2fgSzKs53dMFWeEz zq5Aq7!{525y}fWd+zj9Ae<^(P?A7q~$xGq(M75odRbqEchO;&f?A;2xVh>IQ-%JfN=0g6`D!I=oh>~HOc*@Jt+(0bu( z_g)P5pS}{V_3uPJV#kXh?2nv%U_58ia=G3c)u0&O7hbt0z|v?$Ctec) zk0WK?QqLJ9wv`qO2u)dzSO!9Jq>q?e?dtP$)lLxryCwc)ZW9?yvQ$$53dB^?Xj|FN zvJ;SWD1N905G~JyiL#?P0JR0P8h}aGHbFyxpg+z9*d~GjSOM1ow8sLY0q!YLfu8}i z@#BuldBA(BQX40htvm|{S9j!d4U~^&lhbg1@T=jDfa#BJE9>~$*TQFCcn}Vc3E-*C z)6IiBVQ~0=hp+zmli^b@e_7*&*blxK?oIw-xZe98Rj~ZFXh|=h8X%6ds3}cm>Xqv% zJMmyE{KW?^hkyBwcIUi*UGt@kb!O#*=c;P~m^{(qJDVJ-0`htIU_1=(y|o+u z?Cp<-pMCSi@E^SNR`~hn-wIbo_skD5qgnGHoLmNqhd^Wbb!U$_QJbcJD$t!56uVa= z7;p10-d9z21HWpcPt_C`Sg-77;)jCJ)ah36Xfs2~VF2=LsVhFtCa9z;pL*G}p}YA^52~q($P-aSz@t7` z*ww{+5uM-_9~Lq`Kd>{s&pRIZMOWK-5U(>^UHLz9NBjgTz?^nEM5jkh=`DWUIKC?o zBc1uyG5KlRL-7F5=7)Od^Q7)*dp=aZbk&XjY1`#^plx0*M`8=tBz7Jvy`*y6I?Dd0 zXfdo?7kse0onNuOHtr+7yXi9q8@-~F5uZSum6%y_na zSFGw$yrDOJzP2}~;)P=h!6bi^cJ^Vp<#1`7RMA zKJ^u4@txf;uBub?zqn*$FZ$ivFct~rdw#LL?PlrU$CvRv4b%CF`V@LBlT|mLadiC5 z$Xx18`8jRp+9MI)bHDVNlrk``3k|s}E|{+r;4XDmZW`mJLf3ZN=9kuS3j8<+AdXTx za}557;4|$?G?ckQi}c?hd@y(}yg7e4d}sc%;q}?)!*{124|nx1>d%G4^#n)_&X2+< z+}CD<`gHF{x6sFJP=j}4zQW)57F>|0Y?*ObzQ z4iO{?!(gC|2ydSZ!w+Vk3SSxhxss`&7J<%l7JjBqIuA6h;?s0&v@QNl+f#n2T~N~J z_NC+V>nuP81r}u^xJJ=|HW{?EQhni=E9$ni`5zgs6K#^$7eF@vAQq4q!Ft`>xHE0) zwj1u>JqgDEdaq-Lfy9gfxFz#>fNVfVm1CGt zl0n)U$Su5atQy38!6c(~Bz*ydsii_TGnvHOLNft)&jtEo6B{RvK{urgHV{aFnanq- z@POO~Obdi(_Rq-5CQDbh)Px=k$b?q9z4*_k0*}RKCgc*J8C)H`M=G14{RO3~%9%(v zD?OrYX-c=4zmS)$2sY?L9`XV2rM<=qJQY~lN7nok^5d~;X@3-c{u6H~J6v+~pSh8; z67tpl&b=`D;qQd+eSbH+^6AfoD=H&${`BX=-P`{t?Dzk-aC7%vDs03O91_%{HjNbg z#z!9y|L1qU625Woru;GtPu(1+X0I4*D2*bbkEC;iF`Ve4TswB zl6fH8d{1?qi8;V#HYj{>e`=sVn@FDO!D25hm~Z`v+~$XDb>ea=ohiXWHaCGlzC7<1 zPy(q@6Zz^99qFRqlnTz15GW3A@WI6n=j~itA4*Qta%Oxp&p2m3yqOM)!q_UEkAED` z>*&feca;PBaxAPe`toQO?BGL;58Q@>t|cT5BY*OmiL0iy*(K)c)}?#V=dCHG>iGK@ z=iz5~H1#>nh0ocSqm)mbj%okne(gT1&cWkmSE`@u-0s`O&x3aQ^?Iv*YSb-@ohp4- zsf=q{hjl#aXqU?_KKtr;ZlW!n$CO|6TOq$mS8uPgFXdOgwr75|(eDvni1yZ%&dr}T zi)<_u_zYTQ&?1{oEhfX{tSZbM(mu{UM@~rW zJ;wM|W4p~&UG-_>(bng7I1TshYuqWi%r9XMCo*^DHchPknE9UYzYS zSA^;Kv_F=qDL5fNo%1Xt@*!2fda<}7_lGCKKqcNF)YCuBOA_%JwQqRSzfztm1PWM2Rw z&YYLcY+ma>ltO3Gfa0pOJK5(+Hn`-2D|Q|Fn9-&ZYWKx;~TP{)~- zEQ+vyO=T}skD4snJ=IX=49Lu8dSpz`tUL(fVvvz_%VZCgsSU_#zEKN+Hb5+pINSnh=0z5emRoo3gsMRC{KsdHcg3Ov5+d z`(*e#FFzOl<5yk_FI;_3altwylb&p30QI4K5lguc6U>WcH_OLUy;xvjj3cRY%{PsYHf>)C$~F)!i9r-e!71LB{uem1voq%9na5Se=7Z}gk^Z>pVjVCMK)jA>h@|EA8 zi|35yszr%vUpj8nI$eZcRnLp?O2@XiCMR>6x;S7Rtopf-Grq(N*SRXfZLt3AnRHpc zt;*)UtE{Mh^ApAGsFSg&wq-ih$#MI-tRs7l>bZFtN7MZ0vO3DmpY{2`4PA7Qzi2~@ z2lw$ZO`s$3qv@l!E1zX@I`zxmMU%_%-Blld-Za_ONuL#n(5p*4cVXGp#yVbjYte^w zl=W?k@l;Q@YU@R8GFdefxha1~TZ%YC6h0(|iq=&vZlf>$)@h=;ta(V~f&JK#8EH0W zO&&vjrWu_CeUuNi@i!dJzUClU<+CTsh>V@RwRLbpjDg6Ztk~t;At~2U`gsgC#kbSC zt5?<3@J(leY-0fOBg`ggUuV&qd&Q&B35NPS$QlF(X11n+QolxeT70Vf9I1Rz z^%|_mIk9md`x)KqJr~{^sE+geGvQnFFDT3TmGHp>?Qy1rQh|4&uX?e)^Y_AbW+Z#e zQC@>h0yR9t0d+6Nb75d4Ia(Y;um3%MZQnCMOEtI-@Uj`8O^swY69)|CKAt1BwF^tN z5zy&Bi9);nqDiRzYS<>cfhtG%E)4QRk%?}Nv&V?>|t1jlu+(pubmPl70zgBXj zGJ+Xo9;%1&Ndgg_RDwewsxn*w0WktVno<*#;K1RA>H|pYW+brLHX$TKR*pu-nQHZt znM|hgT;L^X>3c%w0hIy!2HI55(#oX`KsR6@S*BzT1JuU10^+Z+l(B#x^kl@Mpy?r9 ztd_EXUNUxP0l^Lx+X_l z&IG^$nghHdm+}wz68nzspDLRiP(9l~QTmWQd@>ObE?|4Ag>j{SU)kQu$~_p!Wfk%!*8TUw zKmGQX!XMsxIXrcvAD+9u8}>C;(6V?b*w1$bZc9(*Kz2WvZ--s!Iqi+L#)QsllF!Zr zgrBPhj^4tYNsr-pOcC*LtU37I*M{L6?|w1-<`+H|e(AZd3n)I8jq^V6yvwh=RN$WsM8KYfDKW$yps;R6%817AyzC$=p0XTlc8DLS0X>y($H$S!j*9Y7 zLWZ?UMzx$R`2v#l!yNGffsE>{9bRTIk9tcX-N}ZgM8_Eg-Stl!(9<^8)XNHDd3H|4 za@j52>@AR;eKG1Le?Mvdy)v`v;-ib` z+|^x(D>Ob975H(FL1v+$$?$k=3Kbhuu_2}PeU1#MGw0_0RflVAJ|CI0a^4!dvi}Y%BMgAr%lIqxyE0qtxaRP(A14h z=ObNjb+oj(oOE1D>+_JdEnfRG+o%`2m$bC`J004jqtjIT*PSEh*LLp88=aQ^@*YJELdFl%4J@NQO3kS zd+T^%)ABjRy2N31j4Ex&O#M~qegwCL{>x&d={b7QIdQ%iU#mk!fYUpz-<$JV9-vOY zDJPd>+n=Y4+?(dx)>E0u;^3^MO54P(uK3%ktUtFY6_m>kco*@*=oklG>|VyZ=ts};8c6kY`LV5tvXsVsTedinkU2N) zFOM&1{fhpS-rG6T=i`laggtDEyxm7vrXgix*XH9&YdXgGC5qUpOYe0ZWyZ$L)W|#G zYjFtUoZdU}Y19@z5o?KSncpT)lYoz-1aA}G11}py`l%8SwQ=BkgO|f={a*~fz&T&eZAm&^eFAQFQXN*n>HA zaX7Cb8K-lBYzP4urcA+H03?aRSR)8dPK=Hsqt%%J;++W?oC~l91dX|&OoT>BH~=X^ z$fRbRfxMciseTw^Zy9EQ)&d4aOO`ODG{|PP9U^uA;O;E!-ylO+V4#5Gp#V&Kpvj68 zWy6wDESyk3WYv-h3rMWe6xzrTAbos1F<_Yb(il#*t28nYeW>yb9M}yQNd_{&<(7V? zlb9uqty=)|ruW6? z{xtmC?+M%=|9bcjU;b6|{rt%wt1<(tDXX8Y6U+_|bH6AxARD3E{_at{L z5Sm3!tihXl)bfHd%KHMir(s_&El#wdBl_~vfcckKEH1dRC%aupcZ+Q!$Xepb7gnjU za^s6|add?+y|n;N8&WupdI`E@E%e%zZrMRYP zuB6iRd{R$1(I-$6=kJOgT!@Tsipt0BL%Ci7pd619z{N9yx+8B?KF|Tt~(#YL#hm=xR2( zejd~CGoDp8+&9dZ^H{}QWxb1$IWL#rAhQ^;{HjIu*1@?h8I`NVt*cd~X=S)#r^~1t zhW~ud=x?K~8^8HBG)O(<5?0r&X}-L@3rE+){Ny*uKyPv_Ia{|n60)?rvArN-WF6;% z!mpZSFg)!`uYIha7bNK!CzQaui!(g+!~StXeeAn<{M$)?XKD;FY-f!C(F4JQ%Wc(g zelI*9-srs=-spcJymtPD@ZtG&+h0xp1U}5i0-a~?3%wH7nPHEB&4%N_1Lx}pOwt+^ zu;wAdo{+$y7A{2tY82O#+Jy6gLI+8joSeK08??693T%P+;(}CQ6j-KE5Sh7jS=82iVL;eQoUY+Wvcoq zLyb98#6+MsIxB*U3K)TUl-(d@n{o>rfT;sz*5bI4svKAbtH4Hdy8k1kw#u6$WvBuW zqR$y1E|Z`TP_nAh(a8u_pLmfC3=EDfWV-@z(oVS$$~`d05$ufMFl9!Fq@%m121xH; z+flzVlpj#%T0teynsnMTP4rk9veX4W2dEW3zPq<&r6MR0ey)DNX6g{1=}iV8G4{_4 zD2}q?8xLv{D=S-B+AB#xfB4ny{|GB%>PJjSG36?Ma{uxw@6Xe>;R;1&oxhaa}@UXtVGG4d_NUf zeMZe{l{uMVkRA!ga?rncZ4lnL_4DB$ysAwKUwEC_5gTP6%RV*?BpZCBdefl9JBlCT zj+YdG>H7!7h5%>*++>jhvQr*|7bUcB;Trj6daCjp0LkiSag*^*Ep5uDFiuWZOsLMb zd^MNDCSyQ#bR^E=gMnBAj5fZ?*2Yd^*3UgN&3xis8qON|$}L3OFSfn$+LY6HD>_H01#rLb6a4lhNC3R7YVy@-KT;>+1& zVd}RRKKqO_&(*lBljU(%rCmpnI{wS}FvfM@algvZl|Gfx#NR$@+<8qzE$wZ#ch$3n zePh=;G3U(M#`>I?T?A9xkp)ki4;aq3f_#dbSZ1=Syj%soQ#`Dr&Odc+t`EIuBqq?S$Dh*Rq&)QYu6Vzf@*^mD4NznNowPs-0J8rMhC!FSMwU~vXD z=Xzb;E6e7+hT&tpLZ^~@Ug|`QoWCyjnl{bcG|k{;y%7)UuC|!_^e(ZZ>>9*gRDu0w-^2>kE`RH zw&4-XsXjB@Q(d?jkCZ7q^`pOrTMzf`DzaJ`RyyHdnzu@S-EFfjv8OFY;8!PaagGYf z-dD5~SMHyp>lH&_<7!UiG_3DfABaQ9vCK~SOw=%p;N~R3s{cyeg-u1V8_;R7M1JT}Y zJ+L(JPZyeK5?ERM^fB*NB35Ta+nh0Xu#*jTKNJQyJ2tHKo2Q=+zjE+3Iz=C&Mq+KO z$*laMFCJp9gJ(|EJ@1I!OMW9N^+z4K`#S9_`AHqaJ|Lt3LaSFSGo!%*7SB}{LVzL} z$rC_nW$M~yd6J9~WDS$~JSG#F*A#MMPubIWiVfWWn>m=MbCtn3pF{@({>hK#P^AnD ztQFuW@Nsg?CUVk6Xdgh~{o7*$qK7KeK{;nDhajGS)PRm>(qkep6JQw77rJwen@@BW z-B2JWbkta;^a7yzP#fwoiMZ}efF!ko$*|o~hBev&8jp4XU8SD_kgQ!wKCqb`^`6-n zZ%eCSqHI|*o>>?G^1IY<7Sah|JP|NF6OajCtOWG7z?TANpx5%{RAB0sJj8Yw;T4+~OQ?T$cR{6c0h=UBW&HhC}&cLh)nG)JF&;@jcgowvf( zD_;oT{Qeu^AO9b}8x933fA+=a!wWaBg=_N3NQmx0`fiJ{Ez9kBxT{*sbLrRbkHT0Q z;O{3?u41fnPbF0-9WvV975J?==FL!lS2Pn{YAz^WAw_$qs!bykc^UA{_ov~5doPCn z^ovh~Uwh?i;b3@Mb}IW=epS-6*+u!6!6+^&YGZHf#e}_JP#mx*w*_*uF(Cl6)|>ch z{xm0KNZ-FB!*Q{E;~N|THp3XepI9OrUC%1J=7qyVK>b9zUArDLxaR`-dqN%bibXce zEn^ygb67?^4+h1iHA$8~tRhA6ySk`~p86|sT{z@s>G3!oD`-VLKaM7J)QS0BbuK%y zUYqQ3(+5LXb}K~1TDBvD13ATqrZqh_zg#arr;73AAw783^I_lhsN?Bl=}Y7FX4w-mdo6)L)I1E5tBf^d(vLXe0NB+>vUyD7wiX_3h0TZgTF-pYGI( z%8fDJK<~rr?eX5rJFnxHN2Tj!cwt>z*6|z9BVW2p;e8Y7Y zTbAlu%R8>eF8phUml})bg!5bQLy3O18|TR=w-x?e`@Q=&rLB`v*Pru<-!AKhT}oS> zE~y}YVix+zUJ-bEO1tT6k40xen32D|FPw3b5xG}&}f`qb^$J8+6nOM_Y8drBy^?j9QmARmb z0N9g3Insx^%-WJG-0eT74ChaW*Tc_+zY4z;-t2ucoJ=VTE1dy{rnke^bRw)z^^k>6 zv3Vd^kpP@5h{Q+yDDxX1B(gMnhR4SM;53P`%Z_(t-_4l_MxcfD;T< z(u6P_IA*G>SAg89#`H91qzqj)Xd{ai@b^@RF^-_VaWqb6jisE$bgH)!av*$69s-2< z?(V9tuz*eOf%m#=6?UKNZTWw(tH*k1Qz;7%c0r)Ll#a{xHv8`(X zn8I@1I6yS^u}}bF z**vk=S1oD4aMeL(zNCWsRYzLjvoPRrta2sWJ3I2@J=yYRc>BGl!+-lbzZWLE*TNT` z-47qXu_q9F6mE=0%5?6BcLZc#k#3Zt*wUih8JveBEq;=RS$pRKspVCH-y^o*6A&$s zT>K~Fi2`3$kxy74y|6PnmA=xaCn_1=SH0xHU`xQbK=ISV@K67A7=Cc;Z-u}A`EP{h zcHg#aXBIPn?_7W|^R;(nU>gKdFC2Z?0B}z)1ttQ;+1PSdSurf`I(X|{{j&<$ zzPleYr1>}Qfb)R)EK*9Ioav1@=kCu7?B zi;qWr;>Zd7$I+%k9V>I2hPsXEeeT=3fmvHhv~s4+vj<;jr%s; zQd{cAuG&_|ukm@31w-{qQdK7Q^5e1NVegHN{56Iz{2Wf>YipUzIBLD$&f8_Salv0c z*G^aG!zzu_a<0->?^D{vYxnA6PnQ;tt<_|9;e0tXb$a64YCCqhXFO(_G;%ucuikt6 zX3yzUAiZvr66vb8wvDLk>k`*><7h*6TP!<`kI&b-H|X4CEwrg^Rau*ympZ*t*@?Pk zrAi&mH}$-lEN|xXD0|kmf%sZ?u58_AZZi3G6Sd4K*_d2^_^qf=*EN#MSf&#yH0sdU z(Sd83q)zWE7BbT{l3qKP=CjMI`oPL6lvRln+jqpnZEc zvMR0X70*66@-5_)+t;(B@RMXYL!B3;Fh40pLfmi(gk*=9`FA7k`D@8ypEC_Q(thq! zQTPmc_%+Ry{RQOA7!Q-|-gz~Rg>>};=5CYctF+p=$0C*Yi`oh1MXVK|Cj*Mdu2gRIc#+p-1UzcOP=YQD@Ci1jr5|3ZT35g%Pv7M$=7!Gg^vd;~kO4krRI0eS(?`Pn~Ermg}K+4jajfGC>-P8CdZ1+;A{ zDDYOD8YfU(db05z@rl2sngyMW7Y#B1L9vz0=bm_J3q31UK`D_l1^!G6f+TK0VLhUoByDBkqq8AH0dqQxJv{~aw zi(VZwZJ1~aNt+X%u@j)`Mr(-p3e**-PCe`2|MqEk_uf~+-~Yms;mc3|m4ZHIi_19C z;4{GHiIwL#mt9nN(R5l?_`Ygk4qoHku5wC%+A-@~PfKRM%#M zFY%YfXFAa(S*b@$9Y4K!L>{Pn6cU4vB6jLr(70~vh&R}FVf#JQ_5H8uU_GyLILkPO z!Q~d(nNM05d+03=U!tA-3o{p2)p?nqLgd0#o$NFo^Ep6fRqg_}4K<|BOJ%0lokS%A z;zKcxsu;a$40IBG5uyvsXPelH*=?kq`ci%#%bf;pkf_jsxn6W!(O)mrKkM^2uO}-# ziwmAfd^L>g(%kh~Z{Iy+;xb+9WEMI{FPXO%-DN}5xool!es#mWjd4w5tNJz3!K8tKyfJ?{e5?PJ z@Otm(!gt3X3&%4hh>lOgK-g1Xg*UcHEEKi`Yy-vwmc~~F;|<^p)~9wh3FHiJL!bR{ z4_0J|n@$Qw8;5t~lrunQot<73!Y1LV3&kSsu`F@MPqHI5-SNAh*K>x#HZ`Q3k&+D~ zc+9CwY**r`1`;RjKBEPrLJoVZA=_d^eE{gjLm<^aV5PJTcP3lmj~{#?eCoy<0_`P3 z6$%)S%JGO}{H}VYm+uaOw&G#I;WyqCc|aGhGTIHCK=8y=*~9|eY(qRv%0wHx>9vB@ z>ZO#0l|2x+3~}mppP3Z;#e9?0(nt^^Iq$t2x?eM0&2DG~ZlG&urz-Tc=-Q);X7YWqgL7uXFDFZU! zVUfjhBV-Cw#~J_@VE@iXrvicHe~AEGegBPP0n_vF{7ZWR-~~!gx5NMQ#?A1Z`_u69 zGY8?ty;*n$P<*f*ZV61ksc0U{P9=m}-DUvzDZGVjj{wf4YhPK<1C=WoD}#JXz8;^R zgsTTTp?65VY}JF7UEy4tBMOUtphYs;nhDqzNUu^JwGXmdlA^O*LOp{@1y zoe%GafAUAqhF|~u)$n&e`_1t5?t>Vl_q?sr9K@a`S$0sWgK#|2cru(BXW7fPeq$`ejC7o0Y!xt!BV%dCT-%6lC?`21%$r!frZD7=@OT!q} zKs;PvyzlZwstXOgp^I|h?Q){jy5Dtt3q1=Cu6Y}eLT;66xgOOKtK66LU1wq>tj-Uq z4kfk(6cvqT%a+ z_vCVFn7ED5+Pd~ZRoYznYDaPJ_R~_8i68xNRw+GwrF$nz+N(UBU){MnU(jG$_DITy zM49SI^vgT5i&U=Br^Z0T*wk;PI|1UIoOP?g>AHxm@pn^uN>g|3ZsNr*2iXpF$GZIM zc-6Hv*;ZY{QIFJF1-I1CMQd?Ie$tB_vG?W308)u=W-t9^MmRi&yEQQ#@I{b+vQOQ}MjGTKR)6J}bI#b+fDUmM#?JA;)w_ zCKrj|g|RJy8EY|-2S!FrJB;AXk?@ZV-G0~WQZVCB?Re;1H0#oDyg!vEpVl)@i8@`I zluT6Wg6{139$pnChIyOYkrEFq%ho*ty6{=kq*nc->_w7PR?}xp zO4Gz=k#loPnO{D+`S+r7f5s;bsDyQ$&+FO>o|PC%cr9L{TRVN7tSTRSE))t>yY0T( zUAo8i#f7oJr!*$%^?9Yg@ODC=OZ%HUZEBMzeT-*CAZ-!ui;S9B)O`=D^K#+aa&OTd zkBc^$v92I8g$?5nmfax1qOZ;d0lkY@p)p8k1$woYT8C--eh zeq{i%;GOHKYbRl@z$#1QGwDvlt<7`RUOo9=cus#VVr20h!1)?RXaun><_X4Yd^ql3 z32zTR7rt@!mGHIs7sGqXaDI34gjEjcYvaJ)`G=OV3|5u%lp^@fY>+I<``RC(NXF>w z2Qb+_`@n*Xp12hkBN3FVzYF~PiY49Z&Qdf*GigGn6wz;B>T>NF`+k7ncrGFQ?Um_K9xr(+`hnsGT>`V`9HoFP}U%&2S;f|D; z&?MSOFCh5K>z@o2l*a?nIf_({S_o9RqLyn#al(9>{wqCli%s*tQJe znLrNe4+D;~Ng;sZ7ImK$t$@{HWtqtsIuq~;aLxe`j*Vv1NcM@4%wm+-R#}aabTm{& z%Cl)6@f@?hs?fzy;JIaBuTP?PlGo2TK< z2h;G<7q5tiN`5E<`wwsZyWx+2^h|i_YCk-wgycQ+v@By~^$H0dX|~Sw_T*H$Z1uFk zqWpr7sVFj|U9RD+L{Ibiq)J>I9%@rY0nSHy!O$1#J5`-$IYNN3facS&GKHZPa4*1_ z@)rZuo8H^q(HtOJ#lacJ1$nH50 z1?dIY&O0JiYOyjeRBa){{iAosD!DQbH=hx$;$~o3%mU(7PK1nRHmqcA+dtRR#)E+P zyrf_gNuE-aM>&&^_Ep{k5ZyA^m04~tGGzabYEYA1Jy0!k7U4+oK^=R038Mb;!Zg(z zf3+?4#z7CE1HD#1)*7^;7-aAySnVKQ{GnDil0IlI<8Zl`#s4yNUYn&Y0{ZB1CT zxw@^ds@E#C)v=qp$3~0F%LyOjjjor$9-e&mr(Z1E{8E|THqxbid012plS1|quhQ=8 zXer|#LxQSpP0xz>9P4gi-E;hLSTo%%xCo`K%8M{qeQf26^y&=a2#<%RIH>YX=d^Cl ztMv8vEVz7C)|0HzTKlX((anj-wSjGPJr{X%r;F5gbpz_A6x-_B>&B;TO=A}3c|{wL zvDA)b1g7Zat{GRs{IIf(mk>DVQv=JD znpc&NJze~_%O^4PEp%!+#hzl1kVl>{otPscp$p4|yg&OZ#F-hL~5<>lAIXLjBU&urgO`CKK& z+FTeHi1P#f7dsd)eHsqS@~-R4Vxz7rZ5xuk3wVl?sozXyFJK;!G#SHez-IupCL2IB zrnRGxoC#K|iDyGX5Pxbat4R)nk3eiPWi=tabApM?VAViY`2)bjEoHp&@&Y-4z5vNM z`$(JVaah?8F*y+;IuO`8QkELkmb1A)W%3Eo$(3d9w1 zuY}?-!Efq6QyC3xCHt8;U}2FZ{=>IU!r`4sc;S`3u&Wx%@=W;N@hjo~efL$#ISbdd zf#8k3-SC9K;A73l@#$&U-`W%KtW6~q(We5Gcjd3`EoFzx(BtW;HXj^@UF{+`v4tt1 zT!8gpTdC!8&c1;4-sD8S&<1|;s{yzwKO*c`wUvj;ke-HncTd8Wo;+JB-!c}^Khk`j ztLRQ&+0|p|JQqD#-v8gX#wru?v*Ew`W!0yC?i&WMvzV>)hDwl}OKx8}ZYk3nzw++j z{yml6IGlxpYkDyvzwkPNw|JC4Ax0U`xY<;F(jTAl!sArHKVI8bypbtRT%Zf9pYkkn zLpVCrhK!0YHl-xK&(DZrUaIgyMK4%bO#0Dpd6~fJc1R^Vh^?lBKBsG-U?K)yA_L!)>&i)M-<^Twk{XAFdPg>*#}Qm+RyGdjfi_dblk%&KV_4>SDb# zCZWBo%Rb-WAA`evu{l)=yEe~zb6RemlfDmZIEY%=)xU>nzDbBUi zSJ!aecyEI?@?wD(G2{KKoc$T$diYp#(ZyIhtNhIOJ3BT|?U#=L06+jqL_t){7oKI` zE<$l83xC4&Mxi*U1SVsy9gL21IDKoHd={lN_Zl;Oco)LdtD;2uy*LJVxSH3@~< zd&17dWTGV0xoX%x-}@lEvi(N*{Lc5or+42ApV)mr9Q0!K0IUN86HH}e$*3km%vAJ1 znd~q#vtTfN(oQF;oQ))TUevAq_-v{S%w;ADsLlMD_4)OXk`%mf@Rao}CFAmhBkOZZ zz#%&GGgnCtz+N((V@q_PWj>33huR0yn2h2rW%(j{3IGh&r-`BFGAQswS+o19NeqvX zGIV=-J#uEXcG-MRZ%H0#Lp*`ABZ0nS)%<0%zoD*4qlYK)Wm(R04E)IqCeFqJcI}^7 zYjV$|t4*fb$!S&X_P>5w*KdU1F8IP~ZV?Ej z4G|)jO!R53mlj2)6=O+O(-A%K0#3x2$ap3Dsy?R)d;Y;du1da0*s=+ z`1S!h0YsI-E0C0o@V+vTu|=y>!KeDw$_gG&hV~|AqEuY^hI%=$El~OW^ONx6XZORN zKx;C$C$rt~&)@%d!d;!8-k*hQ`@2?RgQDgD%o1xE&mU^DMk^sA`-Wt6%L|yJWbN?u zwldH)Uq=FtuPFO>OC>bO9G_{<2GV`1dv+1+YoaD<-`U>}XVSfQb{Iyof2es^YP9B9 zna@3y&QM7e&Ac8mhguW@fVCjUyy=o{uf2Oe{P(~AT==c8Tnj&c*Yp#>qJEHc+ptJRLr8*OAqt0GR?)y z9vlLcgI*w5wzha}OXfg)$fDj=*8drOiZx;d9tsly&^+MeBo?Fow;!Fl9>fsC{jdjs zG=1EU^g&&U#XOM4*u^+4AN??8p-puSE!S|v6t?ys(wg0XEPG5P>iT?f z#+Mv)^HW`WP76(!Vj6RGSWrXj~h+b=cdoes`ho|zbfCylRkcd-;{1k~E=j)ywx(3s|Ci{V1@W6`z=SUAtj(_S+$?Gp|4LbGU)IvjH_m%o z%q-`h#!Py>Y~*G3dpuvB4EtleBt|jJ-&i%rMf_XCZfR5doG%@|7C|bL>n`gd3c`u& z#SJo2x%PKdraeBt{LlYV0IcP=O%3WAV>UMk%_hiVR%RGrMNsST+30Lt+k*{$rAbml zN5=Uu^jjhWy*G8_ayv0BKD+8;J{rq!{JOGlJL#}K5w@C6b#szA!gY2jDR}K|>zx;c z5-W(css}oQ3Z4mCISMz!`{CK}z5bWOU!8v`d}I7dxINK+a;jC_Q<7s}xYg+VFsdPX z<1D-kuvjRIT21*09i?NNhK4~u4NE(A83Nyk6@g0zrtLyZchMor0il^CYeiIEClU6h( zaRnEK!R#m;oP88t9DW$Svhzmxoa#Eivh!BBzI7~oOgNkPP^i_C%FNW=%15@A7oH0p7}bsOw$wMni9lXJIuL#UL;z$1c?B{K1x8asg1W{`(g46x z4?pG^;{Zr0w_$ghY)VzktYn8kS+Y!JfMt+c4PBMNAnR83djV}NOIjJRl*^zL2W3C* z{75Ahgn-_7S{btfZFk5Z7QjjydJeahwW`7A;xkvFWdP^nBb8r}9%Qpx9ch8F3X?EV zW-@^GRCS?|I~0&P(s)XM5R7Ccvsv9-ePU!bjRbO%9jvG@I{_uVcCdk$l6@>)@R#vY zppjt>$WBQP>O9+qjzT*rL1LjP(3NcGxzfEU8G?3G*=AtAvT>;sO=h>j;nHa;`GEZ~ zE1kN~DvKd{f}fWHJXB5u@V@Y=ePt)>-H~h1t|c9hj@x#)v% zs5dG5LQxNoRV!L{9SEE~7SO&YM0!`~HyO!fU0+ca{JDaF#PX*^v!w~2YThZ^F&AKd zrcE6CD%mkno#kD-Wc=YIH~|LAIX^Ob);{Q779QrXu(G=M%XMlEo~1VKyYyl+}beBm>e zat!GB0Ai6j5N+A(YMATAhNj3K8r1yh@ydz^ivrl{^vItlWLoPb0h=(MD%&0~UsGiN z8i4xH$e7Q0s92aYy!P-fx^XoWB>o}b^abA9ZndgTotFs*zNHsy$nZe^nn8t;#pS10=G|px5tl6*-=e@v|`Ll!b z#iTaLpuNd|O=B{p7Eby&jcTqsaDmh1G(2u=)ZNtN|E^wm-0Z5;+ik7uTfNOc?^hpr z(Gk8PS&bL<`)cD|a@hj+Yf}vQlJrr~`513o>8$Ub9Q?6m=X$lQ&wphYiQn2ef3%fa zyWQCLD@Z%Yc8+y6SKCq_UPM2anVzfjkoNg?p-;16tHq3Sew@#Xwr=8U3*SvsU)9E- z{q1^j-}t3TxofBQ5NH*^#>6h7pNAGG5K8tmo=e8vC1weMHz*I%yI zrjv*Pyb4~7UwTyhzLZ`}vms^DLaeo;H*_?o;4^+(*I#9Ui+Ap;*we*&r-^6AR}Y#} zsv45&kW09C4N}(eQ`h6br@fX^jr7;`r5snk&b*PGh((J>37RNl_nrQy!#Bduh1chw z4?h?`AKo6{3=i}#(E*%CXA|Ks zh0ak8_=p@Hna!S6%n-09{g!N|kmwGqF86m{JWsanxNNEAw^$Zvm4>hXjo>tObHHC4kM8*tqka>J1At24Dm* z-qz-MrviLW4h5=S-!%Z5dd-040N-J9)80ScuEWjg?rXVL{(0J~$=hDP;)Kve8J zqb!6#X~1i;j+sibT(OHIEY2=uN|pob>j+<8Vb;%}**T`1aAU>M$!KTrVZIi;ArSG=DYT`qg(&9PS+b#?fWUaHtO72`b3a&x-)7;Vn-P zw-$f0-QJw(Otw4^FNK8pZ1+GfS=9F6jxxEW9>6pcJ5qTRvZRR-=9vct4@?Uz8aeD? zq&9jv>F^H*V?z*jICwEpJ)}yMm7U~9q}vCIb#>$oZF;z#Y1=B-{t#uoa`<@_9$jhu zS>`f*+-1mI_rdHst#bCM%R*+Ap^db}U2S~uY<`G`t(8Qitp^2fu3Msy$D+@(sY#$$ zk7Nv8CHjok+p0Y6UN8D;V}|vcjrVcc?ZV0V(_y1()La;}W<#n=p<#Dssjgi{xx9Ri zY6d~m#+Oe{o_YM>>*|`VVY$$>&X@%zOB&XZQ5QR_@TnW`YP#?Je${i^oUF>LE4@8y zSUB0@X|thCKOeV@rmnxur(NxUjz`Plr*DNr_Do8bd}ey5IIq*SYy3ppQ^^(G>^Am8 z>o46?D%Uk_YhKSObs^Uu!TRPFKWI{BORjzF60%aakFolSbpar1(=Jsvn-r z@rP2iUHAF3k*Pr)&5`@a$XA@R!nWo$dtu8J!>ZG zrs~%;Oyl=`0bbdgylTt!}zytHvf``TRCzXUFW>$OFJN}9N-5W@QGIi zL6d(9it1ISOsP3?O?_%qC1HGHj({9m>7nqV z`>OV=kz#~xhQ(;CfyKfHEo`ivwMQ1P{-rt?qA4s~OrS22ZVe(PA+x98jqWib>x&>V z_K5~84?qVdmIjP~$kOKChDo-VfKf4)CbJdU8cXSnSko9C;j8gRBtq#!O=ts~0oTCx zXwD9!(=TMP1($!-kOA`dgJgbKO4d?4SqXZ8{SbB0pXgG zC+JHR2BK~z1c!-F)EI%;m1t1Ue{*AcSX=y+#;5numahk69l0dK#3;2c_Rvyotr?l= z;rNWXdRU7&M-AU1c;2_#KcHtL){Oz(!oTr>s^UT+pBE!h%*efN0D&dIAUn z-tLR`)HZxmFF>m#o(d#A)uw_w0!^v2I}zA?B2akmI-48{JQQdRs7O8I<9n0v zSfDSu9;+?{pzXnpUC9^CT>+c|mf0YY5)}Z?TLM(cdSc3%q73et%07^V z%ah#RqMT_HLIa>x!s1*zU+N9c-hpadO9$jq^ZNL1EC;fyk|4kL=1bxG_qGK%%O?Va z$LHJOKyQBTJva_eU)>EK>P^eOK<0t6kZ&F7pFs!=z9QguCPs7lVMeJG`37H(l&MXb z>Ro~3l*Ev0RHynhXd30REz8sFhn4fe+C=Yqaj7PE^AcVnmVNZyss*y&nZf*LPgn@wio9-&aBw7 z)Kum!MUU*<)>l(E?|V2}V7iZD1g4-+foV`D%}h+TTo%`s@eg<{fajgro9UyuSJO|A zzCu3ducsd#zMkISz6Ex-pXQI*IFPg&vopJijJg@1jnBd0EOGR~QaA>FC}R&n7lI;t zk;x#h{bFBYV+IW7=K*xe=6y68{>d*tGWcw%sb=$Gf1z;b5N&O&9VpipatnUoj!}i8 z{+DYwBZC$Y7E%kuih$EuvH&14HM9o{1dgUQ+fCjsSoUHr=!3&U@W*|SUDU z0IcHwrm8TfGC&?Vn4frVJ)C&Be;AR-Pdo!Ui;s2Uf|1VTeGrshTAxifo>~R;BXZfz z2SFH@$jR){BUQpGxucD6HotO2Gy^c60GQ4iZ3`$ZY`ssYtD76n092n4dAqg=V2qA? zyN3Z>yU8H6p=;!620#^L7tA)&`LmBeoB=V`HcX0vRUG_?V5GQ7(vLtljd0cm0_Pyb zfYzj**rJ?%meJSA;Tx7EKxUW#WV*?nxu_Yql!vX*&o{+x`nb$yd*)U)qE}fL7WcT{ zBBjT((2!HPH_wKChvY{Fx;7w)4>d> zP8iVlL6hB#aGQ{SBiHAc@B+XyoC7^4hI=17J+-mQ+ZW#&I0bkS!Cxbe-H>n}znbIu zNrS#wY(PsMglm9*)?`|>iiKb?rn;yleXx|VyX0(%QjuGg- z0P)2`)*sN>YiyJ^E5wJiaZ#AjVEFWqdS5i0kUu(JQ1Ifx%?o#TdTH~6s9`LZJS;dS?lFAxbL)bYSvN7TDu%CEDBJQ zKfqL68cNl&l*idP!^cxn%kp@7)IPbVF5OH0LAtB`LfP_2pvH4PVy>^GXM)30W`Sc=bgr8d5q)#zL@Gd0W zXW~61u=9g^DShqcx6{cde@$>DHqs8T)@ElNh(9>$>p@}Xg$=XahGSz$+{LTvZb&@g zs*_Kjkvh&xWL{nE>e~-N#8tpjoprd!V4z%JF9r_4-8@mK5xx$c5x((u07fgTL_d== z*$Ch{qH9-)c=iw|7PQu?zNJ_r1;Qawyghh3nA}KC!0+RCcZilXX#;>Ch+p1p4>0fn zZ&`@MHNWza^3a!HvBIl|5xDcrgdX6rxr3F-9W~AGEZr{A*Qk)@;BDYD635`&Bf5GC z(3)b}ng`i(B3=dE8QlWkr#+&Bk$oOMR7H-JV*y?O<46SouDOkDoi3{$mB(%FF2D(0~E2?56t=F!tZVyaVw8wsA?APun}>ZU(bA-NYh; z7_R}in!oxm{p`V4(z_3N_r#8wM@9o9)B565>4ACPi6rka@n%W!0Sup(@x>x3HMIF6 zQQfKYzcULz{Zq#6QFzl2>St~FD$!IIHZ`oTAf*54Y3*`d1t2 zijqlZT7o*#DnND zDetqV&9#>G-9J&1PCx0tGJg6Ty84>f!6UAmM#nA@O516NYS7WB&E6V|85bW|M*3?f z&LkSNf0Qxfx$Oa4=zf3BYh#xkd{#COKW>hfHmSZi_hl?7QQKIE51##rThmHk=5zTl z*sPc4@f=!w+L`ruBzj!ET$Rns@=$!*C4ZZh+r~{-`^R5?p4r$=dsQ}8_eu8Ewr%!p zFC1D@-xa9Sps_q?)n@v9$wZM+Abt!-oBpyIreWcs$gnoNt# z7nvueZP>N7GrpT#O!PskR!`A+KOrqrH*`w?Dvi9byXzR23Ec2AHbQWEy zFZ9iETgnSv+gX}syEeMEvNE10dZ3>0eJ15|NE^@2GA5SLPNZ1qu-pI>bwrf#BO zojqN$A_HO~X&&|=x-jX;K7g{Nz3sHW$Fwg3GWGCpX>W;s zmR;60e`XKpLhy;hKEl_Gj2=OdpUpm>-ko_Z{rL1d>1Vw+(ogqZA*}`|5FqIsxX$e9 z9;nT3U|{kzNYOmxIc-QE?+U%>)qyq8QP9}i3=$iBDTX3w>k$AjbrE@uY~pH4D~sF@ zcVI9+1p;T2Z~Pxzqnq%1@VBG*AKi*KI4dvw{PFxCUo!>S6j@f;N)$ng$RPGc!`x>W zs;^V%&M*JLs`d%$JOPlO+uuqH#|P<^rK9x5W=hYKCE?b_W?Cn(R=`=XnYi9aMJpyK zP9=Y{Pu#ilY3jZ5HNYl=Rl%MVv`kVY@Ms&?A#bjp`tOKaG*tfxJ68;mpsFoY=Q~&I zIvYw80JP}_+_(+&8N--E*=SPA0Q|gXtl@9Mg|8GNvjmz(?d5$3)+K;mqhnVnKcHOz(}KK2>^ch_+{)#q zgaXK>Sg@}HqGA1cfayDo>~`=fi)lWT;}|^xRJV7*TLEPhIsizfIRhO5P}+1RIrlS> z+USwfLm(GXxBCF<_O(D;H|;ZbwHqoPF|d!xr87s!{*s{f`X>GEp$8zV{{g@XX47f< z{-@8T+q*MqW5x(np-Ww*y=K=VB| z9n??))(a%~o*@)nzxT+wJ;TO@bD-2SqygEb%{=$9aU>x0%mN@aWj$XkkPGGE;a>WS zH&@a}TmK;a^Vfcuo;v+0wuGm?cG1(lj4A!#Mw6i10Vs17z_dBF&7+M1@lbG#E)(%A zAbpIUMuL--1i4)V;o(?^+>{&UJ2!CKIS`8k@<;Az-5KMB0+e#>auI+1Soa5AHgz9Vv|V&CDLm?75+4ZgZ(Y{L(lTj1JUJvv zo5I&RkIRe?MURJFi*wr^g1JZgWZw42sd5!rOMTnfb-|N-OI!VID! zr!tKk(i9;_Dn6h2G$KN){7^JhxQuT{`uiQdactD%qKuVd4#!eGZ0hSm5N z3cEFXjAz3c;5EAj<8(%r*V(yxM(fIZC<0BOPrqlZ089_bl?l3*)th9+hF3J@Fl=XbRN zF552VCs;~gi?lp3N|dA6-}=~pPEeNi^42~vF1absK0(_64z1LXeIf&9b+r5fyp9h6 zRD$C~UT6uaA6(NsABNW;zilJ>Ry+t@1+GH~fooL4R_+ydgbv4~9N7a~I|V%2B!u$S z#pCoWnFwCJIhUSgU$|8;Er2?jgW)0W1OH$lMfIR74(^dv8!bS4=k_P*^bfy9Q0vp_ zX!`@|umg1EyRR*fBt(kl7K|PC&|aJS95yI+wWRoHgRz|qLofTIHhW<;14;fTD!2RP=G)Dsal4tQFa3la;w z3KAdTgm@W2yU61U*k|;v5woZCv-9vc-T(9`trM!ge$Dh1^zT2~nRdY!0|Ll8BrshF z%vGHY1CVIMbwsviK-|G(?%xBVu?4wL1b78^0YS~7Y@!a6+r(QHcmwN5^CW%0Kc8+ckfRuRltn`?5rVvRw3gnz zy9VmLmkwvi!?S#y{+81Y(Z?tJ3?R~*Fi8&hZ)Keg|Ilv^Fr8rHv_#)GNV5XYpYHoo zfd7orcGu9G=5VHb4<}tCN?Z_o2i+IRnSF>%0m_S{qvsq86!>m>h#$<&Jqx%!$09{6 zG-*`;s^{0ztjU2$Awn!MlS{y#NN{Y?J28caJV-Hex*N9RMG|cQ{mFs-@84r=9DXzX zi!ZOGS2w>OK4FHl&M~$Cs(UVmtY`U+k_!kgIFQpIac$(nCfE%77z2)(eNw&b^KxN< z%@jS{z_3SzwrObgG0Y);T50PWrd@&v02{rz&vZt7M_pwi~%}rKj!$$OXfOOj?wW` zD!M#US`CNmBM@)c$rW6U>NjLKZttvnZ5yejYa2^Vx{CAg^0)p!!Kx^8GYzr=^)*`)N5|p6;@C8`fVh-02*~`iqhGqap@##3t>5yp9 zZ|r<6YhcZ<(02;gQqi0PwW;LYwl*~TuG`etb|;T!ndS9SbdauG59<2`rB7)DFFkL~ zUQ3^(SJN-km(ovr-%M{FeKoze|0DsE;8MrCq<;X%JAIH=1?#|h4EQrWs!9##KPXY8 zvtT{w_f9w_n22Q1ft88p6x+rbnc^jgTT~Cq=!OqO@}5oB?pp+~#KR z;HF>TWhsw#0X9MyZQ%)?hboX~%rCbS(8(B2K;G6#6bC2|JjIsw#W=y%{K@R!?yIuy%w~iz1{6} z|7bm3TYV+XZha844iw4)HE;W+!;^Zq9nIqCx0tTI$q1XiiH8Orsm(U+>bpUjb*As; zK~O6xF|?lc2N*PEg+Q#iPy_=7Ms-pK`6z?bbPq3k|xkka-mq+Az zA+>|;;FvZxmZQCSW=svTLWsRXNFZ8}-s$4lGL4390|emYzv$IRXX zY_*PEmKeyU`YrIL*N8g4!yC0F zHu9SRnBHSU#YHwNT&I&mz~K|3u$R#F6d7jcuaU3)F7g@0P1HN6_5sP;fArx)-pYL` z{i82kOW%F+FVn&-Z=f)`I%p38d4I}a#PSL9#ZN4JQxW!L?BZ+lWxMXV38ZOCd_iF@ zano#=Py3khzH1J0#(^(Jb^+5@0i>N1PO!kE!u-Q6tM~YHeqWna5+d@@pgS_les+DYkAM~ZMTHEhJIt6 z8Kbk8Y?qNfn#QHwK=i&E;j3ElG2T~KP;4?CM|2I_j#9VtK$(^!tJSR2D7kFJRxRxx zM^TpykKQFsyL4;YX7jePZVupR-E z)U}<%=R*@_lImVggT6c14u(PySB>kojqBXebbp7o>kwc%Hhrp5`Oej}A|CpzDm>$| zeNGm6Ms*w5yw7uJgIrMhFXdyOF$ROU5~>!Us<*HBEK- zDmj~Wl1H)4H2h>$=!+-LYFFqBU9{`30Wk&o6`I?+hN+@#(AVIiaBDx;a<)@US9;cS zqgCKG)+pzg$zvxIsAuJxC~WE<^g%R9@m$yRt)+D<{m`_no!0&t_<)9%k4%9+znZ7* zb)|GJ=YCfzQ)R#_&{gz@q2IfJvEtaBT}}6AZlzz%emnh5?_2372VY5_9B%OIa+0x< zHe&X8kG;ZfvvHu`3Bbd^QUpxZGrp(6M;y%P->d`{4&K&hP5m1Y#GJ)lym!qn-)F+U zU^79_Ik1_28?mP7i+j(*AlA|kXM;&mSllfzxv)a^GtKBHuQXYj+_zEL0%_Kq(tbvq zvK;X+R8M(ONWB4yMl9Pxd~kpNARX>(r^Vy#^b|pgFRsm`*PmNTU%I)OZf>lQ0@u{8 z;F#b|1}pAtZ-YIV!nJ&lGY}UUl$%rdiTe|>0vr&@E1tTrun6vlT*xp7uMa7|!+^Apk{6@qwN)Z&BK7E(yUBJZSV{#Fb zta~0nFm|+LHmQ4r<#TaL`U#zyD6b< zR{^CXjR^R!atk^uAH4+VZZ2%W;DcSFU;)S%vBMld=+2g#0w&UlEWr;w0DtYMQ*AGZ7KL{z4izhvFQRp@CiAgXL=-l=6-$- zyC0Ex8J*~f{wPM*IS}cOwwBXFY@T|%@#bk?VD@+`tzILg2KHYvpEbM|7I(<&y@F3% zbmTRg)&YzUS<#I6UgE{U0X|@BLMF#5`Y=(LRQIq0apQo-*a2zl%J>F%<@V) z#vil1adL9IVI-p~{o>Ap^y%)C>9a5WgY-{c{wo%TPgppJmPZaJtZ7~3x9EETtkh$& zJKzPS*?%#jn+@1L*ul4ke?#MoEf=;0761YNL&}fw%L#mZsbM;l8FFMts+T#&RCqL& z2d>(WGD+>*QT3a4JWiPz3co+5uz+jeRShQnD?8UNkMi({n@9P_x$YX?;`G@z1iC|( zB6nSVB|0FVN7$x63HsmP>#n-5OZ#lr&h%-q87w~iJpw@p( zjQ)JB%O90LhjNs$Q}^0+-9_6rmalLu!hR_4xNNnJ!h~(&(9CC6F>yS<`mE|Quj{(; z-eknTjy`JoTCTdh?Yh>lY2~`Et!=YxlzzyqOP;RsCg024JJ*%Vf9IX|qxxhiCi%IR zv-Wvie%|XQpEjGhuD6vB#!4AzRp+hrRsH92d0Cm-{B?0X^7y^1OlS6g=A=K9!ze-D z*VD+|Ic`Qdp6jm6`nrFYtWth1W@iw~xA5-bU&cgPUu$#wQ9C|+%H&Fp!l+A{$(@|l zx|Y3gYU`C8{9f!ma7-bZaJG%Q*YecYrD;YgGNC&9FAsOt zKa2Szbn4eCqlfR#znI=jUrIka{YHAT_ebf?!&lOSeSS3$HZ$Kdbt?EzBKN2Q(4m0y ziEAmmW=)qW*z7|i)>m+x94A@CiSLWr!heD3DFd{FdF3@_QO|V-jqNH*DTg6_jQK0% z$@JOQN+fAMmoy;p9R=E)p@2F-^TJ!1?MaNx3=R6|)SSw3hRS-dt2l{)0q7kOfw%`= z*xLtdJJ=?T?Q!}F5zb$IVKIH_#*Os!wbgWUjbG`5LEQsrzH|RUdhh*@(t3_I`hb$%Vfa^=S925Gj=cr&g3{sFmJxxgYCR| z+$woYB8nLxRxsAo6@q*YOw%cdBd@J51h8ug2j7k?(qJ z)>e5_O9(DHbCv{w(Mbb1VD)=MJ(B|%D3W`o8BX?1O{B$$Kwp4ebhDv$G~Gu}ARS&C zBtK?+M1=kkG9FS+=e$Wd1&HR&5um8;jGT5eKc}&}2!0+5Sh5<@mT;;ULee4x7qb4((Xey?nAPp?2bMMGb=22M`?{06mz?5GD(_{g~i2me+S*+y^MW!aexcm0=cN?1*UoFahf*h z2LRZ`gmfGJ6yyPUcmCqd`SkJ5pQQitjUS{ZXWv3^Y#iV>{C&|7fI2S*!p^2m;iU$f zUOFxuho;{sDN+u3iLgW@wh_-R3WClqkODM|L~OHWhpiWv(Iu~A;ddAMcq^ng9C-ah zSScodnV^)%ge?T0^I2JoJVowu-xRIXTkiZAdtfvhJmULWp3+s9?4|y<>g?3V4<2o{ z$TgGy`k*dtFZ!^U+{3W8JOtyR-!JY0K8LOHm2K;G&HBsSkLX>`-D2DR`7(Xj);V+K z2Tc6};o5WV`PdgomCqxZALqJ`_|V(0Jg?8n_^5ez`Miy`Zda}y+mCilaQ<}LqsY-b ze-YNtrc96P{sQK@v}kAci9?DX%a|+om+rh`kmWefY^xe~UXJs-GT9y4Qx5O`a?&bg zS)0mPq9%R0_gvG}3`3kVU>Mqh_@oC-m zqBd!ci6-xMzQfNqS0O{qb~XXSPeb>%s$1DEuRUem0kWL?fUly$$3QoO=DAD$A2{ER5YC8 zPnnzgd?;_-SuI&RL#d-d!%zNhG~-;aE{sGEK~>$AZ?MtJ+VypwTX z=-T$R9&Oi!chg5Pwu&xsK20Cbekpx_?jNMT>U}4@bMQj?B)uk)h9R7i#OK!bv{9n^dn~W@n`qa4}Sb!`tbb^(x-QBNBI*^ zJe8il#lL5sPJjK^e;wH9mtTK9FeE|cBO)D(&fYJAcX{lyciOPdAjcJ@=1kqz{q*t2 zyKM5ffv>>TqQ&T+zB>km&HF*cgo|S5+CNS5n#Q@VCv-=cyHD(O9yPhLP2MKouKW38 zB0mL5j{td(05%1EPna!6;)<`vPM4sWWNENh08^rhk>CN~u{oMgc#E)4yYm<00|}r|uyzg*Jn}sQemW@xo&`>o)97aZQ{LEQ5fCvp`NNvPy~ya> z2lBR@(L*`0Nd9A ztz&L6@K4~eK;O&oBm6ibtyfk_Pl0axfZ5V%l86e%!)}JSyF-$V^<@S#Ig?eG31AX< z6*1EJu(ud?+%x(dfPQt&lqYQ52yklp2shyqE$toer{mo%e0G>l@##E+e~!06=E2$7 z-A%{ruD8mBTL!ef%gccK_+pzV@eyAf67~{Lk7gZduKb{+y8F*&%b;(KQa8**~0+al~xR*H+gP*ZQhSrhklon|+u2B4fE9h=eYxsO#TpzH-!kmHtcJRA)_GGn}f?7oSKwI+TiP#ZKjXQ4MTg9z5kCb5UN? zwrlq118ve#=(}jHO78p&85hPe9`^4Nprc&5YuV%zA4(ha`t&RtG@6Xu7QV_jHXlbQ zs~dyFWBrVr2c$)JPJQd z)~M4(P8cVibD@)HGVf_?GyCzG(baFgZcN+9RnnF9=t|5|jde}_D32?V_Y48s`xsTM z%dcm!eq`0!y>dfqKka(8l15>9UaEP`yNundut6Dzc6gq#6S{OXxC*(#09XEJx^?Kh zewrj^(-z+6kvuw{C-La2E90Z9-tv0f6uP#uQuiBuZYWmYwpqLBXX)GMw8>bxW1q<* zkGh{p_G|lWI*GontNSmXxklxmBz>OSljI!ovZqXGS?Nnz3XLPUmZz5Uve(V|68w;( z^j-5GN?Mm*{w6gt+Q^kD-R&q9j z%txNq4f1#Qy_&T&&{G|9ud!}{b;X_0njV}pf3CrMjO?Bv4Xrxu^9%jM!##puchlVd zL-IHuq;HcU;A=0ersr>5OV3z|(t=1=1;) zO<@(l6Zj3>?31)|dOO`nZ>7!CU#FFye?Xiy5@U*D&uGY>zUt+cb67g=o3zdQs?k)J z*}n2W`|z2qjW2V|8x`tdw5~v^Kr63F$fuhJgn5p2?jiuOxtt%|J&4p98*CVO1RzR= zhxEiVs{o5ev72XEfSEdsq~T&mOmg!s0XQ!J;F{KBhUnc7-r7pH-#Q20%3OEiJ&4Nby;sg$Z{Cq!omw9X8q90gMJ1UOc@;0{Vrt z28ix~Mrn)m94EY~dE?3J>E^8$(+__6e!9E;kkl8@1L*$czxkW={IfUG#^yEp!k+-M zMo9Y>X_YbX5M8zyBMb?^T>3$q5Y5`it2QMSY(i|@%e*DqhbZ6(K z^e?`4E&cJW|BuA)TfT7!`#TOWQGm~pdzyZ|*Zu~P3r>T%$KeK*NA$gMotFUH=m|J( ziWJv1uVXUPXc0S?56Cg~8!JM2SV`YJ8l=tYFT{PDjPaNnS7%h-{)eB@!qGcP7z%L| z8^%pb+w&}*&eITYglWH{-|$n5X)di%d9Zjcskvc8A{)^EJbe1IH^yaMkQR>Ne7w5&}Ca+PGJGgMIHf(d}g&AH^!z@#tYv&ywS0qBP z$zzz$83@#WS`6lY>E|lt%X*KFm&@|MZ2O|;S?78_cG0?yOggNVMmwBAI*Cnp-J^Ig zw)B?$*sLg^Vaxj2B%N>{ws|DRY3npR(pE8bS$Pf&<_|4-Dh)o^0QCGTnTyv)Mci!_f@(uK#lKFY*%jXMi?%{oT4o@RrY2&%jH`kC>nQEVv z!6lPR9`=>{pFHM=dUtoIMHI7qX6mMFso|mDAHq=?y!vD#<=N~l47!WP+6NE*_&=4! zw6)yD2Tk8}_wq%k$(jn9mR}n;7aUdhpOlWjGIl3PKO7^IsK)Dx4Vy9)$})_L{Y!cC zJm6D1vOyF;Gm14y25q6wbp+N?_}#g`N&o8J-;Q*vVqRLsU)h_yGy}vLFcx_BKV>zj zG^fS2^Sq)OgcoaoMH~XeWnM>*BLa*{SMF*b*f#T^HGR&3UxjYoQ0IxR2Y&N*cn?L;EqqGGEwnUKT^Upn(zV)p?1W4u={LsAr z-h1iiKmQpRVJ}_V*hnwD_+t8dfA7!IjhivgvZeQGG@)vKDu0z1tf7h;Wl9>QJ zY-T@goW7qnPJWp-dT*z-^l4h2xsSAAhTIw^*@dIluu^+u{kbO z%j)a8UVVyAo@=i5m5)N9Y=gp(vrwFMo?j-{v77h_q(;OtfT?*)1fgS7J@_t?W5E;! z+xJ0UjqoLEbPfPifOTcVD=}L*O9Xs8dE-)?m}SPywavY5Fp;!>ZKKA7SLqeMegHeqFGM`&h3&y zBclbc-FPr^_`<{MV*qSHbaSzoE<|K<7TtFMk_Fg%q-ij}wEa@cfMW?n2ko*k5O0Q>Ai z2te=Vg8r3_Mqt^8&-Lj4{A+H!`mrBTm3F2?zDD%8rm; z?xsB9u9|O)RCY0V>&lJS^#>c9K+^xyKaZNl+QCs*#ah+Z!Fh0@gZB1+(dHNGbBab? z`c&)lfJ>p(wwgm-%DE5~8t-c!me==%-g9olm~?LSkHV+^kWlKHXUB1qu8i#_ZK12{ zOIa6fvwYh7F20v-|0wwOhxfQ>IHIj5Z9Hw-DQ}7_TeM(bwF49cXuFzig+~xy$7C))iSv1m1Q|RjPH?)gX`538htObQ8&b0$AC_;JN ztg~#2qwcPa#i+bP>Z|fes6fZH4HUcBZVDA1D@oZ9soAMdo#Kvo0_p!z<29QM?rm4= zo3D9d`M_#(%Ah57D^I!(x@l#oSU&bkeLu-mlj^dtd0bvb*Jj)1{j^4DYd0o;87OT` z>w!RhS=XDSF<(@>PjA2WQ`>T^-(XS?l+kC1tm@QowJsrYHgi*ysqU+f-h7A6hw|0Z zv@x=N5>1ge%+Qx_Ozw(tbDgoO`8}>8Ki6fkx3UzOM|tN`Kb`j7HdrI{bNIU7vJ8>z zPuqoa27F8V5Re;H*RDnPvKd~~_x}``%93GmpL0z%a$R%^T8K-(%GWn{&|5CAqOWyD zhxlwBRJlzlS2v*`8;0!i=wC~2K4uH#Rr9dUG7sq^4Jajq%B43J-b^e0;@#~8VVXf_ z@H@Wz*(M^5qh;pdW`h7DAqU?O06?=JKyHFI@<8X7;UD=GU*-O@d|n5C&V?rTZB0HR zWroV47k{x2f5jF#>|5hT4T78JS=24&O%@z^d?sW)f=9>_&9NUX!Htgj2|Y2Aum!W( z-%G1Rg1;9AaWlR4 z+N;jLX~zr1UJ%Ma7a$wz7Zg#IV;n%OO|ms?%V9ysf$FV? zCM`#~mVeg~b56Tf?J}+701=`tSC+9~^Qzl*h}7p(x9Yy0k4Qp0hDN|mMa|u9w6W0&^D!HefI4ktM0MKm&wDLN^ z+;}iHCIl=rU$m(>mdX8Vx{q0M2n%c*UF>Fm1mT8})vI?bsEhLILGMR^x(m_(Mjim- zDvx0FA<@6fYeX&sZi@8=C{8rAbi1VRFrrsL`UsoEWHgGF|Kzn#y=5jF7jPzn27o$- z2=6itv8gE{l*<9INR+rKB2IdQ{4U<1{CNiH5~(mqaWZdIHTr_+E&<-Y^yGeeVhJ#I z7eE^sdiVlq!`3sW%h;Mt6Imgbn4E4nxJ}<4k>8um4No6@mVWRb-%s~Hd6vEbUdeeg z3!ptm8jTO%dOyAT^{+sT{V=o(=L~*crQHo8f_Jg$flCGsTg5N4vjEk+l%O^IbDM2^ zw~nt)k!{OWtom|jC5{+RR| zj*|^i$H;4kJm^Nf`=TW_t8{#e_MrD1k=c%hc#y<}l8hd)2#pqv-6?}dJ&ALPh5gDw zLI0vWy}b3hqAUDLlUBZ^@;v8$AA|0^@5@p8JWpRXRlBw}mJNAo+~4e@O~*+Zg=rz^ z@cyKxopo6TZFw=SK^L z$#eeVjRzuk@o1Je?*|MzE{Y9H^?BQuc5UaZcCoVaqd)s_%ryN-)0D5RpQ|qG8Uywd zQy(=NOTdT-r7rYuUlvB_%gv>S$vOHeeU|g2rk9qhbn-c#M|GH_?PZ1J^K}{Wtt(77 z|I%($)^2)hrkO{RIml!9S@SH~i`KX4)Lft2XfE2{=h<#5pA=`GL(tB(5dYPEj@orF z80o3LK1at5(yJIR0*&W6isa>7+Lkd>^tZlkb{BI`TH6cHaxT2feL0WXx0DXgrM^v$ ziYhLP+{5zWmF%ZF0CGe)y4O``_(pOzRD6>(=QDoD`UaNn= zM-$p~u!>@RH0ak7Ko;+1SNbY8wz`x_QEYR4=5~5z`F48q;3f9h0x*HYEQI1UVq=5c z$kr5QfKJ*ge4-BA2W~}up|P)025F}C!b3iNVM2cH7>@NeoDZ$+~ii11SMk zXl|L7nOhMrEp5Ae^)(*2YF%8>bJ_%lth4g$1%HJUNq| zUtaD z|NZpiAO9GUX48!)pGbf3jjt04{UTV_+F3DI+!*bVI=z-U7Jk z+W{#X7KrQ>)Dyfr0oXeR@D#9(Jjn1ip|^Qv7TI|3&RgUnCW`j8KUya*ut~cyNQ^wk zB2Hz{2@(qWnw#2aYa@b{(MVx)2D|y8n-&UU?lB$&qF2{VV?jy~Qc`SOUkvcuVXMba z0Be^3c8QlYE$^X^(bdnqNZ-iy;9=Uk{ebgl>F_>pfP5>ox0^Peyp@*8A>4QbVokQJJ&^+AB+%ISk^jn-KG8y|CDURfk;$)6-utEBiBlN;75&a&o z3{Pn{&&vVY0taI!L3%RapzAyl*NeUoz({1TjtKYDnKd|)l!G@ISPZ=|An8TIz0m*Q zgMIQ-zm)#$wWrfRd*l0QeeRPOV@5xlF69^`d)`QN#?LWt0W3G=hSAQO*G)>o_~FgM zz8fz(juGhy8=n|e4xsPi=X&X?K6tCg?U_e4QKfZ^#Ufp1-fa@|0@OKn;AFJhAx zcAtAu+x&UdLKPdf^)qRF#Akx(T;9g|VrA8Usckjwno#s9&&=}(QTKPAcE6|Xwm|Vm zH^s2#D(qkS^L&vpFqG!}S6A6**5jNH_VbH;p%%r)gT~dI9h6OR(dKVIOEk-Ke-~ef zMxIAw!cQS+gQUsAuZ-nr)|K*d)}#Kj&4@-6hPV4?O&ZU1?h9Sh%t7De!+qD$H6M!1 zb$uxt;e{X}OLSbMD&HA%T`4WH)xIosBRsk3Dz~kC%D=i@k+0Ns@~9dGO%beLE}z09 zinuAf3Tf%f@84aYv#M?em-?$7dvZ1cxyiq_`=D|E0wgQ0Kv&W?7k`v~KuXe8(uRt7D3k1-nuwKC~LD1`h`PU`YZhyoG9-&fz@d@ zLQAtd>BR{`7 zLmp@HDlc3owd{V{1B-g^lh4vS@3MIypz{`~I82Li>&d6ncfR%Aw6VSh#TN&-352(jY?&F1FQnn0aO$C8i48oQLaXiKKSe~fMtO^-$dQkFd1r4wZQmxgEe;WfgQb z{l)eJ!{MEHK$U>#=A&LA3VMOT)B~-JC}VUqMTXJc>L>i^hKR>RHk)V0w?)U~gb*M$ zZHUYwg~LN4cHyf%jL1GB`gsmOdl%i}pi*FR4%trhek=gK9ib6#M@;u&1oQ$M?CtGk z8y~`B9nf5G_=t@lPe@5204m_@%as#SSK=Wqc^kjv%IYx1tMudLRbrDc}b+MQJ z?w9VT@4ZJZ8ejL&moEnViN?Y1y#R3MSC-S_ty__Q`*`;t9d7NWojV_-qq{rczJR(+ z_~UJUPB8mfx^`_n?H>R-^YemFnY{P!KTNN_@Ko%aiWi_IM>r#CkI8f$VEGikJj6dc z=(~z;tLVN&1pK=@Oi<)m;>E!}{&7>v1!S9BSk5`60m2snyY<^H8z?RTsvCv8h(AwF z*+I0oZ^xF2v_1luK0Vl@{Zcx>Cj=v>MNs8^7g~Vq(;j1jXz@EcJLxZevYdW#_wS^C z^xDVik8b@mt;{`$#cYB8&3hD)BHPfEp zjs7(9WSWucws9Fgr)+HD?$b3VZMiS)8tmF{k*hu$w{_Lj*TIpA$A}#}EQ`g9+V~kw z8<8}lVvX?`DR7dXA^5cXq;slW^q97vi}LSm$FRDL-0}_CwPoWdMHpoYA%LkNF znC&~>N3K7Q(+kJn?Q`)>mle*n7Qkfq(RGw#m&Zn_M(>Jj%`s49D)pnS+V5)GGRL~c z`=q7d^BiHd4((b#`qE;%x+m?`GVN=U^R34tEzfhqGrLW|4i@3da}LaUES9buN9B6Z z)l|=vs~t^QhBk%l$IlSwv(G@6M|=@-Yr}K&Q|72`C$%en>6+{3=rWV`2YTB$n)mhyf4+ezQM_u35S4~seww0AS&$Uh3>`~X(?F5p` z(M410J&$Np);m|(YIp`sb#FU)Ti!5-RTGbCWGYgfMO&T=%?_1G`zqgn#?*)44kzyAG4Pf5@}SNys8Mbi z|D0QUcx0k-WdYoQ6=73zY|f;%QJUKGJ8*`Dx$YTd;Xk)X@LZCK?VK^fAhze3|b-kKd7@& zqW8&vFvrND{XClGQD$Ag-g^(54{38{@lOzxKQREy&G^|iZAV9OkWQ+bUjcLzWOdWQ zovlM6KN-Y`t8;b6BAW-s#)JsAeq#xsQ*cf|8IT%TjPSLdmlJ7g_b{z)7#&OCEx4U; zVirlSVdUwL{@>4V=t+A0+tuQ)$SLrgb|2+amDCd)Ut1(~3H*r? zc5(UpU%j9H;y-+rezkowEnp9G8#{AOvDp#%oEL6xq@|5bK-%MYJ9NlT5PAU7N9@iy z4-h>HhUK*pY4nAK`s+>M&V}Kz4^uGl}dW z!xG8XeLWoQootj*61&Dz2SVEG;q)B1>ol$Qoa*!=sSoTPW|zns4Q?2GB| zyzq8VCALhj#2==7LA?w@dai21}RC`%>O@ER5-Hc7_XM`i5Q^!2s4i&;SXj*Y!$fzCPK(C8MX&-fG?W$}ab#N6GqeR`ev;v5ty{v?H1m{HLqKC32lU0yT|TuK0%_NJdGJ{OQyzDP;I zmA?-c(~0rwxS%fbQ^Q}r!CUM{;7fz@Pu>BxKphd<@aX~G9P`Eb1dOV8w4YXwchd{A zyXnQn!}R>dae8r+w6E*y;o}2>9=CV*NUQNtdgtwT$k+TSSR}aP+8Sv!UPxd6gFgTp zTmmQBO&@>sQF{CBcLVErg2?8te(h`NS@Jn=u#up0xYwQk_V)JT-XPLQtpV1iP75gP zdB7(3h-5Zx!m`oYBD6*c0~{|32qSWY=)=bd=AHZdF;P~pvB8}YxPZEQ0OY3ph>iak zfG0%q%5R>3dM#3r06q(Nn_9v{@cGDyX-16pu{;I|sYX^eGDvvl z<}J_(QO*Lsrqo!)uLNSJd+)uM?y>>l-ouCKnO9%M?Z^0JpL8m0f(STk-s}S+qkg4N-;o>nVRB5bJ4wr7jEh_?)H0O8&)zD%yHIq;#A}GAvLP0TrD=F!%silxsGc;(oBZ zaV1D)o^B2rfx}b(s++c>Mw`VkxF`kZIWx~7FEWR3xF0cz{3!@mP1#qm_sD}mvD(`A zZDDnN6dt9ld0%*!@^WA3>+@htwhB-vvnSexEHq!-v-%fX6e;WbN%kr97d;RA&JHg9 zmOh*Ju@Ko=F?kF)ZmRK7+7_A0eL0u%U^8@ZT|Bt9|F(7mf+;S}=wb>ElS$h77Xwv> z>9Z+wws0*rF83GJH`b(ncjJ8&?pJ?q_SNj+tol*tRBRHA_|x8nwS!|C znTt%)#%5_a!0FJoEKV)!K!WpJG!-giFY}1;g^*3E^YrjVpdHw?R-_vA@twB_svr8- z`AO-LRZ0p`J`ea-#RLBjE~?5YFUOk>`ya0xXL^-mrhG1F)773!z1Lkd)-{i5^k}vj zzt8igE+5J;v{|VyFMGTn(v@1*is3V3WIm4EWcsVL@lb-{)g=4a_i*hW*K^3_?5j!r zoZWgzaaDYV97nHe-`H1KLp^u(UDMUvTIn4SWiVHRV_HAEa<+RSuc>vPT|B$WYuUZ7 zc{S_m`!>2_x0a33^0~-SOkSunw|p1!P$1_fU8bn1wM&tq zoD0{&Z@hdsk186-lc(;+gfD1Qs#Y|jxAqpZ9X`xFLNMl z${+cb=w12y&u5v>v1(|?7x1X-%UAc`FB*)sBCmq`i6HaLI#}kH`O+qC`!}UOak$c# z@&VhVXXbCGXJ_9}KRLdZ+#<_EOfPeSsPNFH=vA))#cz^`y~x~CH1XCp*7+Yib96NY z9GOORM~fH&Y9Vy+$Wn4|`@RSfQB4g18^MAEDH7rAuh-YWcAf^8d46s;eTi(VPx0&i zbre}$0dyuu@Molr{n4-9O&@;nVfx_x_tW;yHo=oM(iWfA?8`V9&{mbj?}c}vE%$>n<53Zdo`Z7BDt>i&eGNvM0a(G}IdjMW zz?wMRiMKE6$gr~WIlw`IRMSr^lX}DS34(D(3Los6dzgq_HWXA6+Zsvz(Yt%;;irdb zjVS0_FE0m}%NU3h7y_+R7&Uh069r6jXnf}-O$^zgs zq;-h5JD6Y*5EZ->+?KZ*M@AvTAFxe2X&yc#XE1aTZQPd+VKEYEHpp8{w~;5>X(YC7 zY7CvG5#{B4UICo<&B-y5&NBeOhz6)FzzPrz_^i_et9Jq5m(Y3R`U+_}*c1=YxW~?p zki*jnYhUn0|B{Ns$leoEjTn^-cq_0v&rX^>K-Wd`Iv@3jfVOXZV!!4#_NNdYfAY26 z^!2~IpML!5jmWEmwaB{-K+9X7IVK+#kN!`4`{{`GJ#yg86K$RV{xLdH%)RvBS8t`m z?fdwRG$1QW_?HO_Sbem=MlkX+k<5#vzaYmB8%-LO?9QQEL{jfEX%C1fUSLwK;6rm% z@8ZWz(p;>QLgf=ueyour-1H&7%vb>=KjJNy`J`7!58`HnK*%s9${c-ENt@3=r~SdZ z@hvYnj+OxFv4wf8Ns(bb?R_KV84q*5sQ~N_Cz3M5d^?WEhlEn^-aSokKUm`@8#e%~ zUt#k`@;&3b#W`MZFg})NdE+)quIfd{IzHlMg7!0C_7aiRY%`fQ5APcg%^HJ$7+*OL zG`i|Y%1m7EnDJrYD=+gEoy-E7lVZj?7o=m~7UDnO8^&5ixhZHI>qfFOLIQ;M?czLd z|CGxF_UrTa)6Jz%Nk>9zn>q7iZ}E@3MDd?8^$++s8r)P{cMjM~%^7d{Yb=Y=KO zISw0eJ@@v*8-v*XtY-+x0;AN$vV}=^`oSvtD`HRLT$dnzD;qE_V$%^EgAU zuJ%pd&HJu)UFB{1cF}&(%e!o$|FRFSMuV6L8lHnYEO+(t%r{!JGq-pp{Q>GK2 zFatZN+m*Vy?5yjyHbs8z>Oa1Xb{<`0qq9L((bl$2o~p^2m!*9f6W*8WGaFX~Mfa+x zaS3O>z!kb|vka`24hO?Iljg6|eF2ST^(%E56aC^wAH&;N=lhCCTYO|+U2TILA>vso zRnT(B_-!@4tZ3XdquwunyXK-9w2uHsdW zRfER_QBNZ{6`8;_W_W-_^s@r>4KX8D0&vzt;X`P8bNs5EerE|*yvZ-p|8RaQJ$Z6B zZT5E3tIu8sR9*{U`Vg$>&bI%&C#VE>zYxW%Bl8kE^YY>zdYwlt0qmqLy8Auqe*hk z`yn5B=cCr6lzA@YeYK*hl+|=z4<#L~8TL0?-n;|lCGvO+2oSIrTrdWcIhT$2H3#zy z(VzmO4nxZvP=@#8Pe4#Wvj9>_p@E~9$@zOoG^x3w-}=!*faa85{L*S#y*5v@u+gnX z36oAiG#Bp*!kYGC9<&h5F8VN+bfi4Mlcomg0c0P*PZ0Nrh+U&*=ZS!J!@nLQL!LU* z=v9q0OXRkOJ7q;U0!%ce!VJjuD&VmxH_QiZiVOjCDgrq6XA0`*D&n9uVtNsK2=Fp* zVi4>DESm#bz+F2Cq&wJ5Npi#uLAL>h%4ZQBJnnwH6Zx-=JUs%Gb@iVGcx5WagFt!J zN1)#{B?8Zak#2x@$|PJ^y#sWt?&PrsfYx?`oK7GCR`XaJ9lpMt(*O96AEf{3e_J53 zn5bZKBhLT~udx~94E+JmriaIS*aIM%4F`Qe@X~j`4d6MS?)=~f>7$?h1lwX4WSONe z+sJqO0ckqcchk+4wWwR+rNA=jFOW3db~jL>tQX1Gif@7Kg zgtkrL;$y@%%!aT*Py1m325s1JmV48>*pZRr>x;)}lb@qJvAl;b9;ByN?xmMD-c8q+ zK2A3mK8R>;Fn9Eo1-zA$Ygu^2bHX~xaX`P&iatRud2D~9TjaUuQ5M7UKn*=q_$rUS zZR~5%&l&vNXmMq~eYa568#?2(!-^9AwKWEuPTWE*6&7;t{%hqMg z6*eFIRN2p$)*a_e>bcCXOZU^sAg|}e zx1D4csF+rE$5Y+^wC##WWj-|9HSY`k*tuG_$Lg-IU{s#b=cv#3N#o!O#W**sQMp;; zIt0DoW2RG{7!&q2YN|}%Awws#DJq(JPSFICM>v0E?czN_SF4n}YYtF-MaQTU^v#G^ zAw!?WiXxD`OItOs=X!aT)Mb+do|bMBkI%dAtZ{Z${dDpdgO7M=x~6@q=DKXLZXJ=Y zIJRX`sE0u5xV5d7g^!mbM5X2AvrU@))od=pb8?UNH^p;Hy(-((G8K7C*(6Wt&Eiqz zYSYK1qIopiHR~tc`>TcAXH4h;Y3_THv;)#hT@%R6)X^tr zp{s8%|2+N0!+%IQxQ6%yN|$g4-o1P2gAc$wNVD$_?V!rG7dh&7_LmR) z#yeETSveZ~!|#QBnzJ@&&<*=*RJ7dAZ{1}xI1IiDsJHikI8Bf|qTVCQ*;EpT$uAOzSuBo&8?#sVO)=_*#Qk(Pt<1wd!t!r=MK-;{|2HXm4@9lUGkd9ozgKPEJ-zRLB6di7^Xyo?E5odX0=y@V_c_9M` zN_4NlHTp-?D>jmsIim&mSCIV(1pk!DYjpR)!xf@|0h}?+A}^A(r|ks7!DZ98UfoIm z**E9X|Nie0mb4~|$gxKL=KZ640GLbA8&wSex<_7UHV`zj`UKflo_;z#v9*=%^Yevc zBAh8i-`w~82Rmu=NpeSTKV&IcO9u<<5#7B&N($g)HUjiz0e-^XYXZuYxe=qA2`(>@ zM~7*9hYc;4eMy0y>a&im-57Bn-`T`BYrXlUs6PhuJpoKc^t45fPJ0K)A_)6|XY)hP z5s?ke&Y@!i|FgsC!9EQDijB;Ulr7lAoI?J0im&sEnP&>!2JNA>bkQTpy<^Dnb^tgo zz#J)Q@YhU43%kfNb^!JBCX>7barK50gqag#RwGa*;LYd>n>Y`E^z#fS5<9wO5j5go zINFKM?+JJ)q62T-T;O`gz8H}w*2OLY+OtGr-`1Y6$LZ;{ zz4Y4VFVah!ze>-qyo(?1q$R*~UsS}nhj01t;X)BMp>L#rJV#@Ni$z|SAlGG&vY5&z z(u*+m^yOIx>Q8l?XE83cTej12w%QzNPr-P|)hoXX#P@n`dUW!Yg*Ve0znVTG!Kk2e%vb76$vGpS91;)z#5#@O zxm;#_ett8j+d*|-m^wXdH z6kKF3ZEkL+mtTH4Jq>2HPEKfrS5|{xcL}T%WHB>=n2hOQwf*AER$4f@M>>sn()IMK zbYt$5La1vl_k6h3zI3lOen2O@To&5k`*8ra#ujXyO`{922_}BaC>6b|30AL7i7ND$c zOjDsP++OzR8;Lb$}lkk39jnFXcYobV9vQBWBbLuW8> zz`t1lV{`}fJUIrr1Q=wGJ=NkLGH^aY2cya7iBuLpHRp^oSFl~VjE>&HM#}6@00cYd z;6uCQqw0CliHhF5;{?N#4KzD&}?c|L9@Bi_3 zdi(ZL`qyt=Pa8K#i(#}f!0ZNZns!Ou&^z1-yR6^52GC7(Gj>|p+(^eyJ;8!=oVFg` zr`#PtvCqLiDLHo72=W1`Q+Nr$zujFH7dAv(#zx1;5O6Kdm;tcXDa+&x_l;S94Isyl zi_9rtDGLI+ErZ<8q01?}=QSQ;AG1&_lH%ilC}*Rq$y7o_`DwaF?(03k{zIaueVK8} zn=Sorq73EsXC0xE>(`f@j&{;h??DA0%*n#ES=4eHUVc zVG;9Xin95FL4F?62x=c9hdwe*ievnMF#|8gnqvyjntmNyJ1%&sf(eY4Hf8+d^up#|`qH%z(ree>NngJ9LsECVPd;vK<#AXIL-%+QwBkoO$#G*8ZLSv@j{QD-V@&w%iXw z7tu4iDF);w^PEPBaRG}w<2AoMC1IJqq4WHoPPckoIG#PSKZmx~e#OU`;&OjWpQXPh zx8kqTXMoQtH8vh{%R*oHbnz|qo=cn3M_usmA7{y1>}8uac{9(UziQr(;<}dS zQSd*D=V&};w+}925Z!ujFFkp@onD^ZPH$|ouNc2nzrMDd)>l^3HUX?L-( zu?d!8Kk~3Q73h(|^)UU@fA-JPkAC!{be9O|pa1-q(dQGS+Iaqj=hHX7@eTGfTgw>$ zz~GL-QTFyp!4MLIrFexb0z)(KcjLhInfKC-(_aC{zfTV5PrymQUg%OQgx+?o@i63Y zj<^nr&hQ7~B4Tt9E%Q~jhu1@kwfQbnmo}MJy0d8OcIDbO*j=-NJ%I8+=q z4rPb8(_d-P@k2XKbCCT)K#n&BYvlYT&4ej57D&BezTpLuHyp50;K`{#E`TiHSWf#O zkPGk#pe%T8*t-$7I`aOfN9oo}i|P8)ivXY*WHwEO(W#6zz&;1H5ywO#6G3}OB65et z9O1Br$*%xBE4+iO|eDlaJKrVpVBWil) zgme@9TOb1ZvqSSw6KPGIsYn#Y%@$3YVZLg0TA}WL_=k7XPkyD2PoxtfpBDgnaT{%o z+!fTM?IJllPI$3kf)C^%&qZ1$j9Q=jBEYjdk{%HG{Mjc9yhvE0$#Pmk-h~Ag3=w$_ zQ+2FrbJW`cK;G{y0EEu)Z!T>k(;9UzJbjbJVmCd&AWMceqwoqFP0le^4hRLGUtEno z*WfRxZ7%TG^baQhCzC#dg^uwzN0hWXq|UPH#=?i+XXls0?uXdV@;UtnxNZ82gxy>y z(wuL~jB3UR;aQ!>I^eoL(}>g^(41hK$a56=yKN8nOC)q6C-*5) z*f@|fY|r8cIkK?GVGJXWeGBIx;B^Uoz>_l)Mic2z=#Np2pE3|1VDy4~(NWME7Uq$b zhc5?wL7~^NHvf#|XW&40>{AFiw%D=t_QQqr=Ixi$wdGd4nu_ zq}7Fc!K;UCj!ka-Bg3)7nwH`*x@imgJ&lovnYt>+sM`yM3o{yNcF5E(eBW=9^=)t8 zRqA{!4~lYb(&h)kXldPUv}W?%Bze2~pUm-hr#6NVjxR&0bL_TM)MLf#GFJ3Qef_yx z(|LZzF^#-!H1!-hql+&@b=!Q7>HWEj_J{=-4Er|c z4k{-Lq4nBlPZ==nwUfCHYny&4V<*JB9P42xkKoFAP(ODr)5TS*d6lwVMz7zdw#{*R zmTuCwO|jOgSf*0Nw9|)H{0<#m>Q0_}!_S|O(^TOZeA)DS+h^SvAJFD=2D+|7*7zL% zxQ2-1wyQ4%shrnJW+DILtKb>R+f{wk^GFQFqe+uXhkyMh**yF{*mhVtPTfZNn^9J| z;#>k38~9PKwv^?it~uVjI&iFKyNaa@kKk1^?^iWt9^PMOe%n@jfIs}t%p>UUBz<%7 zC+Xkrd;?t0&IvU`DSySCzZj#v3l9Gbl#DS$kv0*TkcXI?`sLPOK>X*t&$aDZdpn9`Q`gPaGC%3qo1Xp{NyL;t6zB| z{n?-WdD`OF_IcbRpM#!>W1q{W<3NsOu%U1K(Kpg_&pt;=RB%XS6^lC}nDmg`(IKa} zj44({P)Z z?hvthl5Uc+qelu0fna%>DndL?(Ap^HSq6~2?RT-CR>Ns+B(hPxMgf~&+K6Sr@Yrlm z9-wlT!4>dT;M@KgT!7I=RUgxzz_7>W4L5qXL^EGz*H)BPbEEcSMQDv68+t%mO&Ao408EI_S1A@U8Bi;Up&);%UA zGOZAWzR%`|Uw-a5{da%=PWnH7@Du3o(W);C}8X}gFRuIdH}Vv%d2Vsm)UO-lZ9YU`!T2=+R=j!82UVBh3rvk`h`u*k zm@%_N8kW5y^M?cBV%XC?qJ+`c6ewhAPFv*9Uc`5>2%voyz#Y+`UBhOMV}D#>y%FvC zMG4mDMS=jbX=7l<+q?M5MvMo5Xg%r+74NB^#eSKdPQtn9k8MwoiD86n*iW+}@F`$9 zGXoiXk;0gRE`Vp}6Mi>0wP{Xf?3=MTOH{UtX*y-Rk`6>r9q#d8L~r}z0G*CW;~@|o z(c4a{7+=ycw%{kozF-73ZxgroX3{4Q=h83lyqNy&+8gPOC+?>2-uixe?b_c&&S=4H zSvYn%p`X|A;XLb{@yLbwy6vwz3BJ^-701oeGW*0b)5oJFT{U)t5Y ztt=B?@zKWl(^nV8Nt%apTKO?h)&ehM|~GAsC04E$^}`55(+a<}u%TrM{z+9gp$WiE5n zK9s@ofgPjF_F12%TA@X*8tWwLhD`_cO~)f*Yudg}u&Vo#V!Xe)-RO0*ZPZ3#!&Y?XSg4Pp z(qL}&nT=A_EzZ6!yvn%GEi%VEA8H;x!_dDps;e$|-Nov><<0I(`=ZxX%4}bSf3x4H z!*8YE;UD3K=IUP&)27JM0`G@>Rc@mDfe*y1cL|x~Y@j zJDNufY0J4V2+E^=21b8TTK(xaig`#eU#ka4W@TSRszFuA*=C!e2GaJ0LgR ze`LPr@ll>brTz5hQF{(Ql1YYdKO^{A;0oH%+K7YrWfTIl^+}rud() z+Hur=9wD4?Gqh!!2ICFezB`BZ7@^27YNBjkp8rMS$L2Bq)HUk)GtfB<>SqLqt*4#W z;uNuWT737{aDCeWYV3=bfg3HrXNh0rKTGoK;sijl)refSLtbll!T7zF} z^KE|#5c+#R`pfiJfAv@7X?`lb^wNvz#g|^0od zc*bek-Ud_#JMu7~Pb_YRXj0Ri@>~40dh!uK^T%oPGv!qWYxj_wo(bg*Kx^ zxjf4=tZ|0qBg;zr{=J%%ZY_$`rTscj%{fw44RD;`QCCd7w&!fWY_s9L&F0PFY0U8C_+(7VDq_#gZjU-k<(^u$60Y%@M9I)x1^7P1?Y=z+G5g_}ad6dN- zv4eS`NhlnXPq1A5gtv1PG$1*kqIe_NU*vx_(*&#o_+AKm03gFYM8L(z@D>OcC{_uPuCByG2rt5&)&Pm%C@EVU9&E$ zYF+pBoIc$?r%!jgaXZG2FA2U0lRyF_QBY(VAq9}%2uK7aFG##09(dyoB%UH6A{0Ob zLHuAyh!9Skm;@4=#Bmba-R^t$x$WD!)xG%rzA;DDthr{@s#umKfn5h!0-_8x2y2R&bCNLf(=iIx`qb0BVaz0lrj9de+97e;brkRpn31XJ@R|= z^M&>~!MmoNVEjTL?8C-{dSizLW7N4U+JMYmWau(R$~zrh5(WJPhYQGH znTySR{B6KfH6w$E@P|1M4agxaXnQf_Clo`TzVU#7R236L(1)kRF~0NS>>{G6$2{zq zn6rCsDj595G#NY$*b^0Id}e_TyQtvb{5i(0?tKQ10Ue()$mCw`n#7)ReGBPKjbYya znAyb8F`l>g8E^Rbk%0=|;9G*vL^J!~L8t7{hFKAHezY+ChLQ1L$6@Ok_6o6F1sDjQ zDE1gUBeY`!LF_R>5BrjU2AjBWLKh#HO-adtO_a;LOuq53{US{%fbV>EPQ60Y4qqH# z%MWGVDi@*PkpDDigjh`-l{Cj@0_>sr|SMjj-b+X7X7o>%~F&b}T_H|@JG`Kb& zw1P8lJofd5!)~zvm#dp|(Zez?^{17iY!`K9+nB&B0BJy$zm3vO=7FFMwY4JJEZXO) zEKI(C4bIoSS=H@TZNIKAm*;cX{)#cKSH3<}TUh;iqO_>$U+YOe<4 zMrx~Sn`^Gu`}AtZ6*@%rO$(G}~E!|ZvcXwAcO1NoJJlGW{scRb}&~49ou8 zh_L!)wN8|^t1>4RlCz*XH|hP>-cz9?73 zBuad?sN1zFa#gwUHC z8nU;pZkh{KqV>;_)w+CL^#kc;9drFI=XLUI8sl|!(yETzx^`K+=LLmTy>-K zT7I`9i+!((v(9e0W$xnl_;>r?Dc%BzeQ)*_K(sY@qW*~Dj-3H`ivzqXQo7Qt1wP^0 zJq2T-n60hAZ-$q}5x>TMyE`i0=?{x{JAlp)$m7hf+go5*^A=bLSQ1HH10xCHWl6BA zDKZRT{i&b&sp5##8DIIzSBm%Fe;*+FU4E^fQiv|(C@;kEL<)pAo3tR>o${+rzpGuRnP>6F;Ux_ly%cIHGR@K|( z_Vsg`573t18e-Mr+&<`(Z@`OwFs;tD=*O6?j=frVIF{)u*XR4PG)@cJsc=;<`b#3O zT{da5QQ38IVrX_clq<`aJ1SST45SK&6F3C~Y~i#<_8yq`nSWzI+&w@|^H4-2FJ(ib zY>lK9WHqIN#7>_Nif{b(8DJ-19H^Y&s^R-1`X52dln&rq0a%X!mIc&IwPCdKgcKiw z;O1Y(qwyNb0Id&>kO2iEikfmkJb4L5cL5FsF+GeDm5z$VCUpXcl(mdDHM*E+Z2|Sc znYo$G@ysOaBAs)QC}X2n1(U(u0e~qahi{*V zK$85JvGK-;-UHeXh?ZtUL}j6`8~m9nM8MwEAU@G@tbXtdlj4`ZM!J&zG4?lI1*ttu z9U*?Hjgf0kTY0ov<0=bH>k~XaHE_pB;g9@(!i^$pGD5 z-)0=A6ahV_zSTcueKf-}x?x>^O42fo2gH@n5MUQqm29KCtuQ7nc`|~2{&i^If^Uml z%q{&4-xA>NF{X}t3xJu~L^pG*Qx=5H^{v0!2H%2gho^YfPsk^ZXD}x8rH>D4_Ke*2 z4Z@ThL{+yLC%eXG0_HuM`jVz8-Hx;jdv8Xb7&Oi;I+pRO^Rjh#ZFtnR68l zR+?28!L;x!>z7v*I`poVuM*R?)MueqDsOmGz7gE8_fFmF`rEnsrjv80t^E$dPTj$?i24E4ML6wiGV&l7Rf0cE6|KlYF_%@ozeEeN)}8Wz})KzVAz%6xTDqb&I>OTfCPvjLU9zTva8>wTo@9 z3b4xmYW*t5mwQ)z56o|2Rr&5>9GW!XBM1xTYEAI3m94gTb$55K_f^I6I$X7PlZMK& zeto%0w~5AT*>ygb>aUY4)7;uth#JynN6Q3maNbt7D(Rc_$^EzOyKbB^o$Jocw&e43 zbHzq-`qxeRts{D^2$cz(`Q~$#=3UEGrCq;P=G%@*oo4=yQZK*LdnZacP3yC@df#N< zyOX2J>2_3=Gp~2IqqKYPjec9PbCt5LoZlPuA1z_iWx03FM`KM{==a=APx{h*8Ap>0 zn|Rqi_wl{?ar=Yfo$hywZ;wCAUHWBwq?-j#@eFDZVn@`oIuTJUS}`D4uLU;K0Y5n+ zkMs9;FN@FZkBhg!avt+b^W%d9+JOy$bBr*6n1n!*zxD1VR`#zvAFMk$z8Cn_(`V0$*6DL{O8;{4_WtjX%lTXE8)v{| zumLwheg@XDO+Yi(Mw@UrR@r~lxNgH-7maW5Qte;8mQ?*dlaF$REsv~o{XW}A#T+qp z@yNDXYNP#9U3y(lJGaX1_2D#X^`6PRS%$idna*v`%8~Wz2H1=xSf&iVgjJG=SF=1pi-C zg?|Fe=6Qx)aYo7x+qp5}@qKeSYn229Mjm8<+yMZ#5zYQ2Qv7d%Q=58WEFc+f#ioR3 z&?Hj(_@oz6sE$bokXQwk(>%?ia{x7f#eK>Hd>sq?I>17IfiVJ-0;mI|3RVjmnt~%z zUI4~P)4>pg7N}PneF<^U*HjAt_eO#8+>~v5Z6Ja~%XA*}!Qiu?_?ZXT6a9_~xkn0IPBM%pf z3FlMVJBFVo&o!7o3kE#1~79R2{P5$y&QDmL z#^#T>G_)XO=r@zd#j1Xwx~jz7DKBF_S9gbz=bu=@v-RS0CxR1Wd zvCWvKmg)66d8%#m^}0UV+y@m!T) zoosns+wj)2N@$C_Otac_9sg<@ueC$72KAOZ+?UF2D?^=fb!>KBt2WW9*WK-Xa?ZR) zZqmJ~-|b{bBbUd&$)`5SvBKNVnNX!-HM+S{(Qw(sE8C;qys2%y%}wt!?c4ZHlDoFx zGzheAESt;Mzb98oXZf!$+x@*Cw9s2cjevW;_JlU zSWe|Szus2AE1xOC=1W@!|2tfm>$bYB{B?TU=$&k;QZ+p(*)&({y!`Ilbd)&bqH4)y zQFP_P@9Xt`)kb+LtolPaFfb4dl#eBw=u~O$axLE`Ju=TKEz7EHV~knTdteT~|KQ8T z-x>W_fKyY`c8X_!B1EcDuz(T!h~d>>M*K2;^kMM;!1-O`nBVRUi_i0G`Zov|-2<0v z@r!CxZ-8bMFX$td7yF0{^R6yCQ=&A z$1?Gxsz2uIe0KcJAaD!@wa1=cFHX;j_s))rFLb|C9Q53D5GcdH#iU9I9-_s-i0fo< zJqC*MuxrIw_3_Ot)W_>zHFy|$rZw<-k zx&z21@GR0PV9+NjSuu?)9)M^afOHzsd-`Ntd%1lXN?&-7V0<@%&fR~eFD#!{TZoX%q+@wha*bAU+E@cPBT{I(d8X8Uxa}HZ4 zsH-&v71hNE<33T)6Znh(-`qq|+6EPOO>2OB>I(K+42`M=FrEaU%VY&`?~&fZK7GM# z=o@+M=7-EUv{X(&TETMckpO&jBBETHtO7`y%8h!q%t1BpwzT$$5Z=Sqhxd&BgpM^~Ut*6r-_VA&F4#oyWI7@G z`a^!|fw{={W5heYf>JMN+hOeY0qW=2d4$5_%RzAj$bG?Phtt?VF;pR%9iIT0pNxnM zNAI!5U@_j)A7*DnEBA3omILIN&xt4=`J)j*V{=lQu7ziS@Qpmq*f$n1A8;7y9G=EC zN%a|K9QYy@QNd(wDoxA@e;0Uc4ItfV--n&Rz2l=FxHy{=-Kcl{#)xJY6#!?SFsFjkei;Ny7oDQT&c%Db?eUmiT_({j6c&}{U(kgJ*U$54u4vOd|T z2TWgDo%biq4koUaMv?>llXoo#a*6!YL(RzP&Au>Gg(Y&hpDw%cb#FcUlmdUJ=h~TeW6?U!EYvMfad^P90{`RZz zil0CzWa14e^O8-SpJgxg*kFe`Uon^;+53%RZ~U~Fv2U0LG);yOo^fhjQgpsK|90`| z;{BpCI4r(+TojLv*e{KMz;5Jm1~UPx7=vxdb_|Xo)a3^c)78!}tk|B|S~5zqQG6b$ zym1?lC-8iJeja}U&wcg^GkvbOmq*V#1J}ePAI5qq_9D+=3X}S!u~#}wcwt>HS)V*qxLj4#thl*E3n=_ zAa>JSOlJbJRJ&7Ej2xM|7XKbJd z57V9)B`mn@LxJA(f{`G%FuacXMvk+I;K6a&#@x@-$#DR~j>Y0h8BANGyx2R~XYs*i zAlbswqf7EfW4pog^Z=qu6!GPmPs#cK=>CkuxZZj3;DiOnaRx9|H`8sHkNZ$S8d=Tf z-Nl9i`;j80GXZ2~Q$p;b=NSz>Vsk)Kb_|WkhPMwbPS1wL3E=ZUdPXVx6A-}QNY33k z(TLCm{01l&#GQ~#oL8cfk77ZMEFC-@1fFN#LH-y6{csjfD*%`UcKdHIX8y!O5FcRL zCw*vDnCl3@xH~7k2fl;49Qq$AHV}gt?ywnN%ed&L^mm~~1{YoT@O)5RbD!jBa{@#ZfS$KCg0-q9fr z7xKst&0Q=-*n8c=mwGH;w{5rVv3jNI)rHU6O*A*vRVmlo)$glq*IifNP;#40r>QQt zX*yT?d0p-AdUo@}vD&^jL(5UO`1bvycOtUVNs6)bt(3Y(FrHM#IG6phEox0yY+{|w zchRj%FZXK@wc2dsb#;t3Hl#B1Yiie|hqlr#^<$MzLxbFb{?1kHdb9DAcIq7zZeu73 ze2j-Sdo|*}Tzgghp=mB<9kY+*`@1xKrY+4T`qlb6-z)7aN@P#oWlq)Ur+dd>b^UB^ zXKCi@^>XaIyQ{-7tEjn=Y;}B_8m7G(lGJ`W@n4hJp-IWNYI8qJ=fiOd*(_*Kz1lDL zsh*?$x<=Iwwd-Y9nJ>p7mv8bTN#%AmvajrRou4k{TIy64s;Qr4={>LBXLVqbANids zQLOq^{WF6tlqK6Sx-@mH3b*09N$#fR)%Wgg`!vzHt}N@dY*+Q8<@|55%d(GG$(UhF z?X_CD_HH@OB?UVA-7Wx;wY^p=)M15AWxQ5fF7K|jFEo-y%RXLZdu4v%8)cL0ZL~`3 z<=-twA^TAF_3Xl%WUzPGfQzQ~*#^?^Tc+)EyUZ`_yc8qmXRfnf{jh)ZDUB~!7u~Ca zVWY|-9G5g|+HkN=e|cXem`j)bo=d7{f_DxKS+>;s z>TC9;t2Il$vIb&U9u`#Zb4@8@zV#eM4TA%8LCT8XEKW!<`yNi?7C_w9eB`0V}%#olC4{OxD|cF}wEC2$9VDnTRMW6MBJ@i9qHz+nue0}MefK53Nv zuuc{r&TW7Veak=bwUCeNJfJN@`%W^n5Lw2F(S5sI$7PFYojC=IZ)6(Tr}3D&<%1&@&5N)@EBo zeXTANu(+$!x!GXPT*9OpFxqlLbY%|^RUlPGb;`4+g8*$GyxBJ`aR8lP{q^U?^N+^G zXTR^TIC%)r1x1~30vL=d0L+m(0>rij0NU2^92ou&hk}goB?4H0_W4slRrAmwi@9e{by z!y$k;A$#pB}S(f#6t zhYS0MM@5H?11H}FobC>IN~ZJl0ZsP-y9bC3_=yY&IAt72aS>yN9VbK~U!1-m+rwUQ z&XX(w@hPDF1e@$19b=DCaZa@D2~Vv|`El+;0|40`QO@t7-vLp>kNJ7WJ_B>kSf2s@ zzX6>Ak-`tK`7>-OV0}dU3jpsEY&c?!F7ShUY;b65jxl4|!$akj7&yglc=Sb^+*>& zMSDhi55}<7!k^&P160?UdF&D&VtQ>;Z{V{mq98 z8pweibTRE)9@_I&9#6)HqCoUY_&{p)o44J7BYtdiSTX5__qBQGF@{Kfd-oly0(8BtfqA@tw&pr#1#(f z-s7$`+)%L1@Gfjj<89h+V7F6Md|t#zO}5*K(9VsQT2^^iubWAhW4Y|!&}{Q~N9{Hf z{0imGbgwk0AltX8&azEvEPP^J=hv*fK6cwqW!q`l>XGeIFR!+_?)tjV_jTIx%w_qU z<+zU5%dOvTe!g6P$rib)=_VW5%kjP736g$(v;3#7UwSnrvc=j0~R8OnW zu64o@GhAmG6V__JUu|cq4&7#-oYAB=4zSUI5O}wr^o^t@BLxxUwgHGl&i4Ex8Z%jl zk{yl@BYO3i^d>E2fJN9r$8~I+sAGWd10t#?q{`?`jDki6>~O%C9vlJy1H_-{Hay`H ze073OiF$5hgbr<5q_UX9t8+m7^d4?imIKYWB9PU&;h zH`-dD{t_QTPXO-+^iii7I`)WGA3J*iR()>pki_;bekVjgKL>=pK<;BAcBNvygED53 z0izF)-F!smVD^d6bbKC@er6Q1g9o78aq&V|eRjs8q@hgV(xs2PyaKag2gVkkY|n{| zX2F=_U-PLsp3$+*LWbXxyn`|TT=QQ5V0R69M&=ISuykhg`_S~E126GV0kC^QzGrM? zlsH@gxUss65dga~m{WO1B(-YonS+`K1Yq;%W%`(Qaky`qjB_$VpA-Z!LyvkJhtKH) zMmifo9*p4ypiQ*>w|UCq?o*}iiD zo{wlhnS97Yj-%om&wsY~*S`3L;txFjE3w-w_&Bog7bj<_M|$-tFEaU!#IoNFN7gs% zpU+j@8NxW?&7)dTyS8tpSF5_!ZRUR~B0F}j>k+z1qqIep+|rAZA65G{*>k0DwR5$i zcAaf--5Aumy|UXTxw6a((Nw>!aM^~*t_=fFBUI&^`_`Jg(%URg)dsn&DoC}yTAr_S zeN*4urum#c6j$rk)41NgRXW>V``Ix*?Ct39+tS~q-Fn@3p_1CZ4F5{{&jRV@vgGnG*+AHuFwc^cC+cW^c&w45McZMRcR~Nh#PiNY(Kwj zb)@m#WZxuLvn{rhxw&uhE%`6&*NuPv@>yxRR=f0_XKnSOVw28`Mq6IoC9S+}Z^!!T zT76x=ENkMkoZq=$s!yNIylx(2`cZMUcc#*O&b)K&wz}5)<^DUJb+cUs-ig7@8aLY) zQJ2$VBjMak)vC1Csms^1osRtZv_1xwTbApyY4bUl_$FSNwA<=*Rlp@x*Me=L?pNi# zs&ukdrkl@KId5B%_+8~=|7@dGnG)sdm~G-!wOuopWpC$|f7{Yt_H4c?vE0T=hd6kZ zCalV7hq4%1yh@VqDp#hVLYYdOAt%3o=fVF{w83ko8^{@7@dG=M9}R(Kiihp57axHa zh}nq|0Wd_})IdP+gu_E|5sGNtF_LgEYe~arPRf z9GTC;M{H{a7){&2Sb_|Vz9xwXDt(^M$EGiZKL5mC2Iz1Bel#W8JMbI=4%>p!raNV` zMFa6#(^KxjeZYG<{IvzI!Ls^9jT=1sY;;0f(w6kUUEC)JbzmikXFA;v2?l-s=D$aF zf_K4Ez*@lSL|E7~kxV?;{Yq|_K8#LvG;kyD|9_D5?y4JfxB3B)BhkF3%>ZW-wo{o#}|FOg3tESBTuvgLb`~1jiszQ98(lfaLA^Jj_{C_B3<-208BP@ zf*`3WLbTlxIgU-)VOoiO0AC}iFVWM8at#Bv9yXX5`D!YPcn3sS79>dE_zW6$w=YO! zJVnuS_K0krbPfT1NzVQPhU9!U0{Rf!Xalc}VwaEL`dEu8mR_q?L|&uHhJV6Lg&dOzW&hEE_fKTr@h_r&3d$ zlN?DM7P7#ui70LZ*v?wyFlQiJ^hxlh#uq(sy3?CP+kz*-%56uI{Zz^9vb#q;V-^XIo zF_Ig};-_5*zDJ&CQ+9O8!R_Xaeh6`qYwsP=55ED+mx_noFWcRoNPs@*3F#|G(*l(juPcpzmlQY# zc~O4VD!**LX>7wW)~Z(ZiDsW+ic~6Wf0sT}wSm{ov^VRpR=H15MY8KAfzyYDO)XwV zU3GkJLqF50`qFLarM@4JO*YA$^{lr^zFjZ<`0qom^4-E|o@ZKhUHG`9yvf?Ujok4i zG%LE3a>?Ja57+5PavT1#wC}>$7VL6F=k0mzLi*!qyKN-&wzh53VcooJYP-3t@@BZX z;hojqM(;Zl;w2H&Dx6$q2BeL)0dH*s@k$D8|+Tu{vZCiW292u9sUzO=D zcu9FlJyCSD>t);W@^hpz);~{Xxtqo^*KNyVQ#)zJ`vD5vREJIUx2eneRBe#U?C*EY z`Vq3OIODR#n&M@HhAZj$wR=-kaznRdwDr@yu}d!8Xm4mrHjn>`TvR0eZ@*?#Wysg@ z-5&0KzxUgGM81|rH7%%i5^e(GDn8x)YVnoPciA)R0pKgYWEU3!QxWSrJUlEvXM`r9 zwmif?9P9K4E$}X{tDtkzTN%>aNd0o3)Zcy{;%i>pc9llLCj_VUa)0TrQG+J9Ta*WV zsn}Nf@)H^Ij%#J(*Xga|e#j`G95pnsjurv51C~$#b^=Hvo?;dVvlW;Hphwk^fXi)zc#Zs{O!N>Pm3@9k;kON1^fml z>VUr(3P~6(7*Z)ZeZ^Bka8APLkRX`RlSo~_MqH~C*R;<^Sh4JDV5ax}X@!7|ybsc1 zL)s%9o+*yMvS{U$$8M$Yj&2YWTP=xo{rSD=C+6m7MmeLEjapUV9@jdUgTa_Y6z>oa z`2BAVi#NV!zxd1-j{qP6%osQ$LdQfjr}%Ku15oaR5W3zsE#a9~>t>m*>#Od$nOqPWqb`WPr|{eWHue zvqu#7v&lY@$3&9D(`e*2V6b4fPlDP#I=Mg~t6;Q_{OSFt#Rq3+L=^)%@7)K4KI4JF z0g>b6cqW~Oz;w}NV?BeR*{n~n*C$J3cR2)<^nP*^Dgja3@*-Y zZscSe!+M890A2D;k9fK^CYoMQTyQ=%7GzrV@Rhca$4rTjnA|TI%P9gpV4ROhw{ptE zf<7ruwB5xRIWOUam{X&$iR#vOTTJjd5z2F(Hu+S`$>r>fhZ}^&ceMc)m?3|gsNaZC zM!z|_6+`TaE^Y27W5UhRlZJr5=Aq_Z|0RJx5Pg*O0nc0V=0lr#rp+~EqW;)Gki5$F znV0pmxzXT6_d`SXYs-(Z0>$RWMNS_?f7-Y3(?HF+*AGWLa543YV-WrEb%rsNcn^uGv1- z1vi&{($B8zn~u6|a59=V`%K+#SNkqG<+j=Vt`TF|vAn9%zD|cLonKKw)-AKkI_LYF zp6hg1=`@ecYMZ;g+bqv2uWj4b+hzZ#m#jFuNr5dJCz~(VlU4p%&U}_=em~ff z!J=7}hK0-fRi29uH@J}IP32paX#?}d$)DOUb=uJXB~@)3sec`NJwiR|qmpN5UB{*G zX#3PEpXu(;X+}}Rce!09#LoiYfG5r<-X3458o3HKS@JZXRsnK&mr@7BIrFENZ z>eHlGQ`sgOSL)DVA;UH12pg0Hw81uqw42Q2c-=(y4O3{rXQ59`K-H`AT-UDFZRNJE zOQI2!@vU?w=oXAZw1Kp=vwrc-(c8s`gX7{7jOuK%S3DVjpPYXU%;5z=lwvSoRs7=J z^r2#40>#!7{|HQlMT5GJ~(#-oLKFdkY%Uq8u^B4)dI` zowUSpq-D9kYR@zTkb|G)29pLtOZ(CZ>vc-?rHXT0}xi07Hv)N$efk8)#{&)T!m*CuTYDm*v`SOqzqw{F{HK7=SCD z4xfXefdS2l#wH?rO7Q1s@Dyz7B7zqW2&QF}ii7UB`05!L4Cz|kYD~P-G$0R2K|2Na zIR}e7J_5f}SOIfY(GKu;w6xV=NMJAc>3(Nid};q{Fs^d%Xjm z%n%VfBo&JCy z(Tbl#_}r<*&nf0c5(B_HA6kIb`jdVeI`@w8F?3;(n3*RU-ujAT)fS+3JoPgppBefN z5$TgLd9lej)JOgiB(;>ZvsWtq$hrSz8+Bjt22P`alLvlPRGgR?${hRrSuR%EEHgbKgZk(ON;mp{g z-7ysi_xPti?1Ji;rsVy~Fk>8;henc9{%c=5Eax#Ew1>D}Uo9{kVf#RCWaIY*X< zZ&=W7Sc$5O&9ePJn^%j)wn4oH(^Oh%O{}W1UV|g%#w|p-s>iCgR8`C$K&oX|`=?rM zxY>?XD!+GqTMOLQ4{pO>_gn8Zx1qdE-yGs`u7`g1-PEF_T4@rWO>(iWMBOGDnfz{RZ$0!Ldu2u3UFKY-mDiK>ooMM;i+bp1v*+4nT`s8z zvF38Ia&l4aRlTlumB%jY^@4ttNO|4FBe!p=vzztn>PoV4vR$TCE%$m||J+_cUvO>P zBagRq{R+r_soAMk5;au%CzmB7V00crit`(P!c+~7O^j9|3E@CUK`HxiWH34VV`@t+^yL#rUI4>k4Vp{*yrmUbRO+?p zOQ*z3{G*&-($gUrAshPwn+eubdT z3&#N5DTHD17(4-o12gK8tzdWx_607JZ5?D483U`T=~~;D&*}4C@n8S&e^-3&=s7Y% zOZKI5RUPfxbmZoFP0aW8w%T7TEQYRL$$iy7qqwiOA*LS4ZTF+_BvgA zPvo>w%0?YKF-JrL>x>a8jIE>{=?`dbl(M;|!5iaRfc+4=nX6iWTKeXHJ^&E+X_5e* z9%3{%QbgL5KItolq-SVPNpI0Tfj3V-NDSZWU695B{gKQ3%5DUy-6l+`OKc)=*(0L; zb0;4aqiIW$e&(PG0OoHn7wAF3(N_fNk*r;NqOb>)7S-8*d^TAUdf-O!J{5 zGpxmc2M4D57$UDJJ1%3gb;+qSE6xSzDU*-Dv_3f}T6qM39-CNVpEmtn3|iztc3k{< zhKrnbpOI3e%Xvy9GLHnJJWW;@({BzSy%#A+xE=9RiY^;60zxaBFtC6z9t6mp{M!u_ zTlk*~+MZ8cP}Z~_kq??S%F^3EWCvON!W1+yn@r-&f7+R2*-aCH-HF-swT&O#gwHl(Bi-_|%1u}-`fST<(IGCb`s8YT^WFNfYi^%Qcb3tC(KxlkcZ+n~ zO#bk&3f-*Fa-J_4u6pCLJU#_iZsW3~nWf5f>&@2EtT$}9Pd3c@H#BH0+T4DfoC{J} zq0M9#&DULIGP!;if2hj17&-~oWb$@VFQYsret@eT>-gvLb?aoVPvkP);9TRddTZ2{ zU*xLCV(nQlTdl9QZM=qUa4x%LAFfiV+OCnthQg(;a=GboweM|Y+C~cL`_1PrkA=*4 zOLj%XrM+`;btH3HI$v$OTm5S5EI>=1jdodC zRJC6cqE@_0QajTufrZG>)Og7@uA+(ZCi1n~tGC$?uGXZIs!al*YHA3xCfWS%=h0{n z$`#SS^qr84%2DkbHlQw4T0^W}9CGJEiIvw);}||c6LHD<5q7K?p=#GmE9;T_+FZOx zsATH{-b>o)`})F17IYM_Vrx3&s%)dp_qLSj2_z8QU8aAJL+*k95IHU)8UD(ejP1WAYhwJO-F z)XHl_u;Y^EvQl{+r??3Fs&A>(OwiVP1LkO+>f^FvheTUyDLPxCcy7oe(e%Q? zWiZ2mIi#^;#>LU4xuH1GEIh+}j7F*1>R+gL~$0 zNx*x``%FIKW2PZ|HKcWd<3_S7i_xi;9|JIh)xpPT=d>B3b=uA{9cc{6svWeJfxB&- zL|w*!l{$saenxI>H&$$sGuS%&7su@Ix%U}lI4Xuj$oA1mD|@sZxae8+G?7VQTxjGY8(FE1{NJ%rppJ}FL5iPR=f^C5tLuYXuvVsrh3 zxLn!;@&lN66&gBy#%qRrM(~;r!p8#wq5$j6?wO`jx3b1fBiK%$SEo zzDc-=W{Za|SQA^I8~L24@H9cc(afd_F`bIJh@87^`pf~LO+i94`uX(J)G=m|=HUYV z_l)Z~8%qw~|90_L$8Q%;KmQ*UKl7RYwz${+T9UV%7mjPJ0qDJYXxHVTpD*4tDE9hw zJgRcl7i?6B<24oibu{NfrdMUVJT{r%LV(x!qV8u^QPaFxmSp{wC0DPq?%TAz+WT%x zvMk$Dja6ZF>DadGC#v19cDYOCvR`Bwb}@IfjZdGuNwm(#uX`70s&AIJxztZxyBzD} zT%iHUupKP3Em!J4h8yMCR+g=F{1|kQ<-)a4D7_aue(T0|J0Ua8yViRhy=})K^v#oS zJ5{b}rJZ*3_iG5QRPUzOO4r5B^)!ukFqR9>tzpM2KU)oHA|ulL_praHZw_G_Y3 z{R~}~uiifHv-3ArzPiFySznHiUg!OC_4|&8V{&CxQOGNWuB+?it&c5V17C}yYVWGg zEQRx%eS@+#Xz-cRY|HNDm3*>!w9!^;GD6ze=ajMd( z+GW|}?yoZLrY!o1SZ^s(jhPrgDI<0l8>!m5N#0~{`jwU0lcgh^b;Pfg_XuE|n|hP) z{iDm%MO$Ssa#uVhp$%XM;1?d!tNMyKLAsC1XhUZZ3*?RdsmNDRfDi`;fnkJF`8|AD zCKzj9kBHnBrvPISmka(9$%ZNokwD-i9}Q^-jf#(|f20?sYtR4?3fo9KtQ+ijpNnaE0#p7tX~_9Gia${465>#Gba>II;J+z2)l^)C`{fd3Q-v3b}} z?5F@&VyGBDgI5tGWS1Tt!akrZnIO%@YtsV)InhMWP&L%u0_vKs#okW-_Yl0pDipjg8Ta*qJbj4s|2Oa_&b8Boi^}#(+NEh$vN;W_Stk#UZ#zH#e(;4EhROvsiFFDu24*Q16p^ zLI7A=0{3AL+Vs(F9Q0F}{o4Y3{FDC^LhX-^06a-!p}|a1G9oodG5r9W8v)xdE(h)6 zKEb$x^R^*aos%g*04pQkvh)Bs0e?+}^7-TM6#w9KT%40z`P1ZBe&d9V7a!g$e)H=O z0V}^*{KtR#Y4M-`_w(ZY(_@~NL6b2cHW!(laE?2mEVLs!U1uiJ*Uj=~MkO-=1u4xt z-46Rc1GqiH-q;gR*R&CPJbk;Mkv29(#ROpZQx6V{C!{kO`ty&(l5(PxVde7(f1)sR z0x=_Jqf78n0AUxKD8ttTN8uO$0Dq10b(270@faz9JkO*c|19f)E!RNE`4SI*?k^rv`xR^CWMrmlESwFtN5^^fOpbNqHpKi3$w@TVN+uQrjrv`ycE zHQ1n#2RuFqV(tiD`*Ru?GDbc;U?*4J-3IXl&IoS&hKPC7d+5t}95(a;4fFxn-F3B1 z&LJbmoez;Ch$o^`qQFfvqTd@`KLUz=cFwplhfTdQ96c(Y4*nc@q5nr7Zv0)%pi}4s zX^w31nilCT8h(sTlri2!G(_#d^c>EQWu0ZIyw>2qbrY|3^$YnIowC0y3U6LYF zmD*h#OO~q~R>%BioR+jx|E#W@>UFm8JA6L;{#ifl-%#94tloLcd&dB2K$gEtF&g=7 zNn=y9rQS;pscd<_?B7(jto5pCoN}*h&#+rATh0L6?g-IbkdKpq;G(l!HiEVVZ&s|Y|gwcXIm$*SLzb6BUhdkHQ!s|2Ov%c5T;Zc z$OJ|s-DnnFN=Yd&NKYcBuMmZeUj}%Y6f)kB+62fanWQ97bkc6+U!?vBEP^@&3SJ5? z))InBCkU6+WGAhXwlw8LNA2ewR*L#WD}AzyGW$VWz2yWN6F_iY4J% zO`PN^?JaHV)*XSrn}TC>IVMemU~K}G6UKEr=tmu+sDlrwf7j>JplZ(Cq@g8i?3o04_najm9;f4wU(4N(OYnC~Y3sSUzRJ zm<@^E1(NLm@DBmsg--VYo*i!tK)R29Bt(CK{o?^bhiF_tT_@rclkNROsAx*O+WKP3tRg{5fS0*GN5KWjEMyY9Vs$3 ziMy`~it8{g-GcIcWbFXxPe^le#O8=YiC{OMWR6Imq3rt|Kz-+jK1Q{_RG9f71RYbf zxetQ?ox{?~fd2TFK>gfF0zD_<9OuQPn2?qL^B1K5m@{4l={NqruRbWwCqG~O@y|aj zK6~)jXsaGKJ7Uq)fnTFTnLP5jDq3Dw>mplTFRS+Z-FV$(zg_H*ZM{pMPh#7<)N>Q9 zPlAuuea8Fzf$ftxws$GB>&vF`-G$c*o%leTde`m03(Z~H-uxY7$z`d-_3s}|de!}^ zsm+qx>eZ6>YPomTY0_8uey~ZNkcs#E(67PpTCbtlZfg(uRrRa$uiCQSb}i9sDniBv zL924qNyIqNr{d!ECO5pl-shSLZ{IJz+-~1I*xjU0vz)K4wCXGCxYqq^UAt=eP5M;( z=j%G1P3v&o*Q>f!uQTntI!8d#g4n9l$T!cCyOg1Bmj(OAi&dE#o7|wVNd`Hs*HR3?_2G=dS~CI#Zn!pM;zKe*TvdHY4)|U z$*oU8A9bXkk9^rQPPWN@p%s?>{Qe!a@w(}yX^tnGWzL&=FYB_qW&7veS-xeL^TOoP5l(g@>e~z>E?#YG`kOsy47~v<_T@dq4_Buud33C^qC5yok1ON4fGv zgBVZwa~E=$k2%{HMaqNr@W{HPGgQTg_ zzp_00pN&IT+U54qX@{54ZHM;TH0>S{PCEv?Jpgdq157j}2ce<#O(WR_)a*AP@{sMd(A==)v zo{5584Y)NVeahcdPg{<; z9$>o^)y={|H^E1vpu5=3r#kW$0NvA0$TLJ%BUlG)0@)R~WI@o*);Swnq`&Z~&U|_r zz-s}R+9q%D;fe9MBrOLQjhxXaX|(ugz*tc>IK$@fIwV!dyN6GT{cpWT1oWq{>k;WX zh=P7}ulOcl^xysZN%50kx+s492gb#p`?+VufBC<5i|3cnAW~Z$1-(Lw9|C&Ma1_DxSa$H49`7UpMAOHO2qyyM zq!Up-b6|HEci(J`P8Mo6a+*mI4{UnSR&TtA+}X?lcy}4YId3ec;TSL;UCw>!1dfe=NP?* z*h!nuzs&YPE{{*B0P<5SV5 zTnF-Uub z(eoJq9j^N0ZVa6D;iv1Te?^hz%j!7ReK>rteE)9ly?tr3zU%wfWvREU)>m~{w)0CP ze&WjxM>J;`uJwx@Xuj&EO|oD0we6&;+Bx@VvUd|t+t>MKKghJIXY1;8!oMqxqW*>- zGODyPoutcdK2i~9tIN7hsnc$AeJe)sCE|KrY_ZOSS*j~C)1f-<)>rMhoqU^HWu9Bn zRnY5I-OR97yNI@}lvj1m#Z6_ou6_<%R|>24&EsHotUDIu29-Xyy$mx|=*ImTqKh%0 zQKicQ%-wc5bqp7@wi?g|fsmnY>r6AZuGW`t)lsGF{;leI)rW1o5Rb-w)^HCggB(-% z8U^4N34jFGkg7!KWT0R0uS_QI^ZTZR{)fb?8P z4>2^ZgBveFyKEXZkU_My9oPOhVvutCo7Y?U%KuT$-IYT!w>hrsA3AK{K@MmKji)8% zFeZw(4H$=a1PH5f1vBU7NhadC%ilxuKd4}7XrH2h+XBXw;4T{v#z2*h(FRbouf~lb zhdh}D0R3=3SWf{UjT9EkHJsdm^avvZkg4E+&FQ2sz|FWse%=XD#e0B=0?7i#pm`Bd z-4)z5KQ$eV6poY{(C7iGo|3X-kIfN{Jf0$FL=U4|Z(p7`HNbbu4^0l-3sM~b!0nR< zTOE4DfEaewx$;=?DOE#7``T%2HsqvNCE0SV`S^($`_=Y#i)`-esGFaJSOWn4Tf z{;OX!Mwduzz;`$PV_F0_8YXsgJMI$G@#|eEp zd!)~Bp3%ZI6C-x~^zq&tVA{yy4vy5Bp8<$p7ULF4_DNf^Z&W|yh6S;extjrZ+idCx zH_Qhp4mHwM$m9TE+q5I*Mj8Y1_OY!XwYkHU4>yRY`ZK`h^9gyMaY*GK0QmY77gJgI zFhoBEi`J_E(%Gz#;lV|8a{NH}9$HYG!p98)^}R9vG2=`X5z+J^)y*YQ>1|~0L)+(+ zM&Iv=R!7Pj#-D+i@CM+t?X($t__4(;D~IDzKdNuU^}6fs^Llg}8dV+)Vfc&|^7^_X@!!-iDcmGqwQrMMuH(IJ{Wf|? zYpMJ7lbe?^=vE?Ez9$I?_+E!IpXJ_qE!l_nNd=S`*RwYbb8ud{Qm-$mGJ zA3tDL?_;ga#7gH|sn2z_bGTmkjP$k}m1OrCq4GAwt(didF8@rKCzyq^zma%?RFzL0IwegwSL`o26&outoK>rQ5tF|UR45h;%n}&qo3#QHuA4w zv9{PzUA6YrlC_<;sA%%F=59-$w|#un8A@$79vOI3esvYusUT*I>$>r;^?~-C_S*4ju z%2yg*wNt9|eN~=;QzY^<#)kaMW0GySPyt5r@;*<;s8fFaMPJ^dZL~;NQMON9%hyaW z6R|j#$)DR02|<$9+@VOwr2tlXcCBr(haBtZ~U3SCBS8M5DTJFmfO1EOeqWmkW++iDvi$SYx|&` z3|Woh4KNoVR|8}}Z-MzpFVE>bcD6}>@C9!P6RC5sQ?gWMk-&ERivdr)LJqB`Eb0RI z7_fV+R zP@JYzJe}zgq-dmVN}Ac0>Y>jVyy}C7Xd!P4IR$BG24J5B@=RSmq764%!vM-Gul0I@ z{=fXJD%~UplE!%1PIK#@_6<65ZEp|yhoFgG6!FsePyWnj{xgdtr?0tbpFR<}ngK-H zD0TdRC}{yUtQMR4v6k-|n}jzO+;V1ZgAloiZW(qvYMd+We0=_dYCq&c_0l1aNJkGJ<+aR@-wo%c7*hYq%N<;oeIxC;R z^rZm3dp}1|OCnw|7ZY!UKHQ=&ZKy7G%5bxZvgxP!iIwbEW4;h zPT?3#Q#b&6gHoC^Ii6S%NgU}^NJ#+{S{&^2G$BAu7Zao6i6mzHJ1mBmfUojDBHe{) zC8!LYha-#){Shd?`I3c9=TlO1xC^DON1Bcq6RJz3@r2z#_ncvYa9vjZ(V^&+wNRXT~9C@nks4dhVMf>OPO-X>iKHDlva-z^0FxN@wQr?d*t5h z$04`8`B~bl^q2E<(^|QzYnF83zE=dwx>nCu8c|TKuU==$A(IsiN3`cr@3#?%2j0@AITT3Y@K8% zd%(cY0=fUn!J@KXulmf6MNIf&R-nhivg-gracPk^$FoOIvO9 zo!MK(FP;B1U;WvSjPLt_1K3%R0Z!sn;yVJMmc_OA^0O?i%X9qN9H0X_?zI(t1+i=^ zPz^(QD0QN1wtz&(oW!<^axoSDq8;?bBLsRqB9}3A1SuoBxRfE}P(J@b$3yQ?Yx{7M z>f#kzl8drZC-}v~1L;Nul?&Q5^*Y*=?1mVdCeJcK_l=;ZlR_AL-1Wsy!*qev1>YmA1tiJl9qQ+radF|Y> zwf8admPZ{g2-7u}ahph8a~=z<34jW&I?MWOT4!T{Q=b%?TlPD@b6I@hhfXjEfGY~` z8Rd)a0(O5Rlr) zQ0aI$z{=p_XLN3xT($zP6Z)CDp~dwK(ER>GB43FhRUd&wWi=g$raAK0PK6wUM?%i2T@F?-l>sKX_I=IlU~7_xr_tBDf#l-!JwbKQ8{}Zyy)` z`tLu*Uf2~I48mz5L6>iOy|H(ozJkto_?Us2n~+B(%5GP7_0n_l~L5OL7q9a z0kqpp#OcIJoRV}JIzGYUL|y~NngbcYcS7n8$NbQcV^euxe<%BX7r>jbb1^zFnwU28 zIg#GT3kD5v3LO_I&YWV6GCJ9bcaI2pb8AOEJjgnNHSi^DVSKQLW5nT$jhGBZDC=$F z)wa{;U7$wLnmEQ7(3C6YtbRfCxamB6@L@`g89=+a$}dN3^2xsrJ|6k7vEMizkU#@q zdXEU_{VsW$=`&=mVFvorNBg0l8y&h~VcLw)jTP~ABF2Zzcd~ZitK_FEd$yXNy1dnZHnw zT|Df9y3voWw>px)K}#X->{v%EoO^3t%6411TxV`em_ET^+79 zRdV_IW!q|3KDqr}>3v%|>BH#l$6-~k+tFL4bz2Ru@1K3-rhM%}FZFII_wCu-C9SG7LHBm9TqSp>*GcD(U$`dQ=lN7uxT$Tu&AR)A zufAlJT@Rb)vpV@XGEW+(&Px{MiYby^o8`OGxGczHaU0vOqiwl#mpoQ2lD_m~DHojA zd?_gI?agFoMi!p7Q>?}!i#kYYhN`$I)3^tt}C*WD=1SF}7OR)*Pf zzJA~8h_SgPjco)-rj(I36zK7Fd8}GCl~?O?%cgd@uIb#g&NQ`aDvK#q`aq=jEE{E; zRi~U~i}XVZ8TU)7S(lPr8qe~(YAQ>g75si{=g_mH;W&Fl73algizwBOHnbxBW%>9T zy=vsLeelZym1UlU(yE66d)4;@`A0X}MQp7%WuG+Xm4xqy)@NF@QKy_HBI~-K!v%;` zfX{#>hQ221VNt2@FL#`PT#? zZ}vp`#9Rwvv;iN9Us@-jgO@yM7Ch2Mgi=U_0bt6L_R&Tmdm^o1Vhq+~9QHiI*!I3g?LmLI4Zm9&M!*B>5;I5MmE5M#Ta0K&Ci+5pm0?U@9nKJUQ(ruLf5@hV0FI{rKo`_Pr&e)J6n3{q zd?-MxK)ItD5!wLJVOJfFG9$b9j10bK^e=;f&4vNs3rZ3di>_l91N#V)clvAs2N2k& z@rXRc>NQ~l#2&%Hj%R@P5N@M~u6ucg-Qk~dDz^cfC&gzwUn$=I=<~(*y?KJY_KT+{ zN5vbTdR%<-pT1dq{mGN!i=UYmPd*?420H!eKY9kBI4%D2|MQr%)7oAqho&3VAymhw zIMWOG8F38g$wPw{V6Q&VF;W>%A2DEVrYC6GZtWBCOB>Q$7@>Z2Bw&n<0oeuqr|8;d z08LL~bg(Wv#P0hRqy6v^##p*z#w8Xh0CLl{>|uv7anf*R(O~{CpuT1J zE+J+=#|{TQcrec3%*e+$z`FyWelbQ?=EEK-I>zKo*OyG0;Xy$6`EY z+=%=(ZOEJrDkp&3Mp)0VtNiCgUUNqq=B1A6>O~3@4Pk_KtKE-sL}Khi7@Tb!bAJB8 zcZ?muUw71b)$Mv+|848#vx#>zKZ^N(J zCds{kaJj@5B# zYH@v8rcsy6Zy@XVUQe}bk>_N*;cwlmv0;`oDjmaIu*NpFTeNAQn7cPrl|>r}ukRE! z`d5I4elNv{@#|U2{7#||#tB}x_#i89Q(_>M-%SF|9 zLZp)Z22pCVrw;4JaA~_NRYifaXkFjjqk6vDJJnvXC&kVU?Nv8*m0rH)EA^FQ3Qo{A zIE?g;28!`*7JKtua)n}AD?mVE9j^B2|m{2rwSepyCs3|O>Uif6HX%R$a!#2P}cXc%2nzmPzYqK}NlC=yT0 z1SkV@;yUDoXQD|PF2vXD9Uur_#6=JC(jvFSg){QV%aSg8IUS$x7w2Ck8n%7JcYgxK zz~Tg$_xnUd+X^|vNCpH!4!{$<4#R-;_}yQyvO_A3x91;#R z6=(p$^bh+ewpfE=@oKv-Z3Rc@9j%b^FqN^>`m-e>sPg>$4slE5sd4Qah zd=ki98=yCaD})zqd)k@Me*>hJ|HX+%_Z=hH#0M`SRnZF95f!qO|2nltjT1w z5BRGC9h2w4G!=rlrVY?KZO(%WCIgBGsET0(Ss4f90d&;KQGVWI^ECkD0TIae9%FD) zf+$B9!1w~bVq@~VI5TH36B6B&e@f>7o1YCRbHhTGn?C&j6roie%a^+-9wCW6?))DuQgUjl?5-zN%}fz#0(Aa$^f`WyMYFAsvs1+<+Q zmz0H%MkvltdXSja; zj}3^JeqQ{w-*^mQuUwb_9y1(V$BUm_kaA-P_}ept7>AnyfX@MECvLEZ9z(`#kBDW# za#LT-NuA+hI6$8GDF#pHFog*nn2-Rm*jmdQG2D$eC%{|~d>2&5V?xyKoH3cBua4Zp zCT{50PU%tB#$k|~=xMq2@R7JL`-2{Y7)?!CwjKE!rJMmrf8<39d02V8D3Kcq2pA#nu? znOV57g-fzg)?@T?;nuco#;G-DQU{pq3`x=dZnCI}=A=>yz#rX;DVtp4TalxhNNC?Q zfzj!MRpCPce6b1o3TnU59?w23e&shG64m|l#h>`XUh#JS?}RN-{$o8HzxokRE$fB9 zcYmiAa7uaPbGnOZm@i|FEpGl+bNu`%WE6i4GS82A*GL&8nX3{ZN&5Y5Q@S!1eLE zv3JdkFVAZnvA)LMM4wD!y)5a=_xk#qaJ^Zdut#M+FKpZ!mkZGF9S>ZKscHE!#kT9ot2D(1b>O z=*nmA8f~_+e%i=>+aK#>t@2t-xh7>{wOX76ST{#f+Z~0xMx8pfg*;)q)y@lDR*Sd0 zlV1O%c=SbnaZLc50G;2%_-cv~{N|5e2ooWI7?kG#Dq~RizDu3;-YRpD}Yc!@lbmAsJUg-N(JBV=Rt8~l_Wltb4`eU2qOa4;Xs9gGhe}o;lw!4KP zuLvvp32grG!(T1_!u~gl_s$;{=VN{~U!24~RTr0}WB~V=O91Bky@LRyr6Fy{H~4Wg zVt=-g_ytO-cYw~?$2JO%pk9j|2rj#?TSuUX#?tW5exWmGF$jOXA4`B8k(93JC7pE7 zZO|11>%ezUq>@I#FDj)?AFj2rSp*E~Gnmyj)JC06#Vo|C#o?llJh|U9Dm-W=I+nQ% z=o_#_Ms>!gLU?s>yl6{*izJrla>=cMyJ;NdN(uFm-&2h?k#7{Ad4qdw z9KM1rBsVdYGWIqocm}qn|ASk@Lx12*Bk_0l#gnfon29M3jP&vueQDD_pf9-Jh<);o z9^o6vqXCuMz0!06nHj*WOU}V*fvYG_>%Ve@8NuMX0=ELFMq}!@f|y3ID##SV=N|#P z;W$3YFja<;y5@U!whTnn(B8BY4qON1*G>EcMU4>NGd}`!N_;ZFKsU+r5X0(#^f~aS z&)H;+H>isb2FPOMtUvKD!7kpw27t<@0XaAUrAPSFrx+IS`rhM%v`He0?_n?5xKMz*zJ%aWDHN8L>-+-MsvsJaDd1I zXqpT9;hP6R^ZAE>*vRY#f&$w|M2C+Bd;zn~Q+xqX?UO#sFVOG)BfwVZxKQ*Dupf#C zFTnGz6E_CONOPl*(F1_oNeCi}!vMgNZ@I%peuEJ?oT1ag<^%`_2nw!??r4US8I7$I z_xDL1fqtfq5M=$))@kv7{=e@P-+BJI;+Q83_YV(>M-T2{gSU#m_2q9BKl6RhSd6f{ z4-W8zeeC!j{HfDog#CZ%w;mR4Hj+e8h$EBFZz4SdQL)fBvYp2c^ddr*jR+%hndY-8 z5!*QG9+A^+H@_RI3wffEwZ+1>hg>aEX^hE-d~ihS2fTI4M3@iH3A_aiCDlZWyw2)6 zVv|AbD%dN?EBM((mdG)rOait5-_()<1c0}DL?raM*e5#J^eiqWu4R}tB7lt?o|h>( zPV@`-UJ^0wlGsB}Hx)$8u>U1}_nAS56qxD+LCX(S`C$K#eHP>cZ)yrxs&3Z-d~q> zQp__FR&Ju?_g;QCS>Wd5@6^M6KY2$*X609p)uV2c`h9ABH~KfzUX|zecUM%rAxS@n z)DO*hwY1*q)!!?sOaGl8R~-fxw`Y{TAl#nLUFqxRq3UtP-#^wtm-(uERav*WuC85; zL%NPg$+pg2d-;xEx7#{0|16g`trOepd;787p4aVLUU?6xJ4VUE>2ziJ%yZ}pk=x6) z9R++ApQ2k?mStS;V&MwyD+ad_7a%tZa5ETN#<@_wM(G2gT{$V>XPR7M~*R zo^`H3xoxJ*w~45klA>?Im&!RoLGvSmv*_36`x4)ni}7=Us4g$~?p^PlsAS*u?2&n` zTZ41Ee2xxww7>oql9soYb3|3TypvbyWzgtO2sRSY5}S`aw@TAR+%cdAARc_oZ_oeNpeKz#GClIpl$!)8O$i-EP$vEmfJ-e08!*8gDr^rXa>aJ8>QSgAsi>(=|Pm;B2ukwvm?iF(zd{eF%^w z%7Z4i@(cUZ*s(Nau4SP^Nz2RxA)Ea>CiM5h z!hG09x|B)7KL0;^ZyF<6mfiQ=%vf^I+Pk`|msxt6vv}A;isW!S=5AV&B?z!ZL#81Z zkYPiHDO(>68=wvPi~T|RWWaz835FrVvIR;MK+53Auqj!h#@x3Vk!()S^z=;cRn=9Q zm1{&~gnz$t-;HI6Wgp|rbY-c^#WAO) zyipT$2sVB9UP=TlNC&&f$1noU;Z6C>sQP4DIR4#!n>vKi4gHA?QSNvjtcV!0;y^us zWw)XW9ifHznTy9T5D2kw9u+*^-#x-(*;;(?j~L0K-tmz@m5b)vew7Wv8+gn% zTHUmF|2QollE8N98etO9X1Jj{S>P=!sJ6NW@Vdv-7CRci)sLezhxRFQqb`g62`NTs7YiLWllA}dC9C$SCp%*8YRlL0~ zUDq9ZK!^*1Cg1@b@t4VK>k00M3^^9CyLbD5j$4Y4C*FJekhs%${qj-7y`AH9>Bedp zQ3vq;Xb-yRdj-&4&uX6vZ*1G+(8G!aj+X$m(H1}>0B_?GUdEf~d>xRK5A@+}Jixtu z6|Zf9?Ur)*kbpM(^rD08;K#ILDP0}hPuF{2Nnd^aBk7a3@M@-kPh8qaPd;@y{mb8a zB7NhX*VA|0vJWz?F1l$W&tLzE{nYq{X8NTsuP1QFkO?KwR>N+rLK$9;c1IH+aTmZ@ zka%rt3wa57LO=Qe!4{s=m@5{JhjI%L-+_0uPd&VF0f>(Q{|!8>XYzph!mcDD4>bJP zA?+SSJM#mKrZWPeHX8g0A{|d&e3X+zMYM_I-2%|%h6nldoNg^00kF5zJ|FM9!axyF zHQa}xP>jvoL|>cq|0UXSOdqddd;5UUK0Vvuyv59fi2)vCztHT0wo2Wv&3akL9}2`~ zY?pTA*a_|c<;dX^S|^WqqDbGG=LN^GAcuH!>&32~s7l{qpIoE|dPDP2!!fZ$_zVN| z`|(6)NuU0~mGa7k-k{wEBtPNTB(%mppmS${tx=}gHXb+zO^qdh?G4&^bN~&6ES}U& z?7oBD`&v!3!I{H2>fwD37i1hX{IY0ni_o}sMW^tj)CVNY4c-MX29|g4r{8-OkMEPe zlz!~ltLgcTe~286BiqK$n!?-X*__zsZ>7L#y7juKJhrkgH0xzbntRh1EnBa5kz@JH zlHABfhC;5`Z0Yxsp6@z`XXidR)dCrvVv@<8Dtm5j(<>A`oEK2?ej%H| zs80&5NlrG&QeJCvJ04eSCOPNxk-47c8?v2Fp4tsTr)sWUHPbGph%tr5u+%KQ4GWxc zFlt{ZSo-8N*HU)gy!x>645c_@oE80*@)}gwVTfPa;M{h)9m6^c-GeMjD^sn{UGq4P zWXSVxU8kjOGlp|iEoCyDGQW;b0d<;|%jYAh7=^jIdDSQzCR7LB=S?-}iGu36-u8*? ziBrqwq`BQQQLBDBzkX#4XAqZ06seRs>gf?D^9!$0YCf7ve!PPwt3K3u(e-@Wp3Y*f z%%w`D7nT&PE(U40c_sZ;`-jsnU;a1Jo9S|dEqt-}rSxYH|JQU2Alh%@xb8)p{L5NY zwq=}(R<8p-AG40}ReIJ3?aQ3A9%{3eJb1%5Izcg9^0H+L`B>hr6Qt-8*wsrHox0*F z1%nfL!IcvocVaCn3DYrM0j1CrofzwnN&$+SV^RkdYnYICwWPDUvDik?vv#Qv`pBY& zC#6|Iee`G47o(hMeVWUS%8V#fZL246Y&$2ns9lj5^3BavRl{vs-C^c}2hx*=edxZ> zR5h;r0zf7+k3YdM3X*C0(4Q3~LW0hD;WbYJOUnQzr5T|e=wPenbLg;e^c~CdLQ{daXuNWeXpjCvljL zsaqP#>i5_}Syl%7P;VB9P`iv1WsEUnvLZMx;VoU$rKfIeYLDmv$2yGDc{;7hKi=Y( zCxitq1EK*&a8rf|F#B@c{vz*D*(NZaa=1NDIsi+(pMty~^!fMH2h7(K-w3-RuOU>ddrF zhC2;QK!6;!BJ0x7s{z4BhBa=|GDT8t z5Uhu!J)pXS+a#L~@901H?$hVTK72r5L~{v1TMuZ00s#NP7g~-lABgyY%FY9HCS(WR z)&am?y-eLJ@K70)ZTy%2$+JI`-NkWt*Yg%|R`9rm2W!*|}rla`({^hd{-(iqh$F|3tOa5+4G0m~gM3agj2;V$f5!R>Xze;D_< zt8!3447Ml-2>$>;5uVCr7!IxF7@%2Tdj)Uc4dP%cud>OL4^4)rbQmBVfDOP8uoIrT z#cvWCWakc^-5Yp^LQB9>uWP|-2gCj@pL-*e#|q%$&fVi2%NM?O-Z9{G0P&Q|pf)zO z@%=z}H`3kPucs%Pze9V884LjL!vWhUq&WcWeXyHDQ7Gdw4;0$)Kcw8iC(le2zy8)! z0NC5I(9Q~u^wRj?q5jj~cVVn_<##qu0 zeq$XWyI#&-kc^*fteIu{$M7H8#wrgZ2GHvo7v%&n9=-zN0-G(ordN>PP!w%!Slejx zM6hi-bd1sMD-A2y;~FxCM>d_bLODIr1%S0UCxUo2gi*D^!xQ`3hbtS*Py)V&-#|0~ zVCpf-FxP00Zwxr=bdXd1ocI*R$%>YBuxAa=DNH?;$qBI`!~+1^4-Ys+l5!sN5k_Td zFv^{bC=h`CGEZ`}EkAlF%`T6ydH}+P&S>$0LO)1IYeGgJB%ScULcZ!$k8j^_uxn@* zJ)*3%8ZU6ij#CcOdB{e`l6L~oZ|rE|)sW__MHy*&@$@2rY}V}2?hf{LK&X-1-S5Um zucw=9U*Vd^OoTqHmg!3S>}|2KA?Q?MPWp>%i}6{6(#6Ys5M(IQUqqH^br#is;k)Sm zJanhYwJ4ui<<7&u(6~sQy6pAiLi2;TuMauzd1Bh}&zvo8#1?4ad@8dTFf*N@e5SFG za~6e*q{n<4?XHwCJiZ9EdYwsn7u3oE`mS@Gc5J_0Y+I~eF2!H_Z?P&D6KA5<8RinD zr;0O?I7uhQhGizo753U;WX;Hraxth%jYqkk#}v8dXOn+Mfdxd(G`L5p(UNMR9OHBf zohV-lk$={BpF(0HX+&&MT6LKvHm}~$F6hhPRM@#M#}g98Y00ncJv^^T)YO~%H4IPV z8~vip8HDGxahmv#GOgZ*ak}#qI9{@THjUo2w0fOj@8`4jYUZSYFt!|88r{B-)Y?LVJRxR39!4(aj=|2JBnO8>m^z3KVxOX-RJomeYs zfbpU@ln9XZg@%{p%X##E+}Ps~QDiez8uVn6GZ%UFADP zt(&aLSTnPRU^NnJDc4@{>OJ-t*$5IoIk#UY4KO>gG<- z;ZUsi!$g(g*psexNgLdeWit5I^N)OQw90y)NUJhxYPSr8z;uV$L>eNlVLmQEJI4-Dr z%7>lEkOs)Zybysua1J`w3EJq&z5mpup>6f_HCjRZYzsi_GLDI{M}F-}+sHZu)98XH zBAYzanVqhko*-Pvs!S%!lR_T*bG(J_a;^9qeQgHqil$kREyjW(T3?+-ItMU^jR9KV4i}%0S{px>M0(Uu-9o(N8r#(u!0Z!*vKEC zvmRc}J>WH;5r(b7M}8fgkNfPqJcJNTkFh4^w29t`l%Q^x(6Qvi^R$%~>8slS)!S$Y z{VpMoZEyVaUwigv1YX<3nl)^LN`)+dDcLC8&=XQJMzjr-*#p2+fk=U1fmK24t;@!b z%|Mjksg2W{R?u^ekR5`$y0hyE9Rg$7y2c+7r`h<(g5PSbV+?3`wD1Z(;5D6Xyp9Ed z?*q~bjO&Fh$a@TseC>(#^gvLW2C3K+09#`+$LL0UJD#B%z_;Nuj2rF5ZS3SW3hF?| zILXGMUgtxGh89@^B=(8FZ@Os!06+jqL_t)@At68XD318P$l?G$M7A|vCCb1ribT#6 zI=sD>!+xv~%A`Sz2S5HMz^5K;m&RZmY4=@Z(2&)QvIx`)%`G1kt0C%yK@Zu*`l-%gj;1$=22LaNUrJi9O9W&W8DAE%dISx#^5 z(ne%ewlEECeXl6YSWA>OEQ(=ZR!B2G z^XiJDqm{Ote2`;}pXCEMr>_X~5z$Uo(fvMsP1DmRD0B1??UJUZwTV3&w!&K~YNSi_ z<1Sw4u;3Ll{I{_kyCnv*R}?zKup54GLGU^9;ftR+S%US9IrctiQGB}#W-&ns@*9_( zST3{?Ed?D^jD2-{y$$f!PFBVSX=pZ&1!x*0`-J-Krt!kHB`&z=P8!;`uR3XW#&tqS z%#F5^9%)iS1}8ON?{O6I$Nz8uq>j)fo~a*h9-SC42!VX>_KbhsAsmViM)r^Q(w&1B z=z!I%~qV?afgBM^kwLfRnn<`Q* zaS>XlY`+q`)Gci)ZSekXk*(CX>?s~I3Dkt2D6<%i+4U#LO+C%FIJ$VqGk!EaW|UFZ z)cW(X>-v~l`BdV=wdE8!62@d0n7>b#!l+kFjs2ul7Ec zK0o*kK5bOR&y_5{KI^;Y)$5_eKCXnW6Pm2SPFNSX_R;w$*4Bj443LO7m%}h=EKmt+ z3DzjgbFW|hSJHp~+<%?^e)GrDuP?ute&ghY^yT))(yL2PrH}OAWZlO4hC1pdJS?ew z09Zh$zcOI0xBW>zLeKhdLlTC+HRWXpF<48}?ZpTh{AqwR8`KMwXK(*TGqD_zS=%q(nOyh?CdiQ=bh zG)ftSb_x(KUDw;{Lb_p%0X7PFlS~C^M?EQ#mceZ*auVPEVxS6o3!EAj1DGOs7^=kl z46svPz9AB6(xp?3vyFyAN9r$t&RFk9!Dy4XS2$*p9vhHGG|O`uu%x^s*p>{^E1JT) z&t(k>s*DsNnX;y{2?v=N*CP%?x8})19anmde2z^sa;fuEY!!ptlbN=9mUqQ08Z9&> zmLcFTxNqMISVzH%lnoHzH_y7Vvxx9W^aJ^}&u}aDiS*oTyEGL=XGQHzWXLk%TxdGS z{>rAVWneS1&a4*%LZm1seRFgj>d z=km3UmZRTfAw&1dVqY(Z9QjmDqw@z?&+&7bkZf;fdnQ4QI_rSdg)f#bCZSpo7+o9miU_Hi5S!D}M3-$`u>Jb}XKZVYr9|?r-Wq@QmS+8aRT)m_Z zcTUn}!c`atdH-HFJc-x02#df6_>^}Ik7Pq(96@)5I?<8vTH6x*L=NZ+faxXOF+2wC z*}9}>H&1W@ogdsMwlyHT`~_T9kU*~B@ZlaGm&0pV&u2S5!h5JplpRlgRJh&dAoeLr zq~|9BOYqGAI|R{_gUb4TFfr%@DCz}mJ$qdTUSm)zfW{kvyFRL(n;&N zbm#8vbhY&de8h1Ny66mgPBwGs}Xdi|Y~^vveH^ob{r_;?^~r9af2V68E+4gK)^Q=RntFE`Rohkl1A zx!8#@qcwDn!$R=@$Cz56z8Yr4(dPDKWo}0I1bR?pB*T5CAys zp|m#j`v9!InzF=G?;ao8YjF&OO8FJq&P@#MX|E8Em?I~%ldg|yph?4}zqPs&p2~VT z`{cfjJgfX#^j8bT8b0HI0p|oJK)X#pXbXqX;F*HAN$}z|24?jK=t8@{g6FnznVoEm zyM2Ine-~PPcpPXy7@FSb5mTLf2a@rUkFf!I6!`_Hvu$Xz;Vn%5Dz8o~LBGebp*`HU z0oOHLCxSOCm_7f?lt1tDy3=!=*!Qf~mPqw&`#3p^3sqOmp z(?de7ytQ|e4qp1o5#`L#g@+E zeewLtSmzL!Y*$^TX?rrf}FZ?=|rEMuG<=p+p$;?4uSTyN zPU@nhpS6!yc4sj@tMX(%&5D+d2|QWIa~M`UFQ~>L(YVc=rk7fq7Sm0w!jQVxn`wPj zD{$&?I-Pp4hi!A6R{gk6cb0uxru<4>n^><~(ntFlZK{&z*Bw!rcQmANhPuy|wa#U_ z+@>~R_&ZzwX8Ione>oj5@f{%$gG0Xmawz)4eg1$qRgaJSCK2msoT6^@_R~kw@3Rh3 zK*p-b1MYAuS8*N#^ffGEfL%Bq4WJRAUP@2n#Iu}s0leRMqmjPS_)z-I<5n)6Pw`skJtvISKYt-X?Q% z>L}my>1A@ss(k%q;&@oQ;yJBt!!pzj-Xxed+{R9Zsb?OrWE`WT2zo%TUoj*qeFESW zmTB_z!YB-L}Va_v!@P(sz6{lfK|(JtQHGM zst(ODs!_%-10-8(z|6S|;k+$tWoFV>_U47liIPS6^ZQ{0+Q{gK8rW z`u0QUkW^A~8X387$>mtx_zQj#=UB|g+E(Ci$SOTg?89icl`_@Xi+=Qw)jsge3H6t8 zEU*eea`mjJwpXHTF_&?yE|f2a4Uw&Fg_is*JF&Mv)J3FAs4AmPDZ2fXzuQ*Xw6-P_ zv!NY$rsblZoU`0wY7g!a8hD3D5dzSJwTN2O$^8#KXytwmV!>Mqz(d*jFXVz^Wz!FK zbzscnAtDx74dpmS1(*idl@DoD4pV9Fduhnn9hjrMqir$hX&d-vcvxcqKzG?@)d>ny zKJ~6`H2(Ad=$W6%ouJVulm#$cpUBV=f~9^u&oseJHSX6B4*+OYYPfVGN4$=paO9V` zJ$m{26vpsRdP~Ps85$bQ*{j`+D|}QBk7@yG>9rVeEr7sy8bOO9-ZI|3o7;RC4xT<$ zFdRk`dU=8{Ft#8Vp2&oqFreLGT& zS+G@^9MtCfdXRCgeQIT2+cAbR3BBPt9r0&K$Im<+w_i+y_6zAA(Z|wvfZ1RC`lYmfX)8T-`4XPAeLPhA=`LXY zTd(h>PprR@Hh8!o2p?afg|_nArd!vS(Tb!H#oebk(g^e zg3L&iJ3hnB)pyl#MklWzq~>48w|b)=o*s+#w@_>hXngEkg}I^%s0rn zhJJK5?$QyfpxNu#5!VZbpSNa9iN;+cNuvEyB#7u%4<``1h>5oO8KK) z{xfE1yCFCHLT-fEpuGb?{HEYFT(zfUL* zzkVw)O^e8Jym1UqCy{)Cq5XjV%e1v+_z+4OTf9lEZ67Lh1-{YcE+ItTy1ScRd8eK3 zcR!t;zxIu^(cBH2LdXkk_G#$gLbd;7R0dCNBkwT^aSs>Um#m|%38{noVxr0T$PM!KiPj1+2=l;rsMPQC|z5$Xx;PFKexbBE6nP_ zsf8CxoaoYFozmB(-NCtHKiQYVN(&s!YQZcy79bgmnj&ZPPZ*cuYLVKx%*Ys%H%hSxOtCTlLvPXSL`hwGu= z-u!dvXV-r!-8tOjEm_|9#fij^tM^!w=$x|5ny1Hl;{flejkPtv=^N?A!LQ(0a+s|K zvL|cT`DP5?E;5)XL|sR?4jy#(g01Ur*Nc9~Xo>aRF<|q_G3)w1&M7P?!Qs+Bd*aWh z-`@IB9LNq>N8=3RBAV~Z;5wF0+Jkhje?9#G-q$VGMfQPlWBW%erq^!Z?B$8xU)n^* zFZ%EiBtKapJje1gdW^9yN4|-1S#@iuRxMRs8SU^Sv{uik)L7TOG%PaB_JeRmy>2_B zvW2+&@vW2}+8Nmu``~B))oTT%T7x{4kMl$1RprZZQ)8*&-SBywHVkB7>d4Vyo;IC?B*~Dt7BR%B_z_?aVETq(zmTPJ0=1nAly8LXb z_>>@>SQtKSteFuFy2=F%k=t6GM_cuG1;^igsAG{K&~FVyFI=m(q9wnBcUIla}x zF2IvOCc{4L8$WL7!;R(&+EO{8Z}d;YmMeTxyNWGdQ~|HjArnKj-2@+qkQ<6-A1q-r zMNjsPSdk#MfVAVrGU2VS-RpgOM9g6;w2c`8)#!8>tBrlVlp!L+aK#TP5Sn1M@o_YW z>BVWN2g6O+Ng9p~HU8}}9-0&VH|&Q`dDXEGT>Lb9tNYAyt zo?d?AW9eg09&iJ(nXX*kPq%KYrI&~g{aWW6>ACd-0PQ~EPw?vH;edTM;4$0@PZB=) zTrd5{KYlO$jh}};IxtK`KRj#ocHUz?T4N&QX){l(>ADzLOYvDuo;qvXL`&C>V#W=f z9MDhDXz*1EP1D(e*|b{RnogttU zlNpEFCHHYrS2^H}E(M=`K;q3Ae0Z=R|9Bu^Kf%XI&8JAs7G4W@G{~vRjb#5A?*;g@ zoC~(Eq$^j}(vy53^AZnoTJ%dB5Z_lb^rODB*GaE_^LF|NzmwAK2Y)gB)$jQ~(v`J$ zbf%86;TRphT4P)1Y!qcfwr}yxIkIe7C&M$<8S_y|S#+Q4Em~S0F}=>MP-LsiJGa6G zGs7${Sob{D=IO{eDK>}RBiPD-o5E?o3Gm}RHjuknp`#hdi|f={P0EDMn@%O z)_(DlvnZH8uRTn8Q?|PNi^(wTt3nMD^Z1rxbIU-qa97{c^cr-$FL661L{5&IuzB`Gos-BHO*r!XnDGVJbQ!HQzZ7^W-JIJbH`YQ@ex%tN}~!r4_u!+%u1$UeK) zw47XDdKarNP1`?`!xZmrCe&i`)UEIxwP{pr+QXqy?)Xvg92aA}z`Qf+X&d1MHK$yC z9G7Bwq0iYA+lVri(GBYguvK!FHW#}rmQ?bsf0j(aqx>m^s`K;OVx4ax>As{FdFy%8 z`hPrs`dOhlJ=fIfoq1N9VqTcE=NhSDKTKhKwoJ6PbdnN6x15){IgN~3UzBpCOxD@# zHck#@AGXQUnwRKX@A*JnYDO)O0po09|x?5S&J>Lucm{0tQT1`HCk8G>w~N5rS5m7 z?`?e%7cFR_4A)kDcy z&uXP?i0wHU({4|tQ*2$fh2@Zb7Tc`xZCtcrUQt0LP*>UlSslWmq$RwdSzqeV6rPq! zL_S@{CYF+qVnIbF% z2~js}N(rJ;tovgKnj+8O;juc*WJ4cAI?9~eANlGGxt*(oj9?P&1!4$(q;SA5Xi<-R zhkbbRvNzc}9;kb1iYq%Em{Nv-&U#UXeDxA|6ywIK{t+AT(6+?AnrD$fTD7-6m$U#~ z!!}~vK_g_PIQhVTu?bDUJ(G}rMnOp-gZ74=q=zt}H;(;ipOFd#m&7K`1C9w@>a8oW z08N#%0(_;b%(lZ9{cV3>DbcoUBhelTgo7Y@@E-yMJ@G&53g`!@Ep7OQ121`1F=WVX zQH{3EHrNVn!)OB9U zyBGsizI1Jr(JDe11){S0X*z8>R-xHgrwn@?Hq}uF09>csHSotC1`&M(E!$1wxPw zEkSe^EO?Uly66kA+A$ad4e8nd#{&WkH3Xr3K;jI>*5tTDtZUpIkb~Fn(EFupPp0Rd zxtcz7V+C*eM%r2;-vJXJD)hr52!)tU9w2Pq+D?rZpG?2;+P(CJUwa)b{Dt(NeeUl8 z>{sD9DEIB{;u_E#RJU8X;ml{}`zxLEY=vj3bKd%;-lN*bP0ScEm9luF>Uta_Q#+a0 z#=?Jbj`o{#Y`)&r*qnJdPmVJyPZX%z%)ABlM72kFS{W}C>$04e!Nb%&FRyy}bN7XJ z(Pb+}Q_jt+=a+Q$lkKywF4AAK?6cTnjpf|Kdb^zqJfEBs3V~7CTHB|zsg$qpRnZKU zjEX^a5zuf}+k<}9h7r-~Xk6q-RHn(UGo#2kYVSl^2#}@l{&m(i+1InmJ+Ac9pO1_F zY`Ud~vTn}OLtW1!nt4vm=OZ15eT)h{wxe1XpPNT%{m_tRP$+t-kGHY$2O$@M=eYdM z;FOaKzqy6ZnOXX-v>~4lH84i4lpRaFz{$DiI8``Zq%*nC8$+ed5-Iejm5({XzAS|( zKds!E!kkgSHNzWAPxEf=YP!Z6X?t}u-P^qv=A+}5A^%+KcDTnEpt`YjIlaBNlOBL< z{Qm0qrSIwfBI^c1WG-!U&(HY7!HWb0XYI-QNzf<06T@1k*~Z}r8m<>z$0Xj8JY2@p zy7i;!L!JLK{j;b3?R39&Deb;>I~~6Z9o}f&y}gre|NQ@zmi+e5M?ai8H=j+fK6@p7 zpP{Ce(*G!C;Fq@u(`cM&*P5b-zKyEGO{=}Zx*aFA{rl`$SFU_AT|W73*2ag?2kJU# zRk|uFAI%`@esq@Sf6rmQ&^D?&zxdhZ3dgfchN|bYo03~=rV5U2EUnZrPu(j}bzHK_ z-n_V8whvNEOhC^g5_GjPtfbJ>Xk2C@u93xCByN4rT^<)cfsbH2(P!k_#}15KgFUA) zW`YJ;QX)-yf?wh$ZnjBJQ1{9q#gK=>n0x$k$t*`XR<@wWANQdfnaC;lWO@oBfEfAY zY3^_z$|4XNZ6{SgxN5ReP)6aDHxu-`U$V$=AxnpiEORq3?m1<^0&KtfGB@a0Gb&qJ z*>lC*4$~;lE{3cp7FX_^m1#s3=$Rd~f*pDChc9wQam)GhU!_we4`4FdoAHonYeh$1 z(ZA#i_zQ3{aYWk#OHdG)W`|X&aV&O6a(0d-h4OBOxXt4<>pe$ysN|81g>0X8z@dDD zo&YgKeB-=qAdJzD5EEuaUm=dnsmT-+3e z)S*}S6>wd=N{m@rO~GfIdiVg(OgwEPhAkkMhO%)9Z%Dmg!;0}HB`~fZPIEKtMHkRk zBewJPgzVTlsI3509_$EU_W`h1BFuwA@4eei>j2vJddJWe0E@nYU~FWcS{g#a5BVJd zZigo}+K5i0C;aMX3t&(WVn3GHA;z@5F5oRtX1IzbfUMxF#-kEetB@Re93OHq><}}> zy2@;53aW&{JQf@l+!YvB7niOYP69(jE-$8r70}B$KV4PnfS@SRSU&;a1-j_%EwOv$ zfy^hm%SKFN07W~7ku6v&up2TEhq_C>6`m^CnJ9+#LVWz|;X&<3=fFn+F6mP|c~{Vb0JvYSw68vM>t4F@jaSlZZ{AE# zU)@T5o@Q@vuBIzbTu(2*bSu4ed^>&o>S20-i~kXx=RPc0!l?H+)?n8N(edNob&%e; zb1(g)KYTJ9Iv}c`W_a*oV1|)6Kn`C4FgA7n1W*%Tx(DzZ1B0^o%|efd8`zB(p$OGE~MYAhH;+;V|@GZlO1y z*lb`=8|1x%Uiu2b$n^q9x56L$LC^d2zr3$Oz!FE;yRoA6%zl7U9k+VgA7g(_yoS|x z1HK&q>-*T=Dz*Bw{Rlm;5VyI@jhr#94Gm&n_IU&>sE#_LFO~q7m*~e1lP*dl58V;J z%o{1?>n#zY1Yp=+ry2JG);(?xySqCvE?4kE_~?gkrcZq6YI^$e zQo6)Lg#PhvywPx^5&NhQelhvm26VT~Csg+i?(iYV-Si_LzLh?7bu;}hzqFOU>(v+2 zk3auKWW{EqCus5qXhXHJMOVDfay^VJrS9Wy!h6MMk#>w^$({J9`fx;d5h>o^GL`u{ z9M82mr@SBA!}5LD`VZA+=2a&7^OKlX$293pY}49jTE5YqC|o-&lrA7$jmqe7mU)!) zvY$TYiZmm7^`mL(DETwgQnI<$U3@Dt&!RAyZ`~|LeB>H_kJ!wWdrXE&wa%^iXfwy;ID_=8va{09Aos^2yR^-@Wa?a5 zy9Ug+-;td^RKp?DJUK|0SOe(wd`Rev9_tnzZ(Lru{xs|XE8|!Pb!fmE>zg-TeJOqY z+2_+Yn=hn~5excQWPvq>xI$C!E*DaaXHC)dt)cxWzzGgZtPPi4H~O~Z!8_^x@^`Y< z{_ga<8y~?5=zuuR2dT|E8>hdt^E+R{0p^HMo11Cxm2af(TZHvvJ+jYQeT6krs7pDa zJz&kDt0yZV;2zk=~97$5K>y@jb+)4 zF=U^3?akWv?~H=v0EfUr+kcpqVBmMyg13q*#^uq(izG6x8!?3S@(Jhmw2ra@*fb96ssod7OXmNX%5 zTIK~76X0bo&)=XIc^1XaT`OBv^42GrwYg4z*@HCrM;_Vk-IS@ed1hX@M;ppSTBgWB z1uiS1HXJ~%Cze5gpb@guOmKx_b2`~Yd)Nm=XP*CDhEqwX&at2bAzw63Cq~a~U#1-g zB*oi7!B-wpjVyUsJnL7PBZLTPq6=WBG70+89_g4CTb9dy2~Tz#$1mFHv9d+GpebG> zrop*58;8+OK}=x<^Q9{gFK#D8`AN$@lALdBI^BeR%qP!Z)QdXGW1`h5Acd&tp3|~{ zL#@EX@VQZ0KGfGPV?N?A)5;X zHIN>7X=%UhvmNS1he$&p$5Rk(1iIpz1M%Q0OouuMok32qw}^Cm?FPqUk?>RKuvzx% zD#{o8P@d4Y{RjcdG!21aJ;QJ4IV{lIxP&0Y(bW^U2Oy`xZC(bvqIHLO&>FMWFc)n+ zY%RZnhx9TrXMLh*EL#U?bCrw27HtV|82}EDbIUl)#u8wn*OOXf5f~NV40ll^?-Ea#?I2b zQAGfRh941x*Sj0V@C1T(bLiw^%x?9tN_&^lqy1=n?}+unXkG?1{J~paNk8|^7t%+b zzM8hSwj!3-P2zFizkDnG<1f9EZnk#-S|R9vWN;$5a-BZ&=0hW-o&VmCJV>v;+e%-4 z=UQrS;$5420=Wf)d7`}RhX(@{qB9p%4^=eA?C$G{c5HXO$eV`7IsQvcxn zOgv7Y#7XD94sEzeI~&G(#+>@}tuE<;$t!I?$jE>IkZ$vljCe9cYn%w2JXW=Fb;B*+ z5n%pOYa>s#>Q-Q(Lw6$+)vBvSJHQW=GpY(N4CW{OWu4T|C$BePBz5aUo*oQux{+-X?On?8^{#5$>hkl)Q z;w_zrW0Zft|2XdITRHba_FisnCMm8AvntI1@rOxD7ua_-U zN}b_3&9vTRy7`4}DI0qVUZlNaG~UCLscm?y{IhM0bTqP~S!|1p*jp&T0KhCov z&qaCEWgcCNFG_8CJ+*wcW>43x%XT{V@uWPgJ|B-%P@OjD3$tmHN6DMm$NWKOjJ2~y zdzQ{!_nJ3uBNyq_ae-JOq^-o8wyfVC@Fu5mo=;dux)whSZ>#)Pvun&%)-Fe^#SV9N z)9&53(kpA%(l@U^n?Bb3eZ1xfy)n=g$MAlvFJe7q7)91T0NPF7?sTy(IMxIV7E$W6 zK58xjE+4*@e(}jao4(e(l?HeBdE>bQREXbN!<%o*(R{xBJfm>TI!Y+p%)cDcT%eu_ouI zth@1kSbTKV`*y7VYh6+=Q%jD+vpCJ78Tz2|?EFbi`F!#kIjvMFQ)sKhthI^;yKop| z-TI&Yjqgm`I5GZrKlNLLw#=bJ#eJ+3t4R)`E_>I&UTn?t=jJde9;Mn*09qQbl_~-r z3anXH*&@WF1USiFv^hPfI;O}{_M-RP9_mZLbavuRdCOz4v{j6()H5yk1p&utrYjMT z0|d=t1iz{n86$bJ9G;i@S%6?q8fO76#v0o)3Ke`TXFYPj4*~>)SbX12T#8M=RIoGV zk8;d;0HKl@3dAw#A~I1mj>X($pt!F0sV9He{828LhkVf<>WGC{&inwm-NPsPg=5~OCnJ0LKu!b zQwA~2%nn*68oxqaS>AIoEY6)q)T+3T_GOm!VvgplAxKS+3nTefI(5Vd!I`?y4UcFA7XOkl`tzZo z*k}j_zT^0iZ%oG=E1`ku5Y#@bj0p` zy(Xz^jA4XTXg9x*Yitk=b8QRw9J&IjdM9sg1Dt_~$ycMdvjX_0Qw5eYhy{?R$F(hn zW-*8d@Dp;>T8(`xIDG_g+@g_&z_7aoo(=KP!D!-w1cehiOHkHE*;X*W>^Y6r4t7C? z)gkqL;$Y|ldk?$}z#EzLP$hgvlc%-EgiL7BoECs~C=q_*Z^nW)R2xt)gtmD=nO3x%eV2jW|`%2MCe?91FtPa-r z;2<)z2<@>?%;tz0O#}mukB++ZgzY-xm&_dX)uebTJ z!(+f>`LytA_UYA$`m+tR&+*b#$K2%gc|Stnm!@KiNUxz%#`NX2b=rW}GvIF<(7M44 zKJfs+-o3M%4&L1%M9pe?@q4}_edgKi^u#J3hupi3CpANBXA^n2N+e2sf_?PR)&t=c_0!r!Jeu7RTH@zf98*+r(+n71be zK|VcgRpax<>3pw>acx*YGqY4$t_@PEi{*#5Q%)ZT7)J6%?$b_F8&7de zt)&CP`gQO=c75hrjL?ycHs5UR`RXTYQF%96qq*Ad9^e?oef_(4Z>P6efA%l2*4PI- zz@f!;j)3p-fbfBQSYFJ?1<8pEPzMWZ0Y9+rx_cQK%?9BDyKkkh@t+bB!`b)2F zrx%{P3s#Y91uw>!*nrE`phQP!HM>5^x(s}U)F@Ay=aCo3`3i3RzzNAHeQG&}Z~&|R zkwYlO^>glXlwkaid^_BAd>Mb1wYopkvVN7847S{ce4O(_e-5A)g@}JGgBv(LUAwypP?{cc z(8j3EACJT=g=`AIs=US$_MS@cSuOguv}LR)9#u9-LOAOobd)LQY-KS~LTrkUYO@@| z2M8n3EDhV6yURNABQGlDKe7o$WF~P~+GD{;wtW@PKnme&J^LtdNXjwI<#t#{yQ5Cf zwtb|juFw$#vqyeDw@mSxr`AwbF-~%#Z>;N?a(NISGw1f5w5&%>n2;Gd!8sNu-k<%Y z52wHN3!h9s{a0Q}Klc4!B|I>tEasT0bf=T64;esO3=APWn(0wc>?!(QzLfLrfui;p z2go2`W}gOFL`UYfm_b?lp^{A;Q(40aRfX^yfZ6K2DePpM#7&)tTi}GU^q$(#APqto zYa%D4i%F^5=DY6@?m|ybfJ(i8y&$ysiJJEiRFHK0T078o8wlnVp)Pt{pl}@}q(vj$ zzkQT8j15ga+hANbYA+-X3jVZlOs0b>>#|U+Q zH@--lkMl*S4-7Cq-iQ0^`nKRN7sL*pwP?`T%!0bo?4Rg4tr&t{Tx2|^j3BUps84VG zqNPCh+7@5<#XDHFZQ=yMOGe|%2eLLxI|H>8^Gkrm*42@S$ZVhi4aMOvTfvm=T5_n$%!C#^c z4o>6S^mv40EU;zzt3v^GYM}UKA9|q6HZ!0gu%65gAP0e--^O$nkX{4SAK*!Bzvu;h z%mC^_bDi158>kK1+u-;J{W=7Ea;F#n1_r;&LkYc@1+MM*ON3fk!;Wqn>ljbv)*2r} zB+tp$Fd-eE);f%+dq_-VJ>L7&*NTlVy~fSYGHu#`MuQu+BV_DEoNC&G9isiPslBd) zoF8mEp&M@VpcP+iri|@%2(@e*a(I*1j>%Y0ckBh-cd6&}5>KBvTE`}O+_)Vc5}E`+ zo~I}27`xG!mK!VBNRJ^4;NPar953^3Tkn|mKu?bazU;}^+)aA`#LLaK^r=sOG=1)c zjdYnC!xLYR<2lVz+|W582|n9uexW*#3EL_&j8>`Mc}=XBom9iTdiK&R-Ies{+KTAK`AkZ+=y|rK z)1?>h?;`)C9_F>>G`|m0iaJ@W{mv;4%h7f!VU+bPcvR-1V(Y3v4Ncn0RMjvq&)g>R z3ZI$f$;g0bRU$f$;|V(R>YHafGA~*&O}sPcP0Cr<%RH;Amn+BPwA5MP|<(P8vc>7Pn~di}agQ;(-8(zZ_9W~EOn zyJ%Xpy~sa@!7SY^%BPe&FP(WdAkoz?0|I|@Ei=H$``({z58Y?D031hd6sO)h#EFJ%!w0JzlKBXZ<6 zpaE-Y5`{&YI0)$xeau=!(Kq;rV1Q2Kt?&lz0UsPZIO2^;;*hpkUvzO`YptwBm`Xto zKZF%v7OO8Y6Tgeo*Kr0<i&Bb(7f3Vf+308?tj5$7#3jtHxG2)uuZ_F2wVk)+g0eT%x| zofYNBN>MR%T2;*4^T~+!D6^PsAsgp4gDLiw!#<92s*5)BxYQ`Tivot{6eu#X9H;Mb z{JL@td%=xUWX%6y0RnUZXd$4-p4v_^4#!3rA9)YZW(m?`{tD@`B(^htrcp#xX?V^- znJ)#=$gxz;M~;|ENpj8VhKa?dm>L*jYEsBmtLwRP#QK~%9#hb=AuFUp$DE51*`|;? zXvSgYZ+ZSj*{WTU!f^(5!63wUprIa>5ST1IE+eAWOK9!#2tD52?orX>&aH4Ry;oLAvw9dJ1CSgcu}7KU#$n0>px{+LuM;8Fp_S9sg`!2rD1v2KcpL zaB$d4|KR_9GTpm>l>XlT^WpR(pZx~)#BHdDfvInw^=Z?F$#CzSCPo%ENg4S@n{0#l z8Tmd`aIP}Uuz0BkC1w;rIGNHFV-(|rYP`MAFr<&U*IhZEdU&K~*I<-1EbRfZYtLCvFk<7VFgG zRsrqRRpRSLjAI%oglbG`J1=4*v+v^f-lyf_k}W)0yP#NBZ4suU&xlwt%mqs50*-g&^ojH`UeFsGE9u(hE9v^R!*uPLXVNc!^LF~v-|~~mMxb_@{<)9urGN32{q*wDCf?5)H4iLM9^FREc}QS?8%hJ&e3z=j z*DKrDgf|lctNlJt%(1N{zPg;cf~wwl0RHmC+lL1Z3gbjK0diYdy93B!-sUy1h#3T} zL4TDG4Z_FDTrw{P@9)?j9?&NSUz; z-$Ez$Bexpfa?ISu^*61*=2V_Z_2Duc4$57m@qALwQ}#^xd6HUxs);_FsPA9xd5xVS zRlWG6?k3NtR6jH6JT%7p@=Ven>;f8TUAYs|4%=Fr<0p#5{5hHSXwS#XV0CS&c8 zAL|y^dkkivW7cb|d0ERX z+p;VN1EeWDCdW>QL78FuhSCI^FgH0WRpy-2{aYkGl|GqJRgw2a=f!FKi(K`*4o+a- z@$7E;!B4-8iU&Bb?IgO;rEF}`{;EO%#n^!Rq8H`U_AFn@4);H6Wf#5)RI(} zv9~^Dte4x%iDl`Ga{pNJtP_f*U|#A5g{oer=~CSKkc}kvp8k6gKJcc=xI3KozI}> zxiTq7cveF&`Wrgt**jULENdoWpFj`1MSE19T#~Y}f#uL&%I2(RKS@SeO!EO+^bN_; zM(ZPs@)W(t!#@64zG2&uPYm!9fX><`Us^qR4I1E(=tyWt=ha4=c@+#&Y_DZqu~K*# zG~b=`WZ<$Yi84lf^{x1jlRthV&J*!fzXw}QMllwe$bbj|hT9(T%KRO=tnX+d1uWxJ z3FQ)4S8nG-v0nN30K&YmKWN}18vP|4*t~ol(2VdID%}{w$9s6<@=q`=z*{KVx%!PB z;wdaZnvru}%~@aJ>Ecql_cpJ|;0?NceTBy1;RygN;Hu}a!mMuUrL2-{EYy+8PF&fH z?t!x($<>o%UjUYuo}ip);ulM#rsAl@3r<+*0d4E@1cC?&VTchP zgnSCY8&DhhF5xxa!Xx+?p3zn=qYZKR{4M5&QHjU8z7M{h%*4FE_y-wQ! z$^#%rmOk;ZI#`cSehp{Q?O7MkX93WoV`N7==t;BdqATsquVm^5e`HK(=nk*~`^Kje zgkee_z0^&gee)~n<$E7VZ{B^7p1gV~-F#v@edDd2^m6mr^sBG@QTj8_zmYZxq0-Rf zn2#KG^#(Tti6B45tlqabQu?8f-%YRm?v02~jb^w}V8RMu8L*z43kSG2AZQ?LLcK5X z#JkVK3`1xv@$`8>d4W!EDD*tXoM{cfH?M5jAM2}IK+`^Oz=NE2_4x?l+WJNS%G&Gz zo^5ou2JJNls_~l*(*fQ~46_VkuM5g^9Iu$+4XkZH02toJ-tGT=Mz0X>7X5XItr_xS zjT@x_`475$6oY>1z#FsY!kHfcASiv?N;`P^`=w`NRJZj$?(pG7%65GE&m*lSang_J zs}*jh=&^X<(PxlF7!nNHkSSg?Y^xyr68h3x--+Ki+70Rx*&Fbda_Tp9NPlwdNbleQ z9~;NshY7Z8Z`z5mu3OqT`q$Ik*>VY*0^7T9-%kg(-%M9;+(_U5gHNSTUFHef{yRK% z^QI9UVRF%55ZB7x%0(*oE3mx=#}*U0@+(86c|0l~g=`&zd~1Cx-CqSQj{ssooxf>8 z**REnEBqEL{!qn~|Ds!^mZ{b2d6PWqc~dLTNW5sh)8uS)?$gtGk2~ej@l~&NolNTa zaku2#^dG$j=8gS%c}0?X8E!}B&N`F!Q)oPvecVGl1m!d8*V}zwE_n>)iSR5s^?a`b zZuPWTyyY=%d`_DU>^GZRAKRG`*PUwKnxr*Zwwz0=Y_-FZG;?3-&n)mk%1~c(x{`gJegrvmY+8cs?IL5%;Q~`<53+C#b1EnJRL7k{Zx@j z-t~T}AD^oD7!r#99|P%`Bp+uR=h@L%TWhwts61o!r?zggXQvh-F^`#9xncmTILh4U zzmvW(_>OQA3Rf5e@a@emUc_zD1K8;-2qlZ zoa8TG{kzc~*P2;L{H=9#97?F#GMHHLEQxdWrKcq^G&xRK2F6zBRVO3gNn4(lR-czYi{DmP_9UYttdw&Pq^@MVs569_m)v6M)0AG1q)60 zVH+H{53gCv#0I8-B7Eytru>h(0@30-@)h$6dhC=zpik5Aa#g@iu>^SsmCQd&7uDjAb;Q{$ zXqvJrg=Lm~cwl`6@sBc4NO_e-;1<{=@Yju%lk}7S`X8qM@#j93e)7j&QaMq62^!t| zRKOsNA`ncl_g>W>;h|4*=uR0VPB;%>SeQ^8fy+l5b=VYNwGkmJylp$MSjf#KA^JpI ziezXpwxpxo0W|sX!5D+2iPvhciV^LCqIjFdL-QOMLRP0pF)D{EfTC>sY42SpXI{f0 zzVQ*UgpGH+L6{0~umEwlwgIeQ(1U=DFl4~qJ%ow(08~Wo%nm@Dd20ljZ{uAp@>Z;ei&Lw7uhp87U`Vd`y@O z<2^fj3gBtTUU1flb!ES!W&Q!gBJ1%k-lxd!hX;9xobK_(UPF@zuKN+hBf#h;pmYGi zfQAD9_In2z)p-YfKiKJ{4W7j6sp_O8AP#rh!mCl2j*t;6!fUm))=t}(X$!#eF<^Lj zry`FItue8oSPjdE3e*lhO90*h9Gm2bpv|9s;BjgviPlyBgDy$J|?nG2h@Eqe!t$f_4M;56?e_~yV0fO_%z?=SgKfGk7LvMO{V1}%2u1>H9tkobA+K$*70C(Gq5vl zB&4t6dRZ#{G2d&x7uhE3mGi9c^AmL*d|15}87|VE_q&cJ`(Dd$UupB--X6UMCUvWt z?cu4Kg{+veTBDOQD&ZNGk@q1ipvt&ApU-V18(8N$Of7^NW1S zEovJREb83f+vE4iChLu?>#?3*j_di4%f6=dFTGhr7@J>*WltH-uz1v**2jz5$B5D& z|D$^UJ-XI>5U+WUU#5lar)HavM*q{SdAv7=*+UH|5Wc`#?51Z zzG5Hz6FAssjnrqY9UBTbWNmY&y~UVxjm$c?amYG!gFSR`o7UOItq)R|^<_D60Os-I zJ=XeyAS_8YE)jl%_3iNkyqockW<9pSo3O5H4j&u@jrf+0iy_u10v&gcSuZLXjUBLt zGDM;uHH>+QFdQtEC<;H&mk4`EY4xuMwCj)_jzq?W?l1AiZ7isiGg#IBuzrdD%Q_F* z+&G|(_#4%@$p$8H858TVe{}q#>3@6YC(_;CwRC-rH^i3!xDVRtpTG67^xZ3;O5eHu zW!`G0Jay{!5Pg=})c9ZgP*TyHQ*F99&3er`*DuE0cbcCSgEtjD)&8<=S8bsVCCG8JrgW{A=Oh8W07NUkA+rm$C|K37 zLaE20(0~OTgJCn4L?jo)xvyd;0Im=Y+%!fyz_b^;(+BWFhAL!dnoy%St z>Mn;&HK(MDwRl?5uK3t4X$3f~aLOYc(%gscDjFnJZ^-BVmK@Mw$ZuJ7Y@33H`l@s< zU)!v_(Z;H5v|0O62IZoBX{Y6^r+Gxaw4zVtBLR20JZz(`so8F_K1v@2ODCvA z6qbs0;GYH0dkO?7Y=VPBB$oThVe z*qBPxNU%A0!`HEBSf!IH?naDJDnL~-(QiP7(U$=QlB&*{9l&2b zcqL#1R{4-X2LN>$57+BYtp(8NMZh@3{xi;QRxIEqpkxEk*w+E{kR4P?*KnGo>;1b& z_zsD;0HOs35BKn3jfbevg0CQ}VJcdD1kXXE*RY`V&bwSd>?}IbhVMTBz~+3FFbRUv z>csFDcJvV--#YlI^_KzL90I9&1`C)Qm)R$) z0?~*2gnVExsM`Sa(^!`Qh7E6f1n{c|bHvU@76EfTtM{ni1SHiH#W2%@bd}D0eOPLcH|NI+#;NY-mQnknAY3dk1Tr8HZ;TlaJJF799_rtKMj0$ z2=LrqZK6ZE5B+kCygdNmFc{isn3M3PMn*yS@HVF{g5xh-+e^mm*s%ehh#OMGi7aAVbJ_po(+|@B_r^6i z3iwkHL6!kYm-_pu*SM6Hc->0RW_xcro=khIz(|3f`d{M7DB2(mTj>Mv#*CqxIWRB^ zGCp|#uk;U^0kjv~2UKOG3d|aw;|SdfbQ+_4b#;aC8P3=|96^T;2f8DG`ODZDTw)?J zj78Tr!D9^@y-J9cJ?aa5A7L*?-o#M$F7|M9UAxt`THI9N6<|4Z9fOLi!V&|}!RY_`8G`S(P6)Wz6q2&9akHm)bzFHfZ)XKxmr$Qx ztWW3&-YbCLUVd1CH#glJ=Ab_N=sLcx>Vpg}4EEU)UfDhw?(Q9?Jv`nI@7_sI-+C(j z(2Gx`n=Qg^?Czvp`fZ@sGrFRiV)N;z6SgxZsf

1j^0P#k+u+j5#E$MZ84Jsz}= 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
    $Ehd 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 z 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 LNkvXXu0mjfxV-CO diff --git a/examples/react-hooks/e-commerce/assets/favicon.png b/examples/react-hooks/e-commerce/assets/favicon.png deleted file mode 100644 index b9cee152b20e4dcd9188aa9e63ff7bbafde5192f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 zcmV-|2YvX7P)%H@?e4l8=be~}ley8cKL>Hn&DY(@1uxC+;!I&w*qPl4*&V{|!fhD0V%#F!EZkJWjmize^}=-+*J50wTvfuA$`#7x zYi!IeqgnWy@NgkpBfY?bMC6ZIC0j)Tkx)#gR7yom#xRj2W0;g9bG6~fn9ya)rHO9& zoHq+uBE3}eCy^l`GM0R>d~S3MOQf zWGF|;DSo*rLCIb1$b>>^y(3juR>Qd>gUFylMw}tVk%@%D9&*afr2KL-p$|xjF(HtI z1QYVJ6ZDXxBDbi>&0dCVjfX;M4~0_uN@m!VWCWFQg?uSr$nPOPL$Od)aw=n-CNf|$ zhA(9@y81FEXcLXnIkq?Gi}*-1w6g(?D}AVZ4NL&^>QV2FDt91J2;3xdfgZiaG< z?YQKE>yV0=kXvrH#xjAB%9vDKa<8a`v!-BZ3#{LnR?eD)msjU6IY-FvAwy*9z!wVo z#}iVSf8Gt-cH>3*_rDJWC8twPWek&XOU`8K!Gx5khfGFS6Z-7{YcaVE4jYA^_rg)K zF6^+6{h}ur+i?#$QYD#DA;FQ!IksO`4_h@Ow=BWq?<(%0r^?^!R!FgV7reMC-YKYv zp(4T9*36g;WtW|1=f5}Vdy(5Il+_6N8TNm@6hjMLw zOvWfn$YeNY)*Am|-quHW%$;der#OB*I1_m?v5?6W2?af*LbjR^pX_8U4j=#D(fuId zj4At^_cWSJX@+#obskbwrV#{3NGYS_&YuIY-uavezN!FUlC^ThK6csMy*&>F8M+RY zX)xlFQ~Z+S2{ll&jxf46A>|ev@b2AtNM)KJ;b*AqDfb}ZH-j*<9W3k`v}qSSr}{pe zhXf}>D$@)_LVgd$QYv)dI1FroS2u}NCA_2nZ)t`nR>Od4P$m>Inb&th%O>_3H!jCJ1(ixYbo2!5+ykZ2 zupVAr=LsoJ$tfb!3a(Iz%;yJ+et6n2PVb;7Iz;qI1(Q*VWF8}OEgLC{sco>MXj z)ymFf{E{=7Hc%m7N@SkV3JbaonGu06iP%HyN!fZvGn{T0OD%E}Op&-l zw=^Qe<7-SNt#=xiYWDgz|Nr}PipsQu33)PB&wm<=cM3CFA_q*sw+7g23~vo?1(Cd2b>|VbKUeZ}C^rIozsvo~{$=%B-);C*r!w&{jMtMa8G;biq z#$E8D`rz)V3Mnen34$+VQv7+M_&QJ!sjF9PIG|(usm<|u=$Q>^%d-|ETjAq4ZZj6iQCpyAMDkh_tOcx|N z9U*7RJw^Wg*JE(zB3!ZzhtI%=`{OC+Y);eq&JOtVFq}RM$4|lc`r)xPDpKj(XjGi% zLn_k^u~0m^z4MUb3zdIgF7NXp&5X$?UdbscVnX-rJXHSkA-|6-Gm2#=67sU6n2=G( zDdbD3OqeCbkx_~=6kk}C@H|xZZLHu{uu8ZxJs^Za`7;y<6=JElhvJ7`ipcb`%MpsR zQ?5`$&K@!u3`fQ(IayB=8771z<4LJdd|{2xLy9A#YwqVmmL)4WlaWW~eVij8LV_a` zk8R@fkP%L}@}CcRqx%C{HpnZpezqnNrQEWM&qFzeLc!@DCFf&F{qH7_Wdr;{v*XAp zDiW2PFB1rPTc7&!o)5{oK|c5SLYAc$u~MNeDioJo#{)LDohj#+oYF0Sf3uJ! - - - - - - - - - - - - - - - - - React InstantSearch Hooks - e-commerce - - - - - -
    - - - - - diff --git a/examples/react-hooks/e-commerce/package.json b/examples/react-hooks/e-commerce/package.json deleted file mode 100644 index 93dde9d812..0000000000 --- a/examples/react-hooks/e-commerce/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "example-react-instantsearch-hooks-e-commerce", - "version": "6.38.26", - "private": true, - "scripts": { - "build": "BABEL_ENV=parcel parcel build index.html", - "website:examples": "BABEL_ENV=parcel parcel build index.html --public-url . --dist-dir=../../../website/examples/react-hooks/e-commerce", - "start": "BABEL_ENV=parcel parcel index.html" - }, - "browserslist": "firefox 68, chrome 78, IE 11", - "dependencies": { - "algoliasearch": "4.14.3", - "instantsearch.js": "4.56.8", - "react": "18.1.0", - "react-compound-slider": "3.4.0", - "react-dom": "18.1.0", - "react-instantsearch-hooks-web": "6.47.3" - }, - "devDependencies": { - "@parcel/core": "2.8.0", - "@parcel/packager-raw-url": "2.8.0", - "@parcel/transformer-webmanifest": "2.8.0", - "parcel": "2.8.0", - "typescript": "5.1.3" - } -} diff --git a/examples/react-hooks/getting-started/.editorconfig b/examples/react-hooks/getting-started/.editorconfig deleted file mode 100644 index dd7255e8a4..0000000000 --- a/examples/react-hooks/getting-started/.editorconfig +++ /dev/null @@ -1,8 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true diff --git a/examples/react-hooks/getting-started/.gitignore b/examples/react-hooks/getting-started/.gitignore deleted file mode 100644 index bf78c5a78c..0000000000 --- a/examples/react-hooks/getting-started/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -.parcel-cache -*.local - -# Editor directories and files -.vscode -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/examples/react-hooks/getting-started/README.md b/examples/react-hooks/getting-started/README.md deleted file mode 100644 index b8c281d6af..0000000000 --- a/examples/react-hooks/getting-started/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# react-instantsearch-hooks-app - -[![Edit getting-started](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/getting-started) - -_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._ - -## Get started - -To run this project locally, install the dependencies and run the local server: - -```sh -npm install -npm start -``` - -Alternatively, you may use [Yarn](https://http://yarnpkg.com/): - -```sh -yarn -yarn start -``` - -Open http://localhost:3000 to see your app. diff --git a/examples/react-hooks/getting-started/favicon.png b/examples/react-hooks/getting-started/favicon.png deleted file mode 100644 index b9cee152b20e4dcd9188aa9e63ff7bbafde5192f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 zcmV-|2YvX7P)%H@?e4l8=be~}ley8cKL>Hn&DY(@1uxC+;!I&w*qPl4*&V{|!fhD0V%#F!EZkJWjmize^}=-+*J50wTvfuA$`#7x zYi!IeqgnWy@NgkpBfY?bMC6ZIC0j)Tkx)#gR7yom#xRj2W0;g9bG6~fn9ya)rHO9& zoHq+uBE3}eCy^l`GM0R>d~S3MOQf zWGF|;DSo*rLCIb1$b>>^y(3juR>Qd>gUFylMw}tVk%@%D9&*afr2KL-p$|xjF(HtI z1QYVJ6ZDXxBDbi>&0dCVjfX;M4~0_uN@m!VWCWFQg?uSr$nPOPL$Od)aw=n-CNf|$ zhA(9@y81FEXcLXnIkq?Gi}*-1w6g(?D}AVZ4NL&^>QV2FDt91J2;3xdfgZiaG< z?YQKE>yV0=kXvrH#xjAB%9vDKa<8a`v!-BZ3#{LnR?eD)msjU6IY-FvAwy*9z!wVo z#}iVSf8Gt-cH>3*_rDJWC8twPWek&XOU`8K!Gx5khfGFS6Z-7{YcaVE4jYA^_rg)K zF6^+6{h}ur+i?#$QYD#DA;FQ!IksO`4_h@Ow=BWq?<(%0r^?^!R!FgV7reMC-YKYv zp(4T9*36g;WtW|1=f5}Vdy(5Il+_6N8TNm@6hjMLw zOvWfn$YeNY)*Am|-quHW%$;der#OB*I1_m?v5?6W2?af*LbjR^pX_8U4j=#D(fuId zj4At^_cWSJX@+#obskbwrV#{3NGYS_&YuIY-uavezN!FUlC^ThK6csMy*&>F8M+RY zX)xlFQ~Z+S2{ll&jxf46A>|ev@b2AtNM)KJ;b*AqDfb}ZH-j*<9W3k`v}qSSr}{pe zhXf}>D$@)_LVgd$QYv)dI1FroS2u}NCA_2nZ)t`nR>Od4P$m>Inb&th%O>_3H!jCJ1(ixYbo2!5+ykZ2 zupVAr=LsoJ$tfb!3a(Iz%;yJ+et6n2PVb;7Iz;qI1(Q*VWF8}OEgLC{sco>M
    Xj z)ymFf{E{=7Hc%m7N@SkV3JbaonGu06iP%HyN!fZvGn{T0OD%E}Op&-l zw=^Qe<7-SNt#=xiYWDgz|Nr}PipsQu33)PB&wm<=cM3CFA_q*sw+7g23~vo?1(Cd2b>|VbKUeZ}C^rIozsvo~{$=%B-);C*r!w&{jMtMa8G;biq z#$E8D`rz)V3Mnen34$+VQv7+M_&QJ!sjF9PIG|(usm<|u=$Q>^%d-|ETjAq4ZZj6iQCpyAMDkh_tOcx|N z9U*7RJw^Wg*JE(zB3!ZzhtI%=`{OC+Y);eq&JOtVFq}RM$4|lc`r)xPDpKj(XjGi% zLn_k^u~0m^z4MUb3zdIgF7NXp&5X$?UdbscVnX-rJXHSkA-|6-Gm2#=67sU6n2=G( zDdbD3OqeCbkx_~=6kk}C@H|xZZLHu{uu8ZxJs^Za`7;y<6=JElhvJ7`ipcb`%MpsR zQ?5`$&K@!u3`fQ(IayB=8771z<4LJdd|{2xLy9A#YwqVmmL)4WlaWW~eVij8LV_a` zk8R@fkP%L}@}CcRqx%C{HpnZpezqnNrQEWM&qFzeLc!@DCFf&F{qH7_Wdr;{v*XAp zDiW2PFB1rPTc7&!o)5{oK|c5SLYAc$u~MNeDioJo#{)LDohj#+oYF0Sf3uJ! - - - - - - - - - - - React InstantSearch Hooks — Getting started - - - - - -
    - - - - - diff --git a/examples/react-hooks/getting-started/package.json b/examples/react-hooks/getting-started/package.json deleted file mode 100644 index 539c08b6a1..0000000000 --- a/examples/react-hooks/getting-started/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "example-react-instantsearch-hooks-getting-started", - "version": "6.38.26", - "private": true, - "scripts": { - "build": "BABEL_ENV=parcel parcel build index.html", - "start": "BABEL_ENV=parcel parcel index.html --port 3000" - }, - "dependencies": { - "algoliasearch": "4.14.3", - "instantsearch.js": "4.56.8", - "react": "18.1.0", - "react-dom": "18.1.0", - "react-instantsearch-hooks-web": "6.47.3" - }, - "devDependencies": { - "@parcel/core": "2.8.0", - "@parcel/packager-raw-url": "2.8.0", - "@parcel/transformer-webmanifest": "2.8.0", - "parcel": "2.8.0", - "typescript": "5.1.3" - } -} diff --git a/examples/react-hooks/getting-started/src/App.css b/examples/react-hooks/getting-started/src/App.css deleted file mode 100644 index e01152732c..0000000000 --- a/examples/react-hooks/getting-started/src/App.css +++ /dev/null @@ -1,71 +0,0 @@ -body, -h1 { - margin: 0; - padding: 0; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -} - -em { - background: cyan; - font-style: normal; -} - -.header { - display: flex; - align-items: center; - min-height: 50px; - padding: 0.5rem 1rem; - background-image: linear-gradient(to right, #8e43e7, #00aeff); - color: #fff; - margin-bottom: 1rem; -} - -.header a { - color: #fff; - text-decoration: none; -} - -.header-title { - font-size: 1.2rem; - font-weight: normal; -} - -.header-title::after { - content: ' ▸ '; - padding: 0 0.5rem; -} - -.header-subtitle { - font-size: 1.2rem; -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 1rem; -} - -.search-panel { - display: flex; -} - -.search-panel__filters { - flex: 1; -} - -.search-panel__results { - flex: 3; -} - -.searchbox { - margin-bottom: 2rem; -} - -.pagination { - margin: 2rem auto; - text-align: center; -} diff --git a/examples/react-hooks/next-routing/README.md b/examples/react-hooks/next-routing/README.md deleted file mode 100644 index ceb90aca59..0000000000 --- a/examples/react-hooks/next-routing/README.md +++ /dev/null @@ -1,18 +0,0 @@ -This example shows how to do server side rendering with next.js and React InstantSearch Hooks. There's a live example here: https://codesandbox.io/s/github/algolia/instantsearch.js/tree/master/examples/react-hooks/next. - -[![Edit next-routing](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/next-routing) - -## Clone the example - -```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react-hooks/next-routing -``` - -## Start the example - -```sh -yarn install --no-lockfile -yarn run dev -``` - -Read more about React InstantSearch Hooks [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/). diff --git a/examples/react-hooks/next-routing/package.json b/examples/react-hooks/next-routing/package.json deleted file mode 100644 index 3e5fbbea3a..0000000000 --- a/examples/react-hooks/next-routing/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "example-react-instantsearch-hooks-next-routing-example", - "version": "6.57.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "algoliasearch": "4.14.3", - "instantsearch.css": "8.0.0", - "next": "12.1.6", - "react": "18.1.0", - "react-dom": "18.1.0", - "react-instantsearch-hooks-router-nextjs": "6.47.3", - "react-instantsearch-hooks-server": "6.47.3", - "react-instantsearch-hooks-web": "6.47.3" - }, - "devDependencies": { - "@types/node": "17.0.40", - "@types/react": "18.0.12", - "eslint": "8.4.0", - "eslint-config-next": "12.0.7", - "typescript": "5.1.3" - } -} diff --git a/examples/react-hooks/next/.gitignore b/examples/react-hooks/next/.gitignore deleted file mode 100644 index 1437c53f70..0000000000 --- a/examples/react-hooks/next/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env.local -.env.development.local -.env.test.local -.env.production.local - -# vercel -.vercel diff --git a/examples/react-hooks/next/README.md b/examples/react-hooks/next/README.md deleted file mode 100644 index 6cfc3f11f4..0000000000 --- a/examples/react-hooks/next/README.md +++ /dev/null @@ -1,18 +0,0 @@ -This example shows how to do server side rendering with next.js and React InstantSearch Hooks. There's a live example here: https://codesandbox.io/s/github/algolia/instantsearch.js/tree/master/examples/react-hooks/next. - -[![Edit next](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/next) - -## Clone the example - -```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react-hooks/next -``` - -## Start the example - -```sh -yarn install --no-lockfile -yarn run dev -``` - -Read more about React InstantSearch Hooks [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/). diff --git a/examples/react-hooks/react-native/.expo-shared/assets.json b/examples/react-hooks/react-native/.expo-shared/assets.json deleted file mode 100644 index 1e6decfbb5..0000000000 --- a/examples/react-hooks/react-native/.expo-shared/assets.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, - "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true -} diff --git a/examples/react-hooks/react-native/.gitignore b/examples/react-hooks/react-native/.gitignore deleted file mode 100644 index ec8a36a257..0000000000 --- a/examples/react-hooks/react-native/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -node_modules/ -.expo/ -dist/ -npm-debug.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision -*.orig.* -web-build/ - -# macOS -.DS_Store diff --git a/examples/react-hooks/react-native/README.md b/examples/react-hooks/react-native/README.md deleted file mode 100644 index b1e4d46e5e..0000000000 --- a/examples/react-hooks/react-native/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# React InstantSearch Hooks with React Native - -[![Edit react-native](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/react-native) - -This example shows how to use React InstantSearch Hooks with React Native. - -## Clone the example - -```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react-hooks/react-native -``` - -## Start the example - -```sh -yarn install --no-lockfile -yarn run start -``` - -Read more about React InstantSearch Hooks [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/). diff --git a/examples/react-hooks/react-native/app.json b/examples/react-hooks/react-native/app.json deleted file mode 100644 index 476db9aec0..0000000000 --- a/examples/react-hooks/react-native/app.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "expo": { - "name": "ais-ecommerce-demo-app", - "slug": "ais-ecommerce-demo-app", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icon.png", - "splash": { - "image": "./assets/splash.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "updates": { - "fallbackToCacheTimeout": 0 - }, - "assetBundlePatterns": ["**/*"], - "ios": { - "supportsTablet": true - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", - "backgroundColor": "#FFFFFF" - } - }, - "web": { - "favicon": "./assets/favicon.png" - } - } -} diff --git a/examples/react-hooks/react-native/assets/icon.png b/examples/react-hooks/react-native/assets/icon.png deleted file mode 100644 index a0b1526fc7b78680fd8d733dbc6113e1af695487..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22380 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 diff --git a/examples/react-hooks/react-native/assets/splash.png b/examples/react-hooks/react-native/assets/splash.png deleted file mode 100644 index 0e89705a9436743e42954d3744a0e7ff0d3d4701..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47346 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 diff --git a/examples/react-hooks/react-native/babel.config.js b/examples/react-hooks/react-native/babel.config.js deleted file mode 100644 index 9d89e13119..0000000000 --- a/examples/react-hooks/react-native/babel.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - }; -}; diff --git a/examples/react-hooks/react-native/package.json b/examples/react-hooks/react-native/package.json deleted file mode 100644 index 6e312a4a7d..0000000000 --- a/examples/react-hooks/react-native/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "example-react-instantsearch-hooks-react-native-example", - "version": "6.38.26", - "private": true, - "main": "node_modules/expo/AppEntry.js", - "scripts": { - "start": "expo start", - "android": "expo start --android", - "ios": "expo start --ios", - "web": "expo start --web", - "eject": "expo eject" - }, - "dependencies": { - "algoliasearch": "4.14.3", - "expo": "~44.0.0", - "expo-status-bar": "~1.2.0", - "instantsearch.js": "4.56.8", - "react": "17.0.1", - "react-instantsearch-hooks": "6.47.3", - "react-native": "0.64.3", - "react-native-web": "0.17.1" - }, - "devDependencies": { - "@babel/core": "7.15.5", - "@types/react": "~17.0.21", - "@types/react-native": "~0.64.12", - "expo-cli": "5.1.1", - "typescript": "5.1.3" - } -} diff --git a/examples/react/autocomplete/README.md b/examples/react/autocomplete/README.md deleted file mode 100644 index 5be6d0b715..0000000000 --- a/examples/react/autocomplete/README.md +++ /dev/null @@ -1,26 +0,0 @@ -This example shows how to use an external autocomplete component with `react-instantsearch` - -[![Edit autocomplete](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/autocomplete) - -It uses [react-autosuggest](https://github.com/moroshko/react-autosuggest) as the autocomplete external component. - -You will find two use cases: - -* How to build an autocomplete displaying hits from different indices -* How to build @-mentions using [ant](https://ant.design) as the external component - -## Clone the example - -```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/autocomplete -``` - -## Start the example - -```sh -yarn install --no-lockfile -yarn start -``` - - -Read more about `react-instantsearch` [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). diff --git a/examples/react/autocomplete/__mocks__/styleMock.js b/examples/react/autocomplete/__mocks__/styleMock.js deleted file mode 100644 index 33c8d29dc0..0000000000 --- a/examples/react/autocomplete/__mocks__/styleMock.js +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line import/no-commonjs -module.exports = {}; diff --git a/examples/react/autocomplete/index.html b/examples/react/autocomplete/index.html deleted file mode 100644 index 9840f70f1c..0000000000 --- a/examples/react/autocomplete/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - autocomplete with react-instantsearch - - - - -

    Autocomplete targeting multiple indices

    -
    -

    Autocomplete of mentions

    -
    - - - - diff --git a/examples/react/autocomplete/package.json b/examples/react/autocomplete/package.json deleted file mode 100644 index df7cdab63d..0000000000 --- a/examples/react/autocomplete/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "example-react-instantsearch-autocomplete", - "version": "17.0.0", - "private": true, - "license": "MIT", - "scripts": { - "start": "BABEL_ENV=parcel parcel index.html --port 3000", - "build": "BABEL_ENV=parcel parcel build index.html --public-url .", - "test": "jest --ci" - }, - "devDependencies": { - "@babel/core": "7.15.5", - "@parcel/core": "2.8.0", - "@parcel/packager-raw-url": "2.8.0", - "@parcel/transformer-webmanifest": "2.8.0", - "parcel": "2.8.0", - "react-test-renderer": "17.0.2" - }, - "jest": { - "transform": { - "^.+\\.(jsx?|tsx?)$": [ - "babel-jest", - { - "rootMode": "upward" - } - ] - }, - "moduleNameMapper": { - "\\.css$": "/__mocks__/styleMock.js" - } - }, - "dependencies": { - "algoliasearch": "4.14.3", - "antd": "3.23.2", - "prop-types": "15.6.2", - "react": "17.0.2", - "react-autosuggest": "9.4.3", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ] -} diff --git a/examples/react/autocomplete/src/App-Mentions.js b/examples/react/autocomplete/src/App-Mentions.js deleted file mode 100644 index 07a57c17d7..0000000000 --- a/examples/react/autocomplete/src/App-Mentions.js +++ /dev/null @@ -1,37 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import Mention from 'antd/lib/mention'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { InstantSearch, connectAutoComplete } from 'react-instantsearch-dom'; -import 'antd/lib/mention/style/css'; - -const AsyncMention = ({ hits, refine }) => ( - hit.name)} - onSearchChange={(query) => refine(query)} - /> -); - -AsyncMention.propTypes = { - hits: PropTypes.array, - refine: PropTypes.func, -}; - -const ConnectedAsyncMention = connectAutoComplete(AsyncMention); - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const App = () => ( - - - -); - -export default App; diff --git a/examples/react/autocomplete/src/App-Multi-Index.js b/examples/react/autocomplete/src/App-Multi-Index.js deleted file mode 100644 index 0cd95dd6a9..0000000000 --- a/examples/react/autocomplete/src/App-Multi-Index.js +++ /dev/null @@ -1,96 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import Autosuggest from 'react-autosuggest'; -import { - InstantSearch, - Configure, - Index, - Highlight, - connectAutoComplete, -} from 'react-instantsearch-dom'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const App = () => ( - - - - - - -); - -class Example extends Component { - static propTypes = { - hits: PropTypes.arrayOf(PropTypes.object).isRequired, - currentRefinement: PropTypes.string.isRequired, - refine: PropTypes.func.isRequired, - }; - - state = { - value: this.props.currentRefinement, - }; - - onChange = (event, { newValue }) => { - this.setState({ - value: newValue, - }); - }; - - onSuggestionsFetchRequested = ({ value }) => { - this.props.refine(value); - }; - - onSuggestionsClearRequested = () => { - this.props.refine(); - }; - - getSuggestionValue(hit) { - return hit.name; - } - - renderSuggestion(hit) { - return ; - } - - renderSectionTitle(section) { - return section.index; - } - - getSectionSuggestions(section) { - return section.hits; - } - - render() { - const { hits } = this.props; - const { value } = this.state; - - const inputProps = { - placeholder: 'Search for a product...', - onChange: this.onChange, - value, - }; - - return ( - - ); - } -} - -const AutoComplete = connectAutoComplete(Example); - -export default App; diff --git a/examples/react/autocomplete/src/__tests__/App.test.js b/examples/react/autocomplete/src/__tests__/App.test.js deleted file mode 100644 index c6470d4b94..0000000000 --- a/examples/react/autocomplete/src/__tests__/App.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Mentions from '../App-Mentions'; -import MultiIndex from '../App-Multi-Index'; - -jest.mock('antd/lib/mention', () => { - return ({ placeholder, suggestions }) => - ` - ${placeholder} - ${suggestions.join('\n')} -`; -}); - -describe('autocomplete recipe', () => { - it('MultiIndex renders without crashing', () => { - const component = renderer.create(); - - expect(component.toJSON()).toMatchSnapshot(); - }); - it('Mentions renders without crashing', () => { - const component = renderer.create(); - - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/examples/react/autocomplete/src/__tests__/__snapshots__/App.test.js.snap b/examples/react/autocomplete/src/__tests__/__snapshots__/App.test.js.snap deleted file mode 100644 index b8a405221d..0000000000 --- a/examples/react/autocomplete/src/__tests__/__snapshots__/App.test.js.snap +++ /dev/null @@ -1,38 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`autocomplete recipe Mentions renders without crashing 1`] = ` -" - give someone an @-mention here - -" -`; - -exports[`autocomplete recipe MultiIndex renders without crashing 1`] = ` -
    - ); -}; - -export default connectStateResults(NoResults); diff --git a/examples/react/e-commerce/widgets/Pagination.css b/examples/react/e-commerce/widgets/Pagination.css deleted file mode 100644 index d8cb995f42..0000000000 --- a/examples/react/e-commerce/widgets/Pagination.css +++ /dev/null @@ -1,3 +0,0 @@ -.ais-Pagination--noRefinement { - display: none; -} diff --git a/examples/react/e-commerce/widgets/PriceSlider.css b/examples/react/e-commerce/widgets/PriceSlider.css deleted file mode 100644 index 3491c587a1..0000000000 --- a/examples/react/e-commerce/widgets/PriceSlider.css +++ /dev/null @@ -1,49 +0,0 @@ -.ais-RangeSlider .slider-rail { - background-color: rgba(65, 66, 71, 0.08); - border-radius: 3px; - cursor: pointer; - height: 3px; - position: absolute; - width: 100%; -} - -.ais-RangeSlider .slider-track { - background-color: #e2a400; - border-radius: 3px; - cursor: pointer; - height: 3px; - position: absolute; -} - -.ais-RangeSlider .slider-tick { - cursor: grab; - display: flex; - font-size: 0.75rem; - font-weight: bold; - position: absolute; - text-align: center; - top: -28px; - transform: translateX(-50%); - user-select: none; -} - -.ais-RangeSlider .slider-handle { - background-image: linear-gradient(to top, #f5f5fa, #fff); - border-radius: 50%; - box-shadow: 0 4px 11px 0 rgba(37, 44, 97, 0.15), - 0 2px 3px 0 rgba(93, 100, 148, 0.2); - cursor: grab; - height: 16px; - outline: none; - position: absolute; - transform: translate(-50%, -50%); - width: 16px; - z-index: 1; -} - -@media (max-width: 899px) { - .ais-RangeSlider .slider-handle { - height: 1.5rem; - width: 1.5rem; - } -} diff --git a/examples/react/e-commerce/widgets/PriceSlider.js b/examples/react/e-commerce/widgets/PriceSlider.js deleted file mode 100644 index 4bc03937a0..0000000000 --- a/examples/react/e-commerce/widgets/PriceSlider.js +++ /dev/null @@ -1,143 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Slider, Rail, Handles, Tracks, Ticks } from 'react-compound-slider'; -import { connectRange } from 'react-instantsearch-dom'; - -import { formatNumber } from '../utils'; -import './PriceSlider.css'; - -function Handle({ - domain: [min, max], - handle: { id, value, percent }, - disabled, - getHandleProps, -}) { - return ( - <> - {/* Dummy element to make the tooltip draggable */} -
    -
    - - ); -} - -const PriceSlider = ({ min, max, refine, currentRefinement, canRefine }) => { - const [ticksValues, setTicksValues] = useState([ - currentRefinement.min, - currentRefinement.max, - ]); - - useEffect(() => { - setTicksValues([currentRefinement.min, currentRefinement.max]); - }, [currentRefinement]); - - const onChange = (values) => { - refine({ min: values[0], max: values[1] }); - }; - - if ( - !canRefine || - ticksValues[0] === undefined || - ticksValues[1] === undefined - ) { - return null; - } - - return ( - - - {({ getRailProps }) => ( -
    - )} - - - - {({ tracks, getTrackProps }) => ( -
    - {tracks.map(({ id, source, target }) => ( -
    - ))} -
    - )} - - - - {({ handles, getHandleProps }) => ( -
    - {handles.map((handle) => ( - - ))} -
    - )} -
    - - - {({ ticks }) => ( -
    - {ticks.map(({ id, count, value, percent }) => ( -
    - $ - {formatNumber(value)} -
    - ))} -
    - )} -
    - - ); -}; - -export default connectRange(PriceSlider); diff --git a/examples/react/e-commerce/widgets/Ratings.js b/examples/react/e-commerce/widgets/Ratings.js deleted file mode 100644 index e0c400b1e6..0000000000 --- a/examples/react/e-commerce/widgets/Ratings.js +++ /dev/null @@ -1,84 +0,0 @@ -import cx from 'classnames'; -import React from 'react'; -import { connectRange } from 'react-instantsearch-dom'; - -const Ratings = ({ currentRefinement, refine, createURL, count }) => { - const ratings = new Array(4).fill(null).map((_, ratingIndex) => { - const value = 4 - ratingIndex; - - const itemsCount = count - .filter((countObj) => value <= parseInt(countObj.value, 10)) - .map((countObj) => countObj.count) - .reduce((sum, currentCount) => sum + currentCount, 0); - - return { - value, - count: itemsCount, - }; - }); - const stars = new Array(5).fill(null); - - return ( -
    - -
    - ); -}; - -export default connectRange(Ratings); diff --git a/examples/react/e-commerce/widgets/ResultsNumberMobile.js b/examples/react/e-commerce/widgets/ResultsNumberMobile.js deleted file mode 100644 index 25ee84949e..0000000000 --- a/examples/react/e-commerce/widgets/ResultsNumberMobile.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { connectStats } from 'react-instantsearch-dom'; - -import { formatNumber } from '../utils'; - -const ResultsNumberMobile = ({ nbHits }) => ( -
    - {formatNumber(nbHits)} results -
    -); - -export default connectStats(ResultsNumberMobile); diff --git a/examples/react/e-commerce/widgets/SaveFiltersMobile.js b/examples/react/e-commerce/widgets/SaveFiltersMobile.js deleted file mode 100644 index c241b57dd6..0000000000 --- a/examples/react/e-commerce/widgets/SaveFiltersMobile.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { connectStats } from 'react-instantsearch-dom'; - -import { formatNumber } from '../utils'; - -const SaveFiltersMobile = ({ nbHits, onClick }) => ( - -); - -export default connectStats(SaveFiltersMobile); diff --git a/examples/react/e-commerce/widgets/index.js b/examples/react/e-commerce/widgets/index.js deleted file mode 100644 index 0503c3fa26..0000000000 --- a/examples/react/e-commerce/widgets/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { default as ClearFiltersMobile } from './ClearFiltersMobile'; -export { default as NoResults } from './NoResults'; -export { default as Ratings } from './Ratings'; -export { default as ResultsNumberMobile } from './ResultsNumberMobile'; -export { default as PriceSlider } from './PriceSlider'; -export { default as SaveFiltersMobile } from './SaveFiltersMobile'; diff --git a/examples/react/geo-search/README.md b/examples/react/geo-search/README.md deleted file mode 100644 index 230df74127..0000000000 --- a/examples/react/geo-search/README.md +++ /dev/null @@ -1,18 +0,0 @@ -This example shows how to perform a geo search using `react-instantsearch`. - -[![Edit geo-search](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/geo-search) - -## Clone the example - -```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/geo-search -``` - -## Start the example - -```sh -yarn install --no-lockfile -yarn start -``` - -Read more about `react-instantsearch` [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). diff --git a/examples/react/geo-search/index.html b/examples/react/geo-search/index.html deleted file mode 100644 index 9069dae4d2..0000000000 --- a/examples/react/geo-search/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - GeoSearch with React InstantSearch - - - -
    - - - - diff --git a/examples/react/geo-search/package.json b/examples/react/geo-search/package.json deleted file mode 100644 index cbd4009bb2..0000000000 --- a/examples/react/geo-search/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "example-react-instantsearch-geo-search", - "version": "17.0.0", - "private": true, - "license": "MIT", - "scripts": { - "start": "BABEL_ENV=parcel parcel index.html --port 3000", - "build": "BABEL_ENV=parcel parcel build index.html --public-url .", - "test": "jest --ci" - }, - "devDependencies": { - "@babel/core": "7.15.5", - "@parcel/core": "2.8.0", - "@parcel/packager-raw-url": "2.8.0", - "@parcel/transformer-webmanifest": "2.8.0", - "parcel": "2.8.0", - "react-test-renderer": "17.0.2" - }, - "jest": { - "transform": { - "^.+\\.(jsx?|tsx?)$": [ - "babel-jest", - { - "rootMode": "upward" - } - ] - } - }, - "dependencies": { - "algoliasearch": "4.14.3", - "instantsearch.css": "8.0.0", - "qs": "6.9.7", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4", - "react-instantsearch-dom-maps": "6.40.4" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ] -} diff --git a/examples/react/geo-search/src/App.js b/examples/react/geo-search/src/App.js deleted file mode 100644 index 5c93bdbc3f..0000000000 --- a/examples/react/geo-search/src/App.js +++ /dev/null @@ -1,100 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import qs from 'qs'; -import React, { Component, Fragment } from 'react'; -import { InstantSearch, SearchBox, Configure } from 'react-instantsearch-dom'; -import { - GoogleMapsLoader, - GeoSearch, - Control, - Marker, -} from 'react-instantsearch-dom-maps'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76', - { - _useRequestCache: true, - } -); - -const updateAfter = 700; -const searchStateToUrl = (searchState) => - searchState ? `${window.location.pathname}?${qs.stringify(searchState)}` : ''; - -class App extends Component { - constructor() { - super(); - - this.state = { - searchState: qs.parse(window.location.search.slice(1)), - }; - - window.addEventListener('popstate', ({ state: searchState }) => { - this.setState({ searchState }); - }); - } - - onSearchStateChange = (searchState) => { - // update the URL when there is a new search state. - clearTimeout(this.debouncedSetState); - this.debouncedSetState = setTimeout(() => { - window.history.pushState( - searchState, - null, - searchStateToUrl(searchState) - ); - }, updateAfter); - - this.setState((previousState) => { - const hasQueryChanged = - previousState.searchState.query !== searchState.query; - - return { - ...previousState, - searchState: { - ...searchState, - boundingBox: !hasQueryChanged ? searchState.boundingBox : null, - }, - }; - }); - }; - - render() { - const { searchState } = this.state; - - const parameters = {}; - if (!searchState.boundingBox) { - parameters.aroundLatLngViaIP = true; - parameters.aroundRadius = 'all'; - } - - return ( - - - Type a destination or move the map to see the closest apartment. - - - {(google) => ( - - {({ hits }) => ( - - - {hits.map((hit) => ( - - ))} - - )} - - )} - - - ); - } -} - -export default App; diff --git a/examples/react/geo-search/src/__tests__/App.test.js b/examples/react/geo-search/src/__tests__/App.test.js deleted file mode 100644 index a22e5e0bd8..0000000000 --- a/examples/react/geo-search/src/__tests__/App.test.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import React from 'react'; -import renderer from 'react-test-renderer'; - -import App from '../App'; - -describe('geo-search recipe', () => { - it('App renders without crashing', () => { - const component = renderer.create(); - - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/examples/react/geo-search/src/__tests__/__snapshots__/App.test.js.snap b/examples/react/geo-search/src/__tests__/__snapshots__/App.test.js.snap deleted file mode 100644 index b0eb2790b3..0000000000 --- a/examples/react/geo-search/src/__tests__/__snapshots__/App.test.js.snap +++ /dev/null @@ -1,71 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`geo-search recipe App renders without crashing 1`] = ` -Array [ - "Type a destination or move the map to see the closest apartment.", -
    -
    - - - -
    -
    , -] -`; diff --git a/examples/react/geo-search/src/index.js b/examples/react/geo-search/src/index.js deleted file mode 100644 index c8325007bd..0000000000 --- a/examples/react/geo-search/src/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App'; -import 'instantsearch.css/themes/algolia.css'; -import './style.css'; - -ReactDOM.render(, document.getElementById('root')); diff --git a/examples/react/geo-search/src/style.css b/examples/react/geo-search/src/style.css deleted file mode 100644 index 8cbc039f85..0000000000 --- a/examples/react/geo-search/src/style.css +++ /dev/null @@ -1,24 +0,0 @@ -body { - font-family: Roboto; - font-size: 16px; - color: #565a5c; -} - -.ais-InstantSearch__root { - display: flex; - flex-direction: column; -} - -.ais-SearchBox { - margin: 10px 0; -} - -.ais-GeoSearch { - padding-right: 0; - padding-left: 0; - height: 700px; -} - -.marker { - transform: translate(-50%, -100%) scale(0.5, 0.5); -} diff --git a/examples/react/getting-started/.editorconfig b/examples/react/getting-started/.editorconfig index 9d08a1a828..dd7255e8a4 100644 --- a/examples/react/getting-started/.editorconfig +++ b/examples/react/getting-started/.editorconfig @@ -1,7 +1,6 @@ root = true [*] -charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf diff --git a/examples/react/getting-started/.gitignore b/examples/react/getting-started/.gitignore index d30f40ef44..bf78c5a78c 100644 --- a/examples/react/getting-started/.gitignore +++ b/examples/react/getting-started/.gitignore @@ -1,21 +1,23 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# dependencies -/node_modules - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - +# Logs +logs +*.log npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +.parcel-cache +*.local + +# Editor directories and files +.vscode +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/react/getting-started/index.html b/examples/react/getting-started/index.html index 733975c4be..0308154383 100644 --- a/examples/react/getting-started/index.html +++ b/examples/react/getting-started/index.html @@ -6,14 +6,13 @@ name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> - + -->
    - + + diff --git a/examples/react/getting-started/package.json b/examples/react/getting-started/package.json index a16b0793b9..44742fd758 100644 --- a/examples/react/getting-started/package.json +++ b/examples/react/getting-started/package.json @@ -1,22 +1,23 @@ { "name": "example-react-instantsearch-getting-started", - "version": "1.0.7", + "version": "6.38.26", "private": true, "scripts": { - "start": "BABEL_ENV=parcel parcel index.html --port 3000", - "build": "BABEL_ENV=parcel parcel build index.html --public-url ." + "build": "BABEL_ENV=parcel parcel build index.html", + "start": "BABEL_ENV=parcel parcel index.html --port 3000" }, "dependencies": { "algoliasearch": "4.14.3", + "instantsearch.js": "4.56.8", "react": "18.1.0", "react-dom": "18.1.0", - "react-instantsearch-dom": "6.40.4" + "react-instantsearch": "6.47.3" }, "devDependencies": { - "@babel/core": "7.15.5", "@parcel/core": "2.8.0", "@parcel/packager-raw-url": "2.8.0", "@parcel/transformer-webmanifest": "2.8.0", - "parcel": "2.8.0" + "parcel": "2.8.0", + "typescript": "5.1.3" } } diff --git a/examples/react/getting-started/src/App.css b/examples/react/getting-started/src/App.css index 925ccd85ca..e01152732c 100644 --- a/examples/react/getting-started/src/App.css +++ b/examples/react/getting-started/src/App.css @@ -1,3 +1,14 @@ +body, +h1 { + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, + Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; +} + em { background: cyan; font-style: normal; diff --git a/examples/react/getting-started/src/App.js b/examples/react/getting-started/src/App.js deleted file mode 100644 index 47ec80bae5..0000000000 --- a/examples/react/getting-started/src/App.js +++ /dev/null @@ -1,78 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import React from 'react'; -import { - InstantSearch, - Configure, - Hits, - SearchBox, - Panel, - RefinementList, - Pagination, - Highlight, -} from 'react-instantsearch-dom'; -import './App.css'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -function App() { - return ( -
    -
    -

    - Getting started -

    -

    - using{' '} - - React InstantSearch - -

    -
    - -
    - - -
    -
    - - - -
    - -
    - - - -
    - -
    -
    -
    -
    -
    -
    - ); -} - -function Hit(props) { - return ( -
    -

    - -

    -

    - -

    -
    - ); -} - -export default App; diff --git a/examples/react-hooks/getting-started/src/App.tsx b/examples/react/getting-started/src/App.tsx similarity index 93% rename from examples/react-hooks/getting-started/src/App.tsx rename to examples/react/getting-started/src/App.tsx index e3c46105af..0945e92432 100644 --- a/examples/react-hooks/getting-started/src/App.tsx +++ b/examples/react/getting-started/src/App.tsx @@ -9,7 +9,7 @@ import { Pagination, RefinementList, SearchBox, -} from 'react-instantsearch-hooks-web'; +} from 'react-instantsearch'; import { Panel } from './Panel'; @@ -29,8 +29,8 @@ export function App() {
    - -
    -
    -`; diff --git a/examples/react/autocomplete/src/index.js b/examples/react/autocomplete/src/index.js deleted file mode 100644 index 7ee36842af..0000000000 --- a/examples/react/autocomplete/src/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import Mentions from './App-Mentions'; -import App from './App-Multi-Index'; - -ReactDOM.render( - , - document.getElementById('autocomplete-with-multi-indices') -); - -ReactDOM.render(, document.getElementById('autocomplete-mentions')); diff --git a/examples/react/autocomplete/src/style.css b/examples/react/autocomplete/src/style.css deleted file mode 100644 index c1b2bebe8d..0000000000 --- a/examples/react/autocomplete/src/style.css +++ /dev/null @@ -1,115 +0,0 @@ -body { - font-family: Helvetica, sans-serif; - margin: 0; - padding: 1em; - box-sizing: border-box; -} - -.highlighted { - background: yellow; -} - -.autocomplete { - display: flex; - justify-content: space-between; - border: 1px solid red; -} - -.content { - display: flex; - justify-content: space-between; -} - -.block { - padding: 5px 10px; -} - -.title { - border: 1px solid red; - display: flex; - justify-content: space-around; - padding: 5px 10px; -} - -.disabled { - display: none; -} - -.ais-MultiIndex__root { - width: 100%; -} - -.title div { - width: 100%; - padding: 5px 10px; -} - -.react-autosuggest__container { - position: relative; -} - -.react-autosuggest__input { - width: 600px; - height: 30px; - padding: 10px 20px; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border: 1px solid #aaa; - border-radius: 4px; -} - -.react-autosuggest__input:focus { - outline: none; -} - -.react-autosuggest__container--open .react-autosuggest__input { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.react-autosuggest__suggestions-container { - display: none; -} - -.react-autosuggest__container--open .react-autosuggest__suggestions-container { - display: block; - position: absolute; - top: 51px; - width: 640px; - border: 1px solid #aaa; - background-color: #fff; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - z-index: 2; -} - -.react-autosuggest__suggestions-list { - margin: 0; - padding: 0; - list-style-type: none; -} - -.react-autosuggest__suggestion { - cursor: pointer; - padding: 10px 20px; -} - -.react-autosuggest__suggestion--focused { - background-color: #ddd; -} - -.react-autosuggest__section-title { - padding: 10px 0 0 10px; - font-size: 12px; - color: #777; - border-top: 1px dashed #ccc; -} - -.react-autosuggest__section-container:first-child - .react-autosuggest__section-title { - border-top: 0; -} diff --git a/examples/react/default-theme/App.css b/examples/react/default-theme/App.css deleted file mode 100644 index 07cd77aa72..0000000000 --- a/examples/react/default-theme/App.css +++ /dev/null @@ -1,84 +0,0 @@ -* { - box-sizing: border-box; -} - -html, -body { - margin: 0; - padding: 0; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -} - -header.header { - display: flex; - align-items: center; - z-index: 99; - padding-top: 56px; - width: 100%; - padding: 10px; - background: #222f3f; -} - -header.header .is-logo { - float: left; - margin-right: 12px; -} - -header.header .logo { - display: block; -} - -aside, -.results-wrapper { - padding: 10px; -} - -.thank-you { - font-size: 12px; - text-align: center; - margin-top: 20px; -} - -.ais-Panel { - margin-top: 20px; -} - -.content-wrapper { - display: flex; -} - -.results-topbar { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; -} - -.sort-by { - display: flex; - align-items: center; -} - -.sort-by label { - margin-right: 5px; -} - -footer { - margin-top: 25px; -} - -.results-wrapper { - width: 100%; -} - -.no-results { - text-align: center; -} - -.no-results .query { - font-style: italic; -} diff --git a/examples/react/default-theme/App.js b/examples/react/default-theme/App.js deleted file mode 100644 index 48ad38bbc5..0000000000 --- a/examples/react/default-theme/App.js +++ /dev/null @@ -1,171 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import React from 'react'; -import { - InstantSearch, - Hits, - HierarchicalMenu, - RefinementList, - SearchBox, - SortBy, - Stats, - Pagination, - Panel, - ClearRefinements, - RatingMenu, - RangeInput, - Highlight, - Configure, - connectStateResults, -} from 'react-instantsearch-dom'; - -import withURLSync from './URLSync'; -import './App.css'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const App = (props) => ( - - -
    -
    - - -
    - -); - -const Header = () => ( -
    - - React InstantSearch - - -
    -); - -const Facets = () => ( - -); - -const Hit = ({ hit }) => { - const icons = []; - for (let i = 0; i < 5; i++) { - const suffixClassName = i >= hit.rating ? '--empty' : ''; - const suffixXlink = i >= hit.rating ? 'Empty' : ''; - - icons.push( - - ); - } - - return ( -
    -
    -
    - -
    -
    - -
    -
    -
    {icons}
    -
    ${hit.price}
    -
    -
    -
    - ); -}; - -const CustomResults = connectStateResults(({ searchState, searchResults }) => ( -
    -
    - -
    - - -
    -
    - - {searchResults && searchResults.nbHits ? ( -
    - -
    - -
    -
    - ) : ( -
    - No results found matching " - {searchState.query} - " -
    - )} -
    -)); - -export default withURLSync(App); diff --git a/examples/react/default-theme/URLSync.js b/examples/react/default-theme/URLSync.js deleted file mode 100644 index 1eaa103822..0000000000 --- a/examples/react/default-theme/URLSync.js +++ /dev/null @@ -1,56 +0,0 @@ -import qs from 'qs'; -import React, { Component } from 'react'; - -const updateAfter = 700; -const searchStateToURL = (searchState) => - searchState ? `${window.location.pathname}?${qs.stringify(searchState)}` : ''; - -const withURLSync = (App) => - class WithURLSync extends Component { - state = { - searchState: qs.parse(window.location.search.slice(1)), - }; - - componentDidMount() { - window.addEventListener('popstate', this.onPopState); - } - - componentWillUnmount() { - clearTimeout(this.debouncedSetState); - window.removeEventListener('popstate', this.onPopState); - } - - onPopState = ({ state }) => - this.setState({ - searchState: state || {}, - }); - - onSearchStateChange = (searchState) => { - clearTimeout(this.debouncedSetState); - - this.debouncedSetState = setTimeout(() => { - window.history.pushState( - searchState, - null, - searchStateToURL(searchState) - ); - }, updateAfter); - - this.setState({ searchState }); - }; - - render() { - const { searchState } = this.state; - - return ( - - ); - } - }; - -export default withURLSync; diff --git a/examples/react/default-theme/index.html b/examples/react/default-theme/index.html index 4151360b6e..ae3320db18 100644 --- a/examples/react/default-theme/index.html +++ b/examples/react/default-theme/index.html @@ -13,15 +13,18 @@ --> - React InstantSearch - Algolia theme + React InstantSearch
    + + + diff --git a/examples/react/default-theme/index.js b/examples/react/default-theme/index.js deleted file mode 100644 index ae31e41347..0000000000 --- a/examples/react/default-theme/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App'; - -ReactDOM.render(, document.getElementById('root')); diff --git a/examples/react/default-theme/package.json b/examples/react/default-theme/package.json index 454cf8d21c..1fffbd24ba 100644 --- a/examples/react/default-theme/package.json +++ b/examples/react/default-theme/package.json @@ -1,12 +1,23 @@ { "name": "example-react-instantsearch-default-theme", - "version": "15.0.0", + "version": "6.50.0", "private": true, + "scripts": { + "build": "BABEL_ENV=parcel parcel build index.html", + "start": "BABEL_ENV=parcel parcel index.html" + }, "dependencies": { "algoliasearch": "4.14.3", - "qs": "6.9.7", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4" + "instantsearch.js": "4.56.8", + "react": "18.1.0", + "react-dom": "18.1.0", + "react-instantsearch": "6.47.3" + }, + "devDependencies": { + "@parcel/core": "2.8.0", + "@parcel/packager-raw-url": "2.8.0", + "@parcel/transformer-webmanifest": "2.8.0", + "parcel": "2.8.0", + "typescript": "5.1.3" } } diff --git a/examples/react-hooks/default-theme/src/App.css b/examples/react/default-theme/src/App.css similarity index 100% rename from examples/react-hooks/default-theme/src/App.css rename to examples/react/default-theme/src/App.css diff --git a/examples/react-hooks/default-theme/src/App.tsx b/examples/react/default-theme/src/App.tsx similarity index 99% rename from examples/react-hooks/default-theme/src/App.tsx rename to examples/react/default-theme/src/App.tsx index 996e779c6f..8da10a66ee 100644 --- a/examples/react-hooks/default-theme/src/App.tsx +++ b/examples/react/default-theme/src/App.tsx @@ -21,7 +21,7 @@ import { SearchBox, SortBy, ToggleRefinement, -} from 'react-instantsearch-hooks-web'; +} from 'react-instantsearch'; import { Panel, diff --git a/examples/react-hooks/default-theme/src/components/NumericMenu.tsx b/examples/react/default-theme/src/components/NumericMenu.tsx similarity index 98% rename from examples/react-hooks/default-theme/src/components/NumericMenu.tsx rename to examples/react/default-theme/src/components/NumericMenu.tsx index 657791b62a..4a79c8350e 100644 --- a/examples/react-hooks/default-theme/src/components/NumericMenu.tsx +++ b/examples/react/default-theme/src/components/NumericMenu.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useNumericMenu, UseNumericMenuProps } from 'react-instantsearch-hooks'; +import { useNumericMenu, UseNumericMenuProps } from 'react-instantsearch-core'; import { cx } from '../cx'; diff --git a/examples/react-hooks/default-theme/src/components/Panel.tsx b/examples/react/default-theme/src/components/Panel.tsx similarity index 100% rename from examples/react-hooks/default-theme/src/components/Panel.tsx rename to examples/react/default-theme/src/components/Panel.tsx diff --git a/examples/react-hooks/default-theme/src/components/QueryRuleContext.tsx b/examples/react/default-theme/src/components/QueryRuleContext.tsx similarity index 95% rename from examples/react-hooks/default-theme/src/components/QueryRuleContext.tsx rename to examples/react/default-theme/src/components/QueryRuleContext.tsx index cce4c9ec0c..939d252074 100644 --- a/examples/react-hooks/default-theme/src/components/QueryRuleContext.tsx +++ b/examples/react/default-theme/src/components/QueryRuleContext.tsx @@ -1,4 +1,4 @@ -import { useQueryRules, UseQueryRulesProps } from 'react-instantsearch-hooks'; +import { useQueryRules, UseQueryRulesProps } from 'react-instantsearch-core'; export type QueryRuleContextProps = Partial< Pick diff --git a/examples/react-hooks/default-theme/src/components/QueryRuleCustomData.tsx b/examples/react/default-theme/src/components/QueryRuleCustomData.tsx similarity index 97% rename from examples/react-hooks/default-theme/src/components/QueryRuleCustomData.tsx rename to examples/react/default-theme/src/components/QueryRuleCustomData.tsx index 9364db9f48..06689d047a 100644 --- a/examples/react-hooks/default-theme/src/components/QueryRuleCustomData.tsx +++ b/examples/react/default-theme/src/components/QueryRuleCustomData.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useQueryRules, UseQueryRulesProps } from 'react-instantsearch-hooks'; +import { useQueryRules, UseQueryRulesProps } from 'react-instantsearch-core'; import { cx } from '../cx'; diff --git a/examples/react-hooks/default-theme/src/components/Refresh.css b/examples/react/default-theme/src/components/Refresh.css similarity index 100% rename from examples/react-hooks/default-theme/src/components/Refresh.css rename to examples/react/default-theme/src/components/Refresh.css diff --git a/examples/react-hooks/default-theme/src/components/Refresh.tsx b/examples/react/default-theme/src/components/Refresh.tsx similarity index 91% rename from examples/react-hooks/default-theme/src/components/Refresh.tsx rename to examples/react/default-theme/src/components/Refresh.tsx index cda6889874..fc271f8177 100644 --- a/examples/react-hooks/default-theme/src/components/Refresh.tsx +++ b/examples/react/default-theme/src/components/Refresh.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useInstantSearch } from 'react-instantsearch-hooks-web'; +import { useInstantSearch } from 'react-instantsearch'; import './Refresh.css'; export function Refresh() { diff --git a/examples/react-hooks/default-theme/src/components/index.ts b/examples/react/default-theme/src/components/index.ts similarity index 100% rename from examples/react-hooks/default-theme/src/components/index.ts rename to examples/react/default-theme/src/components/index.ts diff --git a/examples/react-hooks/default-theme/src/components/layout/Tabs.css b/examples/react/default-theme/src/components/layout/Tabs.css similarity index 100% rename from examples/react-hooks/default-theme/src/components/layout/Tabs.css rename to examples/react/default-theme/src/components/layout/Tabs.css diff --git a/examples/react-hooks/default-theme/src/components/layout/Tabs.tsx b/examples/react/default-theme/src/components/layout/Tabs.tsx similarity index 100% rename from examples/react-hooks/default-theme/src/components/layout/Tabs.tsx rename to examples/react/default-theme/src/components/layout/Tabs.tsx diff --git a/examples/react-hooks/default-theme/src/components/layout/index.tsx b/examples/react/default-theme/src/components/layout/index.tsx similarity index 100% rename from examples/react-hooks/default-theme/src/components/layout/index.tsx rename to examples/react/default-theme/src/components/layout/index.tsx diff --git a/examples/react-hooks/default-theme/src/cx.ts b/examples/react/default-theme/src/cx.ts similarity index 100% rename from examples/react-hooks/default-theme/src/cx.ts rename to examples/react/default-theme/src/cx.ts diff --git a/examples/react-hooks/default-theme/src/env.js b/examples/react/default-theme/src/env.js similarity index 100% rename from examples/react-hooks/default-theme/src/env.js rename to examples/react/default-theme/src/env.js diff --git a/examples/react-hooks/default-theme/src/index.tsx b/examples/react/default-theme/src/index.tsx similarity index 100% rename from examples/react-hooks/default-theme/src/index.tsx rename to examples/react/default-theme/src/index.tsx diff --git a/examples/react-hooks/default-theme/src/isModifierClick.ts b/examples/react/default-theme/src/isModifierClick.ts similarity index 100% rename from examples/react-hooks/default-theme/src/isModifierClick.ts rename to examples/react/default-theme/src/isModifierClick.ts diff --git a/examples/react/e-commerce/.editorconfig b/examples/react/e-commerce/.editorconfig deleted file mode 100644 index 9d08a1a828..0000000000 --- a/examples/react/e-commerce/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true diff --git a/examples/react/e-commerce/.gitignore b/examples/react/e-commerce/.gitignore deleted file mode 100644 index d30f40ef44..0000000000 --- a/examples/react/e-commerce/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# dependencies -/node_modules - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/examples/react/e-commerce/.prettierrc b/examples/react/e-commerce/.prettierrc deleted file mode 100644 index 6e93c0d52f..0000000000 --- a/examples/react/e-commerce/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "proseWrap": "never", - "singleQuote": true, - "trailingComma": "es5" -} diff --git a/examples/react/e-commerce/AlgoliaSvg.js b/examples/react/e-commerce/AlgoliaSvg.js deleted file mode 100644 index 1f03a35afe..0000000000 --- a/examples/react/e-commerce/AlgoliaSvg.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -const AlgoliaSvg = () => ( - - - - - -); - -export default AlgoliaSvg; diff --git a/examples/react/e-commerce/App.js b/examples/react/e-commerce/App.js deleted file mode 100644 index 4c9de7761d..0000000000 --- a/examples/react/e-commerce/App.js +++ /dev/null @@ -1,369 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import React, { useRef } from 'react'; -import { - InstantSearch, - HierarchicalMenu, - RefinementList, - SortBy, - Pagination, - ClearRefinements, - Highlight, - Hits, - HitsPerPage, - Panel, - Configure, - SearchBox, - Snippet, - ToggleRefinement, -} from 'react-instantsearch-dom'; - -import AlgoliaSvg from './AlgoliaSvg'; -import withURLSync from './URLSync'; -import { formatNumber } from './utils'; -import { - ClearFiltersMobile, - PriceSlider, - NoResults, - Ratings, - ResultsNumberMobile, - SaveFiltersMobile, -} from './widgets'; -import './Theme.css'; -import './App.css'; -import './App.mobile.css'; -import './widgets/Pagination.css'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const Hit = ({ hit }) => ( -
    -
    - {hit.name} -
    - -
    -

    {hit.categories[0]}

    -

    - -

    -

    - -

    - -
    -

    - ${' '} - {formatNumber(hit.price)}{' '} - - - - {' '} - {hit.rating} - -

    -
    -
    -
    -); - -const App = (props) => { - const containerRef = useRef(null); - const headerRef = useRef(null); - - function openFilters() { - document.body.classList.add('filtering'); - window.scrollTo(0, 0); - window.addEventListener('keyup', onKeyUp); - window.addEventListener('click', onClick); - } - - function closeFilters() { - document.body.classList.remove('filtering'); - containerRef.current.scrollIntoView(); - window.removeEventListener('keyup', onKeyUp); - window.removeEventListener('click', onClick); - } - - function onKeyUp(event) { - if (event.key !== 'Escape') { - return; - } - - closeFilters(); - } - - function onClick(event) { - if (event.target !== headerRef.current) { - return; - } - - closeFilters(); - } - - return ( - -
    -

    - -

    - -

    Stop looking for an item — find it.

    - - - - - - - - } - /> -
    - - - -
    -
    -
    -
    -

    Filters

    - -
    - - - - - - - - Clear filters - - ), - }} - /> -
    - -
    - -
    -
    - -
    - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    - -
    - -
    - -
    -
    -
    - -
    -
    - - - -
    - - - - -
    - - - - - - ), - next: ( - - - - - - ), - }} - /> -
    -
    -
    - - -
    - ); -}; - -export default withURLSync(App); diff --git a/examples/react-hooks/e-commerce/App.tsx b/examples/react/e-commerce/App.tsx similarity index 99% rename from examples/react-hooks/e-commerce/App.tsx rename to examples/react/e-commerce/App.tsx index ff287b8b7d..0d7731d495 100644 --- a/examples/react-hooks/e-commerce/App.tsx +++ b/examples/react/e-commerce/App.tsx @@ -13,7 +13,7 @@ import { ToggleRefinement, Highlight, Snippet, -} from 'react-instantsearch-hooks-web'; +} from 'react-instantsearch'; import { AlgoliaSvg, diff --git a/examples/react/e-commerce/URLSync.js b/examples/react/e-commerce/URLSync.js deleted file mode 100644 index 9c665209cf..0000000000 --- a/examples/react/e-commerce/URLSync.js +++ /dev/null @@ -1,263 +0,0 @@ -/* eslint-disable complexity */ - -import qs from 'qs'; -import React, { Component } from 'react'; - -const updateAfter = 700; - -const routeStateDefaultValues = { - query: '', - page: '1', - brands: undefined, - category: '', - rating: '', - price: '', - free_shipping: 'false', - sortBy: 'instant_search', - hitsPerPage: '20', -}; - -const encodedCategories = { - Cameras: 'Cameras & Camcorders', - Cars: 'Car Electronics & GPS', - Phones: 'Cell Phones', - TV: 'TV & Home Theater', -}; - -const decodedCategories = Object.keys(encodedCategories).reduce((acc, key) => { - const newKey = encodedCategories[key]; - const newValue = key; - - return { - ...acc, - [newKey]: newValue, - }; -}, {}); - -// Returns a slug from the category name. -// Spaces are replaced by "+" to make -// the URL easier to read and other -// characters are encoded. -function getCategorySlug(name) { - const encodedName = decodedCategories[name] || name; - - return encodedName - .replace(/ > /g, '/') - .split(' ') - .map(encodeURIComponent) - .join('+'); -} - -// Returns a name from the category slug. -// The "+" are replaced by spaces and other -// characters are decoded. -function getCategoryName(slug) { - const decodedSlug = encodedCategories[slug] || slug; - - return decodedSlug - .split('+') - .map(decodeURIComponent) - .join(' ') - .replace(/\//g, ' > '); -} - -const searchStateToURL = (searchState) => { - const routeState = { - query: searchState.query, - page: String(searchState.page), - brands: searchState.refinementList && searchState.refinementList.brand, - category: - searchState.hierarchicalMenu && - searchState.hierarchicalMenu['hierarchicalCategories.lvl0'], - rating: - searchState.range && - searchState.range.rating && - String(searchState.range.rating.min), - price: - searchState.range && - searchState.range.price && - `${searchState.range.price.min || ''}:${ - searchState.range.price.max || '' - }`, - free_shipping: - (searchState.toggle && String(searchState.toggle.free_shipping)) || - undefined, - sortBy: searchState.sortBy, - hitsPerPage: - (searchState.hitsPerPage && String(searchState.hitsPerPage)) || undefined, - }; - - const { protocol, hostname, port = '', pathname, hash } = location; - const portWithPrefix = port === '' ? '' : `:${port}`; - const urlParts = location.href.match(/^(.*?)\/search/); - const baseUrl = - (urlParts && urlParts[0]) || - `${protocol}//${hostname}${portWithPrefix}${pathname}search`; - - const categoryPath = routeState.category - ? `${getCategorySlug(routeState.category)}/` - : ''; - const queryParameters = {}; - - if (routeState.query && routeState.query !== routeStateDefaultValues.query) { - queryParameters.query = encodeURIComponent(routeState.query); - } - if (routeState.page && routeState.page !== routeStateDefaultValues.page) { - queryParameters.page = routeState.page; - } - if ( - routeState.brands && - routeState.brands !== routeStateDefaultValues.brands - ) { - queryParameters.brands = routeState.brands.map(encodeURIComponent); - } - if ( - routeState.rating && - routeState.rating !== routeStateDefaultValues.rating - ) { - queryParameters.rating = routeState.rating; - } - if (routeState.price && routeState.price !== routeStateDefaultValues.price) { - queryParameters.price = routeState.price; - } - if ( - routeState.free_shipping && - routeState.free_shipping !== routeStateDefaultValues.free_shipping - ) { - queryParameters.free_shipping = routeState.free_shipping; - } - if ( - routeState.sortBy && - routeState.sortBy !== routeStateDefaultValues.sortBy - ) { - queryParameters.sortBy = routeState.sortBy; - } - if ( - routeState.hitsPerPage && - routeState.hitsPerPage !== routeStateDefaultValues.hitsPerPage - ) { - queryParameters.hitsPerPage = routeState.hitsPerPage; - } - - const queryString = qs.stringify(queryParameters, { - addQueryPrefix: true, - arrayFormat: 'repeat', - }); - - return `${baseUrl}/${categoryPath}${queryString}${hash}`; -}; - -const urlToSearchState = (location) => { - const pathnameMatches = location.pathname.match(/search\/(.*?)\/?$/); - const category = getCategoryName( - (pathnameMatches && pathnameMatches[1]) || '' - ); - const queryParameters = qs.parse(location.search.slice(1)); - const { - query = '', - page = 1, - brands = [], - price, - free_shipping, - hitsPerPage, - sortBy, - rating, - } = queryParameters; - // `qs` does not return an array when there's a single value. - const allBrands = Array.isArray(brands) ? brands : [brands].filter(Boolean); - - const searchState = { range: {} }; - - if (query) { - searchState.query = decodeURIComponent(query); - } - if (page) { - searchState.page = page; - } - if (category) { - searchState.hierarchicalMenu = { - 'hierarchicalCategories.lvl0': category, - }; - } - if (allBrands.length) { - searchState.refinementList = { - brand: allBrands.map(decodeURIComponent), - }; - } - if (rating) { - searchState.range.rating = { - min: Number(rating), - }; - } - if (price) { - const [min, max = undefined] = price.split(':'); - searchState.range.price = { - min: min || undefined, - max: max || undefined, - }; - } - if (free_shipping) { - searchState.toggle = { - free_shipping: Boolean(free_shipping), - }; - } - if (sortBy) { - searchState.sortBy = sortBy; - } - - if (hitsPerPage) { - searchState.hitsPerPage = hitsPerPage; - } - - return searchState; -}; - -const withURLSync = (App) => - class WithURLSync extends Component { - state = { - searchState: urlToSearchState(window.location), - }; - - componentDidMount() { - window.addEventListener('popstate', this.onPopState); - } - - componentWillUnmount() { - clearTimeout(this.debouncedSetState); - window.removeEventListener('popstate', this.onPopState); - } - - onPopState = ({ state }) => - this.setState({ - searchState: state || {}, - }); - - onSearchStateChange = (searchState) => { - clearTimeout(this.debouncedSetState); - - this.debouncedSetState = setTimeout(() => { - window.history.pushState( - searchState, - null, - searchStateToURL(searchState) - ); - }, updateAfter); - - this.setState({ searchState }); - }; - - render() { - const { searchState } = this.state; - - return ( - - ); - } - }; - -export default withURLSync; diff --git a/examples/react-hooks/e-commerce/components/AlgoliaSvg.tsx b/examples/react/e-commerce/components/AlgoliaSvg.tsx similarity index 100% rename from examples/react-hooks/e-commerce/components/AlgoliaSvg.tsx rename to examples/react/e-commerce/components/AlgoliaSvg.tsx diff --git a/examples/react-hooks/e-commerce/components/ClearFilters.tsx b/examples/react/e-commerce/components/ClearFilters.tsx similarity index 94% rename from examples/react-hooks/e-commerce/components/ClearFilters.tsx rename to examples/react/e-commerce/components/ClearFilters.tsx index 4723f42e0d..2e002539c8 100644 --- a/examples/react-hooks/e-commerce/components/ClearFilters.tsx +++ b/examples/react/e-commerce/components/ClearFilters.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useClearRefinements } from 'react-instantsearch-hooks-web'; +import { useClearRefinements } from 'react-instantsearch'; export function ClearFilters() { const { refine, canRefine } = useClearRefinements(); diff --git a/examples/react-hooks/e-commerce/components/ClearFiltersMobile.tsx b/examples/react/e-commerce/components/ClearFiltersMobile.tsx similarity index 87% rename from examples/react-hooks/e-commerce/components/ClearFiltersMobile.tsx rename to examples/react/e-commerce/components/ClearFiltersMobile.tsx index ead29ce6f5..849730d197 100644 --- a/examples/react-hooks/e-commerce/components/ClearFiltersMobile.tsx +++ b/examples/react/e-commerce/components/ClearFiltersMobile.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useClearRefinements } from 'react-instantsearch-hooks-web'; +import { useClearRefinements } from 'react-instantsearch'; export function ClearFiltersMobile({ containerRef, diff --git a/examples/react-hooks/e-commerce/components/NoResults.tsx b/examples/react/e-commerce/components/NoResults.tsx similarity index 98% rename from examples/react-hooks/e-commerce/components/NoResults.tsx rename to examples/react/e-commerce/components/NoResults.tsx index d8bc4fcdb3..d925812e79 100644 --- a/examples/react-hooks/e-commerce/components/NoResults.tsx +++ b/examples/react/e-commerce/components/NoResults.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useInstantSearch } from 'react-instantsearch-hooks-web'; +import { useInstantSearch } from 'react-instantsearch'; import { ClearFilters } from './ClearFilters'; diff --git a/examples/react-hooks/e-commerce/components/NoResultsBoundary.tsx b/examples/react/e-commerce/components/NoResultsBoundary.tsx similarity index 88% rename from examples/react-hooks/e-commerce/components/NoResultsBoundary.tsx rename to examples/react/e-commerce/components/NoResultsBoundary.tsx index 0cc0db451c..5ca22f2c4b 100644 --- a/examples/react-hooks/e-commerce/components/NoResultsBoundary.tsx +++ b/examples/react/e-commerce/components/NoResultsBoundary.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useInstantSearch } from 'react-instantsearch-hooks-web'; +import { useInstantSearch } from 'react-instantsearch'; export function NoResultsBoundary({ children, diff --git a/examples/react-hooks/e-commerce/components/Pagination.css b/examples/react/e-commerce/components/Pagination.css similarity index 100% rename from examples/react-hooks/e-commerce/components/Pagination.css rename to examples/react/e-commerce/components/Pagination.css diff --git a/examples/react-hooks/e-commerce/components/Panel.tsx b/examples/react/e-commerce/components/Panel.tsx similarity index 100% rename from examples/react-hooks/e-commerce/components/Panel.tsx rename to examples/react/e-commerce/components/Panel.tsx diff --git a/examples/react-hooks/e-commerce/components/PriceSlider.css b/examples/react/e-commerce/components/PriceSlider.css similarity index 100% rename from examples/react-hooks/e-commerce/components/PriceSlider.css rename to examples/react/e-commerce/components/PriceSlider.css diff --git a/examples/react-hooks/e-commerce/components/PriceSlider.tsx b/examples/react/e-commerce/components/PriceSlider.tsx similarity index 98% rename from examples/react-hooks/e-commerce/components/PriceSlider.tsx rename to examples/react/e-commerce/components/PriceSlider.tsx index 2e2dac558f..470c54f66d 100644 --- a/examples/react-hooks/e-commerce/components/PriceSlider.tsx +++ b/examples/react/e-commerce/components/PriceSlider.tsx @@ -11,7 +11,7 @@ import { Ticks, GetHandleProps, } from 'react-compound-slider'; -import { useRange } from 'react-instantsearch-hooks-web'; +import { useRange } from 'react-instantsearch'; import './PriceSlider.css'; import { formatNumber } from '../utils'; diff --git a/examples/react-hooks/e-commerce/components/Ratings.tsx b/examples/react/e-commerce/components/Ratings.tsx similarity index 97% rename from examples/react-hooks/e-commerce/components/Ratings.tsx rename to examples/react/e-commerce/components/Ratings.tsx index c07a3bc32b..515e1f3ed9 100644 --- a/examples/react-hooks/e-commerce/components/Ratings.tsx +++ b/examples/react/e-commerce/components/Ratings.tsx @@ -5,7 +5,7 @@ import { RatingMenuWidgetDescription, } from 'instantsearch.js/es/connectors/rating-menu/connectRatingMenu'; import React from 'react'; -import { useConnector } from 'react-instantsearch-hooks-web'; +import { useConnector } from 'react-instantsearch'; export function Ratings({ attribute }: { attribute: string }) { const { refine, items, createURL } = useConnector< diff --git a/examples/react-hooks/e-commerce/components/ResultsNumberMobile.tsx b/examples/react/e-commerce/components/ResultsNumberMobile.tsx similarity index 80% rename from examples/react-hooks/e-commerce/components/ResultsNumberMobile.tsx rename to examples/react/e-commerce/components/ResultsNumberMobile.tsx index ecd21d0b9d..f6e54f5363 100644 --- a/examples/react-hooks/e-commerce/components/ResultsNumberMobile.tsx +++ b/examples/react/e-commerce/components/ResultsNumberMobile.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useInstantSearch } from 'react-instantsearch-hooks-web'; +import { useInstantSearch } from 'react-instantsearch'; import { formatNumber } from '../utils'; diff --git a/examples/react-hooks/e-commerce/components/SaveFiltersMobile.tsx b/examples/react/e-commerce/components/SaveFiltersMobile.tsx similarity index 83% rename from examples/react-hooks/e-commerce/components/SaveFiltersMobile.tsx rename to examples/react/e-commerce/components/SaveFiltersMobile.tsx index d543f8b8b0..69b565bc96 100644 --- a/examples/react-hooks/e-commerce/components/SaveFiltersMobile.tsx +++ b/examples/react/e-commerce/components/SaveFiltersMobile.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useInstantSearch } from 'react-instantsearch-hooks-web'; +import { useInstantSearch } from 'react-instantsearch'; import { formatNumber } from '../utils'; diff --git a/examples/react-hooks/e-commerce/components/ScrollTo.tsx b/examples/react/e-commerce/components/ScrollTo.tsx similarity index 93% rename from examples/react-hooks/e-commerce/components/ScrollTo.tsx rename to examples/react/e-commerce/components/ScrollTo.tsx index ed2a7e8f82..b715b6010e 100644 --- a/examples/react-hooks/e-commerce/components/ScrollTo.tsx +++ b/examples/react/e-commerce/components/ScrollTo.tsx @@ -1,6 +1,6 @@ import { Middleware } from 'instantsearch.js'; import React, { useEffect, useRef } from 'react'; -import { useInstantSearch } from 'react-instantsearch-hooks-web'; +import { useInstantSearch } from 'react-instantsearch'; export function ScrollTo({ children }: { children: React.ReactNode }) { const { addMiddlewares } = useInstantSearch(); diff --git a/examples/react-hooks/e-commerce/components/index.ts b/examples/react/e-commerce/components/index.ts similarity index 100% rename from examples/react-hooks/e-commerce/components/index.ts rename to examples/react/e-commerce/components/index.ts diff --git a/examples/react-hooks/e-commerce/cx.ts b/examples/react/e-commerce/cx.ts similarity index 100% rename from examples/react-hooks/e-commerce/cx.ts rename to examples/react/e-commerce/cx.ts diff --git a/examples/react-hooks/e-commerce/env.js b/examples/react/e-commerce/env.js similarity index 100% rename from examples/react-hooks/e-commerce/env.js rename to examples/react/e-commerce/env.js diff --git a/examples/react/e-commerce/index.html b/examples/react/e-commerce/index.html index 6efaf5f290..4596053af8 100644 --- a/examples/react/e-commerce/index.html +++ b/examples/react/e-commerce/index.html @@ -8,9 +8,12 @@ /> - + + + + - E-commerce demo | Algolia + + React InstantSearch - e-commerce - +
    + + + diff --git a/examples/react/e-commerce/index.js b/examples/react/e-commerce/index.js deleted file mode 100644 index ae31e41347..0000000000 --- a/examples/react/e-commerce/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App'; - -ReactDOM.render(, document.getElementById('root')); diff --git a/examples/react-hooks/e-commerce/index.tsx b/examples/react/e-commerce/index.tsx similarity index 100% rename from examples/react-hooks/e-commerce/index.tsx rename to examples/react/e-commerce/index.tsx diff --git a/examples/react/e-commerce/package.json b/examples/react/e-commerce/package.json index 274bec4721..d16ab55775 100644 --- a/examples/react/e-commerce/package.json +++ b/examples/react/e-commerce/package.json @@ -1,14 +1,26 @@ { "name": "example-react-instantsearch-e-commerce", - "version": "15.0.0", + "version": "6.38.26", "private": true, + "scripts": { + "build": "BABEL_ENV=parcel parcel build index.html", + "website:examples": "BABEL_ENV=parcel parcel build index.html --public-url . --dist-dir=../../../website/examples/react/e-commerce", + "start": "BABEL_ENV=parcel parcel index.html" + }, + "browserslist": "firefox 68, chrome 78, IE 11", "dependencies": { "algoliasearch": "4.14.3", - "classnames": "2.2.6", - "qs": "6.9.7", - "react": "17.0.2", - "react-compound-slider": "2.2.0", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4" + "instantsearch.js": "4.56.8", + "react": "18.1.0", + "react-compound-slider": "3.4.0", + "react-dom": "18.1.0", + "react-instantsearch": "6.47.3" + }, + "devDependencies": { + "@parcel/core": "2.8.0", + "@parcel/packager-raw-url": "2.8.0", + "@parcel/transformer-webmanifest": "2.8.0", + "parcel": "2.8.0", + "typescript": "5.1.3" } } diff --git a/examples/react-hooks/e-commerce/routing.ts b/examples/react/e-commerce/routing.ts similarity index 100% rename from examples/react-hooks/e-commerce/routing.ts rename to examples/react/e-commerce/routing.ts diff --git a/examples/react/e-commerce/utils.js b/examples/react/e-commerce/utils.js deleted file mode 100644 index fd3146d6af..0000000000 --- a/examples/react/e-commerce/utils.js +++ /dev/null @@ -1,3 +0,0 @@ -export function formatNumber(value) { - return Number(value).toLocaleString(); -} diff --git a/examples/react-hooks/e-commerce/utils/index.ts b/examples/react/e-commerce/utils/index.ts similarity index 100% rename from examples/react-hooks/e-commerce/utils/index.ts rename to examples/react/e-commerce/utils/index.ts diff --git a/examples/react/e-commerce/widgets/ClearFiltersMobile.js b/examples/react/e-commerce/widgets/ClearFiltersMobile.js deleted file mode 100644 index 8efb972c4d..0000000000 --- a/examples/react/e-commerce/widgets/ClearFiltersMobile.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { connectCurrentRefinements } from 'react-instantsearch-dom'; - -const ClearFiltersMobile = ({ items, refine, containerRef }) => { - function onClick() { - refine(items); - document.body.classList.remove('filtering'); - containerRef.current.scrollIntoView(); - } - - return ( -
    - -
    - ); -}; - -export default connectCurrentRefinements(ClearFiltersMobile); diff --git a/examples/react/e-commerce/widgets/NoResults.js b/examples/react/e-commerce/widgets/NoResults.js deleted file mode 100644 index 2ca721dc2f..0000000000 --- a/examples/react/e-commerce/widgets/NoResults.js +++ /dev/null @@ -1,125 +0,0 @@ -import React from 'react'; -import { connectStateResults, ClearRefinements } from 'react-instantsearch-dom'; - -const NoResults = ({ searchResults }) => { - if (!searchResults || searchResults.nbHits > 0) { - return null; - } - - const hasRefinements = searchResults.getRefinements().length > 0; - const description = hasRefinements - ? 'Try to reset your applied filters.' - : 'Please try another query.'; - - return ( -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - Sorry, we can't find any matches to your query! -

    -

    {description}

    - - - - - - - - - Clear filters -
    - ), - }} - /> -

tpk(nveR-6%^x_YbKnlw~;!N9WEvILh? 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

using{' '} - - React InstantSearch Hooks + + React InstantSearch

diff --git a/examples/react-hooks/getting-started/src/Panel.tsx b/examples/react/getting-started/src/Panel.tsx similarity index 100% rename from examples/react-hooks/getting-started/src/Panel.tsx rename to examples/react/getting-started/src/Panel.tsx diff --git a/examples/react-hooks/getting-started/src/env.js b/examples/react/getting-started/src/env.js similarity index 100% rename from examples/react-hooks/getting-started/src/env.js rename to examples/react/getting-started/src/env.js diff --git a/examples/react/getting-started/src/index.css b/examples/react/getting-started/src/index.css deleted file mode 100644 index 12f1b9911a..0000000000 --- a/examples/react/getting-started/src/index.css +++ /dev/null @@ -1,10 +0,0 @@ -body, -h1 { - margin: 0; - padding: 0; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -} diff --git a/examples/react/getting-started/src/index.js b/examples/react/getting-started/src/index.js deleted file mode 100644 index 6f7ec3e532..0000000000 --- a/examples/react/getting-started/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; - -import './index.css'; -import App from './App'; - -createRoot(document.getElementById('root')).render(); diff --git a/examples/react-hooks/getting-started/src/index.tsx b/examples/react/getting-started/src/index.tsx similarity index 100% rename from examples/react-hooks/getting-started/src/index.tsx rename to examples/react/getting-started/src/index.tsx diff --git a/examples/react/media/App.css b/examples/react/media/App.css deleted file mode 100644 index 1ab20f8543..0000000000 --- a/examples/react/media/App.css +++ /dev/null @@ -1,311 +0,0 @@ -html, -body { - margin: 0; - padding: 0; - font-size: 14px; -} - -body { - min-height: 100%; - font-family: Roboto; - background: #f1f1f1; -} - -a { - color: #333333; -} - -article hr { - margin: 10px 14px; - float: none; - width: initial; -} - -.is-logo { - float: left; - margin-left: 18px; -} - -.logo { - margin-left: 15px; - font-size: 30px; - font-weight: bold; - float: left; -} - -.logo:hover { - text-decoration: none; -} - -.logo i { - margin-left: 5px; - color: #e91d00; -} - -header { - background: #ffffff; - padding: 10px; - border-bottom: 1px solid #e8e8e8; - position: relative; - left: 0; - right: 0; - z-index: 2; -} - -.searchbox-container { - margin-left: 240px; - max-width: 400px; -} - -.searchbox-container .input-group { - margin-top: 5px; -} - -.searchbox-container .form-control:focus { - outline: none; - box-shadow: none; -} - -.searchbox-container button { - padding-left: 20px; - padding-right: 20px; - background: #f8f8f8; - border-radius: 0; -} - -section { - background: #f1f1f1; - min-height: 100%; - z-index: 1; -} - -section aside { - position: absolute; - top: 67px; - left: 0; - bottom: 0; - width: 230px; - background: #ffffff; - border-right: 1px solid #e8e8e8; -} - -.nav { - margin: 0 20px; -} - -.nav li a { - display: block; - padding: 2px 10px; - margin: 10px 0; -} - -.nav li a:hover { - color: #ffffff; - background: #333333; -} - -.nav li.separator { - height: 1px; - background: #e8e8e8; -} - -section aside h5 { - color: #ce1312; - margin-left: 30px; - text-transform: uppercase; - font-size: 10px; - margin-top: 20px; -} - -section aside .badge { - font-size: 0.8em; - background: #bbbbbb; - position: relative; - top: 1px; -} - -#genres .item { - display: block; - cursor: pointer; - padding: 4px 4px 4px 8px; - font-weight: normal; - font-size: 0.9em; - margin: 0 0 -1px; -} - -#genres .item:hover { - background: #333333; - color: #ffffff; - text-decoration: none; -} - -#genres .active .item { - border: 1px solid #ce1312; - margin-top: -1px; -} - -#genres .active .badge { - background: #333333; -} - -section article { - margin-top: 7px; - margin-bottom: 10px; - margin-left: 237px; - margin-right: 10px; - padding: 10px 0; - max-width: 100%; - background: #ffffff; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.ais-Stats-text { - padding-right: 14px; - font-size: 0.8em; - line-height: 24px; -} - -#hits { - padding: 0 15px; -} - -.ais-Pagination { - box-shadow: none; - border: none; - border-radius: initial; - padding: 0px; -} - -.ais-Pagination-item { - display: inline-block; - padding: 0px; -} - -.ais-Pagination-item + .ais-Pagination-item { - margin-left: 4px; -} - -.ais-Pagination-item--disabled { - display: none; -} - -.ais-Pagination-link { - display: block; - background: #f8f8f8; - padding: 3px 8px; - color: #333333; -} - -.ais-Pagination-item--selected .ais-Pagination-link { - border-color: #b5b5b5; - background: #e8e8e8; -} - -.ais-Pagination-link:hover { - text-decoration: none; - border-color: #cfcfcf; - background: #ebebeb; - color: #333333; -} - -.ais-Hits-list { - margin: 0; -} - -.ais-Hits-item { - margin: 0; - padding: 0; - border: none; - box-shadow: none; - width: 100%; -} - -.hit { - margin-bottom: 10px; - height: 130px; - border: 1px solid #f3f3f3; -} - -.hit em { - font-style: normal; - background: #ffffd4; - text-decoration: underline; -} - -.hit .media-object { - height: 130px; - width: 130px; - overflow: hidden; - background-size: contain; - background-repeat: no-repeat; - background-position: center center; -} - -.hit .media-heading { - color: #167ac6; - font-weight: normal; - font-size: 18px; -} - -.hit .media-body { - padding: 10px; -} - -.thank-you { - font-size: 0.8em; - margin-top: 18px; - margin-left: 30px; -} - -.thank-you a { - color: #ce1312; -} - -.stars { - margin-left: 5px; -} - -.star { - /* item star */ - display: inline-block; - width: 1em; - height: 1em; -} - -.star:before { - content: '\2605'; - color: #fbae00; -} - -.star__empty { - /* empty star */ - display: inline-block; - width: 1em; - height: 1em; -} - -.star__empty:before { - content: '\2606'; - color: #fbae00; -} - -.genre, -.year { - margin: 12px 0; -} - -.genre .badge + .badge { - margin-left: 4px; -} - -.year { - font-weight: bold; -} - -.genre .badge { - background: #bbbbbb; -} - -.ais-RatingMenu { - margin: 0 24px 0 28px; -} diff --git a/examples/react/media/App.js b/examples/react/media/App.js deleted file mode 100644 index 9f478f78ba..0000000000 --- a/examples/react/media/App.js +++ /dev/null @@ -1,198 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import React from 'react'; -import * as ReactInstantSearch from 'react-instantsearch-dom'; - -import withURLSync from './URLSync'; -import './App.css'; - -// Due to a bug in Webpack, we destructure here instead of using named imports -const { - InstantSearch, - Hits, - Stats, - Pagination, - RatingMenu, - Highlight, - Configure, - connectSearchBox, - connectRefinementList, -} = ReactInstantSearch; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const App = (props) => ( - - -
-
- - -
- -); - -const Header = () => ( -
- - React InstantSearch - - - You - - - -
-); - -const SearchBox = connectSearchBox(({ currentRefinement, refine }) => ( -
-
- refine(e.target.value)} - autoComplete="off" - className="form-control" - /> - - - -
-
-)); - -const Facets = () => ( - -); - -const Panel = ({ title, children, id }) => ( -
-
- {title} -
- {children} -
-); - -const Star = ({ active }) => ( - -); -const Stars = ({ rating }) => { - const stars = []; - for (let i = 1; i <= 5; ++i) { - stars.push(i <= rating); - } - return ( - - {stars.map((active, idx) => ( - - ))} - - ); -}; -const Genre = ({ name }) => {name}; -const Genres = ({ genres }) => ( -

- {genres.map((genre, idx) => ( - - ))} -

-); - -const Hit = (hit) => { - const { image, rating, year, genre } = hit.hit; - return ( -
-
-
-
-
-

- - -

-

{year}

- -
-
- ); -}; - -const Results = connectSearchBox(() => ( -
-
- -
-
-
- -
- -
-)); - -const RefinementListLinks = connectRefinementList( - ({ items, refine, createURL }) => { - const hitComponents = items.map((item) => ( - - )); - - return
{hitComponents}
; - } -); - -export default withURLSync(App); diff --git a/examples/react/media/URLSync.js b/examples/react/media/URLSync.js deleted file mode 100644 index 1eaa103822..0000000000 --- a/examples/react/media/URLSync.js +++ /dev/null @@ -1,56 +0,0 @@ -import qs from 'qs'; -import React, { Component } from 'react'; - -const updateAfter = 700; -const searchStateToURL = (searchState) => - searchState ? `${window.location.pathname}?${qs.stringify(searchState)}` : ''; - -const withURLSync = (App) => - class WithURLSync extends Component { - state = { - searchState: qs.parse(window.location.search.slice(1)), - }; - - componentDidMount() { - window.addEventListener('popstate', this.onPopState); - } - - componentWillUnmount() { - clearTimeout(this.debouncedSetState); - window.removeEventListener('popstate', this.onPopState); - } - - onPopState = ({ state }) => - this.setState({ - searchState: state || {}, - }); - - onSearchStateChange = (searchState) => { - clearTimeout(this.debouncedSetState); - - this.debouncedSetState = setTimeout(() => { - window.history.pushState( - searchState, - null, - searchStateToURL(searchState) - ); - }, updateAfter); - - this.setState({ searchState }); - }; - - render() { - const { searchState } = this.state; - - return ( - - ); - } - }; - -export default withURLSync; diff --git a/examples/react/media/index.html b/examples/react/media/index.html deleted file mode 100644 index fa5e8ca306..0000000000 --- a/examples/react/media/index.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - React InstantSearch - Media - - - - - -
- - diff --git a/examples/react/media/index.js b/examples/react/media/index.js deleted file mode 100644 index ae31e41347..0000000000 --- a/examples/react/media/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App'; - -ReactDOM.render(, document.getElementById('root')); diff --git a/examples/react/media/package.json b/examples/react/media/package.json deleted file mode 100644 index 6d6f235459..0000000000 --- a/examples/react/media/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "example-react-instantsearch-media", - "version": "15.0.0", - "private": true, - "dependencies": { - "algoliasearch": "4.14.3", - "qs": "6.9.7", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4" - } -} diff --git a/examples/react/multi-index/README.md b/examples/react/multi-index/README.md deleted file mode 100644 index 36fdda4a8d..0000000000 --- a/examples/react/multi-index/README.md +++ /dev/null @@ -1,18 +0,0 @@ -This example shows how to target multiple indices using `react-instantsearch`. - -[![Edit multi-index](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/multi-index) - -## Clone the example - -```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/multi-index -``` - -## Start the example - -```sh -yarn install --no-lockfile -yarn start -``` - -Read more about `react-instantsearch` [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). diff --git a/examples/react/multi-index/index.html b/examples/react/multi-index/index.html deleted file mode 100644 index b6358d3fe4..0000000000 --- a/examples/react/multi-index/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - multi-index search with react-instantsearch - - -
- - - - diff --git a/examples/react/multi-index/package.json b/examples/react/multi-index/package.json deleted file mode 100644 index ab96e1c0a9..0000000000 --- a/examples/react/multi-index/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "example-react-instantsearch-multi-index", - "version": "17.0.0", - "private": true, - "license": "MIT", - "scripts": { - "start": "BABEL_ENV=parcel parcel index.html --port 3000", - "build": "BABEL_ENV=parcel parcel build index.html --public-url .", - "test": "jest --ci" - }, - "devDependencies": { - "@babel/core": "7.15.5", - "@parcel/core": "2.8.0", - "@parcel/packager-raw-url": "2.8.0", - "@parcel/transformer-webmanifest": "2.8.0", - "parcel": "2.8.0", - "react-test-renderer": "17.0.2" - }, - "jest": { - "transform": { - "^.+\\.(jsx?|tsx?)$": [ - "babel-jest", - { - "rootMode": "upward" - } - ] - } - }, - "dependencies": { - "algoliasearch": "4.14.3", - "instantsearch.css": "8.0.0", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ] -} diff --git a/examples/react/multi-index/src/App.js b/examples/react/multi-index/src/App.js deleted file mode 100644 index d2c96a4434..0000000000 --- a/examples/react/multi-index/src/App.js +++ /dev/null @@ -1,22 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import React from 'react'; -import { InstantSearch, Hits, SearchBox, Index } from 'react-instantsearch-dom'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const App = () => ( - - -

Results in first dataset

- - -

Results in second dataset

- -
-
-); - -export default App; diff --git a/examples/react/multi-index/src/__tests__/App.test.js b/examples/react/multi-index/src/__tests__/App.test.js deleted file mode 100644 index 87bbba40a4..0000000000 --- a/examples/react/multi-index/src/__tests__/App.test.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import React from 'react'; -import renderer from 'react-test-renderer'; - -import App from '../App'; - -describe('Multi index recipe', () => { - it('App renders without crashing', () => { - const component = renderer.create(); - - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/examples/react/multi-index/src/__tests__/__snapshots__/App.test.js.snap b/examples/react/multi-index/src/__tests__/__snapshots__/App.test.js.snap deleted file mode 100644 index df13ed610e..0000000000 --- a/examples/react/multi-index/src/__tests__/__snapshots__/App.test.js.snap +++ /dev/null @@ -1,90 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Multi index recipe App renders without crashing 1`] = ` -Array [ -
-
- - - -
-
, -

- Results in first dataset -

, -
-
    -
, -

- Results in second dataset -

, -
-
    -
, -] -`; diff --git a/examples/react/multi-index/src/index.js b/examples/react/multi-index/src/index.js deleted file mode 100644 index 158b4bb248..0000000000 --- a/examples/react/multi-index/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App'; -import 'instantsearch.css/themes/algolia.css'; - -ReactDOM.render(, document.getElementById('root')); diff --git a/examples/react-hooks/next-routing/.eslintrc b/examples/react/next-routing/.eslintrc similarity index 100% rename from examples/react-hooks/next-routing/.eslintrc rename to examples/react/next-routing/.eslintrc diff --git a/examples/react-hooks/next-routing/.gitignore b/examples/react/next-routing/.gitignore similarity index 100% rename from examples/react-hooks/next-routing/.gitignore rename to examples/react/next-routing/.gitignore diff --git a/examples/react/next-routing/README.md b/examples/react/next-routing/README.md new file mode 100644 index 0000000000..a909692883 --- /dev/null +++ b/examples/react/next-routing/README.md @@ -0,0 +1,18 @@ +This example shows how to do server side rendering with next.js and React InstantSearch. There's a live example here: https://codesandbox.io/s/github/algolia/instantsearch.js/tree/master/examples/react/next. + +[![Edit next-routing](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/next-routing) + +## Clone the example + +```sh +curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/next-routing +``` + +## Start the example + +```sh +yarn install --no-lockfile +yarn run dev +``` + +Read more about React InstantSearch [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). diff --git a/examples/react-hooks/next-routing/components/Panel.tsx b/examples/react/next-routing/components/Panel.tsx similarity index 100% rename from examples/react-hooks/next-routing/components/Panel.tsx rename to examples/react/next-routing/components/Panel.tsx diff --git a/examples/react-hooks/next-routing/next-env.d.ts b/examples/react/next-routing/next-env.d.ts similarity index 100% rename from examples/react-hooks/next-routing/next-env.d.ts rename to examples/react/next-routing/next-env.d.ts diff --git a/examples/react-hooks/next-routing/next.config.js b/examples/react/next-routing/next.config.js similarity index 100% rename from examples/react-hooks/next-routing/next.config.js rename to examples/react/next-routing/next.config.js diff --git a/examples/react-hooks/next/package.json b/examples/react/next-routing/package.json similarity index 70% rename from examples/react-hooks/next/package.json rename to examples/react/next-routing/package.json index c722e5cea1..166d14efa7 100644 --- a/examples/react-hooks/next/package.json +++ b/examples/react/next-routing/package.json @@ -1,5 +1,5 @@ { - "name": "example-react-instantsearch-hooks-next-example", + "name": "example-react-instantsearch-next-routing-example", "version": "6.57.0", "private": true, "scripts": { @@ -14,9 +14,8 @@ "next": "12.1.6", "react": "18.1.0", "react-dom": "18.1.0", - "react-instantsearch-hooks-router-nextjs": "6.47.3", - "react-instantsearch-hooks-server": "6.47.3", - "react-instantsearch-hooks-web": "6.47.3" + "react-instantsearch-router-nextjs": "6.47.3", + "react-instantsearch": "6.47.3" }, "devDependencies": { "@types/node": "17.0.40", diff --git a/examples/react-hooks/next-routing/pages/_app.js b/examples/react/next-routing/pages/_app.js similarity index 100% rename from examples/react-hooks/next-routing/pages/_app.js rename to examples/react/next-routing/pages/_app.js diff --git a/examples/react-hooks/next-routing/pages/index.tsx b/examples/react/next-routing/pages/index.tsx similarity index 93% rename from examples/react-hooks/next-routing/pages/index.tsx rename to examples/react/next-routing/pages/index.tsx index 9f636446ad..ee50bc2a2e 100644 --- a/examples/react-hooks/next-routing/pages/index.tsx +++ b/examples/react/next-routing/pages/index.tsx @@ -6,8 +6,6 @@ import Link from 'next/link'; import singletonRouter from 'next/router'; import React from 'react'; import { renderToString } from 'react-dom/server'; -import { createInstantSearchRouterNext } from 'react-instantsearch-hooks-router-nextjs'; -import { getServerState } from 'react-instantsearch-hooks-server'; import { DynamicWidgets, InstantSearch, @@ -17,7 +15,9 @@ import { SearchBox, InstantSearchServerState, InstantSearchSSRProvider, -} from 'react-instantsearch-hooks-web'; + getServerState, +} from 'react-instantsearch'; +import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs'; import { Panel } from '../components/Panel'; @@ -52,7 +52,7 @@ export default function HomePage({ serverState, url }: HomePageProps) { return ( - React InstantSearch Hooks - Next.js + React InstantSearch - Next.js {/* If you have navigation links outside of InstantSearch */} diff --git a/examples/react-hooks/next-routing/pages/other-page.tsx b/examples/react/next-routing/pages/other-page.tsx similarity index 100% rename from examples/react-hooks/next-routing/pages/other-page.tsx rename to examples/react/next-routing/pages/other-page.tsx diff --git a/examples/react-hooks/next-routing/styles/globals.css b/examples/react/next-routing/styles/globals.css similarity index 100% rename from examples/react-hooks/next-routing/styles/globals.css rename to examples/react/next-routing/styles/globals.css diff --git a/examples/react-hooks/next-routing/tsconfig.json b/examples/react/next-routing/tsconfig.json similarity index 100% rename from examples/react-hooks/next-routing/tsconfig.json rename to examples/react/next-routing/tsconfig.json diff --git a/examples/react-hooks/next-routing/utils/cx.ts b/examples/react/next-routing/utils/cx.ts similarity index 100% rename from examples/react-hooks/next-routing/utils/cx.ts rename to examples/react/next-routing/utils/cx.ts diff --git a/examples/react-hooks/next/.eslintrc b/examples/react/next/.eslintrc similarity index 100% rename from examples/react-hooks/next/.eslintrc rename to examples/react/next/.eslintrc diff --git a/examples/react/next/.gitignore b/examples/react/next/.gitignore index e2ff68289a..1437c53f70 100644 --- a/examples/react/next/.gitignore +++ b/examples/react/next/.gitignore @@ -1,19 +1,34 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies -node_modules +/node_modules +/.pnp +.pnp.js # testing /coverage +# next.js +/.next/ +/out/ + # production /build -/dist -/.next # misc .DS_Store -.env +*.pem + +# debug npm-debug.log* yarn-debug.log* yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/examples/react/next/README.md b/examples/react/next/README.md index 9b13b63924..f6a99da12c 100644 --- a/examples/react/next/README.md +++ b/examples/react/next/README.md @@ -15,4 +15,4 @@ yarn install --no-lockfile yarn run dev ``` -Read more about `react-instantsearch` [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). +Read more about React InstantSearch [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). diff --git a/examples/react-hooks/next/components/Panel.tsx b/examples/react/next/components/Panel.tsx similarity index 100% rename from examples/react-hooks/next/components/Panel.tsx rename to examples/react/next/components/Panel.tsx diff --git a/examples/react/next/components/app.js b/examples/react/next/components/app.js deleted file mode 100644 index 6e710e2f8c..0000000000 --- a/examples/react/next/components/app.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { - RefinementList, - SearchBox, - Hits, - Configure, - Highlight, - Pagination, - InstantSearch, -} from 'react-instantsearch-dom'; - -const HitComponent = ({ hit }) => ( -
-
-
- -
-
-
-
- - - ${hit.price} - - {hit.rating} stars -
-
- -
-
- -
-
-
-); - -export function App(props) { - return ( - - -
-

React InstantSearch + Next.js

- -
-
-
- -
-
- -
-
- -
- ); -} diff --git a/examples/react/next/components/head.js b/examples/react/next/components/head.js deleted file mode 100644 index 509381a8bf..0000000000 --- a/examples/react/next/components/head.js +++ /dev/null @@ -1,35 +0,0 @@ -import NextHead from 'next/head'; -import React from 'react'; - -const defaultDescription = ''; -const defaultOGURL = ''; -const defaultOGImage = ''; - -export const Head = (props) => ( - - - {props.title || ''} - - - - - - - - - - - - - - -); diff --git a/examples/react/next/components/index.js b/examples/react/next/components/index.js deleted file mode 100644 index 6d94af2e30..0000000000 --- a/examples/react/next/components/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export * from './head'; -export * from './app'; diff --git a/examples/react-hooks/next/next-env.d.ts b/examples/react/next/next-env.d.ts similarity index 100% rename from examples/react-hooks/next/next-env.d.ts rename to examples/react/next/next-env.d.ts diff --git a/examples/react/next/package.json b/examples/react/next/package.json index 251a0a7625..4956a848a9 100644 --- a/examples/react/next/package.json +++ b/examples/react/next/package.json @@ -1,31 +1,27 @@ { - "name": "example-react-instantsearch-next", - "version": "17.0.0", + "name": "example-react-instantsearch-next-example", + "version": "6.57.0", "private": true, - "license": "MIT", "scripts": { - "dev": "next", + "dev": "next dev", "build": "next build", - "start": "next start" - }, - "jest": { - "transform": { - "^.+\\.(js|jsx|ts|tsx)$": [ - "babel-jest", - { - "presets": [ - "next/babel" - ] - } - ] - } + "start": "next start", + "lint": "next lint" }, "dependencies": { "algoliasearch": "4.14.3", - "next": "12.3.1", - "qs": "6.9.7", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4" + "instantsearch.css": "8.0.0", + "next": "12.1.6", + "react": "18.1.0", + "react-dom": "18.1.0", + "react-instantsearch-router-nextjs": "6.47.3", + "react-instantsearch": "6.47.3" + }, + "devDependencies": { + "@types/node": "17.0.40", + "@types/react": "18.0.12", + "eslint": "8.4.0", + "eslint-config-next": "12.0.7", + "typescript": "5.1.3" } } diff --git a/examples/react-hooks/next/pages/_app.js b/examples/react/next/pages/_app.js similarity index 100% rename from examples/react-hooks/next/pages/_app.js rename to examples/react/next/pages/_app.js diff --git a/examples/react/next/pages/index.js b/examples/react/next/pages/index.js deleted file mode 100644 index d67d4dc83a..0000000000 --- a/examples/react/next/pages/index.js +++ /dev/null @@ -1,79 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import { useRouter } from 'next/router'; -import qs from 'qs'; -import React from 'react'; -import { findResultsState } from 'react-instantsearch-dom/server'; - -import { Head, App } from '../components'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const updateAfter = 700; - -const createURL = (state) => `?${qs.stringify(state)}`; - -const pathToSearchState = (path) => - path.includes('?') ? qs.parse(path.substring(path.indexOf('?') + 1)) : {}; - -const searchStateToURL = (searchState) => - searchState ? `${window.location.pathname}?${qs.stringify(searchState)}` : ''; - -const DEFAULT_PROPS = { - searchClient, - indexName: 'instant_search', -}; - -export default function Page(props) { - const [searchState, setSearchState] = React.useState(props.searchState); - const router = useRouter(); - const debouncedSetState = React.useRef(); - - React.useEffect(() => { - if (router) { - router.beforePopState(({ url }) => { - setSearchState(pathToSearchState(url)); - }); - } - }, [router]); - - return ( -
- - { - clearTimeout(debouncedSetState.current); - - debouncedSetState.current = setTimeout(() => { - const href = searchStateToURL(nextSearchState); - - router.push(href, href, { shallow: true }); - }, updateAfter); - - setSearchState(nextSearchState); - }} - createURL={createURL} - /> -
- ); -} - -export async function getServerSideProps({ resolvedUrl }) { - const searchState = pathToSearchState(resolvedUrl); - const resultsState = await findResultsState(App, { - ...DEFAULT_PROPS, - searchState, - }); - - return { - props: { - resultsState: JSON.parse(JSON.stringify(resultsState)), - searchState, - }, - }; -} diff --git a/examples/react-hooks/next/pages/index.tsx b/examples/react/next/pages/index.tsx similarity index 92% rename from examples/react-hooks/next/pages/index.tsx rename to examples/react/next/pages/index.tsx index 1930e8a670..f3f5340179 100644 --- a/examples/react-hooks/next/pages/index.tsx +++ b/examples/react/next/pages/index.tsx @@ -5,8 +5,6 @@ import Head from 'next/head'; import singletonRouter from 'next/router'; import React from 'react'; import { renderToString } from 'react-dom/server'; -import { createInstantSearchRouterNext } from 'react-instantsearch-hooks-router-nextjs'; -import { getServerState } from 'react-instantsearch-hooks-server'; import { DynamicWidgets, InstantSearch, @@ -16,7 +14,9 @@ import { SearchBox, InstantSearchServerState, InstantSearchSSRProvider, -} from 'react-instantsearch-hooks-web'; + getServerState, +} from 'react-instantsearch'; +import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs'; import { Panel } from '../components/Panel'; @@ -47,7 +47,7 @@ export default function HomePage({ serverState, url }: HomePageProps) { return ( - React InstantSearch Hooks - Next.js + React InstantSearch - Next.js N@1PWL!LLfZKyG5c!MTHoP7_p!sBz0k$?pjS;^lmgJ zU6^i~bWuZYHL)9$wuvEKm~qo~(5=Lvx5&Hv;?X#m}i|`yaGY4gX+&b>tew;gcnRQA1kp zBbm04SRuuE{Hn+&1wk%&g;?wja_Is#1gKoFlI7f`Gt}X*-nsMO30b_J@)EFNhzd1QM zdH&qFb9PVqQOx@clvc#KAu}^GrN`q5oP(8>m4UOcp`k&xwzkTio*p?kI4BPtIwX%B zJN69cGsm=x90<;Wmh-bs>43F}ro$}Of@8)4KHndLiR$nW?*{Rl72JPUqRr3ta6e#A z%DTEbi9N}+xPtd1juj8;(CJt3r9NOgb>KTuK|z7!JB_KsFW3(pBN4oh&M&}Nb$Ee2 z$-arA6a)CdsPj`M#1DS>fqj#KF%0q?w50GN4YbmMZIoF{e1yTR=4ablqXHBB2!`wM z1M1ke9+<);|AI;f=2^F1;G6Wfpql?1d5D4rMr?#f(=hkoH)U`6Gb)#xDLjoKjp)1;Js@2Iy5yk zMXUqj+gyk1i0yLjWS|3sM2-1ECc;MAz<4t0P53%7se$$+5Ex`L5TQO_MMXXi04UDIU+3*7Ez&X|mj9cFYBXqM{M;mw_ zpw>azP*qjMyNSD4hh)XZt$gqf8f?eRSFX8VQ4Y+H3jAtvyTrXr`qHAD6`m;aYmH2zOhJC~_*AuT} zvUxC38|JYN94i(05R)dVKgUQF$}#cxV7xZ4FULqFCNX*Forhgp*yr6;DsIk=ub0Hv zpk2L{9Q&|uI^b<6@i(Y+iSxeO_n**4nRLc`P!3ld5jL=nZRw6;DEJ*1z6Pvg+eW|$lnnjO zjd|8>6l{i~UxI244CGn2kK@cJ|#ecwgSyt&HKA2)z zrOO{op^o*- -
-

- React InstantSearch + Next.Js -

-
-
- - - -
-
-
-
-
-
-
-
-
-
    -
-
-
- -
-`; diff --git a/examples/react/next/tests/index.test.js b/examples/react/next/tests/index.test.js deleted file mode 100644 index 37518d463c..0000000000 --- a/examples/react/next/tests/index.test.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import React from 'react'; -import renderer from 'react-test-renderer'; - -import App from '../pages/index'; - -describe('Next app recipes', () => { - it('App renders without crashing', () => { - const component = renderer.create(); - - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/examples/react-hooks/next/tsconfig.json b/examples/react/next/tsconfig.json similarity index 100% rename from examples/react-hooks/next/tsconfig.json rename to examples/react/next/tsconfig.json diff --git a/examples/react-hooks/next/utils/cx.ts b/examples/react/next/utils/cx.ts similarity index 100% rename from examples/react-hooks/next/utils/cx.ts rename to examples/react/next/utils/cx.ts diff --git a/examples/react/react-native-query-suggestions/.expo-shared/assets.json b/examples/react/react-native-query-suggestions/.expo-shared/assets.json deleted file mode 100644 index 1e6decfbb5..0000000000 --- a/examples/react/react-native-query-suggestions/.expo-shared/assets.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, - "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true -} diff --git a/examples/react/react-native-query-suggestions/.gitignore b/examples/react/react-native-query-suggestions/.gitignore deleted file mode 100644 index c409cf6af4..0000000000 --- a/examples/react/react-native-query-suggestions/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -node_modules/**/* -.expo/* -npm-debug.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision -*.orig.* -web-build/ -web-report/ - -# macOS -.DS_Store diff --git a/examples/react/react-native-query-suggestions/App.js b/examples/react/react-native-query-suggestions/App.js deleted file mode 100644 index c36f5f59ff..0000000000 --- a/examples/react/react-native-query-suggestions/App.js +++ /dev/null @@ -1,382 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { - InstantSearch, - Configure, - Index, - connectSearchBox, - connectInfiniteHits, - connectHits, - connectRefinementList, -} from 'react-instantsearch-native'; -import { - StyleSheet, - Text, - View, - TextInput, - FlatList, - Image, - Keyboard, - TouchableHighlight, -} from 'react-native'; -import Icon from 'react-native-vector-icons/FontAwesome'; - -import Highlight from './Highlight'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const styles = StyleSheet.create({ - container: { - marginTop: 30, - flex: 1, - }, - suggestionsContainer: { - flex: 1, - }, - algoliaLogo: { - width: 40, - height: 40, - margin: 10, - }, - searchBoxContainer: { - flexDirection: 'row', - alignItems: 'stretch', - }, - bestResults: { - backgroundColor: 'lightgrey', - height: 40, - justifyContent: 'center', - padding: 10, - }, - searchBox: { - color: 'black', - height: 50, - width: 300, - alignSelf: 'center', - }, - hitsContainer: { - flexDirection: 'row', - margin: 10, - }, - suggestions: { - flexDirection: 'row', - alignItems: 'center', - padding: 10, - }, - suggestionsIcon: { - marginRight: 10, - }, - hitsPicture: { width: 40, height: 40 }, - hitsText: { - alignSelf: 'center', - paddingLeft: 5, - flex: 1, - flexWrap: 'wrap', - }, - hitsSeparator: { - height: 1, - backgroundColor: 'lightgrey', - marginTop: 10, - marginBottom: 10, - }, - categoryTextContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - categoryTextIn: { fontStyle: 'italic' }, - categoryText: { color: '#cc8008' }, -}); - -export default class App extends React.Component { - constructor(props) { - super(props); - this.state = { - displaySuggestions: false, - isFirstKeystroke: true, - searchState: {}, - query: '', - category: null, - }; - this.displaySuggestions = this.displaySuggestions.bind(this); - this.removeSuggestions = this.removeSuggestions.bind(this); - this.setQuery = this.setQuery.bind(this); - this.onSearchStateChange = this.onSearchStateChange.bind(this); - this.firstKeystroke = this.firstKeystroke.bind(this); - this.clearFilter = this.clearFilter.bind(this); - } - - firstKeystroke() { - this.setState({ isFirstKeystroke: false }); - } - - displaySuggestions() { - this.setState({ displaySuggestions: true }); - } - - removeSuggestions() { - this.setState({ displaySuggestions: false, isFirstKeystroke: true }); - } - - setQuery(query, category) { - const { - query: _query, - page: _page, - ...searchState - } = this.state.searchState; - if (searchState.indices && searchState.indices.instant_search) { - searchState.indices.instant_search.page = 0; - } - this.setState({ - query, - searchState, - category, - displaySuggestions: false, - }); - } - - clearFilter() { - this.setState({ - category: null, - query: '', - }); - } - - onSearchStateChange(searchState) { - this.setState({ searchState }); - } - - render() { - const suggestions = this.state.displaySuggestions ? ( - - ) : null; - - const results = this.state.displaySuggestions ? ( - - ) : ( - - ); - return ( - - - - - - {suggestions} - - - - - - Best results - {this.state.category ? ` in ${this.state.category}` : null} - - {results} - - - - ); - } -} - -class SearchBox extends Component { - render() { - return ( - - - { - if (text === '') { - this.props.clearFilter(); - } - this.props.refine(text); - }} - value={this.props.currentRefinement} - placeholder={'Search a product...'} - placeholderTextColor={'black'} - clearButtonMode={'always'} - underlineColorAndroid={'white'} - spellCheck={false} - autoCorrect={false} - autoCapitalize={'none'} - onFocus={this.props.displaySuggestions} - onChange={() => { - if (this.props.isFirstKeystroke) { - this.props.displaySuggestions(); - this.props.firstKeystroke(); - } - }} - /> - - ); - } -} - -const ConnectedSearchBox = connectSearchBox(SearchBox); - -SearchBox.propTypes = { - currentRefinement: PropTypes.string, - displaySuggestions: PropTypes.func, - firstKeystroke: PropTypes.func, - refine: PropTypes.func, - isFirstKeystroke: PropTypes.bool, - clearFilter: PropTypes.func, -}; - -const HitsList = ({ hits, removeSuggestions, onEndReached }) => ( - ( - - - - - - - )} - data={hits} - keyExtractor={(item, index) => item.objectID + index} - onEndReached={onEndReached} - onScroll={() => { - Keyboard.dismiss(); - removeSuggestions(); - }} - ItemSeparatorComponent={() => } - /> -); - -HitsList.propTypes = { - hits: PropTypes.array, - removeSuggestions: PropTypes.func, - onEndReached: PropTypes.func, -}; - -const ResultsInfiniteHits = connectInfiniteHits( - ({ hits, hasMore, refine, removeSuggestions }) => ( - { - if (hasMore) { - refine(); - } - }} - /> - ) -); - -const ResultsHits = connectHits(({ hits, removeSuggestions }) => ( - -)); - -const SuggestionsHits = connectHits(({ hits, onPressItem }) => ( - { - const category = - index === 1 - ? item.instant_search.facets.exact_matches.categories[0].value - : null; - return ( - - ); - }} - keyExtractor={(item, index) => item.objectID + index} - data={hits.reduce((acc, hit, index) => { - if (index === 0) { - acc.push(hit); // we duplicate first hit to allow a refinement under or not category - } - acc.push(hit); - return acc; - }, [])} - keyboardShouldPersistTaps="always" - /> -)); - -const buildItemCategoryText = (categoryText) => ( - - in - {categoryText} - -); - -const Item = ({ item, category, onPressItem, index }) => { - let text = null; - if (index === 0) { - text = buildItemCategoryText('All our categories'); - } - if (category) { - text = buildItemCategoryText(category); - } - return ( - { - Keyboard.dismiss(); - onPressItem(item.query, category); - }} - underlayColor="white" - > - - - - {text} - - - ); -}; - -Item.propTypes = { - item: PropTypes.object, - index: PropTypes.number, - category: PropTypes.string, - onPressItem: PropTypes.func, -}; - -const VirtualRefinementList = connectRefinementList(() => null); diff --git a/examples/react/react-native-query-suggestions/App.test.js b/examples/react/react-native-query-suggestions/App.test.js deleted file mode 100644 index 911027b854..0000000000 --- a/examples/react/react-native-query-suggestions/App.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; - -import App from './App'; - -it('renders without crashing', () => { - const rendered = renderer.create().toJSON(); - expect(rendered).toBeTruthy(); -}); diff --git a/examples/react/react-native-query-suggestions/Highlight.js b/examples/react/react-native-query-suggestions/Highlight.js deleted file mode 100644 index ed8abe2f66..0000000000 --- a/examples/react/react-native-query-suggestions/Highlight.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { connectHighlight } from 'react-instantsearch-native'; -import { Text } from 'react-native'; - -export default connectHighlight( - ({ highlight, attribute, hit, highlightProperty, inverted }) => { - const parsedHit = highlight({ attribute, hit, highlightProperty }); - const styles = inverted ? {} : { backgroundColor: '#ffff99' }; - const highligtedHit = parsedHit.map((part, idx) => { - if (part.isHighlighted) - return ( - - {part.value} - - ); - return ( - - {part.value} - - ); - }); - return {highligtedHit}; - } -); diff --git a/examples/react/react-native-query-suggestions/app.json b/examples/react/react-native-query-suggestions/app.json deleted file mode 100644 index 43de963985..0000000000 --- a/examples/react/react-native-query-suggestions/app.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "expo": { - "name": "NewProject", - "slug": "NewProject", - "platforms": [ - "ios", - "android", - "web" - ], - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icon.png", - "splash": { - "image": "./assets/splash.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "updates": { - "fallbackToCacheTimeout": 0 - }, - "assetBundlePatterns": [ - "**/*" - ], - "ios": { - "supportsTablet": true - } - } -} diff --git a/examples/react/react-native-query-suggestions/assets/icon.png b/examples/react/react-native-query-suggestions/assets/icon.png deleted file mode 100644 index 6eaf3029532605a3eacae6f90b4770cccc8a169f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 642 zcmeAS@N?(olHy`uVBq!ia0vp^2SAtuNHCOdH@?llz~t%a;uunK>+N;j)sF*2S|5J? zUw=@jaj{-+=PGRxE_In2=r*~8kqqT11AV78TEA|IG>q4VXDC4pe3gSy%oMyTxE z8783cW?*=6q{R}5>MRxj8JNK^cZQ#j6f2lrl5|K0 zDtoyTtoK0w`ypMhL$=`G5>dIW+jZbduGn8F4Dgi z%B8;J$+8{X`M>WO_NRHaf9=v=TfBvLUaX2{{9n-rWmDEUGE7^#_^0)O+MN4wA8*ewtGp(6 r?D#GJhTYYX(<*N_R7RebP0l+XkK_K*dU diff --git a/examples/react/react-native-query-suggestions/assets/splash.png b/examples/react/react-native-query-suggestions/assets/splash.png deleted file mode 100644 index cc94f379de325e1292ad7843f958a74fb7547d76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9306 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 diff --git a/examples/react/react-native-query-suggestions/babel.config.js b/examples/react/react-native-query-suggestions/babel.config.js deleted file mode 100644 index 9d89e13119..0000000000 --- a/examples/react/react-native-query-suggestions/babel.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - }; -}; diff --git a/examples/react/react-native-query-suggestions/package.json b/examples/react/react-native-query-suggestions/package.json deleted file mode 100644 index ab60f82b8c..0000000000 --- a/examples/react/react-native-query-suggestions/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "example-react-instantsearch-react-native-query-suggestions", - "version": "14.0.0", - "main": "node_modules/expo/AppEntry.js", - "scripts": { - "build": "echo \"Error: no build specified\" && exit 0", - "start": "expo start", - "android": "expo start --android", - "ios": "expo start --ios", - "web": "expo start --web", - "eject": "expo eject", - "test": "echo \"Error: no test specified\" && exit 0", - "test:actual": "jest" - }, - "dependencies": { - "algoliasearch": "4.14.3", - "expo": "37.0.3", - "prop-types": "15.6.2", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-native": "6.40.4", - "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz", - "react-native-vector-icons": "6.6.0", - "react-native-web": "0.11.7" - }, - "devDependencies": { - "@babel/core": "7.15.5", - "babel-preset-expo": "8.1.0", - "jest-expo": "37.0.0", - "react-test-renderer": "17.0.2" - }, - "private": true, - "jest": { - "preset": "jest-expo", - "transformIgnorePatterns": [ - "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)" - ] - } -} diff --git a/examples/react/react-native-query-suggestions/tests/App.test.js b/examples/react/react-native-query-suggestions/tests/App.test.js deleted file mode 100644 index a1a214221f..0000000000 --- a/examples/react/react-native-query-suggestions/tests/App.test.js +++ /dev/null @@ -1,14 +0,0 @@ -import 'react-native'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import App from '../App'; - -// Note: test renderer must be required after react-native. - -Date.now = jest.fn(() => 0); - -test('renders correctly', () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); -}); diff --git a/examples/react/react-native-query-suggestions/tests/__snapshots__/App.test.js.snap b/examples/react/react-native-query-suggestions/tests/__snapshots__/App.test.js.snap deleted file mode 100644 index 2014155b3e..0000000000 --- a/examples/react/react-native-query-suggestions/tests/__snapshots__/App.test.js.snap +++ /dev/null @@ -1,108 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` - - - - - - - Best results - - - - - - - -`; diff --git a/examples/react-hooks/react-native/.eslintignore b/examples/react/react-native/.eslintignore similarity index 100% rename from examples/react-hooks/react-native/.eslintignore rename to examples/react/react-native/.eslintignore diff --git a/examples/react-hooks/react-native/.eslintrc b/examples/react/react-native/.eslintrc similarity index 100% rename from examples/react-hooks/react-native/.eslintrc rename to examples/react/react-native/.eslintrc diff --git a/examples/react/react-native/.gitignore b/examples/react/react-native/.gitignore index c409cf6af4..ec8a36a257 100644 --- a/examples/react/react-native/.gitignore +++ b/examples/react/react-native/.gitignore @@ -1,5 +1,6 @@ -node_modules/**/* -.expo/* +node_modules/ +.expo/ +dist/ npm-debug.* *.jks *.p8 @@ -8,7 +9,6 @@ npm-debug.* *.mobileprovision *.orig.* web-build/ -web-report/ # macOS .DS_Store diff --git a/examples/react/react-native/App.js b/examples/react/react-native/App.js deleted file mode 100644 index 1834f618b9..0000000000 --- a/examples/react/react-native/App.js +++ /dev/null @@ -1,74 +0,0 @@ -import React, { Component } from 'react'; -import { Platform } from 'react-native'; -import { Router, Scene } from 'react-native-router-flux'; - -import Categories from './src/Categories'; -import Filters from './src/Filters'; -import Home from './src/Home'; -import Price from './src/Price'; -import Rating from './src/Rating'; -import Type from './src/Type'; - -export default class App extends Component { - render() { - return ( - - - - - - - - - - - ); - } -} diff --git a/examples/react-hooks/react-native/App.tsx b/examples/react/react-native/App.tsx similarity index 95% rename from examples/react-hooks/react-native/App.tsx rename to examples/react/react-native/App.tsx index 8f5f867a1a..d00d36a366 100644 --- a/examples/react-hooks/react-native/App.tsx +++ b/examples/react/react-native/App.tsx @@ -2,7 +2,7 @@ import React, { useRef } from 'react'; import { FlatList, SafeAreaView, StyleSheet, Text, View } from 'react-native'; import { StatusBar } from 'expo-status-bar'; import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch } from 'react-instantsearch-hooks'; +import { InstantSearch } from 'react-instantsearch-core'; import { InfiniteHits } from './src/InfiniteHits'; import { SearchBox } from './src/SearchBox'; diff --git a/examples/react/react-router/README.md b/examples/react/react-native/README.md similarity index 54% rename from examples/react/react-router/README.md rename to examples/react/react-native/README.md index 44563a9650..84e51c5d31 100644 --- a/examples/react/react-router/README.md +++ b/examples/react/react-native/README.md @@ -1,18 +1,20 @@ -This example shows how to synchronize your instantsearch url if you are using react-router. +# React InstantSearch with React Native -[![Edit react-router](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/react-router) +[![Edit react-native](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/react-native) + +This example shows how to use React InstantSearch with React Native. ## Clone the example ```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/react-router +curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/react-native ``` ## Start the example ```sh yarn install --no-lockfile -yarn start +yarn run start ``` -Read more about react-instantsearch [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). +Read more about React InstantSearch [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). diff --git a/examples/react/react-native/app.json b/examples/react/react-native/app.json index 74151c399a..476db9aec0 100644 --- a/examples/react/react-native/app.json +++ b/examples/react/react-native/app.json @@ -1,12 +1,7 @@ { "expo": { - "name": "my-project", - "slug": "my-project", - "platforms": [ - "ios", - "android", - "web" - ], + "name": "ais-ecommerce-demo-app", + "slug": "ais-ecommerce-demo-app", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/icon.png", @@ -18,11 +13,18 @@ "updates": { "fallbackToCacheTimeout": 0 }, - "assetBundlePatterns": [ - "**/*" - ], + "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#FFFFFF" + } + }, + "web": { + "favicon": "./assets/favicon.png" } } } diff --git a/examples/react-hooks/react-native/assets/adaptive-icon.png b/examples/react/react-native/assets/adaptive-icon.png similarity index 100% rename from examples/react-hooks/react-native/assets/adaptive-icon.png rename to examples/react/react-native/assets/adaptive-icon.png diff --git a/examples/react-hooks/react-native/assets/favicon.png b/examples/react/react-native/assets/favicon.png similarity index 100% rename from examples/react-hooks/react-native/assets/favicon.png rename to examples/react/react-native/assets/favicon.png diff --git a/examples/react/react-native/assets/icon.png b/examples/react/react-native/assets/icon.png index 6eaf3029532605a3eacae6f90b4770cccc8a169f..a0b1526fc7b78680fd8d733dbc6113e1af695487 100644 GIT binary patch literal 22380 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 literal 642 zcmeAS@N?(olHy`uVBq!ia0vp^2SAtuNHCOdH@?llz~t%a;uunK>+N;j)sF*2S|5J? zUw=@jaj{-+=PGRxE_In2=r*~8kqqT11AV78TEA|IG>q4VXDC4pe3gSy%oMyTxE z8783cW?*=6q{R}5>MRxj8JNK^cZQ#j6f2lrl5|K0 zDtoyTtoK0w`ypMhL$=`G5>dIW+jZbduGn8F4Dgi z%B8;J$+8{X`M>WO_NRHaf9=v=TfBvLUaX2{{9n-rWmDEUGE7^#_^0)O+MN4wA8*ewtGp(6 r?D#GJhTYYX(<*N_R7RebP0l+XkK_K*dU diff --git a/examples/react/react-native/assets/splash.png b/examples/react/react-native/assets/splash.png index cc94f379de325e1292ad7843f958a74fb7547d76..0e89705a9436743e42954d3744a0e7ff0d3d4701 100644 GIT binary patch literal 47346 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 literal 9306 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 diff --git a/examples/react/react-native/package.json b/examples/react/react-native/package.json index 94e3bc7d09..147f50883c 100644 --- a/examples/react/react-native/package.json +++ b/examples/react/react-native/package.json @@ -1,46 +1,30 @@ { - "name": "example-react-instantsearch-react-native", - "version": "14.0.0", + "name": "example-react-instantsearch-react-native-example", + "version": "6.38.26", + "private": true, "main": "node_modules/expo/AppEntry.js", "scripts": { - "build": "echo \"Error: no build specified\" && exit 0", "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web", - "eject": "expo eject", - "test": "echo \"Error: no test specified\" && exit 0", - "test:actual": "jest" + "eject": "expo eject" }, "dependencies": { - "@ptomasroos/react-native-multi-slider": "2.2.2", "algoliasearch": "4.14.3", - "expo": "37.0.3", - "prop-types": "15.6.2", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-native": "6.40.4", - "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz", - "react-native-gesture-handler": "1.6.1", - "react-native-modal-dropdown": "0.7.0", - "react-native-reanimated": "1.7.0", - "react-native-router-flux": "4.2.0", - "react-native-screens": "2.2.0", - "react-native-star-rating": "1.1.0", - "react-native-vector-icons": "6.6.0", - "react-native-web": "~0.11.7" + "expo": "~44.0.0", + "expo-status-bar": "~1.2.0", + "instantsearch.js": "4.56.8", + "react": "17.0.1", + "react-instantsearch-core": "6.47.3", + "react-native": "0.64.3", + "react-native-web": "0.17.1" }, "devDependencies": { "@babel/core": "7.15.5", - "babel-preset-expo": "8.1.0", - "jest-expo": "37.0.0", - "react-test-renderer": "17.0.2" - }, - "private": true, - "jest": { - "preset": "jest-expo", - "transformIgnorePatterns": [ - "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*|deprecated-react-native-listview|@ptomasroos/react-native-multi-slider)" - ] + "@types/react": "~17.0.21", + "@types/react-native": "~0.64.12", + "expo-cli": "5.1.1", + "typescript": "5.1.3" } } diff --git a/examples/react/react-native/src/Categories.js b/examples/react/react-native/src/Categories.js deleted file mode 100644 index 0ae137555d..0000000000 --- a/examples/react/react-native/src/Categories.js +++ /dev/null @@ -1,216 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { - InstantSearch, - connectMenu, - connectRefinementList, - connectSearchBox, - connectRange, -} from 'react-instantsearch-native'; -import { - StyleSheet, - Text, - View, - FlatList, - TextInput, - Platform, - TouchableHighlight, - Keyboard, -} from 'react-native'; -import Icon from 'react-native-vector-icons/FontAwesome'; - -import Highlight from './components/Highlight'; -import Spinner from './components/Spinner'; -import Stats from './components/Stats'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const styles = StyleSheet.create({ - mainContainer: { - backgroundColor: 'white', - flex: 1, - }, - item: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - backgroundColor: 'white', - minHeight: 41, - padding: 11, - }, - itemRefined: { - fontWeight: 'bold', - }, - searchBoxContainer: { - backgroundColor: '#162331', - }, - searchBox: { - backgroundColor: 'white', - height: 40, - flex: 1, - borderWidth: 1, - padding: 10, - margin: 10, - ...Platform.select({ - ios: { - borderRadius: 5, - }, - android: {}, - }), - }, -}); - -class Filters extends Component { - static displayName = 'React Native example'; - - constructor(props) { - super(props); - this.onSearchStateChange = this.onSearchStateChange.bind(this); - this.state = { - searchState: props.searchState, - }; - } - - onSearchStateChange(nextState) { - const searchState = { ...this.state.searchState, ...nextState }; - this.setState({ searchState }); - this.props.onSearchStateChange(searchState); - } - - render() { - return ( - - - - - - - - - - - ); - } -} - -Filters.propTypes = { - searchState: PropTypes.object.isRequired, - onSearchStateChange: PropTypes.func.isRequired, -}; - -export default Filters; - -class Menu extends Component { - constructor(props) { - super(props); - this.saveQuery = this.saveQuery.bind(this); - this.state = { - query: '', - }; - } - - saveQuery(text) { - this.setState({ query: text }); - } - - render() { - const { items, searchForItems } = this.props; - const facets = this ? ( - - ) : null; - - return ( - - - - { - this.saveQuery(text); - searchForItems(text); - }} - placeholder={'Search a category...'} - value={this.state.query} - clearButtonMode={'always'} - underlineColorAndroid={'white'} - spellCheck={false} - autoCorrect={false} - autoCapitalize={'none'} - /> - - {facets} - - ); - } - - _renderRow = ({ item: refinement }) => { - const icon = refinement.isRefined ? ( - - ) : ( - - ); - - const label = this.props.isFromSearch ? ( - - ) : ( - refinement.label - ); - - return ( - { - this.saveQuery(''); - this.props.refine(refinement.value); - Keyboard.dismiss(); - }} - > - - - {label} - - {icon} - - - ); - }; - - _renderSeparator = (sectionID, rowID, adjacentRowHighlighted) => ( - - ); -} - -Menu.propTypes = { - query: PropTypes.string, - saveQuery: PropTypes.func, - searchForItems: PropTypes.func, - refine: PropTypes.func, - items: PropTypes.array, - isFromSearch: PropTypes.bool, -}; - -const ConnectedMenu = connectMenu(Menu); -const VirtualSearchBox = connectSearchBox(() => null); -const VirtualRefinementList = connectRefinementList(() => null); -const VirtualRange = connectRange(() => null); diff --git a/examples/react/react-native/src/Filters.js b/examples/react/react-native/src/Filters.js deleted file mode 100644 index c743ef5039..0000000000 --- a/examples/react/react-native/src/Filters.js +++ /dev/null @@ -1,193 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { - InstantSearch, - connectCurrentRefinements, - connectMenu, - connectRange, - connectRefinementList, - connectSearchBox, -} from 'react-instantsearch-native'; -import { - StyleSheet, - Text, - View, - FlatList, - TouchableHighlight, - Keyboard, -} from 'react-native'; -import { Actions } from 'react-native-router-flux'; -import Icon from 'react-native-vector-icons/FontAwesome'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const styles = StyleSheet.create({ - mainContainer: { - backgroundColor: 'white', - flexGrow: 1, - }, - filtersRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - padding: 10, - }, - clearAll: { - color: 'blue', - fontWeight: 'bold', - padding: 10, - alignSelf: 'center', - }, -}); - -class Filters extends Component { - static displayName = 'React Native example'; - constructor(props) { - super(props); - this.onSearchStateChange = this.onSearchStateChange.bind(this); - this.state = { - searchState: this.props.searchState, - }; - Keyboard.dismiss(); - } - onSearchStateChange(nextState) { - const searchState = { ...this.state.searchState, ...nextState }; - this.setState({ searchState }); - this.props.onSearchStateChange(searchState); - } - - render() { - return ( - - - - - - - - - - - ); - } -} - -Filters.propTypes = { - searchState: PropTypes.object, - onSearchStateChange: PropTypes.func.isRequired, -}; - -class Refinements extends React.Component { - constructor(props) { - super(props); - this._renderRow = this._renderRow.bind(this); - this.mapping = { - Categories: { - attribute: 'category', - value: (item) => item.currentRefinement, - }, - Type: { - attribute: 'type', - value: (item) => { - const values = item.items.map((i) => i.label).join(' - '); - return values; - }, - }, - Price: { - attribute: 'price', - value: (item) => - `From ${item.currentRefinement.min}$ to ${item.currentRefinement.max}$`, - }, - Rating: { - attribute: 'rating', - value: (item) => - `From ${item.currentRefinement.min} stars to ${item.currentRefinement.max} stars`, - }, - ClearRefinements: { - attribute: 'clearAll', - }, - }; - } - - _renderRow = ({ item: refinement }) => { - const item = this.props.items.find( - (i) => i.attribute === this.mapping[refinement].attribute - ); - const refinementValue = item ? this.mapping[refinement].value(item) : '-'; - const filtersRow = - refinement !== 'ClearRefinements' ? ( - { - Actions[refinement]({ - searchState: this.props.searchState, - onSearchStateChange: this.props.onSearchStateChange, - }); - }} - > - - - {refinement} - {refinementValue} - - - - - - - ) : ( - this.props.refine(this.props.items)}> - - CLEAR ALL - - - ); - return {filtersRow}; - }; - - _renderSeparator = (sectionID, rowId, adjacentRowHighlighted) => ( - - ); - render() { - return ( - - - - ); - } -} - -Refinements.propTypes = { - searchState: PropTypes.object.isRequired, - refine: PropTypes.func.isRequired, - onSearchStateChange: PropTypes.func.isRequired, - items: PropTypes.array.isRequired, -}; - -const ConnectedRefinements = connectCurrentRefinements(Refinements); -const VirtualRefinementList = connectRefinementList(() => null); -const VirtualSearchBox = connectSearchBox(() => null); -const VirtualMenu = connectMenu(() => null); -const VirtualRange = connectRange(() => null); - -export default Filters; diff --git a/examples/react-hooks/react-native/src/Highlight.tsx b/examples/react/react-native/src/Highlight.tsx similarity index 100% rename from examples/react-hooks/react-native/src/Highlight.tsx rename to examples/react/react-native/src/Highlight.tsx diff --git a/examples/react/react-native/src/Home.js b/examples/react/react-native/src/Home.js deleted file mode 100644 index 0f71f3bb01..0000000000 --- a/examples/react/react-native/src/Home.js +++ /dev/null @@ -1,351 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { - InstantSearch, - connectSearchBox, - connectInfiniteHits, - connectRefinementList, - connectStats, - connectMenu, - connectSortBy, - connectRange, - connectCurrentRefinements, -} from 'react-instantsearch-native'; -import { - StyleSheet, - Text, - View, - FlatList, - TextInput, - Image, - StatusBar, - Button, - Platform, - Dimensions, -} from 'react-native'; -import ModalDropdown from 'react-native-modal-dropdown'; -import { Actions } from 'react-native-router-flux'; -import RatingMenu from 'react-native-star-rating'; -import IosIcon from 'react-native-vector-icons/Ionicons'; -import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; - -import Highlight from './components/Highlight'; -import Spinner from './components/Spinner'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const { height } = Dimensions.get('window'); - -const styles = StyleSheet.create({ - maincontainer: { - flex: 1, - }, - items: { - ...Platform.select({ - ios: { - height: height - 170, - }, - android: { height: height - 165 }, - }), - }, - item: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: 'white', - }, - options: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - backgroundColor: 'white', - padding: 5, - borderBottomColor: 'gray', - borderBottomWidth: 1, - }, - sortBy: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingLeft: 8, - }, - searchBoxContainer: { - backgroundColor: '#162331', - flexDirection: 'row', - alignItems: 'center', - }, - searchBox: { - backgroundColor: 'white', - height: 40, - borderWidth: 1, - padding: 10, - margin: 10, - flexGrow: 1, - ...Platform.select({ - ios: { - borderRadius: 5, - }, - android: {}, - }), - }, - itemContent: { - paddingLeft: 15, - display: 'flex', - marginRight: 5, - }, - itemName: { - fontSize: 15, - fontWeight: 'bold', - paddingBottom: 5, - }, - itemType: { - fontSize: 13, - fontWeight: '200', - paddingBottom: 5, - }, - itemPrice: { - fontSize: 15, - fontWeight: 'bold', - paddingBottom: 5, - }, - starRating: { alignSelf: 'flex-start' }, - filters: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }, -}); -class Home extends Component { - static displayName = 'React Native example'; - constructor(props) { - super(props); - - this.state = { - searchState: this.props.searchState ? this.props.searchState : {}, - }; - } - - onSearchStateChange = (nextState) => { - this.setState({ searchState: { ...this.state.searchState, ...nextState } }); - }; - - render() { - return ( - - - - - - - - - - - - - - - - - - ); - } -} - -Home.propTypes = { - searchState: PropTypes.object, -}; - -export default Home; - -class SearchBox extends Component { - render() { - return ( - - - this.props.refine(text)} - value={this.props.currentRefinement} - placeholder={'Search a product...'} - clearButtonMode={'always'} - underlineColorAndroid={'white'} - spellCheck={false} - autoCorrect={false} - autoCapitalize={'none'} - /> - - ); - } -} - -SearchBox.propTypes = { - refine: PropTypes.func.isRequired, - currentRefinement: PropTypes.string, -}; - -const ConnectedSearchBox = connectSearchBox(SearchBox); - -class Hits extends Component { - onEndReached = () => { - if (this.props.hasMore) { - this.props.refine(); - } - }; - - render() { - const hits = - this.props.hits.length > 0 ? ( - - - - ) : null; - return hits; - } - - _renderRow = ({ item: hit }) => ( - - - - - - - - - - ${hit.price} - - - - - - ); - - _renderSeparator = (sectionID, rowID, adjacentRowHighlighted) => ( - - ); -} - -Hits.propTypes = { - hits: PropTypes.array.isRequired, - refine: PropTypes.func.isRequired, - hasMore: PropTypes.bool.isRequired, -}; - -const ConnectedHits = connectInfiniteHits(Hits); -const ConnectedStats = connectStats(({ nbHits }) => ( - {nbHits} products found -)); - -const ConnectedSortBy = connectSortBy( - ({ refine, items, currentRefinement }) => { - const icon = - Platform.OS === 'ios' ? ( - - ) : ( - - ); - return ( - - item.value === currentRefinement).label - } - onSelect={(index, value) => - refine(items.find((item) => item.label === value).value) - } - options={items.map((item) => item.label)} - renderRow={(item) => { - const itemValue = items.find((i) => i.label === item).value; - return ( - - {item} - - ); - }} - dropdownStyle={{ - width: 200, - height: 110, - }} - textStyle={{ fontSize: 15 }} - /> - {icon} - - ); - } -); - -const Filters = connectCurrentRefinements( - ({ items, searchState, onSearchStateChange }) => ( - - - - -

- - Search by - - - - - - - -
- -
-
-

- Hierarchical Menu -

-
-

- Menu -

- -`; diff --git a/examples/react/react-router-v3/src/index.js b/examples/react/react-router-v3/src/index.js deleted file mode 100644 index 30d3445317..0000000000 --- a/examples/react/react-router-v3/src/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { Router, Route, browserHistory } from 'react-router'; - -import App from './App'; -import 'instantsearch.css/themes/algolia.css'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/react/react-router/index.html b/examples/react/react-router/index.html deleted file mode 100644 index 2b8d16e55d..0000000000 --- a/examples/react/react-router/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - react-router feat react-instantsearch - - -
- - - - diff --git a/examples/react/react-router/package.json b/examples/react/react-router/package.json deleted file mode 100644 index 60f8911cb6..0000000000 --- a/examples/react/react-router/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "example-react-instantsearch-react-router", - "version": "17.0.0", - "private": true, - "license": "MIT", - "scripts": { - "start": "BABEL_ENV=parcel parcel index.html --port 3000", - "build": "BABEL_ENV=parcel parcel build index.html --public-url .", - "test": "jest --ci" - }, - "devDependencies": { - "@babel/core": "7.15.5", - "@parcel/core": "2.8.0", - "@parcel/packager-raw-url": "2.8.0", - "@parcel/transformer-webmanifest": "2.8.0", - "history": "4.10.1", - "parcel": "2.8.0", - "react-test-renderer": "17.0.2" - }, - "jest": { - "transform": { - "^.+\\.(jsx?|tsx?)$": [ - "babel-jest", - { - "rootMode": "upward" - } - ] - } - }, - "dependencies": { - "algoliasearch": "4.14.3", - "instantsearch.css": "8.0.0", - "prop-types": "15.6.2", - "qs": "6.9.7", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4", - "react-router-dom": "5.2.0" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ] -} diff --git a/examples/react/react-router/src/App.js b/examples/react/react-router/src/App.js deleted file mode 100644 index 943f70f791..0000000000 --- a/examples/react/react-router/src/App.js +++ /dev/null @@ -1,120 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import qs from 'qs'; -import React from 'react'; -import { - InstantSearch, - HierarchicalMenu, - Hits, - Menu, - Pagination, - PoweredBy, - RatingMenu, - RefinementList, - SearchBox, - ClearRefinements, -} from 'react-instantsearch-dom'; -import { useLocation, useHistory } from 'react-router-dom'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const DEBOUNCE_TIME = 700; - -const createURL = (state) => `?${qs.stringify(state)}`; - -const searchStateToUrl = (location, searchState) => - searchState ? `${location.pathname}${createURL(searchState)}` : ''; - -const urlToSearchState = (location) => qs.parse(location.search.slice(1)); - -function App() { - const location = useLocation(); - const history = useHistory(); - const [searchState, setSearchState] = React.useState( - urlToSearchState(location) - ); - const setStateId = React.useRef(); - - React.useEffect(() => { - const nextSearchState = urlToSearchState(location); - - if (JSON.stringify(searchState) !== JSON.stringify(nextSearchState)) { - setSearchState(nextSearchState); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location]); - - function onSearchStateChange(nextSearchState) { - clearTimeout(setStateId.current); - - setStateId.current = setTimeout(() => { - history.push( - searchStateToUrl(location, nextSearchState), - nextSearchState - ); - }, DEBOUNCE_TIME); - - setSearchState(nextSearchState); - } - - return ( - -
-
- - -
- -
-
-

Hierarchical Menu

- -

Menu

- -

Refinement List

- -

Range Ratings

- -
- -
-
- -
-
- -
-
- -
-
-
-
-
- ); -} - -export default App; diff --git a/examples/react/react-router/src/__tests__/App.test.js b/examples/react/react-router/src/__tests__/App.test.js deleted file mode 100644 index 48a0974423..0000000000 --- a/examples/react/react-router/src/__tests__/App.test.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { createMemoryHistory } from 'history'; -import React from 'react'; -import { Router, Route } from 'react-router-dom/cjs/react-router-dom.min'; -import renderer from 'react-test-renderer'; - -import App from '../App'; - -const history = createMemoryHistory('/'); - -describe('react-router recipe', () => { - it('App renders without crashing', () => { - const component = renderer.create( - - - - ); - - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/examples/react/react-router/src/__tests__/__snapshots__/App.test.js.snap b/examples/react/react-router/src/__tests__/__snapshots__/App.test.js.snap deleted file mode 100644 index f8c748c6ca..0000000000 --- a/examples/react/react-router/src/__tests__/__snapshots__/App.test.js.snap +++ /dev/null @@ -1,749 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`react-router recipe App renders without crashing 1`] = ` -
-
-
-
- - - -
-
-
- - Search by - - - - - - - -
-
-
-
-

- Hierarchical Menu -

-
-

- Menu -

- -`; diff --git a/examples/react/react-router/src/index.js b/examples/react/react-router/src/index.js deleted file mode 100644 index a2e3434281..0000000000 --- a/examples/react/react-router/src/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { BrowserRouter as Router, Route } from 'react-router-dom'; - -import App from './App'; -import 'instantsearch.css/themes/algolia.css'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/react/server-side-rendering/.babelrc b/examples/react/server-side-rendering/.babelrc deleted file mode 100644 index 5ac80001d4..0000000000 --- a/examples/react/server-side-rendering/.babelrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "browsers": ["last 2 versions", "ie >= 9"] - } - } - ], - "@babel/preset-react" - ], - "plugins": ["@babel/plugin-proposal-class-properties"] -} diff --git a/examples/react/server-side-rendering/.gitignore b/examples/react/server-side-rendering/.gitignore deleted file mode 100644 index 6b5733e87b..0000000000 --- a/examples/react/server-side-rendering/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -.DS_STORE -.idea -dist -npm-debug.log diff --git a/examples/react/server-side-rendering/README.md b/examples/react/server-side-rendering/README.md deleted file mode 100644 index 51c0b5338c..0000000000 --- a/examples/react/server-side-rendering/README.md +++ /dev/null @@ -1,20 +0,0 @@ -This example shows how to do server side rendering and React InstantSearch. - -[![Edit server-side-rendering](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/server-side-rendering) - -## Clone the example - -```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/server-side-rendering -``` - -## Start the example - -```sh -yarn install --no-lockfile -yarn start -``` - -Read more about react-instantsearch [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). - -This example was made possible thanks to https://github.com/Roilan/react-server-boilerplate. diff --git a/examples/react/server-side-rendering/package.json b/examples/react/server-side-rendering/package.json deleted file mode 100644 index 977d712098..0000000000 --- a/examples/react/server-side-rendering/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "example-react-instantsearch-server-side-rendering", - "version": "17.0.0", - "private": true, - "license": "MIT", - "scripts": { - "start": "webpack && nodemon dist/server.js", - "watch": "webpack --watch", - "build": "webpack --mode=production", - "test": "jest" - }, - "devDependencies": { - "@babel/core": "7.15.5", - "@babel/plugin-proposal-class-properties": "7.4.4", - "@babel/polyfill": "7.4.4", - "@babel/preset-env": "7.4.5", - "@babel/preset-react": "7.0.0", - "babel-core": "6.26.3", - "babel-jest": "27.4.6", - "babel-loader": "8.2.2", - "jest": "27.4.7", - "nodemon": "1.19.3", - "react-test-renderer": "17.0.2", - "webpack": "4.41.5", - "webpack-cli": "3.3.7", - "webpack-node-externals": "1.7.2" - }, - "dependencies": { - "algoliasearch": "4.14.3", - "express": "4.17.1", - "prop-types": "15.6.2", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4" - } -} diff --git a/examples/react/server-side-rendering/src/App.js b/examples/react/server-side-rendering/src/App.js deleted file mode 100644 index 30107b60f2..0000000000 --- a/examples/react/server-side-rendering/src/App.js +++ /dev/null @@ -1,53 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { - InstantSearch, - RefinementList, - SearchBox, - Hits, - Configure, - CurrentRefinements, -} from 'react-instantsearch-dom'; - -class App extends Component { - static propTypes = { - indexName: PropTypes.string.isRequired, - searchClient: PropTypes.object.isRequired, - searchState: PropTypes.object, - resultsState: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.object), - PropTypes.object, - ]), - }; - - static defaultProps = { - searchState: {}, - }; - - state = { - searchState: this.props.searchState, - }; - - onSearchStateChange = (nextSearchState) => - this.setState({ searchState: nextSearchState }); - - render() { - const { searchState } = this.state; - - return ( - - - - - - - - ); - } -} - -export default App; diff --git a/examples/react/server-side-rendering/src/App.test.js b/examples/react/server-side-rendering/src/App.test.js deleted file mode 100644 index e649516171..0000000000 --- a/examples/react/server-side-rendering/src/App.test.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import React from 'react'; -import renderer from 'react-test-renderer'; - -import App from './App'; - -describe('Server-side rendering recipes', () => { - it('App renders without crashing', () => { - const props = { - indexName: 'index', - searchClient: { - search() {}, - }, - }; - - const component = renderer.create(); - - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/examples/react/server-side-rendering/src/__snapshots__/App.test.js.snap b/examples/react/server-side-rendering/src/__snapshots__/App.test.js.snap deleted file mode 100644 index eaee2542aa..0000000000 --- a/examples/react/server-side-rendering/src/__snapshots__/App.test.js.snap +++ /dev/null @@ -1,87 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Server-side rendering recipes App renders without crashing 1`] = ` -Array [ -
-
- - - -
-
, -
-
    -
, -
, -
-
    -
, -] -`; diff --git a/examples/react/server-side-rendering/src/browser.js b/examples/react/server-side-rendering/src/browser.js deleted file mode 100644 index ffe39ab140..0000000000 --- a/examples/react/server-side-rendering/src/browser.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { hydrate } from 'react-dom'; - -import { createApp } from './createApp'; - -const { App, props } = createApp(); - -const __APP_INITIAL_STATE__ = window.__APP_INITIAL_STATE__; - -delete window.__APP_INITIAL_STATE__; - -hydrate( - , - document.getElementById('root') -); diff --git a/examples/react/server-side-rendering/src/createApp.js b/examples/react/server-side-rendering/src/createApp.js deleted file mode 100644 index 719b62e577..0000000000 --- a/examples/react/server-side-rendering/src/createApp.js +++ /dev/null @@ -1,23 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; - -import App from './App'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -export const createApp = () => { - const indexName = 'instant_search'; - const props = { - indexName, - searchClient, - }; - - return { - App, - props, - }; -}; - -export default App; diff --git a/examples/react/server-side-rendering/src/server.js b/examples/react/server-side-rendering/src/server.js deleted file mode 100644 index 136865f2a4..0000000000 --- a/examples/react/server-side-rendering/src/server.js +++ /dev/null @@ -1,50 +0,0 @@ -import { join } from 'path'; - -import express from 'express'; -import React from 'react'; -import { renderToString } from 'react-dom/server'; -import { findResultsState } from 'react-instantsearch-dom/server'; - -import { createApp } from './createApp'; -import template from './template'; - -const server = express(); - -server.use('/assets', express.static(join(__dirname, 'assets'))); - -server.get('/', async (_, res) => { - const { App, props } = createApp(); - - const searchState = { - query: 'iPhone', - page: 5, - refinementList: { - brand: ['Apple'], - }, - }; - - const resultsState = await findResultsState(App, { - ...props, - searchState, - }); - - const initialState = { - searchState, - resultsState, - }; - - const plainHTML = renderToString(); - - res.send( - template({ - body: plainHTML, - title: 'Hello World from the server', - initialState: JSON.stringify(initialState), - }) - ); -}); - -server.listen(8080, () => { - // eslint-disable-next-line no-console - console.log('Listening on: http://localhost:8080'); -}); diff --git a/examples/react/server-side-rendering/src/template.js b/examples/react/server-side-rendering/src/template.js deleted file mode 100644 index af8840c660..0000000000 --- a/examples/react/server-side-rendering/src/template.js +++ /dev/null @@ -1,16 +0,0 @@ -export default ({ body, title, initialState }) => - ` - - - - - ${title} - - - -
${body}
- - - - - `; diff --git a/examples/react/server-side-rendering/webpack.config.js b/examples/react/server-side-rendering/webpack.config.js deleted file mode 100644 index 8f62568efa..0000000000 --- a/examples/react/server-side-rendering/webpack.config.js +++ /dev/null @@ -1,64 +0,0 @@ -const path = require('path'); - -const nodeExternals = require('webpack-node-externals'); - -module.exports = [ - { - mode: 'development', - entry: ['@babel/polyfill', './src/server.js'], - output: { - path: path.join(__dirname, 'dist'), - filename: 'server.js', - libraryTarget: 'commonjs2', - publicPath: '/', - }, - target: 'node', - node: { - console: false, - global: false, - process: false, - Buffer: false, - __filename: false, - __dirname: false, - }, - externals: nodeExternals(), - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: [ - { - loader: 'babel-loader', - }, - ], - }, - ], - }, - }, - { - mode: 'development', - entry: ['@babel/polyfill', './src/browser.js'], - output: { - path: path.join(__dirname, 'dist/assets'), - publicPath: '/', - filename: 'bundle.js', - }, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: [ - { - loader: 'babel-loader', - }, - ], - }, - ], - }, - resolve: { - extensions: ['.js', '.jsx'], - }, - }, -]; diff --git a/examples/react-hooks/ssr/.babelrc b/examples/react/ssr/.babelrc similarity index 100% rename from examples/react-hooks/ssr/.babelrc rename to examples/react/ssr/.babelrc diff --git a/examples/react-hooks/ssr/.eslintrc.js b/examples/react/ssr/.eslintrc.js similarity index 100% rename from examples/react-hooks/ssr/.eslintrc.js rename to examples/react/ssr/.eslintrc.js diff --git a/examples/react-hooks/ssr/.gitignore b/examples/react/ssr/.gitignore similarity index 100% rename from examples/react-hooks/ssr/.gitignore rename to examples/react/ssr/.gitignore diff --git a/examples/react-hooks/ssr/README.md b/examples/react/ssr/README.md similarity index 67% rename from examples/react-hooks/ssr/README.md rename to examples/react/ssr/README.md index 01e9e81973..2383220fce 100644 --- a/examples/react-hooks/ssr/README.md +++ b/examples/react/ssr/README.md @@ -1,13 +1,13 @@ -# React InstantSearch Hooks | Server-side rendering +# React InstantSearch | Server-side rendering -[![Edit ssr](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/ssr) +[![Edit ssr](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/ssr) -This example shows how to do server-side rendering with React InstantSearch Hooks. +This example shows how to do server-side rendering with React InstantSearch. ## Clone the example ```sh -curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react-hooks/ssr +curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/ssr ``` ## Start the example diff --git a/examples/react-hooks/ssr/package.json b/examples/react/ssr/package.json similarity index 80% rename from examples/react-hooks/ssr/package.json rename to examples/react/ssr/package.json index 8b9bb9d61c..16447684b7 100644 --- a/examples/react-hooks/ssr/package.json +++ b/examples/react/ssr/package.json @@ -1,5 +1,5 @@ { - "name": "example-react-instantsearch-hooks-server-side-rendering", + "name": "example-react-instantsearch-server-side-rendering", "version": "6.38.26", "private": true, "license": "MIT", @@ -26,7 +26,6 @@ "express": "4.17.1", "react": "18.1.0", "react-dom": "18.1.0", - "react-instantsearch-hooks-server": "6.47.3", - "react-instantsearch-hooks-web": "6.47.3" + "react-instantsearch": "6.47.3" } } diff --git a/examples/react-hooks/ssr/src/App.js b/examples/react/ssr/src/App.js similarity index 98% rename from examples/react-hooks/ssr/src/App.js rename to examples/react/ssr/src/App.js index c561be4549..5d7e479095 100644 --- a/examples/react-hooks/ssr/src/App.js +++ b/examples/react/ssr/src/App.js @@ -11,7 +11,7 @@ import { Pagination, RefinementList, SearchBox, -} from 'react-instantsearch-hooks-web'; +} from 'react-instantsearch'; // because this is ran on node without type: "module" set in the package.json // we need to use commonjs instead of esm. // If you use ESM in Node, you can rely on these import statements instead: diff --git a/examples/react-hooks/ssr/src/browser.js b/examples/react/ssr/src/browser.js similarity index 100% rename from examples/react-hooks/ssr/src/browser.js rename to examples/react/ssr/src/browser.js diff --git a/examples/react-hooks/ssr/src/cx.js b/examples/react/ssr/src/cx.js similarity index 100% rename from examples/react-hooks/ssr/src/cx.js rename to examples/react/ssr/src/cx.js diff --git a/examples/react-hooks/ssr/src/searchClient.js b/examples/react/ssr/src/searchClient.js similarity index 100% rename from examples/react-hooks/ssr/src/searchClient.js rename to examples/react/ssr/src/searchClient.js diff --git a/examples/react-hooks/ssr/src/server.js b/examples/react/ssr/src/server.js similarity index 93% rename from examples/react-hooks/ssr/src/server.js rename to examples/react/ssr/src/server.js index b6ed8c6957..56a1804e5b 100644 --- a/examples/react-hooks/ssr/src/server.js +++ b/examples/react/ssr/src/server.js @@ -3,7 +3,7 @@ import { join } from 'path'; import express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; -import { getServerState } from 'react-instantsearch-hooks-server'; +import { getServerState } from 'react-instantsearch'; import App from './App'; @@ -30,11 +30,11 @@ app.get('/', async (req, res) => { - +
${html}
- + ` ); diff --git a/examples/react-hooks/ssr/webpack.config.js b/examples/react/ssr/webpack.config.js similarity index 100% rename from examples/react-hooks/ssr/webpack.config.js rename to examples/react/ssr/webpack.config.js diff --git a/examples/react/tourism/App.css b/examples/react/tourism/App.css deleted file mode 100644 index 3b3bd6aa70..0000000000 --- a/examples/react/tourism/App.css +++ /dev/null @@ -1,448 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -body { - font-family: Roboto; - font-size: 16px; - color: #565a5c; -} - -.search-filters { - display: flex; -} - -.aisdemo--left-column { - height: 400px; - flex: 2 1 0; -} - -.aisdemo--right-column { - position: relative; - height: 400px; - flex: 1 1 0; -} - -/* HEADER -// ------------------------- */ -.aisdemo-navbar { - margin: 0; - border-bottom: 1px solid #dce0e0; - position: relative; - z-index: 99; -} - -.aisdemo-navbar .is-logo { - display: inline-block; - position: relative; - top: -8px; - left: 16px; -} - -.aisdemo-navbar .logo { - display: inline-block; - padding: 14px 22px; - font-size: 40px; - line-height: 1; - font-family: Courier; - color: #ff585b; - border-right: 1px solid #dce0e0; -} - -.aisdemo-navbar .fa-search { - font-size: 30px; - margin-left: 10px; - color: #dce0e0; -} - -.ais-SearchBox { - position: absolute; - width: 280px; - height: 33px; - white-space: nowrap; - box-sizing: border-box; - font-size: 14px; - left: 205px; - top: 20px; -} - -.ais-SearchBox-body, -.ais-SearchBox-form { - width: 100%; - height: 100%; -} - -.ais-SearchBox-input { - display: inline-block; - -webkit-transition: box-shadow 0.4s ease, background 0.4s ease; - transition: box-shadow 0.4s ease, background 0.4s ease; - border: 0; - border-radius: 1px; - box-shadow: inset 0 0 0 0px #cccccc; - background: #ffffff; - padding: 0; - width: 100%; - height: 100%; - vertical-align: middle; - white-space: normal; - font-size: inherit; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.ais-SearchBox-input::-webkit-search-decoration, -.ais-SearchBox-input::-webkit-search-cancel-button, -.ais-SearchBox-input::-webkit-search-results-button, -.ais-SearchBox-input::-webkit-search-results-decoration { - display: none; -} - -.ais-SearchBox-input:hover { - box-shadow: inset 0 0 0 0px #b3b3b3; -} - -.ais-SearchBox-input:focus, -.SearchBox__input:active { - outline: 0; - box-shadow: inset 0 0 0 0px #d6dee3; - background: #ffffff; -} - -.ais-SearchBox-input::-webkit-input-placeholder { - color: #9aaeb5; -} - -.ais-SearchBox-input::-moz-placeholder { - color: #9aaeb5; -} - -.ais-SearchBox-input:-ms-input-placeholder { - color: #9aaeb5; -} - -.ais-SearchBox-input::placeholder { - color: #9aaeb5; -} - -.ais-SearchBox-submit { - display: none; -} - -.ais-SearchBox-reset { - display: none; -} - -/* FILTERS -// ------------------------- */ -.aisdemo-filters .aisdemo-filter { - margin-top: 0; - margin-bottom: 0; - padding-top: 15px; - padding-bottom: 15px; - border-bottom: 1px solid #dce0e0; -} - -.aisdemo-filters .aisdemo-filter-title { - font-size: 1em; - font-weight: normal; - padding-top: 9px; - padding-left: 30px; -} - -.aisdemo-filters .date, -.aisdemo-filters #guests select { - font-size: 0.8em; - padding: 8px 10px; - border: 1px solid #dce0e0; -} - -.aisdemo-filters #guests select { - width: 100%; - padding-right: 20px; - border-radius: 0; - background: transparent; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.aisdemo-filters #guests select:focus { - outline: none; -} - -.aisdemo-filters .guests-caret:before { - line-height: 1; - position: absolute; - top: 0; - right: 20px; - width: 2em; - padding-top: 0.8em; - content: '\25bc'; - text-align: center; - pointer-events: none; - color: #82888a; -} - -.ais-refinement-list--count { - display: none; -} - -.ais-refinement-list--item { - display: inline-block; - cursor: pointer; -} - -.ais-refinement-list--item label { - font-size: 0.8em; - font-weight: normal; - box-sizing: border-box; - width: 100%; - padding: 9px 10px 7px; - background: #edefed; - border-radius: 4px; -} - -.ais-refinement-list--checkbox { - position: relative; - bottom: 3px; - float: right; - width: 1.25em; - height: 1.25em; - margin-left: 20px; - vertical-align: top; - border: 1px solid #dce0e0; - background: #ffffff; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.ais-refinement-list--checkbox:focus { - outline: none; -} - -.ais-refinement-list--item__active .ais-refinement-list--checkbox:before { - font-size: 0.85em; - position: absolute; - width: 1.25em; - content: '\2713'; - text-align: center; - color: #ff5a5f; -} - -.ais-GeoSearch-map { - height: 400px; -} - -.ClearGeoRefinement button { - display: block; - position: absolute; - left: 50%; - bottom: 20px; - transform: translateX(-50%); - background-color: #ff585b; - color: #fff; - padding: 5px; - border-radius: 3px; - font-size: 0.85em; -} - -.ClearGeoRefinement button:disabled { - display: none; -} - -/* STATS -// ------------------------- */ -#stats { - font-size: 1em; - font-weight: normal; - position: absolute; - bottom: 0; - padding: 5px 15px; -} - -/* MAP -// ------------------------- */ -#map { - height: 400px; -} - -.marker { - transform: translate(-50%, -100%) scale(0.5, 0.5); -} - -.capacity-filter { - padding: 9px 10px 7px; - background: #edefed; -} - -.capacity-menu-wrapper { - font-size: 0.8em; - font-weight: normal; - box-sizing: border-box; - width: 100%; - padding: 6px 0px 5px; -} - -.capacity-menu-wrapper select { - width: 100%; - background: #edefed; - height: 27px; -} - -/* RESULTS -// ------------------------- */ -#results { - padding: 40px 20px; - background: #edefed; -} - -#hits .hit .pictures-wrapper { - position: relative; -} - -#hits .hit .pictures-wrapper .picture { - width: 100%; -} - -#hits .hit .pictures-wrapper .profile { - position: absolute; - bottom: -16px; - right: 12px; - width: 60px; - border: 4px solid rgba(255, 255, 255, 0.3); - border-radius: 30px; -} - -#hits .hit .infos { - height: 90px; - padding: 16px 20px; -} - -#hits .hit .infos h4 { - font-size: 1em; - font-weight: normal; -} - -#hits .hit .infos p { - font-size: 0.8em; - color: #949697; -} - -#hits .hit .infos em { - font-style: normal; - color: #ff585b; -} - -/* PAGINATION -// ------------------------- */ -.ais-Pagination { - text-align: center; - margin-bottom: 10px; - display: flex; - justify-content: center; -} - -.ais-Pagination .ais-Pagination-item { - display: inline-block; - border: 1px solid; - border-radius: 4px; - padding: 3px; - margin: 1px; - border-color: #ddd; - background: transparent; -} - -.ais-Pagination-item .ais-Pagination-link { - display: block; - color: #ff585b; - line-height: 30px; - width: 30px; - height: 30px; -} - -.ais-Pagination-item.ais-Pagination-item--selected.ais-Pagination-item--page { - background: #ff585b; -} - -.ais-Pagination-item.ais-Pagination-item--selected.ais-Pagination-item--page - .ais-Pagination-link { - color: #ffffff; - border-color: #ff585b; -} - -.ais-Pagination-item--disabled { - visibility: hidden; -} - -.thank-you { - text-align: center; - font-size: 0.8em; -} - -.thank-you a { - color: #ff585b; -} - -/* RHEOSTAT RANGE -// ------------------------- */ -.rheostat-container { - display: flex; - align-items: center; -} -.rheostat { - height: 24px; - position: relative; - overflow: visible; -} - -.rheostat-background { - background: #dce0e0; - height: 2px; - position: relative; - top: 14px; - width: 100%; -} - -.rheostat-values { - display: flex; - font-size: 12px; - justify-content: space-between; - color: rgba(0, 0, 0, 0.6); -} - -.rheostat--disabled .rheostat-progress { - background-color: #edefed; -} - -.rheostat--disabled .rheostat-handle { - cursor: default; -} - -.rheostat-progress { - background-color: #ff585b; - height: 4px; - position: absolute; - top: 13px; -} - -.rheostat-handle { - border: 1px solid #ff585b; - background: #fff; - -webkit-border-radius: 100%; - -moz-border-radius: 100%; - border-radius: 100%; - -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - cursor: pointer; - height: 24px; - margin-left: -12px; - position: absolute; - z-index: 2; - width: 24px; - font-size: 0; -} diff --git a/examples/react/tourism/App.js b/examples/react/tourism/App.js deleted file mode 100644 index 97c0e556aa..0000000000 --- a/examples/react/tourism/App.js +++ /dev/null @@ -1,356 +0,0 @@ -import algoliasearch from 'algoliasearch/lite'; -import PropTypes from 'prop-types'; -import React, { Component, Fragment } from 'react'; -import { - InstantSearch, - ClearRefinements, - SearchBox, - Pagination, - Highlight, - Configure, - connectHits, - connectNumericMenu, - connectRefinementList, - connectRange, -} from 'react-instantsearch-dom'; -import { - GoogleMapsLoader, - GeoSearch, - Marker, -} from 'react-instantsearch-dom-maps'; -import Rheostat from 'rheostat'; - -import withURLSync from './URLSync'; -import './App.css'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const App = (props) => ( - - -
- - - -); - -function Header() { - return ( -
- - React InstantSearch - - - BnB - - - -
- ); -} - -function Filters() { - return ( - -
-
-
- - - - -
- -
-
-
-
- -
- - {(google) => ( - - {({ hits }) => ( - - {hits.map((hit) => ( - - ))} - - items.filter((item) => item.id === 'boundingBox') - } - translations={{ - reset: 'Clear the map refinement', - }} - /> - - )} - - )} - -
-
- - ); -} - -function Capacity() { - return ( -
-
Capacity
-
- -
-
- ); -} - -function OptionCapacity({ label, value }) { - return ; -} - -OptionCapacity.propTypes = { - label: PropTypes.string, - value: PropTypes.string, -}; - -const CapacitySelector = connectNumericMenu( - ({ items, currentRefinement, refine }) => { - const selectValue = (e) => refine(e.target.value); - const options = items.map((item) => ( - - )); - - return ( -
- -
- ); - } -); - -function DatesAndGuest() { - return ( -
-
Dates
-
- -
-
- -
-
-
- ); -} - -const RoomType = connectRefinementList(({ items, refine }) => { - const sortedItems = items.sort((i1, i2) => i1.label.localeCompare(i2.label)); - const hitComponents = sortedItems.map((item) => { - const selectedClassName = item.isRefined - ? ' ais-refinement-list--item__active' - : ''; - const itemClassName = `ais-refinement-list--item col-sm-3 ${selectedClassName}`; - return ( -
-
- -
-
- ); - }); - - return ( -
-
Room Type
-
{hitComponents}
-
- ); -}); - -function Price() { - return ( -
-
Price Range
-
- -
-
- ); -} - -const MyHits = connectHits(({ hits }) => { - const hs = hits.map((hit) => ); - return
{hs}
; -}); - -function HitComponent({ hit }) { - return ( -
-
- {hit.name} - {hit.user.user.first_name} -
-
-

- -

- -
-
- ); -} - -function HitDescription({ hit }) { - return ( -

- {hit.room_type} - ,{' '} - -

- ); -} - -HitComponent.propTypes = { - hit: PropTypes.object, -}; - -function Results() { - return ( -
-
- -
-
- -
- Data from airbnb.com, user pics - from randomuser.me -
-
-
- ); -} - -class Range extends Component { - static propTypes = { - min: PropTypes.number, - max: PropTypes.number, - currentRefinement: PropTypes.object, - refine: PropTypes.func.isRequired, - canRefine: PropTypes.bool.isRequired, - }; - - state = { currentValues: { min: this.props.min, max: this.props.max } }; - - componentDidUpdate(prevProps) { - if ( - this.props.canRefine && - (prevProps.currentRefinement.min !== this.props.currentRefinement.min || - prevProps.currentRefinement.max !== this.props.currentRefinement.max) - ) { - this.setState({ - currentValues: { - min: this.props.currentRefinement.min, - max: this.props.currentRefinement.max, - }, - }); - } - } - - onValuesUpdated = (sliderState) => { - this.setState({ - currentValues: { min: sliderState.values[0], max: sliderState.values[1] }, - }); - }; - - onChange = (sliderState) => { - if ( - this.props.currentRefinement.min !== sliderState.values[0] || - this.props.currentRefinement.max !== sliderState.values[1] - ) { - this.props.refine({ - min: sliderState.values[0], - max: sliderState.values[1], - }); - } - }; - - render() { - const { min, max, currentRefinement } = this.props; - const { currentValues } = this.state; - return min !== max ? ( -
- -
-
{currentValues.min}
-
{currentValues.max}
-
-
- ) : null; - } -} - -const ConnectedRange = connectRange(Range); - -export default withURLSync(App); diff --git a/examples/react/tourism/URLSync.js b/examples/react/tourism/URLSync.js deleted file mode 100644 index 1eaa103822..0000000000 --- a/examples/react/tourism/URLSync.js +++ /dev/null @@ -1,56 +0,0 @@ -import qs from 'qs'; -import React, { Component } from 'react'; - -const updateAfter = 700; -const searchStateToURL = (searchState) => - searchState ? `${window.location.pathname}?${qs.stringify(searchState)}` : ''; - -const withURLSync = (App) => - class WithURLSync extends Component { - state = { - searchState: qs.parse(window.location.search.slice(1)), - }; - - componentDidMount() { - window.addEventListener('popstate', this.onPopState); - } - - componentWillUnmount() { - clearTimeout(this.debouncedSetState); - window.removeEventListener('popstate', this.onPopState); - } - - onPopState = ({ state }) => - this.setState({ - searchState: state || {}, - }); - - onSearchStateChange = (searchState) => { - clearTimeout(this.debouncedSetState); - - this.debouncedSetState = setTimeout(() => { - window.history.pushState( - searchState, - null, - searchStateToURL(searchState) - ); - }, updateAfter); - - this.setState({ searchState }); - }; - - render() { - const { searchState } = this.state; - - return ( - - ); - } - }; - -export default withURLSync; diff --git a/examples/react/tourism/index.html b/examples/react/tourism/index.html deleted file mode 100644 index 1b2648df15..0000000000 --- a/examples/react/tourism/index.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - React InstantSearch - Tourism - - - - - -
- - diff --git a/examples/react/tourism/index.js b/examples/react/tourism/index.js deleted file mode 100644 index ae31e41347..0000000000 --- a/examples/react/tourism/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import App from './App'; - -ReactDOM.render(, document.getElementById('root')); diff --git a/examples/react/tourism/package.json b/examples/react/tourism/package.json deleted file mode 100644 index 7eeae33719..0000000000 --- a/examples/react/tourism/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "example-react-instantsearch-tourism", - "version": "15.0.0", - "private": true, - "dependencies": { - "algoliasearch": "4.14.3", - "qs": "6.9.7", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-instantsearch-dom": "6.40.4", - "rheostat": "2.2.0" - } -} diff --git a/global.d.ts b/global.d.ts index eda71845aa..5038ed77bd 100644 --- a/global.d.ts +++ b/global.d.ts @@ -23,9 +23,9 @@ declare module 'algoliasearch-v3' { }; } -// fake typing for legacy modules -declare module 'react-instantsearch-core'; -declare module 'react-instantsearch-dom'; +declare module 'react-instantsearch-core-v6' { + export const InstantSearch: any; +} declare module 'jest-serializer-html/createSerializer' { export default function createSerializer(): jest.SnapshotSerializerPlugin; diff --git a/jest.config.js b/jest.config.js index b3fbb1af55..312d523166 100644 --- a/jest.config.js +++ b/jest.config.js @@ -14,7 +14,7 @@ const config = { '/examples/', '/packages/algoliasearch-helper', '/packages/create-instantsearch-app', - '/packages/react-instantsearch-hooks-router-nextjs', + '/packages/react-instantsearch-router-nextjs', '/__utils__/', ], watchPathIgnorePatterns: [ @@ -36,7 +36,8 @@ const config = { }, moduleFileExtensions: ['tsx', 'ts', 'js', 'vue'], moduleNameMapper: { - '^react-instantsearch-(.*)$': + '^react-instantsearch$': '/packages/react-instantsearch/src/', + '^react-instantsearch-(.*[^v6])$': '/packages/react-instantsearch-$1/src/', '^instantsearch.js$': '/packages/instantsearch.js/src/', }, diff --git a/packages/instantsearch.css/README.md b/packages/instantsearch.css/README.md index f7bd3884d9..980369c567 100644 --- a/packages/instantsearch.css/README.md +++ b/packages/instantsearch.css/README.md @@ -1,6 +1,7 @@ -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +**Table of Contents** _generated with [DocToc](https://github.com/thlorenz/doctoc)_ - [Getting started](#getting-started) - [Installation](#installation) @@ -20,10 +21,7 @@ --- -[![Version][version-svg]][package-url] -[![License][license-image]][license-url] -[![Build Status][ci-svg]][ci-url] -[![Website][website-svg]][website-url] +[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url] [![Website][website-svg]][website-url] ## Getting started @@ -38,8 +36,8 @@ import 'instantsearch.css/themes/satellite.css'; ``` To learn more about the library, follow the guide on how to style your InstantSearch widgets: + - [InstantSearch.js](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/js/#style-your-widgets) -- [React InstantSearch Hooks](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react-hooks/#style-your-widgets) - [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react/#style-your-widgets) - [Vue InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/vue/#style-your-widgets) - [Angular InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/angular/#style-your-widgets) @@ -55,6 +53,7 @@ yarn add instantsearch.css ## Available themes InstantSearch.css exposes two themes: + - [Algolia](src/themes/algolia.scss) - [Satellite](src/themes/satellite.scss) @@ -70,14 +69,12 @@ We support the **last two versions of major browsers** (Chrome, Edge, Firefox, S InstantSearch.css is a living standard. This table tracks down the version implemented in each InstantSearch flavor. -| Project | Version | -| ------------------------- | ------- | -| InstantSearch.js | 7 | -| React InstantSearch Hooks | 7 | -| React InstantSearch | 7 | -| Vue InstantSearch | 7 | -| Angular InstantSearch | 7 | - +| Project | Version | +| --------------------- | ------- | +| InstantSearch.js | 7 | +| React InstantSearch | 7 | +| Vue InstantSearch | 7 | +| Angular InstantSearch | 7 | ## Contributing diff --git a/packages/instantsearch.js/src/connectors/breadcrumb/connectBreadcrumb.ts b/packages/instantsearch.js/src/connectors/breadcrumb/connectBreadcrumb.ts index 45e01fd3dc..59720fc306 100644 --- a/packages/instantsearch.js/src/connectors/breadcrumb/connectBreadcrumb.ts +++ b/packages/instantsearch.js/src/connectors/breadcrumb/connectBreadcrumb.ts @@ -188,7 +188,7 @@ const connectBreadcrumb: BreadcrumbConnector = function connectBreadcrumb( function getItems() { // The hierarchicalFacets condition is required for flavors // that render immediately with empty results, without relying - // on init() (like React InstantSearch Hooks). + // on init() (like React InstantSearch). if (!results || state.hierarchicalFacets.length === 0) { return []; } diff --git a/packages/instantsearch.js/src/middlewares/__tests__/createInsightsMiddleware.ts b/packages/instantsearch.js/src/middlewares/__tests__/createInsightsMiddleware.ts index 5209995701..f21dcc2958 100644 --- a/packages/instantsearch.js/src/middlewares/__tests__/createInsightsMiddleware.ts +++ b/packages/instantsearch.js/src/middlewares/__tests__/createInsightsMiddleware.ts @@ -1076,7 +1076,7 @@ See documentation: https://www.algolia.com/doc/guides/building-search-ui/going-f search.start(); - // insights is added *after start*, like in React InstantSearch Hooks + // insights is added *after start*, like in React InstantSearch search.use( createInsightsMiddleware({ insightsClient(eventName, ...args) { @@ -1108,7 +1108,7 @@ See documentation: https://www.algolia.com/doc/guides/building-search-ui/going-f }, }); - // insights is added *after start*, like in React InstantSearch Hooks + // insights is added *after start*, like in React InstantSearch search.use( createInsightsMiddleware({ insightsClient(eventName, ...args) { diff --git a/packages/react-instantsearch-core/CHANGELOG.md b/packages/react-instantsearch-core/CHANGELOG.md index 0dfd2fe8c6..1036d686f4 100644 --- a/packages/react-instantsearch-core/CHANGELOG.md +++ b/packages/react-instantsearch-core/CHANGELOG.md @@ -3,15 +3,27 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [6.40.4](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.40.3...react-instantsearch-core@6.40.4) (2023-07-25) +## [6.47.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.47.2...react-instantsearch-hooks@6.47.3) (2023-07-27) -**Note:** Version bump only for package react-instantsearch-core + +### Bug Fixes + +* add a future warning when the package name changes ([#5778](https://github.com/algolia/instantsearch.js/issues/5778)) ([3d22ee4](https://github.com/algolia/instantsearch.js/commit/3d22ee45e1f03a443323a371621262f1fe45e664)) +* **useInstantSearch:** deprecate `use` function ([#5781](https://github.com/algolia/instantsearch.js/issues/5781)) ([ec16c6e](https://github.com/algolia/instantsearch.js/commit/ec16c6e74cc9e364aca47d64983be32bf0cce0fe)) + + + + + +## [6.47.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.47.1...react-instantsearch-hooks@6.47.2) (2023-07-25) + +**Note:** Version bump only for package react-instantsearch-hooks -## [6.40.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.40.2...react-instantsearch-core@6.40.3) (2023-07-19) +## [6.47.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.47.0...react-instantsearch-hooks@6.47.1) (2023-07-19) ### Bug Fixes @@ -22,15 +34,38 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.40.1...react-instantsearch-core@6.40.2) (2023-07-18) +# [6.47.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.46.0...react-instantsearch-hooks@6.47.0) (2023-07-18) -**Note:** Version bump only for package react-instantsearch-core +**Note:** Version bump only for package react-instantsearch-hooks +# [6.46.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.45.1...react-instantsearch-hooks@6.46.0) (2023-07-10) + + +### Bug Fixes + +* **url:** base createURL on UiState instead of SearchParameters ([#5696](https://github.com/algolia/instantsearch.js/issues/5696)) ([7e2c8a2](https://github.com/algolia/instantsearch.js/commit/7e2c8a295a6fc5ba36d9482f645ef55b422d5e75)), closes [#5694](https://github.com/algolia/instantsearch.js/issues/5694) + + +### Features + +* **GeoSearch:** expose useGeoSearch() hook in RISH ([#5693](https://github.com/algolia/instantsearch.js/issues/5693)) ([b951b7b](https://github.com/algolia/instantsearch.js/commit/b951b7bcafb384d990ccf02538d9bb9e248a6bba)) + -## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.40.0...react-instantsearch-core@6.40.1) (2023-06-20) + + + +## [6.45.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.45.0...react-instantsearch-hooks@6.45.1) (2023-07-04) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +# [6.45.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.3...react-instantsearch-hooks@6.45.0) (2023-06-20) ### Bug Fixes @@ -38,10 +73,42 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline * **dependencies:** update helper requirement ([#5676](https://github.com/algolia/instantsearch.js/issues/5676)) ([c289120](https://github.com/algolia/instantsearch.js/commit/c2891205c1125b1203b3b3db946d57e0fc4e4687)), closes [#5658](https://github.com/algolia/instantsearch.js/issues/5658) +### Features + +* **algoliaAgent:** track Next.js version as Algolia agent ([#5677](https://github.com/algolia/instantsearch.js/issues/5677)) ([b205ac0](https://github.com/algolia/instantsearch.js/commit/b205ac019f79699cd608d63792b8ff5a0832aa7c)) + + + + + +## [6.44.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.2...react-instantsearch-hooks@6.44.3) (2023-06-13) + +**Note:** Version bump only for package react-instantsearch-hooks -# [6.40.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.39.1...react-instantsearch-core@6.40.0) (2023-05-16) + + +## [6.44.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.1...react-instantsearch-hooks@6.44.2) (2023-06-05) + + +### Bug Fixes + +* **hooks:** generate version for algoliaAgent in build ([#5655](https://github.com/algolia/instantsearch.js/issues/5655)) ([4d92ac4](https://github.com/algolia/instantsearch.js/commit/4d92ac4769e19e81fb55481aedc71121bef981cb)) + + + + + +## [6.44.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.0...react-instantsearch-hooks@6.44.1) (2023-05-30) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +# [6.44.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.43.0...react-instantsearch-hooks@6.44.0) (2023-05-16) ### Bug Fixes @@ -57,15 +124,137 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.38.3...react-instantsearch-core@6.39.1) (2023-01-26) +# [6.43.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.42.2...react-instantsearch-hooks@6.43.0) (2023-04-24) + + +### Bug Fixes + +* **lifecycle:** prevent extra network requests when unmounting multiple widgets ([#5602](https://github.com/algolia/instantsearch.js/issues/5602)) ([11458ee](https://github.com/algolia/instantsearch.js/commit/11458eee7e7f0f3e9c5f368584a16f58646b1cdd)) + + +### Features + + +* **insights:** add insights option to InstantSearch ([#5488](https://github.com/algolia/instantsearch.js/issues/5488)) ([9031573](https://github.com/algolia/instantsearch.js/commit/9031573807fa6803dcfae9f33d61b8f111f68423)) ([#5578](https://github.com/algolia/instantsearch.js/issues/5578)) ([8fb517f](https://github.com/algolia/instantsearch.js/commit/8fb517f15381ecb32ea00cf4b01a0fd5e70e1d17)) ([#5545](https://github.com/algolia/instantsearch.js/issues/5545)) ([99a0972](https://github.com/algolia/instantsearch.js/commit/99a0972663b8f3284cac3b5621571ced7a33908f)) ([#5493](https://github.com/algolia/instantsearch.js/issues/5493)) ([cff723f](https://github.com/algolia/instantsearch.js/commit/cff723fc95a90ebb2ed14c46c51ab05764835a47)) +* **insights:** always pass Algolia credentials locally ([#5554](https://github.com/algolia/instantsearch.js/issues/5554)) ([654ab81](https://github.com/algolia/instantsearch.js/commit/654ab81e1669354c249710b6756610fba35d54b4)) ([#5558](https://github.com/algolia/instantsearch.js/issues/5558)) ([82144c0](https://github.com/algolia/instantsearch.js/commit/82144c0a0b18e6b47d6f508e5c670a9de274c121)) ([#5529](https://github.com/algolia/instantsearch.js/issues/5529)) ([8537f8f](https://github.com/algolia/instantsearch.js/commit/8537f8f7a10bcaf053ff62180c082e077b1b052d)) +* **insights:** annotate events with algoliaSource ([#5580](https://github.com/algolia/instantsearch.js/issues/5580)) ([c419307](https://github.com/algolia/instantsearch.js/commit/c419307a5f7fe46d5032c9437a17c8e3dad57fe5)) +* **insights:** automatically load search-insights if not passed ([#5484](https://github.com/algolia/instantsearch.js/issues/5484)) ([a85797b](https://github.com/algolia/instantsearch.js/commit/a85797b503edc94e001c5bfb3b754db6cb556943)) +* **insights:** enable default click events on hits and infinite hits ([#5522](https://github.com/algolia/instantsearch.js/issues/5522)) ([271bd12](https://github.com/algolia/instantsearch.js/commit/271bd12e34bc55656976bb53c90282193083eb86)) ([#5527](https://github.com/algolia/instantsearch.js/issues/5527)) ([0e55821](https://github.com/algolia/instantsearch.js/commit/0e558213c807cd17d592fadec052f3d1fc692e6c)) +* **insights:** prevent potential errors ([#5487](https://github.com/algolia/instantsearch.js/issues/5487)) ([33fe510](https://github.com/algolia/instantsearch.js/commit/33fe510307e4b382a5ba1153a0eaf160420acd11)) ([#5606](https://github.com/algolia/instantsearch.js/issues/5606)) ([bdd9290](https://github.com/algolia/instantsearch.js/commit/bdd92901b59ae4e5d7311eadfbf53ed656bbaf4a)) ([#5512](https://github.com/algolia/instantsearch.js/issues/5512)) ([85dfbc9](https://github.com/algolia/instantsearch.js/commit/85dfbc9ebd722fbe6a7e1bd056950fdbcc16d8d9)) +* **metadata:** register metadata around middleware ([#5492](https://github.com/algolia/instantsearch.js/issues/5492)) ([3e72ec8](https://github.com/algolia/instantsearch.js/commit/3e72ec82894a05a071328a4802d2f764233fe005)) + + + + +## [6.42.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.42.1...react-instantsearch-hooks@6.42.2) (2023-04-11) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +## [6.42.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.42.0...react-instantsearch-hooks@6.42.1) (2023-03-28) + +### Features + +* **InstantSearch**: warn when an unstable search client is detected ([#5563](https://github.com/algolia/instantsearch.js/issues/5563)) ([0fcf716](https://github.com/algolia/instantsearch/commit/0fcf7162ce77246133c0d4a6ff7ea975ba17cc4c)) + + + +# [6.42.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.41.0...react-instantsearch-hooks@6.42.0) (2023-03-21) + + +### Features + +* **current-refinements:** provide indexId of refinements in transformItems ([#5546](https://github.com/algolia/instantsearch.js/issues/5546)) ([89781db](https://github.com/algolia/instantsearch.js/commit/89781db6cb1d2b8ebbc116e9bcd8a10f646e7baf)) + + + + + +# [6.41.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.40.1...react-instantsearch-hooks@6.41.0) (2023-03-07) + + +### Features + +* **index:** introduce setIndexUiState ([#5504](https://github.com/algolia/instantsearch.js/issues/5504)) ([c199feb](https://github.com/algolia/instantsearch.js/commit/c199febbc3381df574afbb2504edd7373b32904a)) +* **react:** export `InstantSearchApi` type ([#5518](https://github.com/algolia/instantsearch.js/issues/5518)) ([27b478f](https://github.com/algolia/instantsearch.js/commit/27b478f8f20c4e8835914cceabbdce57ff5d4650)) + + +## Bug Fixes + +* **DynamicWidgets**: prevent non-stable fallbackComponent ([#5532](https://github.com/algolia/instantsearch.js/issues/5532)) ([0625c90](https://github.com/algolia/instantsearch.js/commit/0625c90346e32b926d6ce276a4e0b13d4fa4bf6c)) + + +## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.40.1...react-instantsearch-hooks@6.40.2) (2023-02-28) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.40.0...react-instantsearch-hooks@6.40.1) (2023-02-21) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +# [6.40.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.3...react-instantsearch-hooks@6.40.0) (2023-02-14) + + +### Features + +* Warn when not using the dedicated Next.js router ([#5432](https://github.com/algolia/instantsearch.js/issues/5432)) ([39b5859](https://github.com/algolia/instantsearch.js/commit/39b5859ba78a5e8472a80e357a35ba900c963b61)) + + +### Bug Fixes + +* Prevent issue where instantsearch instance got created twice in ssr ([#5432](https://github.com/algolia/instantsearch.js/issues/5432)) ([39b5859](https://github.com/algolia/instantsearch.js/commit/39b5859ba78a5e8472a80e357a35ba900c963b61)) + + + +## [6.39.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.2...react-instantsearch-hooks@6.39.3) (2023-02-07) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +## [6.39.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.1...react-instantsearch-hooks@6.39.2) (2023-01-30) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.0...react-instantsearch-hooks@6.39.1) (2023-01-26) + +**Note:** Version bump only for package react-instantsearch-hooks + + + + + +# [6.39.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.38.3...react-instantsearch-hooks@6.39.0) (2023-01-25) + + +### Features +* **react-instantsearch-hooks-web:** Add stats widget and ui component ([#5427](https://github.com/algolia/instantsearch.js/issues/5427)) ([d07cf0d](https://github.com/algolia/instantsearch.js/commit/d07cf0d0310bf4e49d4a4c2142b3783d9bcda79d)) +* **react-instantsearch-hooks:** Add useStats hook ([#5425](https://github.com/algolia/instantsearch.js/issues/5425)) ([772c918](https://github.com/algolia/instantsearch.js/commit/772c918f47aec183af3f1aa78c65505f70dd0088)) +* **rendering:** always render with current state ([#5429](https://github.com/algolia/instantsearch.js/issues/5429)) ([920e951](https://github.com/algolia/instantsearch.js/commit/920e951f03aada0e6a1ce16bc389a82a2f00b202)) -**Note:** Version bump only for package react-instantsearch-core -## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.38.2...react-instantsearch-core@6.38.3) (2023-01-09) +## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.38.2...react-instantsearch-hooks@6.38.3) (2023-01-09) ### Bug Fixes @@ -76,9 +265,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-core@6.38.1...react-instantsearch-core@6.38.2) (2023-01-03) +## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.38.1...react-instantsearch-hooks@6.38.2) (2023-01-03) -**Note:** Version bump only for package react-instantsearch-core +**Note:** Version bump only for package react-instantsearch-hooks diff --git a/packages/react-instantsearch-core/README.md b/packages/react-instantsearch-core/README.md index 98816fd7a9..31010ec41a 100644 --- a/packages/react-instantsearch-core/README.md +++ b/packages/react-instantsearch-core/README.md @@ -3,11 +3,79 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [react-instantsearch-core](#react-instantsearch-core) + - [Installation](#installation) + - [Getting started](#getting-started) + - [API reference](#api-reference) + - [Documentation](#documentation) + - [Contributing](#contributing) + - [License](#license) # react-instantsearch-core -This is the [React](https://facebook.github.io/react) version of Algolia's `instantsearch` library. +React InstantSearch Core is an open-source UI library for React that lets you quickly build a search interface in your front-end application. -Go to the [React InstantSearch website](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) or the [React InstantSearch GitHub repository](https://github.com/algolia/instantsearch.js) for more information. +InstantSearch’s goal is to help you implement awesome search experiences as smoothly as possible by providing a [complete search ecosystem](https://algolia.com/doc/guides/getting-started/how-algolia-works/#the-full-ecosystem). InstantSearch tackles an important part of this vast goal by providing front-end primitives that you can assemble into unique search interfaces. + +

+ + Edit on CodeSandbox + +

+ +> Note: `react-instantsearch-core` exports renderless components and hooks which can be used for both web and React Native. If you are using React in a web project, we recommend using the package `react-instantsearch` instead, as it includes complete components that render to the DOM. + +## Installation + +React InstantSearch Core is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs. + +```sh +yarn add algoliasearch react-instantsearch-core +# or +npm install algoliasearch react-instantsearch-core +``` + +## Getting started + +React InstantSearch Core is a headless React library that lets you create an instant search results experience using Algolia’s search API. + +Check out our [**Getting Started guide**](https://algolia.com/doc/guides/building-search-ui/getting-started/react/) to start implementing a full-featured search experience with React InstantSearch Core. + +## API reference + +Check out the [**API reference**](https://www.algolia.com/doc/api-reference/widgets/react/). + +## Documentation + +The documentation is available on [algolia.com/doc](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/). + +## Contributing + +We welcome all contributors, from casual to regular 💙 + +- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport]. +- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest]. +- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it. +- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore]. + +To start contributing to code, you need to: + +1. [Fork the project](https://help.github.com/articles/fork-a-repo/) +1. [Clone the repository](https://help.github.com/articles/cloning-a-repository/) +1. Install the dependencies: `yarn` + +Please read [our contribution process](https://github.com/algolia/instantsearch.js/blob/master/CONTRIBUTING.md) to learn more. + +## License + +React InstantSearch is [MIT licensed](../../LICENSE). + + + +[contributing-bugreport]: https://github.com/algolia/instantsearch.js/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch +[contributing-featurerequest]: https://github.com/algolia/instantsearch.js/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20 +[contributing-newissue]: https://github.com/algolia/instantsearch.js/issues/new?labels=triage,Library%3A%20React+InstantSearch +[contributing-label-easy]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22 +[contributing-label-bug]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22 +[contributing-label-chore]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22 diff --git a/packages/react-instantsearch-hooks-server/global.d.ts b/packages/react-instantsearch-core/global.d.ts similarity index 100% rename from packages/react-instantsearch-hooks-server/global.d.ts rename to packages/react-instantsearch-core/global.d.ts diff --git a/packages/react-instantsearch-core/package.json b/packages/react-instantsearch-core/package.json index 187838203c..364f51475f 100644 --- a/packages/react-instantsearch-core/package.json +++ b/packages/react-instantsearch-core/package.json @@ -1,9 +1,18 @@ { "name": "react-instantsearch-core", - "version": "6.40.4", + "version": "6.47.3", "description": "⚡ Lightning-fast search for React, by Algolia", + "source": "src/index.ts", + "types": "dist/es/index.d.ts", "main": "dist/cjs/index.js", "module": "dist/es/index.js", + "type": "module", + "exports": { + ".": { + "import": "./dist/es/index.js", + "require": "./dist/cjs/index.js" + } + }, "sideEffects": false, "license": "MIT", "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/", @@ -30,20 +39,26 @@ "scripts": { "clean": "rm -rf dist", "watch": "yarn build:cjs --watch", - "build": "yarn build:cjs && yarn build:es && yarn build:umd", - "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", + "build": "yarn build:cjs && yarn build:es && yarn build:umd && yarn build:types", + "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet && ../../scripts/prepare-cjs.sh", "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", "build:umd": "BABEL_ENV=rollup rollup -c rollup.config.js", - "version": "./scripts/version.js" + "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es", + "test:exports": "node ./test/module/is-es-module.mjs && node ./test/module/is-cjs-module.cjs", + "version": "./scripts/version.cjs" }, "dependencies": { "@babel/runtime": "^7.1.2", "algoliasearch-helper": "3.14.0", - "prop-types": "^15.6.2", - "react-fast-compare": "^3.0.0" + "instantsearch.js": "4.56.8", + "use-sync-external-store": "^1.0.0" + }, + "devDependencies": { + "@types/use-sync-external-store": "0.0.3", + "react-instantsearch-core-v6": "npm:react-instantsearch-core@6.40.4" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 5", - "react": ">= 16.3.0 < 19" + "react": ">= 16.8.0 < 19" } } diff --git a/packages/react-instantsearch-core/rollup.config.js b/packages/react-instantsearch-core/rollup.config.js index 99dc225193..2e7d9b556a 100644 --- a/packages/react-instantsearch-core/rollup.config.js +++ b/packages/react-instantsearch-core/rollup.config.js @@ -26,7 +26,13 @@ const plugins = [ preferBuiltins: false, extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], }), - commonjs(), + commonjs({ + namedExports: { + '../../node_modules/use-sync-external-store/shim/index.js': [ + 'useSyncExternalStore', + ], + }, + }), globals(), replace({ 'process.env.NODE_ENV': JSON.stringify('production'), diff --git a/packages/react-instantsearch-hooks/scripts/version.cjs b/packages/react-instantsearch-core/scripts/version.cjs similarity index 100% rename from packages/react-instantsearch-hooks/scripts/version.cjs rename to packages/react-instantsearch-core/scripts/version.cjs diff --git a/packages/react-instantsearch-core/scripts/version.js b/packages/react-instantsearch-core/scripts/version.js deleted file mode 100755 index a60770b59c..0000000000 --- a/packages/react-instantsearch-core/scripts/version.js +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable import/no-commonjs */ - -const fs = require('fs'); -const path = require('path'); - -const version = require('../package.json').version; - -fs.writeFileSync( - path.resolve(__dirname, '../src/core/version.js'), - `export default '${version}';\n` -); diff --git a/packages/react-instantsearch-hooks/src/__tests__/compat.test.tsx b/packages/react-instantsearch-core/src/__tests__/compat.test.tsx similarity index 87% rename from packages/react-instantsearch-hooks/src/__tests__/compat.test.tsx rename to packages/react-instantsearch-core/src/__tests__/compat.test.tsx index 59b0711a3f..2dee61ffdf 100644 --- a/packages/react-instantsearch-hooks/src/__tests__/compat.test.tsx +++ b/packages/react-instantsearch-core/src/__tests__/compat.test.tsx @@ -5,7 +5,7 @@ import { createSearchClient } from '@instantsearch/mocks'; import { render } from '@testing-library/react'; import React from 'react'; -import { InstantSearch as InstantSearchCore } from 'react-instantsearch-core'; +import { InstantSearch as InstantSearchCore } from 'react-instantsearch-core-v6'; import { useSearchBox } from '../connectors/useSearchBox'; import { noop } from '../lib/noop'; @@ -32,7 +32,7 @@ describe('Compat', () => { }).toThrowErrorMatchingInlineSnapshot(` "[InstantSearch] Hooks must be used inside the component. - They are not compatible with the \`react-instantsearch-core\` and \`react-instantsearch-dom\` packages, so make sure to use the component from \`react-instantsearch-hooks\`." + They are not compatible with the \`react-instantsearch-core@6.x\` and \`react-instantsearch-dom\` packages, so make sure to use the component from \`react-instantsearch-core@7.x\`." `); jest.spyOn(console, 'error').mockRestore(); diff --git a/packages/react-instantsearch-hooks/src/__tests__/insights.test.tsx b/packages/react-instantsearch-core/src/__tests__/insights.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/__tests__/insights.test.tsx rename to packages/react-instantsearch-core/src/__tests__/insights.test.tsx diff --git a/packages/react-instantsearch-hooks/src/components/Configure.tsx b/packages/react-instantsearch-core/src/components/Configure.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/components/Configure.tsx rename to packages/react-instantsearch-core/src/components/Configure.tsx diff --git a/packages/react-instantsearch-hooks/src/components/DynamicWidgets.tsx b/packages/react-instantsearch-core/src/components/DynamicWidgets.tsx similarity index 98% rename from packages/react-instantsearch-hooks/src/components/DynamicWidgets.tsx rename to packages/react-instantsearch-core/src/components/DynamicWidgets.tsx index 8d42da145b..666def0d9b 100644 --- a/packages/react-instantsearch-hooks/src/components/DynamicWidgets.tsx +++ b/packages/react-instantsearch-core/src/components/DynamicWidgets.tsx @@ -34,7 +34,7 @@ export function DynamicWidgets({ warn( Fallback === FallbackComponent.current, - 'The `fallbackComponent` prop of `DynamicWidgets` changed between renders. Please provide a stable reference, as described in https://www.algolia.com/doc/api-reference/widgets/dynamic-facets/react-hooks/#widget-param-fallbackcomponent' + 'The `fallbackComponent` prop of `DynamicWidgets` changed between renders. Please provide a stable reference, as described in https://www.algolia.com/doc/api-reference/widgets/dynamic-facets/react/#widget-param-fallbackcomponent' ); const { attributesToRender } = useDynamicWidgets(props, { diff --git a/packages/react-instantsearch-hooks/src/components/Index.tsx b/packages/react-instantsearch-core/src/components/Index.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/components/Index.tsx rename to packages/react-instantsearch-core/src/components/Index.tsx diff --git a/packages/react-instantsearch-hooks/src/components/InstantSearch.tsx b/packages/react-instantsearch-core/src/components/InstantSearch.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/components/InstantSearch.tsx rename to packages/react-instantsearch-core/src/components/InstantSearch.tsx diff --git a/packages/react-instantsearch-hooks/src/components/InstantSearchSSRProvider.tsx b/packages/react-instantsearch-core/src/components/InstantSearchSSRProvider.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/components/InstantSearchSSRProvider.tsx rename to packages/react-instantsearch-core/src/components/InstantSearchSSRProvider.tsx diff --git a/packages/react-instantsearch-hooks/src/components/InstantSearchServerContext.ts b/packages/react-instantsearch-core/src/components/InstantSearchServerContext.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/components/InstantSearchServerContext.ts rename to packages/react-instantsearch-core/src/components/InstantSearchServerContext.ts diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/Configure.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/Configure.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/components/__tests__/Configure.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/Configure.test.tsx diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/DynamicWidgets.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/DynamicWidgets.test.tsx similarity index 99% rename from packages/react-instantsearch-hooks/src/components/__tests__/DynamicWidgets.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/DynamicWidgets.test.tsx index b1ebd295e0..a481cf5238 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/DynamicWidgets.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/DynamicWidgets.test.tsx @@ -405,7 +405,7 @@ describe('DynamicWidgets', () => { expect(() => { rerender(); }).toWarnDev( - '[InstantSearch] The `fallbackComponent` prop of `DynamicWidgets` changed between renders. Please provide a stable reference, as described in https://www.algolia.com/doc/api-reference/widgets/dynamic-facets/react-hooks/#widget-param-fallbackcomponent' + '[InstantSearch] The `fallbackComponent` prop of `DynamicWidgets` changed between renders. Please provide a stable reference, as described in https://www.algolia.com/doc/api-reference/widgets/dynamic-facets/react/#widget-param-fallbackcomponent' ); await waitFor(() => { diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/Index.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/Index.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/components/__tests__/Index.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/Index.test.tsx diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/InstantSearch.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx similarity index 99% rename from packages/react-instantsearch-hooks/src/components/__tests__/InstantSearch.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx index 3c70486967..79b8456f7b 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/InstantSearch.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx @@ -9,7 +9,7 @@ import userEvent from '@testing-library/user-event'; import { history } from 'instantsearch.js/es/lib/routers'; import { simple } from 'instantsearch.js/es/lib/stateMappings'; import React, { StrictMode, Suspense, version as ReactVersion } from 'react'; -import { SearchBox } from 'react-instantsearch-hooks-web'; +import { SearchBox } from 'react-instantsearch'; import { useRefinementList } from '../../connectors/useRefinementList'; import { warn } from '../../lib/warn'; @@ -108,7 +108,7 @@ describe('InstantSearch', () => { `react-instantsearch (${version})` ); expect(searchClient.addAlgoliaAgent).toHaveBeenCalledWith( - `react-instantsearch-hooks (${version})` + `react-instantsearch-core (${version})` ); expect(searchClient.addAlgoliaAgent).toHaveBeenCalledWith( `next.js (${nextVersion})` @@ -574,7 +574,7 @@ describe('InstantSearch', () => { `react-instantsearch (${version})` ); expect(searchClient2.addAlgoliaAgent).toHaveBeenCalledWith( - `react-instantsearch-hooks (${version})` + `react-instantsearch-core (${version})` ); }); @@ -591,7 +591,7 @@ describe('InstantSearch', () => { `react-instantsearch (${version})` ); expect(searchClient3.addAlgoliaAgent).toHaveBeenCalledWith( - `react-instantsearch-hooks (${version})` + `react-instantsearch-core (${version})` ); }); @@ -631,7 +631,7 @@ describe('InstantSearch', () => { expect(warn).toHaveBeenCalledWith( false, - 'The `searchClient` prop of `` changed between renders, which may cause more search requests than necessary. If this is an unwanted behavior, please provide a stable reference: https://www.algolia.com/doc/api-reference/widgets/instantsearch/react-hooks/#widget-param-searchclient' + 'The `searchClient` prop of `` changed between renders, which may cause more search requests than necessary. If this is an unwanted behavior, please provide a stable reference: https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/#widget-param-searchclient' ); }); diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/InstantSearchSSRProvider.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/InstantSearchSSRProvider.test.tsx similarity index 99% rename from packages/react-instantsearch-hooks/src/components/__tests__/InstantSearchSSRProvider.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/InstantSearchSSRProvider.test.tsx index 3deeeca2b6..10f74e60a2 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/InstantSearchSSRProvider.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/InstantSearchSSRProvider.test.tsx @@ -12,7 +12,7 @@ import userEvent from '@testing-library/user-event'; import { history } from 'instantsearch.js/es/lib/routers'; import { simple } from 'instantsearch.js/es/lib/stateMappings'; import React, { StrictMode } from 'react'; -import { Hits, RefinementList, SearchBox } from 'react-instantsearch-hooks-web'; +import { Hits, RefinementList, SearchBox } from 'react-instantsearch'; import { InstantSearch } from '../InstantSearch'; import { InstantSearchSSRProvider } from '../InstantSearchSSRProvider'; diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/InstantSearchServerContext.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/InstantSearchServerContext.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/components/__tests__/InstantSearchServerContext.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/InstantSearchServerContext.test.tsx diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/routing/dispose-start.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/routing/dispose-start.test.tsx similarity index 96% rename from packages/react-instantsearch-hooks/src/components/__tests__/routing/dispose-start.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/routing/dispose-start.test.tsx index f29df0b426..76831b2e3e 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/routing/dispose-start.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/routing/dispose-start.test.tsx @@ -6,11 +6,7 @@ import { createSearchClient } from '@instantsearch/mocks'; import { render, waitFor } from '@testing-library/react'; import historyRouter from 'instantsearch.js/es/lib/routers/history'; import React, { useEffect } from 'react'; -import { - InstantSearch, - SearchBox, - useSearchBox, -} from 'react-instantsearch-hooks-web'; +import { InstantSearch, SearchBox, useSearchBox } from 'react-instantsearch'; describe('routing back and forth to an InstantSearch instance', () => { test('updates the URL after the instance is disposed then restarted', async () => { diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/routing/external-influence.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/routing/external-influence.test.tsx similarity index 95% rename from packages/react-instantsearch-hooks/src/components/__tests__/routing/external-influence.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/routing/external-influence.test.tsx index c003eb0731..3b435b91d9 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/routing/external-influence.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/routing/external-influence.test.tsx @@ -6,11 +6,7 @@ import { createSearchClient } from '@instantsearch/mocks'; import { render, waitFor } from '@testing-library/react'; import historyRouter from 'instantsearch.js/es/lib/routers/history'; import React, { useEffect } from 'react'; -import { - InstantSearch, - SearchBox, - useSearchBox, -} from 'react-instantsearch-hooks-web'; +import { InstantSearch, SearchBox, useSearchBox } from 'react-instantsearch'; describe('routing with external influence', () => { test('keeps on working when the URL is updated by another program', async () => { diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/routing/modal.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/routing/modal.test.tsx similarity index 95% rename from packages/react-instantsearch-hooks/src/components/__tests__/routing/modal.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/routing/modal.test.tsx index dccf619390..a11e775927 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/routing/modal.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/routing/modal.test.tsx @@ -7,7 +7,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import historyRouter from 'instantsearch.js/es/lib/routers/history'; import React from 'react'; -import { InstantSearch, SearchBox } from 'react-instantsearch-hooks-web'; +import { InstantSearch, SearchBox } from 'react-instantsearch'; describe('routing with no navigation', () => { test('cleans the URL when InstantSearch is disposed within the same page', async () => { diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/routing/spa-debounced.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/routing/spa-debounced.test.tsx similarity index 97% rename from packages/react-instantsearch-hooks/src/components/__tests__/routing/spa-debounced.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/routing/spa-debounced.test.tsx index 988203177d..3ec72830d8 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/routing/spa-debounced.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/routing/spa-debounced.test.tsx @@ -7,7 +7,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import historyRouter from 'instantsearch.js/es/lib/routers/history'; import React from 'react'; -import { InstantSearch, SearchBox } from 'react-instantsearch-hooks-web'; +import { InstantSearch, SearchBox } from 'react-instantsearch'; describe('routing with debounced third-party client-side router', () => { test('does not clean the URL after navigating', async () => { diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/routing/spa-replace-state.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/routing/spa-replace-state.test.tsx similarity index 97% rename from packages/react-instantsearch-hooks/src/components/__tests__/routing/spa-replace-state.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/routing/spa-replace-state.test.tsx index 293eb52ee5..4c96c520f1 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/routing/spa-replace-state.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/routing/spa-replace-state.test.tsx @@ -7,7 +7,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import historyRouter from 'instantsearch.js/es/lib/routers/history'; import React from 'react'; -import { InstantSearch, SearchBox } from 'react-instantsearch-hooks-web'; +import { InstantSearch, SearchBox } from 'react-instantsearch'; describe('routing using `replaceState`', () => { // We can't assert whether another router did update the URL diff --git a/packages/react-instantsearch-hooks/src/components/__tests__/routing/spa.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/routing/spa.test.tsx similarity index 96% rename from packages/react-instantsearch-hooks/src/components/__tests__/routing/spa.test.tsx rename to packages/react-instantsearch-core/src/components/__tests__/routing/spa.test.tsx index 31006766c0..1c43580225 100644 --- a/packages/react-instantsearch-hooks/src/components/__tests__/routing/spa.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/routing/spa.test.tsx @@ -7,7 +7,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import historyRouter from 'instantsearch.js/es/lib/routers/history'; import React from 'react'; -import { InstantSearch, SearchBox } from 'react-instantsearch-hooks-web'; +import { InstantSearch, SearchBox } from 'react-instantsearch'; describe('routing with third-party client-side router', () => { test('does not clean the URL after navigating', async () => { diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectAutoComplete.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectAutoComplete.js deleted file mode 100644 index cbb7a4124f..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectAutoComplete.js +++ /dev/null @@ -1,264 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectAutoComplete'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectAutoComplete', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides current hits to the component', () => { - const hits = [{}]; - let props = connect.getProvidedProps( - { contextValue }, - {}, - { - results: { hits, page: 0, hitsPerPage: 20 }, - } - ); - expect(props).toEqual({ - hits: [{ __position: 1 }], - currentRefinement: '', - }); - - props = connect.getProvidedProps( - { contextValue }, - { query: 'query' }, - { - results: { hits, page: 0, hitsPerPage: 20 }, - } - ); - expect(props).toEqual({ - hits: [{ __position: 1 }], - currentRefinement: 'query', - }); - - props = connect.getProvidedProps( - { defaultRefinement: 'query', contextValue }, - {}, - { - results: { hits, page: 0, hitsPerPage: 20 }, - } - ); - expect(props).toEqual({ - hits: [{ __position: 1 }], - currentRefinement: 'query', - }); - }); - - it('provides current hits to the component with queryID & position', () => { - const hits = [{}]; - const hitsWithExtraInfo = [{ __queryID: 'zombo.com', __position: 1 }]; - let props = connect.getProvidedProps( - { contextValue }, - {}, - { - results: { hits, page: 0, hitsPerPage: 20, queryID: 'zombo.com' }, - } - ); - expect(props).toEqual({ - hits: hitsWithExtraInfo, - currentRefinement: '', - }); - - props = connect.getProvidedProps( - { contextValue }, - { query: 'query' }, - { - results: { hits, page: 0, hitsPerPage: 20, queryID: 'zombo.com' }, - } - ); - expect(props).toEqual({ - hits: hitsWithExtraInfo, - currentRefinement: 'query', - }); - - props = connect.getProvidedProps( - { defaultRefinement: 'query', contextValue }, - {}, - { - results: { hits, page: 0, hitsPerPage: 20, queryID: 'zombo.com' }, - } - ); - expect(props).toEqual({ - hits: hitsWithExtraInfo, - currentRefinement: 'query', - }); - }); - - it('refines the query parameter', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { contextValue }, - { query: 'bar' } - ); - expect(params.query).toBe('bar'); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { contextValue }, - { otherKey: 'val' }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - query: 'yep', - page: 1, - }); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { contextValue }, - { - query: { searchState: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ another: { searchState: 'searchState' } }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides current hits to the component', () => { - const firstHits = [{}]; - const secondHits = [{}]; - const firstHitsWithExtraInfo = [ - { __queryID: 'zombo.com', __position: 1 }, - ]; - const secondHitsWithExtraInfo = [ - { __queryID: 'html5zombo.com', __position: 1 }, - ]; - let props = connect.getProvidedProps( - { contextValue, indexContextValue }, - {}, - { - results: { - first: { - hits: firstHits, - page: 0, - hitsPerPage: 20, - queryID: 'zombo.com', - }, - second: { - hits: secondHits, - page: 0, - hitsPerPage: 20, - queryID: 'html5zombo.com', - }, - }, - } - ); - expect(props).toEqual({ - hits: [ - { - hits: firstHitsWithExtraInfo, - index: 'first', - }, - { - hits: secondHitsWithExtraInfo, - - index: 'second', - }, - ], - currentRefinement: '', - }); - - props = connect.getProvidedProps( - { contextValue, indexContextValue }, - { indices: { second: { query: 'query' } } }, - { - results: { - first: { - hits: firstHits, - page: 0, - hitsPerPage: 20, - queryID: 'zombo.com', - }, - second: { - hits: secondHits, - page: 0, - hitsPerPage: 20, - queryID: 'html5zombo.com', - }, - }, - } - ); - expect(props).toEqual({ - hits: [ - { hits: firstHitsWithExtraInfo, index: 'first' }, - { hits: secondHitsWithExtraInfo, index: 'second' }, - ], - currentRefinement: 'query', - }); - - props = connect.getProvidedProps( - { defaultRefinement: 'query', contextValue, indexContextValue }, - {}, - { - results: { - first: { - hits: firstHits, - page: 0, - hitsPerPage: 20, - queryID: 'zombo.com', - }, - second: { - hits: secondHits, - page: 0, - hitsPerPage: 20, - queryID: 'html5zombo.com', - }, - }, - } - ); - expect(props).toEqual({ - hits: [ - { hits: firstHitsWithExtraInfo, index: 'first' }, - { hits: secondHitsWithExtraInfo, index: 'second' }, - ], - currentRefinement: 'query', - }); - }); - - it('refines the query parameter', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { contextValue, indexContextValue }, - { indices: { second: { query: 'bar' } } } - ); - expect(params.query).toBe('bar'); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { contextValue, indexContextValue }, - { otherKey: 'val' }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { second: { query: 'yep', page: 1 } }, - }); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { contextValue, indexContextValue }, - { - indices: { second: { query: '' } }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - indices: { second: {} }, - another: { searchState: 'searchState' }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectBreadcrumb.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectBreadcrumb.js deleted file mode 100644 index 141fc438dd..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectBreadcrumb.js +++ /dev/null @@ -1,279 +0,0 @@ -import connect from '../connectBreadcrumb'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; - -describe('connectBreadcrumb', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - const results = { - getFacetValues: jest.fn(), - getFacetByName: () => true, - hits: [], - }; - - results.getFacetValues.mockImplementationOnce(() => ({})); - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue }, - { hierarchicalMenu: { ok: 'wat' } }, - { results } - ); - expect(props).toEqual({ - canRefine: false, - items: [], - }); - - results.getFacetValues.mockClear(); - results.getFacetValues.mockImplementation(() => ({ - data: [ - { - name: 'wat', - path: 'wat', - escapedValue: 'wat', - count: 20, - isRefined: true, - data: [ - { - name: 'wot', - path: 'wat > wot', - escapedValue: 'wat > wot', - isRefined: true, - count: 15, - }, - { - name: 'wut', - path: 'wat > wut', - escapedValue: 'wat > wut', - isRefined: false, - count: 5, - }, - ], - }, - { - name: 'oy', - path: 'oy', - escapedValue: 'oy', - isRefined: false, - count: 10, - }, - ], - })); - props = connect.getProvidedProps( - { - attributes: ['ok'], - contextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - }, - { - label: 'wot', - value: 'wat > wot', - }, - ]); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { attributes: ['ok'], transformItems, contextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - label: 'wat', - value: 'wat', - }, - { - label: 'wot', - value: 'wat > wot', - }, - ]); - expect(props.items).toEqual(['items']); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { attributes: ['ok'], contextValue }, - { otherKey: 'val', hierarchicalMenu: { otherKey: 'val' } }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - const results = { - second: { - getFacetValues: jest.fn(), - getFacetByName: () => true, - }, - }; - - results.second.getFacetValues.mockImplementationOnce(() => ({})); - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue, indexContextValue }, - { indices: { second: { hierarchicalMenu: { ok: 'wat' } } } }, - { results } - ); - expect(props).toEqual({ - canRefine: false, - items: [], - }); - - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue, indexContextValue }, - {}, - {} - ); - expect(props).toEqual({ - canRefine: false, - items: [], - }); - - results.second.getFacetValues.mockClear(); - results.second.getFacetValues.mockImplementation(() => ({ - data: [ - { - name: 'wat', - path: 'wat', - escapedValue: 'wat', - count: 20, - isRefined: true, - data: [ - { - name: 'wot', - path: 'wat > wot', - escapedValue: 'wat > wot', - isRefined: true, - count: 15, - }, - { - name: 'wut', - path: 'wat > wut', - escapedValue: 'wat > wut', - isRefined: false, - count: 5, - }, - ], - }, - { - name: 'oy', - path: 'oy', - escapedValue: 'oy', - isRefined: false, - count: 10, - }, - ], - })); - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue, indexContextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - }, - { - label: 'wot', - value: 'wat > wot', - }, - ]); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { attributes: ['ok'], transformItems, contextValue, indexContextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - label: 'wat', - value: 'wat', - }, - { - label: 'wot', - value: 'wat > wot', - }, - ]); - expect(props.items).toEqual(['items']); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { - attributes: ['ok'], - contextValue, - indexContextValue, - }, - { - indices: { - first: { otherKey: 'val1', hierarchicalMenu: { otherKey: 'val1' } }, - second: { otherKey: 'val', hierarchicalMenu: { otherKey: 'val' } }, - }, - }, - 'yep' - ); - - expect(nextState).toEqual({ - indices: { - first: { - otherKey: 'val1', - hierarchicalMenu: { otherKey: 'val1' }, - }, - second: { - page: 1, - otherKey: 'val', - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }, - }, - }); - - nextState = connect.refine( - { - attributes: ['ok'], - contextValue, - indexContextValue: { targetedIndex: 'second' }, - }, - { - indices: { - first: { page: 1, hierarchicalMenu: { otherKey: 'val1' } }, - second: { - otherKey: 'val', - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }, - }, - }, - 'yep' - ); - - expect(nextState).toEqual({ - indices: { - first: { page: 1, hierarchicalMenu: { otherKey: 'val1' } }, - second: { - page: 1, - otherKey: 'val', - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }, - }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js deleted file mode 100644 index 3c1f0a5a36..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js +++ /dev/null @@ -1,271 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectConfigure'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectConfigure', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('propagates the props to the SearchParameters without children & contextValue', () => { - const searchParameters = connect.getSearchParameters.call( - {}, - new SearchParameters(), - { distinct: 1, whatever: 'please', children: 'whatever', contextValue }, - {} - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - distinct: 1, - whatever: 'please', - }) - ); - expect(searchParameters).not.toEqual( - expect.objectContaining({ - children: 'whatever', - contextValue, - }) - ); - expect(searchParameters.distinct).toEqual(1); - expect(searchParameters.whatever).toEqual('please'); - expect(searchParameters.children).toBeUndefined(); - expect(searchParameters.contextValue).toBeUndefined(); - }); - - it('calling transitionState should add configure parameters to the search state', () => { - const instance = {}; - let searchState = connect.transitionState.call( - instance, - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue, - }, - {}, - {} - ); - expect(searchState).toEqual({ - configure: { distinct: 1, whatever: 'please' }, - }); - - searchState = connect.transitionState.call( - instance, - { whatever: 'other', children: 'whatever', contextValue }, - { configure: { distinct: 1, whatever: 'please' } }, - { configure: { distinct: 1, whatever: 'please' } } - ); - - expect(searchState).toEqual({ configure: { whatever: 'other' } }); - }); - - it('calling cleanUp should remove configure parameters from the search state', () => { - let searchState = connect.cleanUp.call( - {}, - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue, - }, - { - configure: { - distinct: 1, - whatever: 'please', - another: 'parameters', - }, - } - ); - expect(searchState).toEqual({ configure: { another: 'parameters' } }); - - searchState = connect.cleanUp.call( - {}, - { distinct: 1, whatever: 'please', children: 'whatever', contextValue }, - { configure: { distinct: 1, whatever: 'please' } } - ); - expect(searchState).toEqual({ configure: {} }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('propagates the props to the SearchParameters without children', () => { - const searchParameters = connect.getSearchParameters.call( - {}, - new SearchParameters(), - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue, - indexContextValue, - }, - {} - ); - expect(searchParameters.distinct).toEqual(1); - expect(searchParameters.whatever).toEqual('please'); - expect(searchParameters.children).toBeUndefined(); - expect(searchParameters.contextValue).toBeUndefined(); - expect(searchParameters.indexContextValue).toBeUndefined(); - }); - - it('calling transitionState should add configure parameters to the search state', () => { - const instance = {}; - let searchState = connect.transitionState.call( - instance, - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue, - indexContextValue, - }, - {}, - {} - ); - expect(searchState).toEqual({ - indices: { second: { configure: { distinct: 1, whatever: 'please' } } }, - }); - - searchState = connect.transitionState.call( - instance, - { - whatever: 'other', - children: 'whatever', - contextValue, - indexContextValue, - }, - { - indices: { - second: { configure: { distinct: 1, whatever: 'please' } }, - }, - }, - { - indices: { - second: { configure: { distinct: 1, whatever: 'please' } }, - }, - } - ); - - expect(searchState).toEqual({ - indices: { second: { configure: { whatever: 'other' } } }, - }); - - searchState = connect.transitionState.call( - instance, - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue: { mainTargetedIndex: 'first' }, - indexContextValue: { targetedIndex: 'first' }, - }, - { indices: { second: { configure: { whatever: 'other' } } } }, - { indices: { second: { configure: { whatever: 'other' } } } } - ); - - expect(searchState).toEqual({ - indices: { - second: { configure: { whatever: 'other' } }, - first: { configure: { distinct: 1, whatever: 'please' } }, - }, - }); - - searchState = connect.transitionState.call( - instance, - { - whatever: 'other', - children: 'whatever', - contextValue: { mainTargetedIndex: 'first' }, - indexContextValue: { targetedIndex: 'first' }, - }, - { - indices: { - second: { configure: { whatever: 'other' } }, - first: { configure: { distinct: 1, whatever: 'please' } }, - }, - }, - { - indices: { - second: { configure: { whatever: 'other' } }, - first: { configure: { distinct: 1, whatever: 'please' } }, - }, - } - ); - - expect(searchState).toEqual({ - indices: { - second: { configure: { whatever: 'other' } }, - first: { configure: { whatever: 'other' } }, - }, - }); - }); - - it('calling cleanUp should remove configure parameters from the search state', () => { - const instance = {}; - let searchState = connect.cleanUp.call( - instance, - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue, - indexContextValue, - }, - { - indices: { - second: { configure: { whatever: 'other' } }, - first: { configure: { distinct: 1, whatever: 'please' } }, - }, - } - ); - expect(searchState).toEqual({ - indices: { - first: { configure: { distinct: 1, whatever: 'please' } }, - second: { configure: {} }, - }, - }); - - searchState = connect.cleanUp.call( - instance, - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue: { mainTargetedIndex: 'first' }, - indexContextValue: { targetedIndex: 'first' }, - }, - { - indices: { - first: { configure: { distinct: 1, whatever: 'please' } }, - second: { configure: {} }, - }, - } - ); - expect(searchState).toEqual({ - indices: { first: { configure: {} }, second: { configure: {} } }, - }); - - searchState = connect.cleanUp.call( - instance, - { - distinct: 1, - whatever: 'please', - children: 'whatever', - contextValue: { mainTargetedIndex: 'first' }, - indexContextValue: { targetedIndex: 'first' }, - }, - { indices: {} } - ); - expect(searchState).toEqual({ - indices: { - first: { configure: {} }, - }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigureRelatedItems.ts b/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigureRelatedItems.ts deleted file mode 100644 index 411276cbfd..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigureRelatedItems.ts +++ /dev/null @@ -1,583 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connectConfigureRelatedItems from '../connectConfigureRelatedItems'; - -import type { PlainSearchParameters } from 'algoliasearch-helper'; - -jest.mock('../../core/createConnector', () => (x: any) => x); -const connect = connectConfigureRelatedItems as any; - -const hit = { - objectID: '1', - name: 'Amazon - Fire TV Stick with Alexa Voice Remote - Black', - description: - 'Enjoy smart access to videos, games and apps with this Amazon Fire TV stick.', - brand: 'Amazon', - categories: ['TV & Home Theater', 'Streaming Media Players'], - hierarchicalCategories: { - lvl0: 'TV & Home Theater', - lvl1: 'TV & Home Theater > Streaming Media Players', - }, - price: 39.99, - free_shipping: false, - rating: 4, - _snippetResult: { - description: { - value: - 'Enjoy smart access to videos, games and apps with this Amazon Fire TV stick. Its […]', - matchLevel: 'none', - }, - }, - _highlightResult: { - description: { - value: - 'Enjoy smart access to videos, games and apps with this Amazon Fire TV stick. Its […]', - matchLevel: 'none', - matchedWords: [], - }, - }, -}; - -describe('connectConfigure', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - const defaultProps = { - transformSearchParameters: (x: PlainSearchParameters) => x, - contextValue, - }; - - it('does not provide props', () => { - const providedProps = connect.getProvidedProps({ contextValue }, null, { - results: {}, - }); - - expect(providedProps).toEqual({}); - }); - - it('sets the optionalFilters search parameter based on matchingPatterns', () => { - const searchParameters = connect.getSearchParameters.call( - { contextValue }, - new SearchParameters(), - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - } - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - }) - ); - }); - - it('sets the optionalFilters search parameter based on matchingPatterns with nested attributes', () => { - const searchParameters = connect.getSearchParameters.call( - { contextValue }, - new SearchParameters(), - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - 'hierarchicalCategories.lvl0': { score: 2 }, - }, - } - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - 'hierarchicalCategories.lvl0:TV & Home Theater', - ], - }) - ); - }); - - it('sets transformed search parameters based on transformSearchParameters', () => { - const searchParameters = connect.getSearchParameters.call( - { contextValue }, - new SearchParameters(), - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - transformSearchParameters(parameters: PlainSearchParameters) { - return { - ...parameters, - optionalWords: hit.name.split(' '), - }; - }, - } - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - optionalWords: [ - 'Amazon', - '-', - 'Fire', - 'TV', - 'Stick', - 'with', - 'Alexa', - 'Voice', - 'Remote', - '-', - 'Black', - ], - }) - ); - }); - - it('filters out and warns for incorrect optionalFilters value type', () => { - // We need to simulate being in development mode and to mock the global console object - // in this test to assert that development warnings are displayed correctly. - const originalNodeEnv = process.env.NODE_ENV; - process.env.NODE_ENV = 'development'; - const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); - - const searchParameters = connect.getSearchParameters.call( - { contextValue }, - new SearchParameters(), - { - ...defaultProps, - hit, - matchingPatterns: { - rating: { score: 1 }, // `rating` is a number, which is not supported by the `optionalFilters` API - brand: { score: 3 }, - categories: { score: 2 }, - }, - } - ); - - expect(warnSpy).toHaveBeenCalledTimes(1); - expect(warnSpy).toHaveBeenCalledWith( - 'The `matchingPatterns` option returned a value of type Number for the "rating" key. This value was not sent to Algolia because `optionalFilters` only supports strings and array of strings.\n\n' + - 'You can remove the "rating" key from the `matchingPatterns` option.\n\n' + - 'See https://www.algolia.com/doc/api-reference/api-parameters/optionalFilters/' - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - }) - ); - - process.env.NODE_ENV = originalNodeEnv; - warnSpy.mockRestore(); - }); - - it('calling transitionState should add configure parameters to the search state', () => { - const instance = {}; - - let searchState = connect.transitionState.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - {}, - {} - ); - - expect(searchState).toEqual({ - configure: { - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - }, - }); - - searchState = connect.transitionState.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - }, - }, - { configure: { facets: ['categories'] } }, - { configure: { facets: ['categories'] } } - ); - - expect(searchState).toEqual({ - configure: { - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: ['brand:Amazon'], - facets: ['categories'], - }, - }); - }); - - it('calling cleanUp should remove configure parameters from the search state', () => { - const instance = {}; - - // Running `transitionState` allows to set previous search parameters - // in the connector state. - let searchState = connect.transitionState.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - {}, - {} - ); - - searchState = connect.cleanUp.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - { - configure: { - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: ['brand:Amazon'], - facets: ['categories'], - }, - } - ); - - expect(searchState).toEqual({ - configure: { - facets: ['categories'], - }, - }); - - searchState = connect.cleanUp.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - { - configure: { - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: ['brand:Amazon'], - }, - } - ); - - expect(searchState).toEqual({ configure: {} }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - const defaultProps = { - transformSearchParameters: (x: PlainSearchParameters) => x, - contextValue, - indexContextValue, - }; - - it('does not provide props', () => { - const providedProps = connect.getProvidedProps({ contextValue }, null, { - results: {}, - }); - - expect(providedProps).toEqual({}); - }); - - it('sets the optionalFilters search parameter based on matchingPatterns', () => { - const searchParameters = connect.getSearchParameters.call( - { contextValue }, - new SearchParameters(), - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - } - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - }) - ); - }); - - it('sets transformed search parameters based on transformSearchParameters', () => { - const searchParameters = connect.getSearchParameters.call( - { contextValue }, - new SearchParameters(), - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - transformSearchParameters(parameters: PlainSearchParameters) { - return { - ...parameters, - optionalWords: hit.name.split(' '), - }; - }, - } - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - optionalWords: [ - 'Amazon', - '-', - 'Fire', - 'TV', - 'Stick', - 'with', - 'Alexa', - 'Voice', - 'Remote', - '-', - 'Black', - ], - }) - ); - }); - - it('filters out and warns for incorrect optionalFilters value type', () => { - // We need to simulate being in development mode and to mock the global console object - // in this test to assert that development warnings are displayed correctly. - const originalNodeEnv = process.env.NODE_ENV; - process.env.NODE_ENV = 'development'; - const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); - - const searchParameters = connect.getSearchParameters.call( - { contextValue }, - new SearchParameters(), - { - ...defaultProps, - hit, - matchingPatterns: { - rating: { score: 1 }, // `rating` is a number, which is not supported by the `optionalFilters` API - brand: { score: 3 }, - categories: { score: 2 }, - }, - } - ); - - expect(warnSpy).toHaveBeenCalledTimes(1); - expect(warnSpy).toHaveBeenCalledWith( - 'The `matchingPatterns` option returned a value of type Number for the "rating" key. This value was not sent to Algolia because `optionalFilters` only supports strings and array of strings.\n\n' + - 'You can remove the "rating" key from the `matchingPatterns` option.\n\n' + - 'See https://www.algolia.com/doc/api-reference/api-parameters/optionalFilters/' - ); - - expect(searchParameters).toEqual( - expect.objectContaining({ - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - }) - ); - - process.env.NODE_ENV = originalNodeEnv; - warnSpy.mockRestore(); - }); - - it('calling transitionState should add configure parameters to the search state', () => { - const instance = {}; - - const searchState = connect.transitionState.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - {}, - {} - ); - - expect(searchState).toEqual({ - indices: { - second: { - configure: { - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: [ - 'brand:Amazon', - [ - 'categories:TV & Home Theater', - 'categories:Streaming Media Players', - ], - ], - }, - }, - }, - }); - }); - - it('calling cleanUp should remove configure parameters from the search state', () => { - const instance = {}; - - // Running `transitionState` allows to set previous search parameters - // in the connector state. - let searchState = connect.transitionState.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - {}, - {} - ); - - searchState = connect.cleanUp.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - { - indices: { - second: { - configure: { - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: ['brand:Amazon'], - facets: ['categories'], - }, - }, - }, - } - ); - - expect(searchState).toEqual({ - indices: { - second: { - configure: { - facets: ['categories'], - }, - }, - }, - }); - - searchState = connect.cleanUp.call( - instance, - { - ...defaultProps, - hit, - matchingPatterns: { - brand: { score: 3 }, - categories: { score: 2 }, - }, - }, - { - indices: { - second: { - configure: { - facetFilters: ['objectID:-1'], - sumOrFiltersScores: true, - optionalFilters: ['brand:Amazon'], - }, - }, - }, - } - ); - - expect(searchState).toEqual({ indices: { second: { configure: {} } } }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectCurrentRefinements.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectCurrentRefinements.js deleted file mode 100644 index 935804f899..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectCurrentRefinements.js +++ /dev/null @@ -1,124 +0,0 @@ -import connect from '../connectCurrentRefinements'; - -jest.mock('../../core/createConnector', () => (x) => x); - -const { refine } = connect; - -describe('connectCurrentRefinements', () => { - it('provides the correct props to the component', () => { - let props = connect.getProvidedProps({}, null, null, [ - { items: [{ label: 'one' }], id: 1, index: 'something' }, - { items: [{ label: 'two' }], id: 2, index: 'something' }, - { items: [{ label: 'three' }], id: 'query', index: 'something' }, - ]); - expect(props.items).toEqual([ - { id: 1, index: 'something', label: 'one' }, - { id: 2, index: 'something', label: 'two' }, - ]); - - props = connect.getProvidedProps({}, null, null, []); - expect(props).toEqual({ canRefine: false, items: [] }); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps({ transformItems }, null, null, [ - { items: [{ label: 'one' }], id: 1, index: 'something' }, - { items: [{ label: 'two' }], id: 2, index: 'something' }, - { items: [{ label: 'three' }], id: 3, index: 'something' }, - ]); - expect(transformItems.mock.calls[0][0]).toEqual([ - { id: 1, index: 'something', label: 'one' }, - { id: 2, index: 'something', label: 'two' }, - { id: 3, index: 'something', label: 'three' }, - ]); - expect(props.items).toEqual(['items']); - }); - - it('provides the query if clearsQuery props is true', () => { - const results = { - index: { - query: 'query', - }, - }; - - const props = connect.getProvidedProps( - { clearsQuery: true }, - null, - { results }, - [{ items: [{ currentRefinement: 'query' }], id: 'query', index: '' }] - ); - - expect(props.items).toEqual([ - { currentRefinement: 'query', id: 'query', index: '' }, - ]); - }); - - it('dont provide the query if clearsQuery props is true but the current refinement is an empty string', () => { - const results = { - index: { - query: '', - }, - }; - - const props = connect.getProvidedProps( - { clearsQuery: true }, - null, - { results }, - [{ items: [{ currentRefinement: '' }], id: 'query' }] - ); - - expect(props.items).toEqual([]); - }); - - it('refine applies the selected filters clear method on searchState', () => { - let searchState = refine({}, { wow: 'sweet' }, [ - { - value: (nextState) => ({ ...nextState, cool: 'neat' }), - }, - ]); - expect(searchState).toEqual({ wow: 'sweet', cool: 'neat' }); - - searchState = refine({ clearsQuery: true }, { wow: 'sweet' }, [ - { - value: (nextState) => ({ ...nextState, cool: 'neat' }), - }, - ]); - expect(searchState).toEqual({ wow: 'sweet', cool: 'neat' }); - }); - - it('deduplicates entries with transformItems', () => { - const transformItems = (items) => - items - .map(({ id, index, ...rest }) => ({ - __dedupe: `${index}.${id}`, - id, - index, - ...rest, - })) - .sort((a, b) => a.id > b.__dedupe) - .filter( - (current, index, array) => - index === 0 || current.__dedupe !== array[index - 1].__dedupe - ) - .map(({ __dedupe, ...item }) => item); - - const props = connect.getProvidedProps({ transformItems }, null, null, [ - { items: [{ label: 'abra' }], id: 1, index: 'something' }, - { items: [{ label: 'cadabra' }], id: 2, index: 'something' }, - { items: [{ label: 'cadabra' }], id: 2, index: 'something' }, - ]); - - expect(props.items).toEqual([ - { id: 1, index: 'something', label: 'abra' }, - { id: 2, index: 'something', label: 'cadabra' }, - ]); - }); - - it('computes canRefine based on the length of the transformed items list', () => { - const transformItems = () => []; - const props = connect.getProvidedProps({ transformItems }, null, null, [ - { items: [{ label: 'one' }], id: 1, index: 'something' }, - ]); - - expect(props.canRefine).toEqual(false); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectDynamicWidgets.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectDynamicWidgets.js deleted file mode 100644 index 313892cc39..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectDynamicWidgets.js +++ /dev/null @@ -1,504 +0,0 @@ -import { SearchResults, SearchParameters } from 'algoliasearch-helper'; - -import connector from '../connectDynamicWidgets'; - -jest.mock('../../core/createConnector', () => (x) => x); - -const EMPTY_RESPONSE = { - results: [ - { - hits: [], - nbHits: 0, - page: 0, - nbPages: 0, - hitsPerPage: 20, - exhaustiveNbHits: true, - query: '', - queryAfterRemoval: '', - params: - 'highlightPreTag=%3Cais-highlight-0000000000%3E&highlightPostTag=%3C%2Fais-highlight-0000000000%3E&query=&facets=%5B%5D&tagFilters=', - index: 'instant_search', - processingTimeMS: 2, - }, - ], -}; - -describe('connectDynamicWidgets', () => { - const empty = {}; - const contextValue = { - mainTargetedIndex: 'index', - }; - - describe('getSearchParameters', () => { - it('sets facets * and maxValuesPerFacet by default', () => { - const props = { - contextValue, - ...connector.defaultProps, - }; - const searchState = {}; - - const actual = connector.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(actual).toEqual( - new SearchParameters({ - facets: ['*'], - maxValuesPerFacet: 20, - }) - ); - }); - - it('gets added onto existing parameters', () => { - const props = { - contextValue, - ...connector.defaultProps, - }; - const searchState = {}; - - const actual = connector.getSearchParameters( - new SearchParameters({ - facets: ['123'], - maxValuesPerFacet: 1000, - }), - props, - searchState - ); - - expect(actual).toEqual( - new SearchParameters({ - facets: ['123', '*'], - maxValuesPerFacet: 1000, - }) - ); - }); - - it('allows override of facets and maxValuesPerFacet', () => { - const props = { - contextValue, - ...connector.defaultProps, - facets: ['lol'], - maxValuesPerFacet: 1000, - }; - const searchState = {}; - - const actual = connector.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(actual).toEqual( - new SearchParameters({ - facets: ['lol'], - maxValuesPerFacet: 1000, - }) - ); - }); - }); - - describe('single index', () => { - const createSingleIndexSearchResults = (result = {}, state) => ({ - results: new SearchResults(new SearchParameters(state), [ - { - ...EMPTY_RESPONSE.results[0], - ...result, - }, - ]), - }); - - describe('getProvidedProps', () => { - it('empty results', () => { - const props = { contextValue }; - const searchState = {}; - const searchResults = empty; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = { - attributesToRender: [], - }; - - expect(actual).toEqual(expectation); - }); - - it('attributesToRender is the return value of transformItems', () => { - const returnValue = ['test1', 'test2']; - const props = { contextValue, transformItems: () => returnValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual(returnValue); - }); - - it('default items is []', () => { - const props = { contextValue, transformItems: (items) => items }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual([]); - }); - - it('reads from facetOrdering by default', () => { - const props = { - contextValue, - ...connector.defaultProps, - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - renderingContent: { - facetOrdering: { facets: { order: ['one', 'two'] } }, - }, - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual(['one', 'two']); - }); - - it('transformItems gets called with results', () => { - const props = { - contextValue, - transformItems: jest.fn((items) => items), - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - userData: [{ MOCK_FACET_ORDER: ['one', 'two'] }], - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(props.transformItems).toHaveBeenCalledTimes(1); - expect(props.transformItems).toHaveBeenCalledWith([], { - results: searchResults.results, - }); - - expect(actual.attributesToRender).toEqual([]); - }); - - it('userData is usable as a source for transformItems', () => { - const props = { - contextValue, - transformItems: (_items, { results }) => - results.userData[0].MOCK_FACET_ORDER, - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - userData: [{ MOCK_FACET_ORDER: ['one', 'two'] }], - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual(['one', 'two']); - }); - - it('fails when a non-star facet is given', () => { - const props = { - contextValue, - ...connector.defaultProps, - facets: ['lol'], - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({}); - - expect(() => - connector.getProvidedProps(props, searchState, searchResults) - ).toThrowErrorMatchingInlineSnapshot( - `"The \`facets\` prop only accepts [] or [\\"*\\"], you passed [\\"lol\\"]"` - ); - }); - - it('fails when a multiple star facets are given', () => { - const props = { - contextValue, - ...connector.defaultProps, - facets: ['*', '*'], - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({}); - - expect(() => - connector.getProvidedProps(props, searchState, searchResults) - ).toThrowErrorMatchingInlineSnapshot( - `"The \`facets\` prop only accepts [] or [\\"*\\"], you passed [\\"*\\",\\"*\\"]"` - ); - }); - - it('does not fail when only star facet is given', () => { - const props = { - contextValue, - ...connector.defaultProps, - facets: ['*'], - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({}); - - expect(() => - connector.getProvidedProps(props, searchState, searchResults) - ).not.toThrow(); - }); - - it('does not fail when no facet is given', () => { - const props = { - contextValue, - ...connector.defaultProps, - facets: [], - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({}); - - expect(() => - connector.getProvidedProps(props, searchState, searchResults) - ).not.toThrow(); - }); - - it('warns if maxValuesPerFacet is lower than set by another widget', () => { - const spy = jest.spyOn(console, 'warn'); - const props = { - contextValue, - ...connector.defaultProps, - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults( - {}, - new SearchParameters({ maxValuesPerFacet: 100 }) - ); - - connector.getProvidedProps(props, searchState, searchResults); - - expect(spy).toHaveBeenCalledWith( - 'The maxValuesPerFacet set by dynamic widgets (20) is smaller than one of the limits set by a widget (100). This causes a mismatch in query parameters and thus an extra network request when that widget is mounted.' - ); - }); - - it('warns if >20 facets are displayed due to implicit *', () => { - const spy = jest.spyOn(console, 'warn'); - const props = { - contextValue, - transformItems: (_items, { results }) => - results.userData[0].MOCK_FACET_ORDER, - }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - userData: [ - { - MOCK_FACET_ORDER: Array.from( - { length: 21 }, - (_, i) => `item${i}` - ), - }, - ], - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(spy).toHaveBeenCalledWith( - 'More than 20 facets are requested to be displayed without explicitly setting which facets to retrieve. This could have a performance impact. Set "facets" to [] to do two smaller network requests, or explicitly to [\'*\'] to avoid this warning.' - ); - - expect(actual.attributesToRender).toEqual([ - 'item0', - 'item1', - 'item2', - 'item3', - 'item4', - 'item5', - 'item6', - 'item7', - 'item8', - 'item9', - 'item10', - 'item11', - 'item12', - 'item13', - 'item14', - 'item15', - 'item16', - 'item17', - 'item18', - 'item19', - 'item20', - ]); - }); - }); - }); - - describe('multi index', () => { - const indexContextValue = { targetedIndex: 'second' }; - - const createMultiIndexSearchState = (state = {}) => ({ - indices: { - second: state, - }, - }); - - const createMultiIndexSearchResults = (result = {}, state) => ({ - results: { - second: new SearchResults(new SearchParameters(state), [ - { - ...EMPTY_RESPONSE.results[0], - ...result, - }, - ]), - }, - }); - - describe('getProvidedProps', () => { - it('empty results', () => { - const searchState = createMultiIndexSearchState(); - const props = { contextValue, indexContextValue }; - - const searchResults = empty; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = { - attributesToRender: [], - }; - - expect(actual).toEqual(expectation); - }); - - it('attributesToRender is the return value of transformItems', () => { - const returnValue = ['one', 'two']; - const props = { - contextValue, - indexContextValue, - transformItems: () => returnValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual(returnValue); - }); - - it('default items is []', () => { - const props = { - contextValue, - indexContextValue, - transformItems: (items) => items, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual([]); - }); - - it('reads from facetOrdering by default', () => { - const props = { - contextValue, - indexContextValue, - transformItems: (items) => items, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - renderingContent: { - facetOrdering: { facets: { order: ['one', 'two'] } }, - }, - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual(['one', 'two']); - }); - - it('transformItems gets called with results', () => { - const props = { - contextValue, - indexContextValue, - transformItems: jest.fn((items) => items), - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(props.transformItems).toHaveBeenCalledTimes(1); - expect(props.transformItems).toHaveBeenCalledWith([], { - results: searchResults.results.second, - }); - - expect(actual.attributesToRender).toEqual([]); - }); - - it('userData is usable as a source for transformItems', () => { - const props = { - contextValue, - indexContextValue, - transformItems: (_items, { results }) => - results.userData[0].MOCK_FACET_ORDER, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - userData: [{ MOCK_FACET_ORDER: ['one', 'two'] }], - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.attributesToRender).toEqual(['one', 'two']); - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectGeoSearch.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectGeoSearch.js deleted file mode 100644 index 25f78c2c60..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectGeoSearch.js +++ /dev/null @@ -1,1434 +0,0 @@ -import { SearchResults, SearchParameters } from 'algoliasearch-helper'; - -import connector from '../connectGeoSearch'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectGeoSearch', () => { - const empty = {}; - - describe('single index', () => { - const contextValue = { - mainTargetedIndex: 'index', - }; - - const createSingleIndexSearchResults = (hits = [], state) => ({ - results: new SearchResults(new SearchParameters(state), [ - { - hits, - }, - ]), - }); - - describe('getProvidedProps', () => { - it('expect to return default provided props', () => { - const props = { contextValue }; - const searchState = {}; - const searchResults = empty; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = { - hits: [], - position: undefined, - currentRefinement: undefined, - isRefinedWithMap: false, - }; - - expect(actual).toEqual(expectation); - }); - - describe('hits', () => { - it('expect to return hits when we have results', () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(hits); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - expect(actual.hits).toEqual(expectation); - }); - - it('expect to return hits with only "_geoloc" when we have results', () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: false }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(hits); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = [ - { objectID: '123', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - expect(actual.hits).toEqual(expectation); - }); - - it("expect to return empty hits when we don't have results", () => { - const props = { contextValue }; - const searchState = {}; - const searchResults = empty; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = []; - - expect(actual.hits).toEqual(expectation); - }); - }); - - describe('position', () => { - it('expect to return the position from the searchState (aroundLatLng)', () => { - const props = { contextValue }; - const searchResults = createSingleIndexSearchResults(); - const searchState = { - aroundLatLng: { - lat: 10, - lng: 12, - }, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to return the position from the searchState (configure.aroundLatLng)', () => { - const props = { contextValue }; - const searchResults = createSingleIndexSearchResults(); - const searchState = { - configure: { - aroundLatLng: '10, 12', - }, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to return the position from the SearchResults', () => { - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults([], { - aroundLatLng: '10, 12', - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to return undefined from an empty searchState', () => { - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toBe(undefined); - }); - - it('expect to return undefined with the default refinement', () => { - const searchState = {}; - const searchResults = createSingleIndexSearchResults(); - const props = { - contextValue, - defaultRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toBe(undefined); - }); - }); - - describe('currentRefinement', () => { - it('expect to return the boundingBox from the searchState', () => { - const props = { contextValue }; - const searchResults = createSingleIndexSearchResults(); - const searchState = { - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to return the boundingBox from the searchState with string values', () => { - const props = { contextValue }; - const searchResults = createSingleIndexSearchResults(); - const searchState = { - boundingBox: { - northEast: { - lat: '10.12', - lng: 12.1, - }, - southWest: { - lat: 12.14, - lng: '14.12', - }, - }, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10.12, - lng: 12.1, - }, - southWest: { - lat: 12.14, - lng: 14.12, - }, - }); - }); - - it('expect to return the boundingBox from the SearchResults', () => { - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults([], { - insideBoundingBox: '10, 12, 12, 14', - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to return the default refinement', () => { - const searchState = {}; - const searchResults = createSingleIndexSearchResults(); - const props = { - contextValue, - defaultRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to return undefined from an empty searchState', () => { - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(undefined); - }); - }); - - describe('isRefinedWithMap', () => { - it("expect to return true when it's refined with the map (from the searchState)", () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue }; - const searchResults = createSingleIndexSearchResults(hits); - const searchState = { - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.isRefinedWithMap).toBe(true); - }); - - it("expect to return true when it's refined with the map (from the searchParameters)", () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(hits, { - insideBoundingBox: '10, 12, 12, 14', - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.isRefinedWithMap).toBe(true); - }); - - it("expect to return false when it's not refined with the map", () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults(hits); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.isRefinedWithMap).toBe(false); - }); - }); - }); - - describe('refine', () => { - it('expect to set the boundingBox when boundingBox is provided', () => { - const props = { contextValue }; - const searchState = {}; - const nextRefinement = { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }; - - const actual = connector.refine(props, searchState, nextRefinement); - - expect(actual).toEqual({ - page: 1, - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - }); - - it('expect to replace the previous value when boundingBox is provided', () => { - const props = { contextValue }; - const searchState = { - boundingBox: { - northEast: { - lat: 8, - lng: 10, - }, - southWest: { - lat: 10, - lng: 12, - }, - }, - }; - - const nextRefinement = { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }; - - const actual = connector.refine(props, searchState, nextRefinement); - - expect(actual).toEqual({ - page: 1, - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - }); - - it('expect to clear the previous value when boundingBox is omit', () => { - const props = { contextValue }; - const searchState = { - boundingBox: { - northEast: { - lat: 8, - lng: 10, - }, - southWest: { - lat: 10, - lng: 12, - }, - }, - }; - - const actual = connector.refine(props, searchState); - - const expectation = { - page: 1, - }; - - expect(actual).toEqual(expectation); - }); - }); - - describe('getSearchParameters', () => { - it('expect to set the parameter "insideBoundingBox" when boundingBox is provided', () => { - const searchParameters = new SearchParameters(); - const props = { contextValue }; - const searchState = { - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const actual = connector.getSearchParameters( - searchParameters, - props, - searchState - ); - - const expectation = '10,12,12,14'; - - expect(actual.insideBoundingBox).toEqual(expectation); - }); - - it('expect to return the given searchParameters when boundingBox is omit', () => { - const searchParameters = new SearchParameters(); - const props = { contextValue }; - const searchState = {}; - - const actual = connector.getSearchParameters( - searchParameters, - props, - searchState - ); - - expect(actual).toEqual(searchParameters); - }); - }); - - describe('cleanUp', () => { - it('expect to remove the refinement from the searchState when boundingBox is provided', () => { - const props = { contextValue }; - const searchState = { - query: 'studio', - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const actual = connector.cleanUp(props, searchState); - - const expectation = { - query: 'studio', - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to return the given searchState when boundingBox is omit', () => { - const props = { contextValue }; - const searchState = { - query: 'studio', - }; - - const actual = connector.cleanUp(props, searchState); - - const expectation = { - query: 'studio', - }; - - expect(actual).toEqual(expectation); - }); - }); - - describe('getMetadata', () => { - it('expect to return the meta when boundingBox is provided', () => { - const props = { contextValue }; - const searchState = { - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const actual = connector.getMetadata(props, searchState); - - const expectation = { - id: 'boundingBox', - index: 'index', - items: [ - { - label: 'boundingBox: 10,12,12,14', - value: expect.any(Function), - currentRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }, - ], - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to return an empty meta when boundingBox is omit', () => { - const props = { contextValue }; - const searchState = {}; - - const actual = connector.getMetadata(props, searchState); - - expect(actual).toEqual({ - id: 'boundingBox', - index: 'index', - items: [], - }); - }); - - it('expect to clear the boundingBox when value is called', () => { - const props = { contextValue }; - const searchState = { - query: 'studio', - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const metadata = connector.getMetadata(props, searchState); - const actual = metadata.items[0].value(searchState); - - expect(actual).toEqual({ - query: 'studio', - boundingBox: {}, - page: 1, - }); - }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - const createMultiIndexSearchState = (state = {}) => ({ - indices: { - second: state, - }, - }); - - const createMultiIndexSearchResults = (hits = [], state) => ({ - results: { - second: new SearchResults(new SearchParameters(state), [ - { - hits, - }, - ]), - }, - }); - - describe('getProvidedProps', () => { - it('expect to return default provided props', () => { - const searchState = createMultiIndexSearchState(); - const props = { contextValue, indexContextValue }; - - const searchResults = empty; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = { - hits: [], - position: undefined, - currentRefinement: undefined, - isRefinedWithMap: false, - }; - - expect(actual).toEqual(expectation); - }); - - describe('hits', () => { - it('expect to return hits when we have results', () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(hits); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - expect(actual.hits).toEqual(expectation); - }); - - it('expect to return hits with only "_geoloc" when we have results', () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: false }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(hits); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = [ - { objectID: '123', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - expect(actual.hits).toEqual(expectation); - }); - - it("expect to return empty hits when we don't have results", () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const searchResults = empty; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - const expectation = []; - - expect(actual.hits).toEqual(expectation); - }); - }); - - describe('position', () => { - it('expect to return the position from the searchState (aroundLatLng)', () => { - const props = { contextValue, indexContextValue }; - const searchResults = createMultiIndexSearchResults(); - const searchState = createMultiIndexSearchState({ - aroundLatLng: { - lat: 10, - lng: 12, - }, - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to return the position from the searchState (configure.aroungLatLng)', () => { - const props = { contextValue, indexContextValue }; - const searchResults = createMultiIndexSearchResults(); - const searchState = createMultiIndexSearchState({ - configure: { - aroundLatLng: '10, 12', - }, - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to return undefined from an empty searchState', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toBe(undefined); - }); - - it('expect to return undefined with the default refinement', () => { - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(); - const props = { - defaultRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - contextValue, - indexContextValue, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.position).toBe(undefined); - }); - }); - - describe('currentRefinement', () => { - it('expect to return the boundingBox from the searchState', () => { - const props = { contextValue, indexContextValue }; - const searchResults = createMultiIndexSearchResults(); - const searchState = createMultiIndexSearchState({ - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to return the boundingBox from the searchState with string values', () => { - const props = { contextValue, indexContextValue }; - const searchResults = createMultiIndexSearchResults(); - const searchState = createMultiIndexSearchState({ - boundingBox: { - northEast: { - lat: '10.12', - lng: 12.1, - }, - southWest: { - lat: 12.14, - lng: '14.12', - }, - }, - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10.12, - lng: 12.1, - }, - southWest: { - lat: 12.14, - lng: 14.12, - }, - }); - }); - - it('expect to return the boundingBox from the SearchResults', () => { - const props = { contextValue, indexContextValue }; - const searchState = {}; - const searchResults = createMultiIndexSearchResults([], { - insideBoundingBox: '10, 12, 12, 14', - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to return the default refinement', () => { - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(); - const props = { - defaultRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - contextValue, - indexContextValue, - }; - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to return an undefined from an empty searchState', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(undefined); - }); - }); - - describe('isRefinedWithMap', () => { - it("expect to return true when it's refined with the map (from the searchState)", () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue, indexContextValue }; - const searchResults = createMultiIndexSearchResults(hits); - const searchState = createMultiIndexSearchState({ - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.isRefinedWithMap).toBe(true); - }); - - it("expect to return true when it's refined with the map (from the searchParameters)", () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(hits, { - insideBoundingBox: '10, 12, 12, 14', - }); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.isRefinedWithMap).toBe(true); - }); - - it("expect to return false when it's not refined with the map", () => { - const hits = [ - { objectID: '123', _geoloc: true }, - { objectID: '456', _geoloc: true }, - { objectID: '789', _geoloc: true }, - ]; - - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults(hits); - - const actual = connector.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.isRefinedWithMap).toBe(false); - }); - }); - }); - - describe('refine', () => { - it('expect to set the boundingBox when boundingBox is provided', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - const nextRefinement = { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }; - - const actual = connector.refine(props, searchState, nextRefinement); - - const expectation = { - indices: { - second: { - page: 1, - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to replace the previous value when boundingBox is provided', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState({ - boundingBox: { - northEast: { - lat: 8, - lng: 10, - }, - southWest: { - lat: 10, - lng: 12, - }, - }, - }); - - const nextRefinement = { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }; - - const actual = connector.refine(props, searchState, nextRefinement); - - const expectation = { - indices: { - second: { - page: 1, - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to clear the previous value when boundingBox is omit', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState({ - boundingBox: { - northEast: { - lat: 8, - lng: 10, - }, - southWest: { - lat: 10, - lng: 12, - }, - }, - }); - - const actual = connector.refine(props, searchState); - - const expectation = { - indices: { - second: { - page: 1, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - }); - - describe('getSearchParameters', () => { - it('expect to set the parameter "insideBoundingBox" when boundingBox is provided', () => { - const searchParameters = new SearchParameters(); - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState({ - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - - const actual = connector.getSearchParameters( - searchParameters, - props, - searchState - ); - - const expectation = '10,12,12,14'; - - expect(actual.insideBoundingBox).toEqual(expectation); - }); - - it('expect to return the given searchParameters when boundingBox is omit', () => { - const searchParameters = new SearchParameters(); - const props = { contextValue, indexContextValue }; - const searchState = {}; - - const actual = connector.getSearchParameters( - searchParameters, - props, - searchState - ); - - expect(actual).toEqual(searchParameters); - }); - }); - - describe('cleanUp', () => { - it('expect to remove the refinement from the searchState when boundingBox is provided', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState({ - query: 'studio', - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - - const actual = connector.cleanUp(props, searchState); - - const expectation = { - indices: { - second: { - query: 'studio', - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to return the given searchState when boundingBox is omit', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState({ - query: 'studio', - }); - - const actual = connector.cleanUp(props, searchState); - - const expectation = { - indices: { - second: { - query: 'studio', - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - }); - - describe('getMetadata', () => { - it('expect to return the meta when boundingBox is provided', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState({ - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - - const actual = connector.getMetadata(props, searchState); - - const expectation = { - id: 'boundingBox', - index: 'second', - items: [ - { - label: 'boundingBox: 10,12,12,14', - value: expect.any(Function), - currentRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }, - ], - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to return an empty meta when boundingBox is omit', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState(); - - const actual = connector.getMetadata(props, searchState); - - const expectation = { - id: 'boundingBox', - index: 'second', - items: [], - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to clear the boundingBox when value is called', () => { - const props = { contextValue, indexContextValue }; - const searchState = createMultiIndexSearchState({ - query: 'studio', - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }); - - const metadata = connector.getMetadata(props, searchState); - - const actual = metadata.items[0].value(searchState); - - const expectation = { - indices: { - second: { - query: 'studio', - boundingBox: {}, - page: 1, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - }); - }); - - describe('shouldComponentUpdate', () => { - it('expect to always return true', () => { - const expectation = true; - const actual = connector.shouldComponentUpdate(); - - expect(actual).toBe(expectation); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js deleted file mode 100644 index 45f9a1ffa2..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js +++ /dev/null @@ -1,1123 +0,0 @@ -import { SearchResults, SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectHierarchicalMenu'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectHierarchicalMenu', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - const results = { - getFacetValues: jest.fn(), - getFacetByName: () => true, - hits: [], - }; - - results.getFacetValues.mockImplementationOnce(() => ({})); - let props = connect.getProvidedProps( - { attributes: ['ok'], contextValue }, - { hierarchicalMenu: { ok: 'wat' } }, - { results } - ); - - expect(props).toEqual({ - canRefine: false, - currentRefinement: 'wat', - items: [], - }); - - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue }, - {}, - {} - ); - expect(props).toEqual({ - canRefine: false, - currentRefinement: null, - items: [], - }); - - results.getFacetValues.mockClear(); - results.getFacetValues.mockImplementation(() => ({ - data: [ - { - name: 'wat', - path: 'wat', - escapedValue: 'wat', - count: 20, - data: [ - { - name: 'wot', - path: 'wat > wot', - escapedValue: 'wat > wot', - count: 15, - }, - { - name: 'wut', - path: 'wat > wut', - escapedValue: 'wat > wut', - count: 5, - }, - ], - }, - { - name: 'oy', - path: 'oy', - escapedValue: 'oy', - count: 10, - }, - ], - })); - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - { - label: 'wut', - value: 'wat > wut', - count: 5, - }, - ], - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - ]); - - props = connect.getProvidedProps( - { attributes: ['ok'], limit: 1, contextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - ], - }, - ]); - - props = connect.getProvidedProps( - { - attributes: ['ok'], - showMore: true, - limit: 0, - showMoreLimit: 1, - contextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - ], - }, - ]); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { attributes: ['ok'], transformItems, contextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - { - label: 'wut', - value: 'wat > wut', - count: 5, - }, - ], - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - ]); - expect(props.items).toEqual(['items']); - }); - - it('facetValues results uses facetOrdering by default', () => { - const props = { - ...connect.defaultProps, - attributes: ['lvl0', 'lvl1'], - contextValue, - }; - const searchState = { hierarchicalMenu: { lvl0: 'wat' } }; - const state = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - const results = new SearchResults(state, [ - { - hits: [], - renderingContent: { - facetOrdering: { - values: { - lvl0: { - order: ['wat'], - }, - lvl1: { - order: ['wat > wut'], - }, - }, - }, - }, - facets: { - lvl0: { - wat: 20, - oy: 10, - }, - lvl1: { - 'wat > wot': 15, - 'wat > wut': 5, - }, - }, - }, - ]); - - const providedProps = connect.getProvidedProps(props, searchState, { - results, - }); - expect(providedProps.items).toEqual([ - { - label: 'wat', - value: undefined, - count: 20, - isRefined: true, - items: [ - { - label: 'wut', - value: 'wat > wut', - count: 5, - isRefined: false, - items: null, - }, - { - label: 'wot', - value: 'wat > wot', - count: 15, - isRefined: false, - items: null, - }, - ], - }, - { - label: 'oy', - value: 'oy', - count: 10, - isRefined: false, - items: null, - }, - ]); - }); - - it('facetValues results does not use facetOrdering if disabled', () => { - const props = { - attributes: ['lvl0', 'lvl1'], - facetOrdering: false, - contextValue, - }; - const searchState = { hierarchicalMenu: { lvl0: 'wat' } }; - const state = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - const results = new SearchResults(state, [ - { - hits: [], - renderingContent: { - facetOrdering: { - values: { - lvl0: { - order: ['wat'], - }, - lvl1: { - order: ['wat > wut'], - }, - }, - }, - }, - facets: { - lvl0: { - wat: 20, - oy: 10, - }, - lvl1: { - 'wat > wot': 15, - 'wat > wut': 5, - }, - }, - }, - ]); - - const providedProps = connect.getProvidedProps(props, searchState, { - results, - }); - expect(providedProps.items).toEqual([ - { - label: 'oy', - value: 'oy', - count: 10, - isRefined: false, - items: null, - }, - { - label: 'wat', - value: undefined, - count: 20, - isRefined: true, - items: [ - // default ordering: alphabetical - { - label: 'wot', - value: 'wat > wot', - count: 15, - isRefined: false, - items: null, - }, - { - label: 'wut', - value: 'wat > wut', - count: 5, - isRefined: false, - items: null, - }, - ], - }, - ]); - }); - - it('shows the effect of showMoreLimit when there is no transformItems', () => { - const results = { - getFacetValues: jest.fn(), - getFacetByName: () => true, - hits: [], - }; - results.getFacetValues.mockImplementation(() => ({ - data: [ - { - name: 'wat', - path: 'wat', - escapedValue: 'wat', - count: 20, - data: [ - { - name: 'wot', - path: 'wat > wot', - escapedValue: 'wat > wot', - count: 15, - }, - { - name: 'wut', - path: 'wat > wut', - escapedValue: 'wat > wut', - count: 3, - }, - { - name: 'wit', - path: 'wat > wit', - escapedValue: 'wat > wit', - count: 5, - }, - ], - }, - { - name: 'oy', - path: 'oy', - escapedValue: 'oy', - count: 10, - }, - { - name: 'ay', - path: 'ay', - escapedValue: 'ay', - count: 3, - }, - ], - })); - - const props = connect.getProvidedProps( - { - attributes: ['ok'], - showMore: true, - limit: 0, - showMoreLimit: 2, - contextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - { - label: 'wut', - value: 'wat > wut', - count: 3, - }, - ], - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - ]); - }); - - it('applies limit after transforming items', () => { - const results = { - getFacetValues: jest.fn(), - getFacetByName: () => true, - hits: [], - }; - results.getFacetValues.mockClear(); - results.getFacetValues.mockImplementation(() => ({ - data: [ - { - name: 'wat', - path: 'wat', - escapedValue: 'wat', - count: 20, - data: [ - { - name: 'wot', - path: 'wat > wot', - escapedValue: 'wat > wot', - count: 15, - }, - { - name: 'wut', - path: 'wat > wut', - escapedValue: 'wat > wut', - count: 3, - }, - { - name: 'wit', - path: 'wat > wit', - escapedValue: 'wat > wit', - count: 5, - }, - ], - }, - { - name: 'oy', - path: 'oy', - escapedValue: 'oy', - count: 10, - }, - { - name: 'ay', - path: 'ay', - escapedValue: 'ay', - count: 3, - }, - ], - })); - let props = connect.getProvidedProps( - { - attributes: ['ok'], - showMore: true, - limit: 0, - showMoreLimit: 3, - contextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - { - label: 'wut', - value: 'wat > wut', - count: 3, - }, - { - label: 'wit', - value: 'wat > wit', - count: 5, - }, - ], - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - { - label: 'ay', - value: 'ay', - count: 3, - }, - ]); - - function compareItem(a, b) { - if (a.label < b.label) return -1; - if (a.label > b.label) return 1; - return 0; - } - const transformItems = jest.fn((items) => items.sort(compareItem)); - props = connect.getProvidedProps( - { attributes: ['ok'], transformItems, contextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - label: 'ay', - value: 'ay', - count: 3, - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - { - label: 'wut', - value: 'wat > wut', - count: 3, - }, - { - label: 'wit', - value: 'wat > wit', - count: 5, - }, - ], - }, - ]); - props = connect.getProvidedProps( - { - attributes: ['ok'], - transformItems, - showMore: true, - limit: 0, - showMoreLimit: 2, - contextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'ay', - value: 'ay', - count: 3, - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - ]); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { attributes: ['ok'], contextValue }, - { otherKey: 'val', hierarchicalMenu: { otherKey: 'val' } }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }); - }); - - it("increases maxValuesPerFacet when it isn't big enough", () => { - const initSP = new SearchParameters({ maxValuesPerFacet: 100 }); - - let params = connect.getSearchParameters( - initSP, - { - attributes: ['attribute'], - limit: 101, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(101); - - params = connect.getSearchParameters( - initSP, - { - attributes: ['attribute'], - showMore: true, - showMoreLimit: 101, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(101); - - params = connect.getSearchParameters( - initSP, - { - attributes: ['attribute'], - limit: 99, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(100); - - params = connect.getSearchParameters( - initSP, - { - attributes: ['attribute'], - showMore: true, - showMoreLimit: 99, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(100); - }); - - it('correctly applies its state to search parameters', () => { - const initSP = new SearchParameters(); - - const params = connect.getSearchParameters( - initSP, - { - attributes: ['ATTRIBUTE'], - separator: 'SEPARATOR', - rootPath: 'ROOT_PATH', - showParentLevel: true, - limit: 1, - contextValue, - }, - { hierarchicalMenu: { ATTRIBUTE: 'ok' } } - ); - expect(params).toEqual( - initSP - .addHierarchicalFacet({ - name: 'ATTRIBUTE', - attributes: ['ATTRIBUTE'], - separator: 'SEPARATOR', - rootPath: 'ROOT_PATH', - showParentLevel: true, - }) - .toggleHierarchicalFacetRefinement('ATTRIBUTE', 'ok') - .setQueryParameter('maxValuesPerFacet', 1) - ); - }); - - describe('getMetaData', () => { - it('registers its id in metadata', () => { - const metadata = connect.getMetadata( - { attributes: ['ok'], contextValue }, - {} - ); - expect(metadata).toEqual({ items: [], index: 'index', id: 'ok' }); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { attributes: ['ok'], contextValue }, - { hierarchicalMenu: { ok: 'wat' } } - ); - expect(metadata).toEqual({ - id: 'ok', - index: 'index', - items: [ - { - label: 'ok: wat', - attribute: 'ok', - currentRefinement: 'wat', - // Ignore clear, we test it later - value: metadata.items[0].value, - }, - ], - }); - }); - - it('registers escaped filter in metadata', () => { - const metadata = connect.getMetadata( - { attributes: ['ok'], contextValue }, - { hierarchicalMenu: { ok: '\\-wat' } } - ); - expect(metadata).toEqual({ - id: 'ok', - index: 'index', - items: [ - { - label: 'ok: -wat', - attribute: 'ok', - currentRefinement: '\\-wat', - value: metadata.items[0].value, - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attributes: ['one'], contextValue }, - { hierarchicalMenu: { one: 'one', two: 'two' } } - ); - - const searchState = metadata.items[0].value({ - hierarchicalMenu: { one: 'one', two: 'two' }, - }); - - expect(searchState).toEqual({ - page: 1, - hierarchicalMenu: { one: '', two: 'two' }, - }); - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attributes: ['name'], contextValue }, - { - hierarchicalMenu: { name: 'searchState', name2: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - hierarchicalMenu: { name2: 'searchState' }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attributes: ['name2'], contextValue }, - searchState - ); - expect(searchState).toEqual({ - another: { searchState: 'searchState' }, - hierarchicalMenu: {}, - }); - }); - - it('computes canRefine based on the length of the transformed items list', () => { - const transformItems = () => []; - const results = { - getFacetValues: () => ({ data: [{ id: 'test' }] }), - getFacetByName: () => true, - hits: [], - }; - - const props = connect.getProvidedProps( - { attributes: ['ok'], transformItems, contextValue }, - {}, - { results } - ); - - expect(props.canRefine).toEqual(false); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - const results = { - second: { - getFacetValues: jest.fn(), - getFacetByName: () => true, - }, - }; - - results.second.getFacetValues.mockImplementationOnce(() => ({})); - let props = connect.getProvidedProps( - { attributes: ['ok'], contextValue, indexContextValue }, - { indices: { second: { hierarchicalMenu: { ok: 'wat' } } } }, - { results } - ); - expect(props).toEqual({ - canRefine: false, - currentRefinement: 'wat', - items: [], - }); - - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue, indexContextValue }, - {}, - {} - ); - expect(props).toEqual({ - canRefine: false, - currentRefinement: null, - items: [], - }); - - results.second.getFacetValues.mockClear(); - results.second.getFacetValues.mockImplementation(() => ({ - data: [ - { - name: 'wat', - path: 'wat', - escapedValue: 'wat', - count: 20, - data: [ - { - name: 'wot', - path: 'wat > wot', - escapedValue: 'wat > wot', - count: 15, - }, - { - name: 'wut', - path: 'wat > wut', - escapedValue: 'wat > wut', - count: 5, - }, - ], - }, - { - name: 'oy', - path: 'oy', - escapedValue: 'oy', - count: 10, - }, - ], - })); - props = connect.getProvidedProps( - { attributes: ['ok'], contextValue, indexContextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - { - label: 'wut', - value: 'wat > wut', - count: 5, - }, - ], - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - ]); - - props = connect.getProvidedProps( - { attributes: ['ok'], limit: 1, contextValue, indexContextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - ], - }, - ]); - - props = connect.getProvidedProps( - { - attributes: ['ok'], - showMore: true, - limit: 0, - showMoreLimit: 1, - contextValue, - indexContextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - ], - }, - ]); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { attributes: ['ok'], transformItems, contextValue, indexContextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - label: 'wat', - value: 'wat', - count: 20, - items: [ - { - label: 'wot', - value: 'wat > wot', - count: 15, - }, - { - label: 'wut', - value: 'wat > wut', - count: 5, - }, - ], - }, - { - label: 'oy', - value: 'oy', - count: 10, - }, - ]); - expect(props.items).toEqual(['items']); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { attributes: ['ok'], contextValue, indexContextValue }, - { - indices: { - first: { otherKey: 'val1', hierarchicalMenu: { otherKey: 'val1' } }, - second: { otherKey: 'val', hierarchicalMenu: { otherKey: 'val' } }, - }, - }, - 'yep' - ); - - expect(nextState).toEqual({ - indices: { - first: { - otherKey: 'val1', - hierarchicalMenu: { otherKey: 'val1' }, - }, - second: { - otherKey: 'val', - page: 1, - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }, - }, - }); - - nextState = connect.refine( - { - attributes: ['ok'], - contextValue: { mainTargetedIndex: 'first' }, - indexContextValue: { targetedIndex: 'second' }, - }, - { - indices: { - first: { - otherKey: 'val', - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }, - }, - }, - 'yep' - ); - - expect(nextState).toEqual({ - indices: { - first: { - otherKey: 'val', - hierarchicalMenu: { ok: 'yep', otherKey: 'val' }, - }, - second: { page: 1, hierarchicalMenu: { ok: 'yep' } }, - }, - }); - }); - - it('correctly applies its state to search parameters', () => { - const initSP = new SearchParameters(); - - const params = connect.getSearchParameters( - initSP, - { - attributes: ['ATTRIBUTE'], - separator: 'SEPARATOR', - rootPath: 'ROOT_PATH', - showParentLevel: true, - limit: 1, - contextValue, - indexContextValue, - }, - { - indices: { - second: { otherKey: 'val', hierarchicalMenu: { ATTRIBUTE: 'ok' } }, - }, - } - ); - expect(params).toEqual( - initSP - .addHierarchicalFacet({ - name: 'ATTRIBUTE', - attributes: ['ATTRIBUTE'], - separator: 'SEPARATOR', - rootPath: 'ROOT_PATH', - showParentLevel: true, - }) - .toggleHierarchicalFacetRefinement('ATTRIBUTE', 'ok') - .setQueryParameter('maxValuesPerFacet', 1) - ); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { attributes: ['ok'], contextValue, indexContextValue }, - { indices: { second: { hierarchicalMenu: { ok: 'wat' } } } } - ); - expect(metadata).toEqual({ - id: 'ok', - index: 'second', - items: [ - { - label: 'ok: wat', - attribute: 'ok', - currentRefinement: 'wat', - // Ignore clear, we test it later - value: metadata.items[0].value, - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attributes: ['one'], contextValue, indexContextValue }, - { - indices: { second: { hierarchicalMenu: { one: 'one', two: 'two' } } }, - } - ); - - const searchState = metadata.items[0].value({ - indices: { second: { hierarchicalMenu: { one: 'one', two: 'two' } } }, - }); - - expect(searchState).toEqual({ - indices: { - second: { page: 1, hierarchicalMenu: { one: '', two: 'two' } }, - }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attributes: ['one'], contextValue, indexContextValue }, - { - indices: { - second: { - hierarchicalMenu: { one: 'one', two: 'two' }, - another: { searchState: 'searchState' }, - }, - }, - } - ); - expect(searchState).toEqual({ - indices: { - second: { - hierarchicalMenu: { two: 'two' }, - another: { searchState: 'searchState' }, - }, - }, - }); - - searchState = connect.cleanUp( - { attributes: ['two'], contextValue, indexContextValue }, - searchState - ); - expect(searchState).toEqual({ - indices: { - second: { - another: { searchState: 'searchState' }, - hierarchicalMenu: {}, - }, - }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectHitInsights.ts b/packages/react-instantsearch-core/src/connectors/__tests__/connectHitInsights.ts deleted file mode 100644 index d4e35e26b5..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectHitInsights.ts +++ /dev/null @@ -1,140 +0,0 @@ -import connectReal from '../connectHitInsights'; - -jest.mock('../../core/createConnector', () => (x: any) => x); -// our mock implementation is diverging from the regular createConnector, -// so we redefine it as `any` here, since we have no more information -// @TODO: refactor these tests to work better with TS -const connect: (client: any) => any = connectReal; - -function setup() { - const insightsClient = jest.fn(); - - const contextValue = { - mainTargetedIndex: 'firstIndex', - }; - const indexContextValue = { - targetedIndex: 'theIndex', - }; - - const hit = { - objectID: 'objectID_42', - __position: 42, - __queryID: 'theQueryID', - }; - const searchResults = { results: { theIndex: { index: 'theIndex' } } }; - const props = connect(insightsClient).getProvidedProps( - { hit, contextValue, indexContextValue }, - null, - searchResults - ); - return { insightsClient, props }; -} - -describe('connectHitInsights', () => { - it('should expose an `insights` property', () => { - const { props } = setup(); - expect(props).toHaveProperty('insights'); - }); - - it('exposed `insights` should be a function', () => { - const { props } = setup(); - expect(typeof props.insights).toBe('function'); - }); - - describe('when called with `clickedObjectIDsAfterSearch`', () => { - let insightsClient: jest.Mock; - beforeEach(() => { - const { insightsClient: aa, props } = setup(); - insightsClient = aa; - props.insights('clickedObjectIDsAfterSearch', { - eventName: 'Add to cart', - }); - }); - - it('should forward call to insightsClient with the correct payload', () => { - expect(insightsClient).toHaveBeenCalledTimes(1); - const [method, payload] = insightsClient.mock.calls[0]; - expect(method).toEqual('clickedObjectIDsAfterSearch'); - expect(payload).toEqual({ - eventName: 'Add to cart', - objectIDs: ['objectID_42'], - positions: [42], - queryID: 'theQueryID', - index: 'theIndex', - }); - }); - }); - - describe('when called with `convertedObjectIDsAfterSearch`', () => { - let insightsClient: jest.Mock; - beforeEach(() => { - const { insightsClient: aa, props } = setup(); - insightsClient = aa; - props.insights('convertedObjectIDsAfterSearch', { - eventName: 'Add to cart', - }); - }); - - it('should forward call to insightsClient with the correct payload', () => { - expect(insightsClient).toHaveBeenCalledTimes(1); - - const [method, payload] = insightsClient.mock.calls[0]; - expect(method).toEqual('convertedObjectIDsAfterSearch'); - expect(payload).toEqual({ - eventName: 'Add to cart', - objectIDs: ['objectID_42'], - queryID: 'theQueryID', - index: 'theIndex', - }); - }); - }); - - describe('when called with an unsupported method', () => { - it('should reject the call', () => { - expect(() => { - const { props } = setup(); - // @ts-ignore - props.insights('wrong-method-name', { - eventName: 'Add to cart', - }); - }).toThrowErrorMatchingInlineSnapshot( - `"Unsupported method \\"wrong-method-name\\" passed to the insights function. The supported methods are: \\"clickedObjectIDsAfterSearch\\", \\"convertedObjectIDsAfterSearch\\"."` - ); - }); - }); - - describe('when queryID is undefined', () => { - it('should throw an error message inviting to add clickAnalytics: true', () => { - const insightsClient = jest.fn(); - - const contextValue = { - mainTargetedIndex: 'firstIndex', - }; - const indexContextValue = { - targetedIndex: 'theIndex', - }; - - const hit = { - objectID: 'objectID_42', - __position: 42, - // no queryID - }; - - const searchResults = { results: { theIndex: { index: 'theIndex' } } }; - const props = connect(insightsClient).getProvidedProps( - { hit, contextValue, indexContextValue }, - null, - searchResults - ); - - expect(() => { - props.insights('clickedObjectIDsAfterSearch', { - eventName: 'Add to wishlist', - }); - }).toThrowErrorMatchingInlineSnapshot(` -"Could not infer \`queryID\`. Ensure \`clickAnalytics: true\` was added with the Configure widget. -See: https://alg.li/VpPpLt" -`); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js deleted file mode 100644 index ddeadb638d..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js +++ /dev/null @@ -1,116 +0,0 @@ -import connect from '../connectHits'; - -jest.mock('../../core/createConnector', () => (x) => x); - -const { getSearchParameters } = connect; - -describe('connectHits', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the current hits to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps({ contextValue }, null, { - results: { hits, hitsPerPage: 2, page: 2 }, - }); - expect(props).toEqual({ - hits: hits.map((hit) => expect.objectContaining(hit)), - }); - }); - - it('adds positions to the hits provided to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps({ contextValue }, null, { - results: { hits, hitsPerPage: 2, page: 2 }, - }); - expect(props).toEqual({ - hits: [{ __position: 5 }], - }); - }); - - it('adds queryID to the hits provided to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps({ contextValue }, null, { - results: { hits, hitsPerPage: 2, page: 2, queryID: 'theQueryID' }, - }); - expect(props).toEqual({ - hits: [expect.objectContaining({ __queryID: 'theQueryID' })], - }); - }); - - it("doesn't render when no hits are available", () => { - const props = connect.getProvidedProps({ contextValue }, null, { - results: null, - }); - expect(props).toEqual({ hits: [] }); - }); - - it('should return the searchParameters unchanged', () => { - const searchParameters = getSearchParameters({ hitsPerPage: 10 }); - expect(searchParameters).toEqual({ hitsPerPage: 10 }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the current hits to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { second: { hits, hitsPerPage: 2, page: 2 } }, - } - ); - expect(props).toEqual({ - hits: hits.map((hit) => expect.objectContaining(hit)), - }); - }); - - it('adds positions to the hits provided to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { second: { hits, hitsPerPage: 2, page: 2 } }, - } - ); - expect(props).toEqual({ - hits: [{ __position: 5 }], - }); - }); - - it('adds queryID to the hits provided to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { - second: { hits, hitsPerPage: 2, page: 2, queryID: 'theQueryID' }, - }, - } - ); - expect(props).toEqual({ - hits: [expect.objectContaining({ __queryID: 'theQueryID' })], - }); - }); - - it("doesn't render when no hits are available", () => { - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { results: { second: null } } - ); - expect(props).toEqual({ hits: [] }); - }); - - it('should return the searchParameters unchanged', () => { - const searchParameters = getSearchParameters({ hitsPerPage: 10 }); - expect(searchParameters).toEqual({ hitsPerPage: 10 }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectHitsPerPage.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectHitsPerPage.js deleted file mode 100644 index cbd558758a..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectHitsPerPage.js +++ /dev/null @@ -1,246 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectHitsPerPage'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectHitsPerPage', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - const items = [ - { label: '10', value: 10 }, - { label: '20', value: 20 }, - ]; - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { items, contextValue }, - { hitsPerPage: '10' } - ); - expect(props).toEqual({ - currentRefinement: 10, - items: [ - { label: '10', value: 10, isRefined: true }, - { - label: '20', - value: 20, - isRefined: false, - }, - ], - }); - - props = connect.getProvidedProps( - { defaultRefinement: 20, items, contextValue }, - {} - ); - expect(props).toEqual({ - currentRefinement: 20, - items: [ - { - label: '10', - value: 10, - isRefined: false, - }, - { label: '20', value: 20, isRefined: true }, - ], - }); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { items, transformItems, contextValue }, - { hitsPerPage: '10' } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { label: '10', value: 10, isRefined: true }, - { label: '20', value: 20, isRefined: false }, - ]); - expect(props.items).toEqual(['items']); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { contextValue }, - { otherKey: 'val' }, - 30 - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - hitsPerPage: 30, - }); - }); - - it('refines the hitsPerPage parameter', () => { - const sp = new SearchParameters(); - - params = connect.getSearchParameters( - sp, - { contextValue }, - { hitsPerPage: 10 } - ); - expect(params).toEqual(sp.setQueryParameter('hitsPerPage', 10)); - - params = connect.getSearchParameters( - sp, - { contextValue }, - { hitsPerPage: '10' } - ); - expect(params).toEqual(sp.setQueryParameter('hitsPerPage', 10)); - - params = connect.getSearchParameters( - sp, - { defaultRefinement: 20, contextValue }, - {} - ); - expect(params).toEqual(sp.setQueryParameter('hitsPerPage', 20)); - }); - - it('registers its id in metadata', () => { - const metadata = connect.getMetadata({ contextValue }); - expect(metadata).toEqual({ id: 'hitsPerPage' }); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { contextValue }, - { - hitsPerPage: 'searchState', - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ another: { searchState: 'searchState' } }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - const items = [ - { label: '10', value: 10 }, - { label: '20', value: 20 }, - ]; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { items, contextValue, indexContextValue }, - { indices: { second: { hitsPerPage: '10' } } } - ); - expect(props).toEqual({ - currentRefinement: 10, - items: [ - { label: '10', value: 10, isRefined: true }, - { - label: '20', - value: 20, - isRefined: false, - }, - ], - }); - - props = connect.getProvidedProps( - { defaultRefinement: 20, items, contextValue, indexContextValue }, - {} - ); - expect(props).toEqual({ - currentRefinement: 20, - items: [ - { - label: '10', - value: 10, - isRefined: false, - }, - { label: '20', value: 20, isRefined: true }, - ], - }); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { items, transformItems, contextValue, indexContextValue }, - { indices: { second: { hitsPerPage: '10' } } } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { label: '10', value: 10, isRefined: true }, - { label: '20', value: 20, isRefined: false }, - ]); - expect(props.items).toEqual(['items']); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { contextValue, indexContextValue }, - { indices: { second: { otherKey: 'val' } } }, - 30 - ); - expect(nextState).toEqual({ - indices: { - second: { page: 1, otherKey: 'val', hitsPerPage: 30 }, - }, - }); - - nextState = connect.refine( - { - contextValue: { mainTargetedIndex: 'second' }, - indexContextValue: { targetedIndex: 'second' }, - }, - { indices: { second: { otherKey: 'val', hitsPerPage: 30 } } }, - 30 - ); - expect(nextState).toEqual({ - indices: { - second: { otherKey: 'val', hitsPerPage: 30, page: 1 }, - }, - }); - }); - - it('correctly applies its state to search parameters', () => { - const sp = new SearchParameters(); - - params = connect.getSearchParameters( - sp, - { contextValue, indexContextValue }, - { indices: { second: { hitsPerPage: '10' } } } - ); - expect(params).toEqual(sp.setQueryParameter('hitsPerPage', 10)); - - params = connect.getSearchParameters( - sp, - { contextValue, indexContextValue }, - { indices: { second: { hitsPerPage: '10' } } } - ); - expect(params).toEqual(sp.setQueryParameter('hitsPerPage', 10)); - - params = connect.getSearchParameters( - sp, - { defaultRefinement: 20, contextValue, indexContextValue }, - {} - ); - expect(params).toEqual(sp.setQueryParameter('hitsPerPage', 20)); - }); - - it('registers its id in metadata', () => { - const metadata = connect.getMetadata({}); - expect(metadata).toEqual({ id: 'hitsPerPage' }); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { contextValue, indexContextValue }, - { - indices: { - second: { - hitsPerPage: 'searchState', - another: { searchState: 'searchState' }, - }, - }, - } - ); - expect(searchState).toEqual({ - indices: { second: { another: { searchState: 'searchState' } } }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectInfiniteHits.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectInfiniteHits.js deleted file mode 100644 index 0fb58a0643..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectInfiniteHits.js +++ /dev/null @@ -1,1059 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import isEqual from 'react-fast-compare'; - -import connect from '../connectInfiniteHits'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectInfiniteHits', () => { - describe('single index', () => { - const contextValue = { - mainTargetedIndex: 'index', - }; - - it('provides the current hits to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps.call({}, { contextValue }, null, { - results: { hits, page: 0, hitsPerPage: 2, nbPages: 3 }, - }); - - expect(props).toEqual({ - hits: hits.map((hit) => expect.objectContaining(hit)), - hasPrevious: false, - hasMore: true, - refinePrevious: expect.any(Function), - refineNext: expect.any(Function), - }); - }); - - it('accumulate hits internally', () => { - const hits = [{}, {}]; - const hits2 = [{}, {}]; - const instance = {}; - - const res1 = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { hits, page: 0, hitsPerPage: 2, nbPages: 3 }, - } - ); - - expect(res1.hits).toEqual( - hits.map((hit) => expect.objectContaining(hit)) - ); - expect(res1.hasMore).toBe(true); - - const res2 = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits: hits2, - page: 1, - hitsPerPage: 2, - nbPages: 3, - }, - } - ); - - expect(res2.hits).toEqual( - [...hits, ...hits2].map((hit) => expect.objectContaining(hit)) - ); - expect(res2.hasMore).toBe(true); - }); - - it('prepend hits internally', () => { - const instance = {}; - const initialPageHits = [{}, {}]; - const previousPageHits = [{}, {}]; - const initialPageProps = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits: initialPageHits, - page: 1, - hitsPerPage: 2, - nbPages: 3, - }, - } - ); - - expect(initialPageProps.hits).toEqual( - initialPageHits.map((hit) => expect.objectContaining(hit)) - ); - expect(initialPageProps.hasPrevious).toBe(true); - - const previousPageProps = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits: previousPageHits, - page: 0, - hitsPerPage: 2, - nbPages: 3, - }, - } - ); - - expect(previousPageProps.hits).toEqual( - [...previousPageHits, ...initialPageHits].map((hit) => - expect.objectContaining(hit) - ) - ); - expect(previousPageProps.hasPrevious).toBe(false); - }); - - it('accumulate hits internally while changing hitsPerPage configuration', () => { - const hits = [{}, {}, {}, {}, {}, {}]; - const hits2 = [{}, {}, {}, {}, {}, {}]; - const hits3 = [{}, {}, {}, {}, {}, {}, {}, {}]; - const instance = {}; - - const res1 = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits, - page: 0, - hitsPerPage: 6, - nbPages: 10, - queryID: 'theQueryID_0', - }, - } - ); - - expect(res1.hits).toEqual( - hits.map((hit) => expect.objectContaining(hit)) - ); - expect(res1.hits.map((hit) => hit.__position)).toEqual([ - 1, 2, 3, 4, 5, 6, - ]); - expect(res1.hits.map((hit) => hit.__queryID)).toEqual([ - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - ]); - expect(res1.hasMore).toBe(true); - - const res2 = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits: hits2, - page: 1, - hitsPerPage: 6, - nbPages: 10, - queryID: 'theQueryID_1', - }, - } - ); - - expect(res2.hits).toEqual( - [...hits, ...hits2].map((hit) => expect.objectContaining(hit)) - ); - expect(res2.hits.map((hit) => hit.__position)).toEqual([ - // page 0 - 1, 2, 3, 4, 5, 6, - // page 1 - 7, 8, 9, 10, 11, 12, - ]); - expect(res2.hits.map((hit) => hit.__queryID)).toEqual([ - // page 0 - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - // page 1 - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - ]); - expect(res2.hasMore).toBe(true); - - let res3 = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits: hits3, - page: 2, - hitsPerPage: 8, - nbPages: 10, - queryID: 'theQueryID_2', - }, - } - ); - - expect(res3.hits).toEqual( - [...hits, ...hits2, ...hits3].map((hit) => expect.objectContaining(hit)) - ); - expect(res3.hits.map((hit) => hit.__position)).toEqual([ - // page: 0, hitsPerPage: 6 - 1, 2, 3, 4, 5, 6, - // page: 1, hitsPerPage: 6 - 7, 8, 9, 10, 11, 12, - // hitsPerPage changed from 6 to 8, elements 13-16 are skipped - // page: 2, hitsPerPage: 8 - 17, 18, 19, 20, 21, 22, 23, 24, - ]); - expect(res3.hits.map((hit) => hit.__queryID)).toEqual([ - // page 0 - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - // page 1 - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - // page 2 - 'theQueryID_2', - 'theQueryID_2', - 'theQueryID_2', - 'theQueryID_2', - 'theQueryID_2', - 'theQueryID_2', - 'theQueryID_2', - 'theQueryID_2', - ]); - expect(res3.hasMore).toBe(true); - - // re-render with the same property - res3 = connect.getProvidedProps.call(instance, { contextValue }, null, { - results: { - hits: hits3, - page: 2, - hitsPerPage: 8, - nbPages: 10, - queryID: 'theQueryID_2_', - }, - }); - - expect(res3.hits).toEqual( - [...hits, ...hits2, ...hits3].map((hit) => expect.objectContaining(hit)) - ); - expect(res3.hits.map((hit) => hit.__position)).toEqual([ - // page: 0, hitsPerPage: 6 - 1, 2, 3, 4, 5, 6, - // page: 1, hitsPerPage: 6 - 7, 8, 9, 10, 11, 12, - // hitsPerPage changed from 6 to 8, elements 13-16 are skipped - // page: 2, hitsPerPage: 8 - 17, 18, 19, 20, 21, 22, 23, 24, - ]); - expect(res3.hits.map((hit) => hit.__queryID)).toEqual([ - // page 0 - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_0', - // page 1 - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_1', - // page 2 - 'theQueryID_2_', - 'theQueryID_2_', - 'theQueryID_2_', - 'theQueryID_2_', - 'theQueryID_2_', - 'theQueryID_2_', - 'theQueryID_2_', - 'theQueryID_2_', - ]); - expect(res3.hasMore).toBe(true); - }); - - it('should not reset while accumulating results', () => { - const hits = [{}, {}]; - const nbPages = 5; - const instance = {}; - - let allHits = []; - for (let page = 0; page < nbPages - 1; page++) { - allHits = [...allHits, ...hits]; - - const res = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits, - page, - hitsPerPage: hits.length, - nbPages, - queryID: `theQueryID_${page}`, - }, - } - ); - - expect(res.hits).toEqual( - allHits.map((hit) => expect.objectContaining(hit)) - ); - expect(res.hits).toHaveLength((page + 1) * 2); - expect(res.hasMore).toBe(true); - } - - allHits = [...allHits, ...hits]; - - const res = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits, - page: nbPages - 1, - hitsPerPage: hits.length, - nbPages, - queryID: `theQueryID_${nbPages - 1}`, - }, - } - ); - - expect(res.hits).toHaveLength(nbPages * 2); - expect(res.hits).toEqual( - allHits.map((hit) => expect.objectContaining(hit)) - ); - expect(res.hits.map((hit) => hit.__position)).toEqual([ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - ]); - expect(res.hits.map((hit) => hit.__queryID)).toEqual([ - 'theQueryID_0', - 'theQueryID_0', - 'theQueryID_1', - 'theQueryID_1', - 'theQueryID_2', - 'theQueryID_2', - 'theQueryID_3', - 'theQueryID_3', - 'theQueryID_4', - 'theQueryID_4', - ]); - expect(res.hasMore).toBe(false); - }); - - it('Indicates the last page after several pages', () => { - const hits = [{}, {}]; - const hits2 = [{}, {}]; - const hits3 = [{}]; - const instance = {}; - - connect.getProvidedProps.call(instance, { contextValue }, null, { - results: { hits, page: 0, hitsPerPage: 2, nbPages: 3 }, - }); - - connect.getProvidedProps.call(instance, { contextValue }, null, { - results: { - hits: hits2, - page: 1, - hitsPerPage: 2, - nbPages: 3, - }, - }); - - const props = connect.getProvidedProps.call( - instance, - { contextValue }, - null, - { - results: { - hits: hits3, - page: 2, - hitsPerPage: 2, - nbPages: 3, - }, - } - ); - - expect(props.hits).toEqual( - [...hits, ...hits2, ...hits3].map((hit) => expect.objectContaining(hit)) - ); - expect(props.hasMore).toBe(false); - }); - - it('calls refine with next page when calling refineNext', () => { - const instance = { refine: jest.fn() }; - const hits = [{}, {}]; - const event = new Event('click'); - - const props = connect.getProvidedProps.call( - instance, - { contextValue }, - {}, - { - results: { - hits, - page: 2, - hitsPerPage: 2, - nbPages: 3, - }, - } - ); - - props.refineNext.call(instance, event); - - expect(instance.refine).toHaveBeenCalledTimes(1); - expect(instance.refine).toHaveBeenLastCalledWith(event, 3); - }); - - it('calls refine with previous page when calling refinePrevious', () => { - const instance = { refine: jest.fn() }; - const hits = [{}, {}]; - const event = new Event('click'); - - const props = connect.getProvidedProps.call( - instance, - { contextValue }, - {}, - { - results: { - hits, - page: 2, - hitsPerPage: 2, - nbPages: 3, - }, - } - ); - - props.refinePrevious.call(instance, event); - - expect(instance.refine).toHaveBeenCalledTimes(1); - expect(instance.refine).toHaveBeenLastCalledWith(event, 1); - }); - - it('set page to the corresponding index', () => { - const props = { contextValue }; - const state0 = {}; - const event = new Event('click'); - const index = 5; - - const state1 = connect.refine.call({}, props, state0, event, index); - // `index` is indexed from 0 but page number is indexed from 1 - expect(state1).toEqual({ page: 6 }); - }); - - it('expect to always return an array of hits', () => { - const props = { contextValue }; - const searchState = {}; - - // Retrieve the results from the cache that's why - // the page it's not zero on the first render - const searchResults = { - results: { - hits: [{}, {}, {}], - hitsPerPage: 3, - page: 1, - nbPages: 3, - }, - }; - - const expectation = { - hits: [{}, {}, {}].map((hit) => expect.objectContaining(hit)), - hasPrevious: true, - hasMore: true, - refinePrevious: expect.any(Function), - refineNext: expect.any(Function), - }; - - const actual = connect.getProvidedProps.call( - {}, - props, - searchState, - searchResults - ); - - expect(actual).toEqual(expectation); - }); - - it('does not read from given cache when results is empty', () => { - const cache = { - read: jest.fn(), - write: jest.fn(), - }; - const props = { cache, contextValue }; - const searchState = {}; - const searchResults = {}; - connect.getProvidedProps.call({}, props, searchState, searchResults); - expect(cache.read).toHaveBeenCalledTimes(0); - }); - - it('read from given cache', () => { - const cache = { - read: jest.fn(), - write: jest.fn(), - }; - const props = { cache, contextValue }; - const searchState = {}; - const searchResults = { - results: { - hits: [{}, {}, {}], - hitsPerPage: 3, - page: 1, - nbPages: 3, - }, - }; - connect.getProvidedProps.call({}, props, searchState, searchResults); - expect(cache.read).toHaveBeenCalledTimes(1); - }); - - it('read from new cache prop', () => { - const cache = { - read: jest.fn(), - write: jest.fn(), - }; - const searchState = {}; - const searchResults = { - results: { - hits: [{}, {}, {}], - hitsPerPage: 3, - page: 1, - nbPages: 3, - }, - }; - const instance = {}; - connect.getProvidedProps.call( - instance, - { cache, contextValue }, - searchState, - searchResults - ); - expect(cache.read).toHaveBeenCalledTimes(1); - - const cache2 = { - read: jest.fn(), - write: jest.fn(), - }; - connect.getProvidedProps.call( - instance, - { cache: cache2, contextValue }, - searchState, - searchResults - ); - expect(cache.read).toHaveBeenCalledTimes(1); - expect(cache2.read).toHaveBeenCalledTimes(1); - }); - - it('keep the same in-memory cache', () => { - const searchState = {}; - const searchResults = {}; - const instance = {}; - connect.getProvidedProps.call( - instance, - { contextValue }, - searchState, - searchResults - ); - const memoryCache = instance._cache; - - connect.getProvidedProps.call( - instance, - { contextValue }, - searchState, - searchResults - ); - expect(instance._cache).toBe(memoryCache); - }); - - it('render hits correctly after invalidating cache', () => { - const getStateWithoutPage = (state) => { - const { page, ...rest } = state || {}; - return rest; - }; - - const getInMemoryCache = () => { - let cachedHits = undefined; - let cachedState = undefined; - return { - read({ state }) { - return isEqual(cachedState, getStateWithoutPage(state)) - ? cachedHits - : null; - }, - write({ state, hits }) { - cachedState = getStateWithoutPage(state); - cachedHits = hits; - }, - clear() { - cachedHits = undefined; - cachedState = undefined; - }, - }; - }; - - const instance = {}; - const cache = getInMemoryCache(); - const props = { cache, contextValue }; - const searchState = {}; - const searchResults1 = { - results: { - hits: [{ objectID: 'a' }, { objectID: 'b' }, { objectID: 'c' }], - hitsPerPage: 3, - page: 0, - nbPages: 3, - }, - }; - expect( - connect.getProvidedProps.call( - instance, - props, - searchState, - searchResults1 - ).hits - ).toMatchInlineSnapshot(` - [ - { - "__position": 1, - "objectID": "a", - }, - { - "__position": 2, - "objectID": "b", - }, - { - "__position": 3, - "objectID": "c", - }, - ] - `); - - const searchResults2 = { - results: { - hits: [{ objectID: 'd' }, { objectID: 'e' }, { objectID: 'f' }], - hitsPerPage: 3, - page: 1, - nbPages: 3, - }, - }; - expect( - connect.getProvidedProps.call( - instance, - props, - searchState, - searchResults2 - ).hits - ).toMatchInlineSnapshot(` - [ - { - "__position": 1, - "objectID": "a", - }, - { - "__position": 2, - "objectID": "b", - }, - { - "__position": 3, - "objectID": "c", - }, - { - "__position": 4, - "objectID": "d", - }, - { - "__position": 5, - "objectID": "e", - }, - { - "__position": 6, - "objectID": "f", - }, - ] - `); - - cache.clear(); - expect( - connect.getProvidedProps.call( - instance, - props, - searchState, - searchResults2 - ).hits - ).toMatchInlineSnapshot(` - [ - { - "__position": 4, - "objectID": "d", - }, - { - "__position": 5, - "objectID": "e", - }, - { - "__position": 6, - "objectID": "f", - }, - ] - `); - }); - }); - - describe('multi index', () => { - const contextValue = { - mainTargetedIndex: 'first', - }; - const indexContextValue = { - targetedIndex: 'second', - }; - - it('provides the current hits to the component', () => { - const hits = [{}]; - const props = connect.getProvidedProps.call( - {}, - { contextValue, indexContextValue }, - null, - { - results: { second: { hits, page: 0, hitsPerPage: 2, nbPages: 3 } }, - } - ); - - expect(props).toEqual({ - hits: hits.map((hit) => expect.objectContaining(hit)), - hasPrevious: false, - hasMore: true, - refinePrevious: expect.any(Function), - refineNext: expect.any(Function), - }); - }); - - it('accumulate hits internally', () => { - const hits = [{}, {}]; - const hits2 = [{}, {}]; - - const instance = {}; - - const res1 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { second: { hits, page: 0, hitsPerPage: 2, nbPages: 3 } }, - } - ); - - expect(res1.hits).toEqual( - hits.map((hit) => expect.objectContaining(hit)) - ); - expect(res1.hasMore).toBe(true); - - const res2 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { hits: hits2, page: 1, hitsPerPage: 2, nbPages: 3 }, - }, - } - ); - - expect(res2.hits).toEqual( - [...hits, ...hits2].map((hit) => expect.objectContaining(hit)) - ); - expect(res2.hasMore).toBe(true); - }); - - it('prepend hits internally', () => { - const initialPageHits = [{}, {}]; - const previousPageHits = [{}, {}]; - const instance = {}; - const initialPageProps = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits: initialPageHits, - page: 1, - hitsPerPage: 2, - nbPages: 3, - }, - }, - } - ); - - expect(initialPageProps.hits).toEqual( - initialPageHits.map((hit) => expect.objectContaining(hit)) - ); - expect(initialPageProps.hasPrevious).toBe(true); - - const previousPageProps = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits: previousPageHits, - page: 0, - hitsPerPage: 2, - nbPages: 3, - }, - }, - } - ); - - expect(previousPageProps.hits).toEqual( - [...previousPageHits, ...initialPageHits].map((hit) => - expect.objectContaining(hit) - ) - ); - expect(previousPageProps.hasPrevious).toBe(false); - }); - - it('accumulate hits internally while changing hitsPerPage configuration', () => { - const hits = [{}, {}, {}, {}, {}, {}]; - const hits2 = [{}, {}, {}, {}, {}, {}]; - const hits3 = [{}, {}, {}, {}, {}, {}, {}, {}]; - const instance = {}; - - const res1 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { second: { hits, page: 0, hitsPerPage: 6, nbPages: 10 } }, - } - ); - - expect(res1.hits).toEqual( - hits.map((hit) => expect.objectContaining(hit)) - ); - expect(res1.hasMore).toBe(true); - - const res2 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { hits: hits2, page: 1, hitsPerPage: 6, nbPages: 10 }, - }, - } - ); - - expect(res2.hits).toEqual( - [...hits, ...hits2].map((hit) => expect.objectContaining(hit)) - ); - expect(res2.hasMore).toBe(true); - - let res3 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { hits: hits3, page: 2, hitsPerPage: 8, nbPages: 10 }, - }, - } - ); - - expect(res3.hits).toEqual( - [...hits, ...hits2, ...hits3].map((hit) => expect.objectContaining(hit)) - ); - expect(res3.hasMore).toBe(true); - - // re-render with the same property - res3 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { hits: hits3, page: 2, hitsPerPage: 8, nbPages: 10 }, - }, - } - ); - - expect(res3.hits).toEqual( - [...hits, ...hits2, ...hits3].map((hit) => expect.objectContaining(hit)) - ); - expect(res3.hasMore).toBe(true); - }); - - it('should not accumulate hits internally while changing query', () => { - const instance = {}; - const hits = [{}, {}, {}, {}, {}, {}]; - const hits2 = [{}, {}, {}, {}, {}, {}]; - - const res1 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits, - page: 0, - hitsPerPage: 6, - nbPages: 10, - _state: { page: 0, query: 'a' }, - }, - }, - } - ); - - expect(res1.hits).toEqual( - hits.map((hit) => expect.objectContaining(hit)) - ); - expect(res1.hasMore).toBe(true); - - const res2 = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits: hits2, - page: 0, - hitsPerPage: 6, - nbPages: 10, - _state: { page: 0, query: 'b' }, - }, - }, - } - ); - - expect(res2.hits).toEqual( - hits2.map((hit) => expect.objectContaining(hit)) - ); - expect(res2.hasMore).toBe(true); - }); - - it('should not reset while accumulating results', () => { - const hits = [{}, {}]; - const nbPages = 100; - const instance = {}; - - let allHits = []; - for (let page = 0; page < nbPages - 1; page++) { - allHits = [...allHits, ...hits]; - - const res = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits, - page, - hitsPerPage: hits.length, - nbPages, - }, - }, - } - ); - - expect(res.hits).toEqual( - allHits.map((hit) => expect.objectContaining(hit)) - ); - expect(res.hits).toHaveLength((page + 1) * 2); - expect(res.hasMore).toBe(true); - } - - allHits = [...allHits, ...hits]; - - const res = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits, - page: nbPages - 1, - hitsPerPage: hits.length, - nbPages, - }, - }, - } - ); - - expect(res.hits).toHaveLength(nbPages * 2); - expect(res.hits).toEqual( - allHits.map((hit) => expect.objectContaining(hit)) - ); - expect(res.hasMore).toBe(false); - }); - - it('Indicates the last page after several pages', () => { - const hits = [{}, {}]; - const hits2 = [{}, {}]; - const hits3 = [{}]; - const instance = {}; - - connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { second: { hits, page: 0, hitsPerPage: 2, nbPages: 3 } }, - } - ); - - connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { hits: hits2, page: 1, hitsPerPage: 2, nbPages: 3 }, - }, - } - ); - - const props = connect.getProvidedProps.call( - instance, - { contextValue, indexContextValue }, - null, - { - results: { - second: { hits: hits3, page: 2, hitsPerPage: 2, nbPages: 3 }, - }, - } - ); - - expect(props.hits).toEqual( - [...hits, ...hits2, ...hits3].map((hit) => expect.objectContaining(hit)) - ); - expect(props.hasMore).toBe(false); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js deleted file mode 100644 index eee944af54..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js +++ /dev/null @@ -1,1087 +0,0 @@ -import { SearchParameters, SearchResults } from 'algoliasearch-helper'; - -import connect from '../connectMenu'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectMenu', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - const results = { - getFacetValues: jest.fn(() => []), - getFacetByName: () => true, - hits: [], - }; - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - {}, - {} - ); - expect(props).toEqual({ - items: [], - currentRefinement: null, - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - { menu: { ok: 'wat' } }, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: 'wat', - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - { menu: { ok: 'wat' } }, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: 'wat', - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', defaultRefinement: 'wat', contextValue }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: 'wat', - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: null, - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - results.getFacetValues.mockClear(); - results.getFacetValues.mockImplementation(() => [ - { - name: 'wat', - escapedValue: 'wat', - isRefined: true, - count: 20, - }, - { - name: 'oy', - escapedValue: 'oy', - isRefined: false, - count: 10, - }, - ]); - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - { - value: 'oy', - label: 'oy', - isRefined: false, - count: 10, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { - attribute: 'ok', - showMore: true, - limit: 0, - showMoreLimit: 1, - contextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue }, - {}, - { results }, - {}, - { - query: 'query', - ok: [ - { - value: 'wat', - escapedValue: 'wat', - count: 10, - highlighted: 'wat', - isRefined: false, - }, - ], - } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: false, - count: 10, - _highlightResult: { label: { value: 'wat' } }, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue }, - {}, - { results }, - {}, - { query: '' } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { attribute: 'ok', transformItems, contextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - { - value: 'oy', - label: 'oy', - isRefined: false, - count: 10, - }, - ]); - expect(props.items).toEqual(['items']); - }); - - it('if an item is equal to the currentRefinement, its value should be an empty string', () => { - const results = { - getFacetValues: jest.fn(() => []), - getFacetByName: () => true, - hits: [], - }; - results.getFacetValues.mockImplementation(() => [ - { - name: 'wat', - escapedValue: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - { menu: { ok: 'wat' } }, - { results } - ); - - expect(props.items).toEqual([ - { - value: '', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - }); - - it('facetValues have facetOrdering by default', () => { - const userProps = { - ...connect.defaultProps, - attribute: 'ok', - contextValue, - }; - const searchState = { - menu: { ok: 'wat' }, - }; - const parameters = connect.getSearchParameters( - new SearchParameters(), - userProps, - searchState - ); - - const searchResults = new SearchResults(parameters, [ - { - hits: [], - renderingContent: { - facetOrdering: { - values: { - ok: { - order: ['wat'], - }, - }, - }, - }, - facets: { - ok: { - wat: 20, - lol: 2000, - }, - }, - }, - ]); - - const providedProps = connect.getProvidedProps(userProps, searchState, { - results: searchResults, - }); - - expect(providedProps.items).toEqual([ - { - count: 20, - isRefined: true, - label: 'wat', - value: '', - }, - { - count: 2000, - isRefined: false, - label: 'lol', - value: 'lol', - }, - ]); - expect(providedProps.isFromSearch).toBe(false); - }); - - it('facetValues results does not use facetOrdering if disabled', () => { - const userProps = { attribute: 'ok', facetOrdering: false, contextValue }; - const searchState = { - menu: { ok: 'wat' }, - }; - const parameters = connect.getSearchParameters( - new SearchParameters(), - userProps, - searchState - ); - - const searchResults = new SearchResults(parameters, [ - { - hits: [], - renderingContent: { - facetOrdering: { - values: { - ok: { - order: ['wat'], - }, - }, - }, - }, - facets: { - ok: { - wat: 20, - lol: 2000, - }, - }, - }, - ]); - - const providedProps = connect.getProvidedProps(userProps, searchState, { - results: searchResults, - }); - - expect(providedProps.items).toEqual([ - { - count: 2000, - isRefined: false, - label: 'lol', - value: 'lol', - }, - { - count: 20, - isRefined: true, - label: 'wat', - value: '', - }, - ]); - expect(providedProps.isFromSearch).toBe(false); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { attribute: 'ok', contextValue }, - { otherKey: 'val', menu: { otherKey: 'val' } }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - menu: { ok: 'yep', otherKey: 'val' }, - }); - }); - - it("increases maxValuesPerFacet when it isn't big enough", () => { - const initSP = new SearchParameters({ maxValuesPerFacet: 100 }); - - params = connect.getSearchParameters( - initSP, - { - limit: 101, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(101); - - params = connect.getSearchParameters( - initSP, - { - showMore: true, - showMoreLimit: 101, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(101); - - params = connect.getSearchParameters( - initSP, - { - limit: 99, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(100); - - params = connect.getSearchParameters( - initSP, - { - showMore: true, - showMoreLimit: 99, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(100); - }); - - it('correctly applies its state to search parameters', () => { - const initSP = new SearchParameters(); - - params = connect.getSearchParameters( - initSP, - { - attribute: 'ok', - limit: 1, - contextValue, - }, - { menu: { ok: 'wat' } } - ); - expect(params).toEqual( - initSP - .addDisjunctiveFacet('ok') - .addDisjunctiveFacetRefinement('ok', 'wat') - .setQueryParameter('maxValuesPerFacet', 1) - ); - }); - - it('registers its id in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'ok', contextValue }, - {} - ); - expect(metadata).toEqual({ id: 'ok', index: 'index', items: [] }); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'wot', contextValue }, - { menu: { wot: 'wat' } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [ - { - label: 'wot: wat', - attribute: 'wot', - currentRefinement: 'wat', - // Ignore clear, we test it later - value: metadata.items[0].value, - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attribute: 'one', contextValue }, - { menu: { one: 'one', two: 'two' } } - ); - - const searchState = metadata.items[0].value({ - menu: { one: 'one', two: 'two' }, - }); - - expect(searchState).toEqual({ page: 1, menu: { one: '', two: 'two' } }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue }, - { - menu: { name: 'searchState', name2: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - menu: { name2: 'searchState' }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue }, - searchState - ); - expect(searchState).toEqual({ - menu: {}, - another: { searchState: 'searchState' }, - }); - }); - - it('calling searchForItems return the right searchForItems parameters with limit', () => { - const parameters = connect.searchForFacetValues( - { attribute: 'ok', limit: 15, showMoreLimit: 25, showMore: false }, - {}, - 'yep' - ); - - expect(parameters).toEqual({ - facetName: 'ok', - query: 'yep', - maxFacetHits: 15, - }); - }); - - it('calling searchForItems return the right searchForItems parameters with showMoreLimit', () => { - const parameters = connect.searchForFacetValues( - { attribute: 'ok', limit: 15, showMoreLimit: 25, showMore: true }, - {}, - 'yep' - ); - - expect(parameters).toEqual({ - facetName: 'ok', - query: 'yep', - maxFacetHits: 25, - }); - }); - - it('if not searchable: uses a static sortBy order', () => { - const results = { - getFacetValues: jest.fn(() => [ - { - name: 'oy', - escapedValue: 'oy', - isRefined: true, - count: 10, - }, - { - name: 'wat', - escapedValue: 'wat', - isRefined: false, - count: 20, - }, - ]), - getFacetByName: () => true, - hits: [], - }; - - const { defaultProps } = connect; - props = connect.getProvidedProps( - { ...defaultProps, attribute: 'ok', contextValue }, - {}, - { results } - ); - - expect(results.getFacetValues).toHaveBeenCalledWith('ok', { - sortBy: ['count:desc', 'name:asc'], - facetOrdering: true, - }); - - expect(props.items).toEqual([ - { - value: 'oy', - label: 'oy', - isRefined: true, - count: 10, - }, - { - value: 'wat', - label: 'wat', - isRefined: false, - count: 20, - }, - ]); - }); - - it('if searchable: use the default sortBy order', () => { - const results = { - getFacetValues: jest.fn(() => [ - { - name: 'oy', - escapedValue: 'oy', - isRefined: true, - count: 10, - }, - { - name: 'wat', - escapedValue: 'wat', - isRefined: false, - count: 20, - }, - ]), - getFacetByName: () => true, - hits: [], - }; - - const { defaultProps } = connect; - props = connect.getProvidedProps( - { - ...defaultProps, - attribute: 'ok', - searchable: true, - contextValue, - }, - {}, - { results } - ); - - expect(results.getFacetValues).toHaveBeenCalledWith('ok', { - sortBy: undefined, - facetOrdering: true, - }); - - expect(props.items).toEqual([ - { - value: 'oy', - label: 'oy', - isRefined: true, - count: 10, - }, - { - value: 'wat', - label: 'wat', - isRefined: false, - count: 20, - }, - ]); - }); - - it('computes canRefine based on the length of the transformed items list', () => { - const transformItems = () => []; - const results = { - getFacetValues: () => [ - { count: 1, id: 'test', isRefined: true, name: 'test' }, - ], - getFacetByName: () => true, - hits: [], - }; - - props = connect.getProvidedProps( - { attribute: 'ok', transformItems, contextValue }, - {}, - { results } - ); - - expect(props.canRefine).toEqual(false); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - const results = { - second: { - getFacetValues: jest.fn(() => []), - getFacetByName: () => true, - }, - }; - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - {}, - {} - ); - expect(props).toEqual({ - items: [], - currentRefinement: null, - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - { indices: { second: { menu: { ok: 'wat' } } } }, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: 'wat', - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - { indices: { second: { menu: { ok: 'wat' } } } }, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: 'wat', - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { - attribute: 'ok', - defaultRefinement: 'wat', - contextValue, - indexContextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: 'wat', - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: null, - isFromSearch: false, - canRefine: false, - searchForItems: undefined, - }); - - results.second.getFacetValues.mockClear(); - results.second.getFacetValues.mockImplementation(() => [ - { - name: 'wat', - escapedValue: 'wat', - isRefined: true, - count: 20, - }, - { - name: 'oy', - escapedValue: 'oy', - isRefined: false, - count: 10, - }, - ]); - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - { - value: 'oy', - label: 'oy', - isRefined: false, - count: 10, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue, indexContextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { - attribute: 'ok', - showMore: true, - limit: 0, - showMoreLimit: 1, - contextValue, - indexContextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue, indexContextValue }, - {}, - { results }, - {}, - { - query: 'query', - ok: [ - { - value: 'wat', - escapedValue: 'wat', - count: 10, - highlighted: 'wat', - isRefined: false, - }, - ], - } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: false, - count: 10, - _highlightResult: { label: { value: 'wat' } }, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue, indexContextValue }, - {}, - { results }, - {}, - { query: '' } - ); - expect(props.items).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { attribute: 'ok', transformItems, contextValue, indexContextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - value: 'wat', - label: 'wat', - isRefined: true, - count: 20, - }, - { - value: 'oy', - label: 'oy', - isRefined: false, - count: 10, - }, - ]); - expect(props.items).toEqual(['items']); - }); - - it('if an item is equal to the currentRefinement, its value should be an empty string', () => { - const results = { - second: { - getFacetValues: jest.fn(() => []), - getFacetByName: () => true, - }, - }; - results.second.getFacetValues.mockImplementation(() => [ - { - name: 'wat', - escapedValue: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - { indices: { second: { menu: { ok: 'wat' } } } }, - { results } - ); - - expect(props.items).toEqual([ - { - value: '', - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { attribute: 'ok', contextValue, indexContextValue }, - { - indices: { - second: { otherKey: 'val', menu: { ok: 'wat', otherKey: 'val' } }, - }, - }, - 'yep' - ); - expect(nextState).toEqual({ - indices: { - second: { - page: 1, - otherKey: 'val', - menu: { ok: 'yep', otherKey: 'val' }, - }, - }, - }); - - nextState = connect.refine( - { - attribute: 'ok', - contextValue, - indexContextValue: { targetedIndex: 'second' }, - }, - { - indices: { - first: { otherKey: 'val', menu: { ok: 'wat', otherKey: 'val' } }, - }, - }, - 'yep' - ); - expect(nextState).toEqual({ - indices: { - first: { otherKey: 'val', menu: { ok: 'wat', otherKey: 'val' } }, - second: { page: 1, menu: { ok: 'yep' } }, - }, - }); - }); - - it('correctly applies its state to search parameters', () => { - const initSP = new SearchParameters(); - - params = connect.getSearchParameters( - initSP, - { - attribute: 'ok', - limit: 1, - contextValue, - indexContextValue, - }, - { indices: { second: { menu: { ok: 'wat' } } } } - ); - expect(params).toEqual( - initSP - .addDisjunctiveFacet('ok') - .addDisjunctiveFacetRefinement('ok', 'wat') - .setQueryParameter('maxValuesPerFacet', 1) - ); - }); - - describe('getMetaData', () => { - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'wot', contextValue, indexContextValue }, - { indices: { second: { menu: { wot: 'wat' } } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'second', - items: [ - { - label: 'wot: wat', - attribute: 'wot', - currentRefinement: 'wat', - // Ignore clear, we test it later - value: metadata.items[0].value, - }, - ], - }); - }); - - it('registers escaped metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'wot', contextValue, indexContextValue }, - { indices: { second: { menu: { wot: '\\-wat' } } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'second', - items: [ - { - label: 'wot: -wat', - attribute: 'wot', - currentRefinement: '\\-wat', - // Ignore clear, we test it later - value: metadata.items[0].value, - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attribute: 'one', contextValue, indexContextValue }, - { indices: { second: { menu: { one: 'one', two: 'two' } } } } - ); - - const searchState = metadata.items[0].value({ - indices: { second: { menu: { one: 'one', two: 'two' } } }, - }); - - expect(searchState).toEqual({ - indices: { second: { page: 1, menu: { one: '', two: 'two' } } }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue, indexContextValue }, - { - indices: { - first: { - random: { untouched: 'yes' }, - }, - second: { - menu: { name: 'searchState', name2: 'searchState2' }, - another: { searchState: 'searchState' }, - }, - }, - } - ); - - expect(searchState).toEqual({ - indices: { - first: { - random: { untouched: 'yes' }, - }, - second: { - another: { - searchState: 'searchState', - }, - menu: { - name2: 'searchState2', - }, - }, - }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue, indexContextValue }, - searchState - ); - expect(searchState).toEqual({ - indices: { - first: { - random: { untouched: 'yes' }, - }, - second: { another: { searchState: 'searchState' }, menu: {} }, - }, - }); - }); - }); - - it('errors if searchable is used in a multi index context', () => { - expect(() => { - connect.getProvidedProps( - { - contextValue, - indexContextValue, - searchable: true, - }, - {}, - {} - ); - }).toThrowErrorMatchingInlineSnapshot( - `"react-instantsearch: searching in *List is not available when used inside a multi index context"` - ); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectNumericMenu.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectNumericMenu.js deleted file mode 100644 index 6073bc7345..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectNumericMenu.js +++ /dev/null @@ -1,710 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectNumericMenu'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectNumericMenu', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - const results = { - getFacetStats: () => ({ min: 0, max: 300 }), - getFacetByName: () => true, - hits: [], - }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { - items: [], - contextValue, - }, - {}, - { results } - ); - - expect(props).toEqual({ - items: [ - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - - props = connect.getProvidedProps( - { - items: [{ label: 'ALL' }], - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [ - { label: 'ALL', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - - props = connect.getProvidedProps( - { - items: [{ label: 'Ok', start: 100 }], - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [ - { label: 'Ok', value: '100:', isRefined: false, noRefinement: false }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - - props = connect.getProvidedProps( - { - items: [{ label: 'Not ok', end: 200 }], - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [ - { - label: 'Not ok', - value: ':200', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - - props = connect.getProvidedProps( - { - items: [ - { label: 'Ok', start: 100 }, - { label: 'Not ok', end: 200 }, - { label: 'Maybe ok?', start: 100, end: 200 }, - ], - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [ - { label: 'Ok', value: '100:', isRefined: false, noRefinement: false }, - { - label: 'Not ok', - value: ':200', - isRefined: false, - noRefinement: false, - }, - { - label: 'Maybe ok?', - value: '100:200', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - - props = connect.getProvidedProps( - { - items: [ - { label: 'is 0', start: 0, end: 0 }, - { label: 'in 0..0.5', start: 0, end: 0.5 }, - ], - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [ - { - label: 'is 0', - value: '0:0', - isRefined: false, - noRefinement: false, - }, - { - label: 'in 0..0.5', - value: '0:0.5', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - - props = connect.getProvidedProps( - { - items: [ - { label: 'is 0', start: 0, end: 0 }, - { label: 'in 0..10', start: 0, end: 10 }, - ], - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [ - { - label: 'is 0', - value: '0:0', - isRefined: false, - noRefinement: false, - }, - { - label: 'in 0..10', - value: '0:10', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - }); - - it('no items define', () => { - props = connect.getProvidedProps( - { attribute: 'ok', items: [], contextValue }, - { multiRange: { ok: 'wat' } }, - { results } - ); - expect(props).toEqual({ - items: [ - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: 'wat', - canRefine: true, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', items: [], contextValue }, - { multiRange: { ok: 'wat' } }, - {} - ); - expect(props).toEqual({ - items: [ - { label: 'All', value: '', isRefined: true, noRefinement: true }, - ], - currentRefinement: 'wat', - canRefine: false, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', items: [], defaultRefinement: 'wat', contextValue }, - {}, - {} - ); - expect(props).toEqual({ - items: [ - { label: 'All', value: '', isRefined: true, noRefinement: true }, - ], - currentRefinement: 'wat', - canRefine: false, - }); - }); - - it('use the transform items props if passed', () => { - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { - items: [ - { label: 'Ok', start: 100 }, - { label: 'Not ok', end: 200 }, - { label: 'Maybe ok?', start: 100, end: 200 }, - ], - transformItems, - contextValue, - }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { label: 'Ok', value: '100:', isRefined: false, noRefinement: false }, - { - label: 'Not ok', - value: ':200', - isRefined: false, - noRefinement: false, - }, - { - label: 'Maybe ok?', - value: '100:200', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ]); - expect(props.items).toEqual(['items']); - }); - - it('compute the no refinement value for each item range when stats exists', () => { - props = connect.getProvidedProps( - { - items: [ - { label: '1', start: 100 }, - { label: '2', start: 400 }, - { label: '3', end: 200 }, - { label: '4', start: 100, end: 200 }, - { label: '5', start: 300 }, - { label: '6', start: 300, end: 300 }, - ], - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - items: [ - { label: '1', value: '100:', isRefined: false, noRefinement: false }, - { label: '2', value: '400:', isRefined: false, noRefinement: true }, - { label: '3', value: ':200', isRefined: false, noRefinement: false }, - { - label: '4', - value: '100:200', - isRefined: false, - noRefinement: false, - }, - { label: '5', value: '300:', isRefined: false, noRefinement: false }, - { - label: '6', - value: '300:300', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: '', - canRefine: true, - }); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { attribute: 'ok', contextValue }, - { otherKey: 'val', multiRange: { otherKey: 'val' } }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - multiRange: { ok: 'yep', otherKey: 'val' }, - }); - }); - - it('refines the corresponding numeric facet', () => { - const initSP = new SearchParameters(); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { facet: '' } - ); - expect(params.getNumericRefinements('facet')).toEqual({}); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { multiRange: { facet: '100:' } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [100], - }); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { multiRange: { facet: ':200' } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '<=': [200], - }); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { multiRange: { facet: '100:200' } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [100], - '<=': [200], - }); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { multiRange: { facet: '0:' } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [0], - }); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { multiRange: { facet: ':0' } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '<=': [0], - }); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { multiRange: { facet: '0:0' } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [0], - '<=': [0], - }); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue }, - { multiRange: { facet: '0:0.5' } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [0], - '<=': [0.5], - }); - }); - - it('registers its id in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'ok', contextValue }, - {} - ); - expect(metadata).toEqual({ id: 'ok', index: 'index', items: [] }); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { - attribute: 'wot', - items: [ - { - label: 'YAY', - start: 100, - end: 200, - }, - ], - contextValue, - }, - { multiRange: { wot: '100:200' } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [ - { - label: 'wot: YAY', - // Ignore clear, we test it later - value: metadata.items[0].value, - attribute: 'wot', - currentRefinement: 'YAY', - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { - attribute: 'one', - items: [ - { - label: 'YAY', - start: 100, - end: 200, - }, - ], - contextValue, - }, - { multiRange: { one: '100:200', two: '200:400' } } - ); - - const searchState = metadata.items[0].value({ - multiRange: { one: '100:200', two: '200:400' }, - }); - - expect(searchState).toEqual({ - page: 1, - multiRange: { one: '', two: '200:400' }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue }, - { - multiRange: { name: 'searchState', name2: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - multiRange: { name2: 'searchState' }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue }, - searchState - ); - expect(searchState).toEqual({ - multiRange: {}, - another: { searchState: 'searchState' }, - }); - }); - - it('computes canRefine based on the length of the transformed items list', () => { - const transformItems = () => []; - - props = connect.getProvidedProps( - { - items: [{ label: 'Ok', start: 100 }], - transformItems, - contextValue, - }, - {}, - { results } - ); - - expect(props.canRefine).toEqual(false); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - const results = { - second: { - getFacetStats: () => ({ min: 0, max: 300 }), - getFacetByName: () => true, - }, - }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { - attribute: 'ok', - items: [ - { label: 'Ok', start: 100 }, - { label: 'Not ok', end: 200 }, - { label: 'Maybe ok?', start: 100, end: 200 }, - ], - contextValue, - indexContextValue, - }, - { indices: { second: { multiRange: { ok: 'wat' } } } }, - { results } - ); - expect(props).toEqual({ - items: [ - { label: 'Ok', value: '100:', isRefined: false, noRefinement: false }, - { - label: 'Not ok', - value: ':200', - isRefined: false, - noRefinement: false, - }, - { - label: 'Maybe ok?', - value: '100:200', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ], - currentRefinement: 'wat', - canRefine: true, - }); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { attribute: 'ok', contextValue, indexContextValue }, - { - indices: { - second: { otherKey: 'val', multiRange: { otherKey: 'val' } }, - }, - }, - 'yep' - ); - expect(nextState).toEqual({ - indices: { - second: { - otherKey: 'val', - page: 1, - multiRange: { ok: 'yep', otherKey: 'val' }, - }, - }, - }); - - nextState = connect.refine( - { - attribute: 'ok', - contextValue, - indexContextValue, - }, - { - indices: { - second: { - otherKey: 'val', - multiRange: { ok: 'yep', otherKey: 'val' }, - }, - }, - }, - 'yep' - ); - expect(nextState).toEqual({ - indices: { - second: { - page: 1, - otherKey: 'val', - multiRange: { ok: 'yep', otherKey: 'val' }, - }, - }, - }); - }); - - it('refines the corresponding numeric facet', () => { - const initSP = new SearchParameters(); - - params = connect.getSearchParameters( - initSP, - { attribute: 'facet', contextValue, indexContextValue }, - { indices: { second: { multiRange: { facet: '100:' } } } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [100], - }); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { - attribute: 'wot', - items: [ - { - label: 'YAY', - start: 100, - end: 200, - }, - ], - contextValue, - indexContextValue, - }, - { indices: { second: { multiRange: { wot: '100:200' } } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'second', - items: [ - { - label: 'wot: YAY', - // Ignore clear, we test it later - value: metadata.items[0].value, - attribute: 'wot', - currentRefinement: 'YAY', - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { - attribute: 'one', - items: [ - { - label: 'YAY', - start: 100, - end: 200, - }, - ], - contextValue, - indexContextValue, - }, - { - indices: { - second: { multiRange: { one: '100:200', two: '200:400' } }, - }, - } - ); - - const searchState = metadata.items[0].value({ - indices: { second: { multiRange: { one: '100:200', two: '200:400' } } }, - }); - - expect(searchState).toEqual({ - indices: { - second: { page: 1, multiRange: { one: '', two: '200:400' } }, - }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue, indexContextValue }, - { - indices: { - first: { - another: { name: 'searchState' }, - }, - second: { - multiRange: { name: 'searchState', name2: 'searchState' }, - }, - }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - indices: { - first: { - another: { name: 'searchState' }, - }, - second: { multiRange: { name2: 'searchState' } }, - }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue, indexContextValue }, - searchState - ); - expect(searchState).toEqual({ - indices: { - first: { - another: { name: 'searchState' }, - }, - second: { multiRange: {} }, - }, - another: { searchState: 'searchState' }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js deleted file mode 100644 index 7f730b2594..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js +++ /dev/null @@ -1,189 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectPagination'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectPagination', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { contextValue }, - {}, - { results: { nbPages: 666, hits: [] } } - ); - expect(props).toEqual({ - currentRefinement: 1, - nbPages: 666, - canRefine: true, - }); - - props = connect.getProvidedProps( - { contextValue }, - { page: 5 }, - { results: { nbPages: 666, hits: [] } } - ); - expect(props).toEqual({ - currentRefinement: 5, - nbPages: 666, - canRefine: true, - }); - - props = connect.getProvidedProps( - { contextValue }, - { page: '5' }, - { results: { nbPages: 666, hits: [] } } - ); - expect(props).toEqual({ - currentRefinement: 5, - nbPages: 666, - canRefine: true, - }); - - props = connect.getProvidedProps( - { contextValue }, - { page: '1' }, - { results: { nbPages: 1, hits: [] } } - ); - expect(props).toEqual({ - currentRefinement: 1, - nbPages: 1, - canRefine: false, - }); - }); - - it("doesn't render when no results are available", () => { - props = connect.getProvidedProps({ contextValue }, {}, {}); - expect(props).toBe(null); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { contextValue }, - { otherKey: 'val' }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 'yep', - }); - }); - - it('refines the page parameter', () => { - const initSP = new SearchParameters(); - params = connect.getSearchParameters( - initSP, - { contextValue }, - { page: 667 } - ); - expect(params.page).toBe(666); - }); - - it('registers its id in metadata', () => { - const metadata = connect.getMetadata({ contextValue }, {}); - expect(metadata).toEqual({ id: 'page' }); - }); - - it('should return the right searchState when clean up', () => { - const newState = connect.cleanUp( - { contextValue }, - { - page: { searchState: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(newState).toEqual({ another: { searchState: 'searchState' } }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { contextValue, indexContextValue }, - {}, - { results: { second: { nbPages: 666 } } } - ); - expect(props).toEqual({ - currentRefinement: 1, - nbPages: 666, - canRefine: true, - }); - - props = connect.getProvidedProps( - { contextValue, indexContextValue }, - { indices: { second: { page: 5 } } }, - { results: { second: { nbPages: 666 } } } - ); - expect(props).toEqual({ - currentRefinement: 5, - nbPages: 666, - canRefine: true, - }); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { contextValue, indexContextValue }, - { indices: { second: { otherKey: 'val' } } }, - 'yep' - ); - expect(nextState).toEqual({ - indices: { - second: { - otherKey: 'val', - page: 'yep', - }, - }, - }); - - nextState = connect.refine( - { - contextValue: { mainTargetedIndex: 'second' }, - indexContextValue: { targetedIndex: 'second' }, - }, - { indices: { second: { otherKey: 'val', page: 'yep' } } }, - 'yep' - ); - expect(nextState).toEqual({ - indices: { - second: { otherKey: 'val', page: 'yep' }, - }, - }); - }); - - it('refines the page parameter', () => { - const initSP = new SearchParameters(); - params = connect.getSearchParameters( - initSP, - { contextValue, indexContextValue }, - { indices: { second: { page: 667 } } } - ); - expect(params.page).toBe(666); - }); - - it('should return the right searchState when clean up', () => { - const newState = connect.cleanUp( - { contextValue, indexContextValue }, - { - indices: { - second: { - page: { searchState: 'searchState' }, - another: { searchState: 'searchState' }, - }, - }, - } - ); - expect(newState).toEqual({ - indices: { second: { another: { searchState: 'searchState' } } }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.jsdom.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.jsdom.js deleted file mode 100644 index cd1b32d9c9..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.jsdom.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import connect from '../connectPoweredBy'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectPoweredBy', () => { - const { getProvidedProps } = connect; - - it('provides the correct props to the component', () => { - const props = getProvidedProps(); - - expect(props).toEqual({ - url: 'https://www.algolia.com/?utm_source=react-instantsearch&utm_medium=website&utm_content=localhost&utm_campaign=poweredby', - }); - }); - - it('handles react native environment in window without location object', () => { - const originalWindow = { ...window }; - const windowSpy = jest.spyOn(global, 'window', 'get'); - const { location, ...windowWithoutLocation } = originalWindow; - windowSpy.mockImplementation(() => windowWithoutLocation); - const props = getProvidedProps(); - expect(props).toEqual({ - url: 'https://www.algolia.com/?utm_source=react-instantsearch&utm_medium=website&utm_content=&utm_campaign=poweredby', - }); - windowSpy.mockRestore(); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.node.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.node.js deleted file mode 100644 index 3ed6d4067b..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectPoweredBy.node.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @jest-environment node - */ - -import connect from '../connectPoweredBy'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectPoweredBy', () => { - const { getProvidedProps } = connect; - - it('provides the correct props to the component', () => { - const props = getProvidedProps(); - - expect(props).toEqual({ - url: 'https://www.algolia.com/?utm_source=react-instantsearch&utm_medium=website&utm_content=&utm_campaign=poweredby', - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectQueryRules.ts b/packages/react-instantsearch-core/src/connectors/__tests__/connectQueryRules.ts deleted file mode 100644 index 6708c35ef7..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectQueryRules.ts +++ /dev/null @@ -1,686 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connectReal from '../connectQueryRules'; - -import type { - ConnectorDescription, - ConnectedProps, -} from '../../core/createConnector'; -import type { QueryRulesProps } from '../connectQueryRules'; - -jest.mock( - '../../core/createConnector', - () => (connector: ConnectorDescription) => connector -); -// our mock implementation is diverging from the regular createConnector, -// so we redefine it as `any` here, since we have no more information -// @TODO: refactor these tests to work better with TS -const connect: any = connectReal; - -describe('connectQueryRules', () => { - const defaultProps: QueryRulesProps = { - transformItems: (items) => items, - trackedFilters: {}, - transformRuleContexts: (ruleContexts) => ruleContexts, - }; - - describe('single index', () => { - const indexName = 'index'; - const contextValue: any = { mainTargetedIndex: indexName }; - const defaultPropsSingleIndex = { - ...defaultProps, - contextValue, - }; - - describe('default', () => { - it('without userData provides the correct props to the component', () => { - const props: ConnectedProps = defaultPropsSingleIndex; - const searchState = {}; - const searchResults = { - results: { [indexName]: { userData: undefined } }, - }; - - expect( - connect.getProvidedProps(props, searchState, searchResults) - ).toEqual({ - items: [], - canRefine: false, - }); - }); - - it('with userData provides the correct props to the component', () => { - const props: ConnectedProps = defaultPropsSingleIndex; - const searchState = {}; - const searchResults = { - results: { - [indexName]: { userData: [{ banner: 'image.png' }] }, - }, - }; - - expect( - connect.getProvidedProps(props, searchState, searchResults) - ).toEqual({ - items: [{ banner: 'image.png' }], - canRefine: true, - }); - }); - }); - - describe('transformItems', () => { - it('transforms items before passing the props to the component', () => { - const transformItemsSpy = jest.fn(() => [ - { banner: 'image-transformed.png' }, - ]); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - transformItems: transformItemsSpy, - }; - const searchState = {}; - const searchResults = { - results: { - [indexName]: { userData: [{ banner: 'image.png' }] }, - }, - }; - - expect( - connect.getProvidedProps(props, searchState, searchResults) - ).toEqual({ - items: [{ banner: 'image-transformed.png' }], - canRefine: true, - }); - expect(transformItemsSpy).toHaveBeenCalledTimes(1); - expect(transformItemsSpy).toHaveBeenCalledWith([ - { banner: 'image.png' }, - ]); - }); - }); - - describe('trackedFilters', () => { - it('does not set ruleContexts without search state and trackedFilters', () => { - const props: ConnectedProps = defaultPropsSingleIndex; - const searchState = {}; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(searchParameters.ruleContexts).toEqual(undefined); - }); - - it('does not set ruleContexts with search state but without tracked filters', () => { - const props: ConnectedProps = defaultPropsSingleIndex; - const searchState = { - range: { - price: { - min: 20, - max: 3000, - }, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(searchParameters.ruleContexts).toEqual(undefined); - }); - - it('does not reset initial ruleContexts with trackedFilters', () => { - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - price: (values) => values, - }, - }; - const searchState = {}; - const searchParameters = connect.getSearchParameters( - SearchParameters.make({ - ruleContexts: ['initial-rule'], - }), - props, - searchState - ); - - expect(searchParameters.ruleContexts).toEqual(['initial-rule']); - }); - - it('does not throw an error with search state that contains `undefined` value', () => { - const props: QueryRulesProps = { - ...defaultProps, - trackedFilters: { - price: (values) => values, - }, - }; - - const searchState = { - refinementList: undefined, - }; - - expect(() => { - connect.getSearchParameters( - SearchParameters.make({}), - props, - searchState - ); - }).not.toThrow(); - }); - - it('sets ruleContexts based on range', () => { - const priceSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - price: priceSpy, - }, - }; - const searchState = { - range: { - price: { - min: 20, - max: 3000, - }, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(priceSpy).toHaveBeenCalledTimes(1); - expect(priceSpy).toHaveBeenCalledWith([20, 3000]); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-price-20', - 'ais-price-3000', - ]); - }); - - it('sets ruleContexts based on refinementList', () => { - const fruitSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - fruit: fruitSpy, - }, - }; - const searchState = { - refinementList: { - fruit: ['lemon', 'orange'], - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(fruitSpy).toHaveBeenCalledTimes(1); - expect(fruitSpy).toHaveBeenCalledWith(['lemon', 'orange']); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-fruit-lemon', - 'ais-fruit-orange', - ]); - }); - - it('sets ruleContexts based on hierarchicalMenu', () => { - const productsSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - products: productsSpy, - }, - }; - const searchState = { - hierarchicalMenu: { - products: 'Laptops > Surface', - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(productsSpy).toHaveBeenCalledTimes(1); - expect(productsSpy).toHaveBeenCalledWith(['Laptops > Surface']); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-products-Laptops_Surface', - ]); - }); - - it('sets ruleContexts based on menu', () => { - const brandsSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - brands: brandsSpy, - }, - }; - const searchState = { - menu: { - brands: 'Sony', - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(brandsSpy).toHaveBeenCalledTimes(1); - expect(brandsSpy).toHaveBeenCalledWith(['Sony']); - expect(searchParameters.ruleContexts).toEqual(['ais-brands-Sony']); - }); - - it('sets ruleContexts based on multiRange', () => { - const rankSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - rank: rankSpy, - }, - }; - const searchState = { - multiRange: { - rank: '2:5', - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(rankSpy).toHaveBeenCalledTimes(1); - expect(rankSpy).toHaveBeenCalledWith(['2', '5']); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-rank-2', - 'ais-rank-5', - ]); - }); - - it('sets ruleContexts based on toggle', () => { - const freeShippingSpy = jest.fn((values) => values); - const availableInStockSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - freeShipping: freeShippingSpy, - availableInStock: availableInStockSpy, - }, - }; - const searchState = { - toggle: { - freeShipping: true, - availableInStock: false, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(freeShippingSpy).toHaveBeenCalledTimes(1); - expect(freeShippingSpy).toHaveBeenCalledWith([true]); - expect(availableInStockSpy).toHaveBeenCalledTimes(1); - expect(availableInStockSpy).toHaveBeenCalledWith([false]); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-freeShipping-true', - 'ais-availableInStock-false', - ]); - }); - - it('escapes all rule contexts before passing them to search parameters', () => { - const brandSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - brand: brandSpy, - }, - }; - const searchState = { - refinementList: { - brand: ['Insignia™', '© Apple'], - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(brandSpy).toHaveBeenCalledTimes(1); - expect(brandSpy).toHaveBeenCalledWith(['Insignia™', '© Apple']); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-brand-Insignia_', - 'ais-brand-_Apple', - ]); - }); - - it('slices and warns in development when more than 10 rule contexts are applied', () => { - // We need to simulate being in development mode and to mock the global console object - // in this test to assert that development warnings are displayed correctly. - const originalNodeEnv = process.env.NODE_ENV; - process.env.NODE_ENV = 'development'; - const warnSpy = jest - .spyOn(console, 'warn') - .mockImplementation(() => {}); - - const brandFacetRefinements = [ - 'Insignia', - 'Canon', - 'Dynex', - 'LG', - 'Metra', - 'Sony', - 'HP', - 'Apple', - 'Samsung', - 'Speck', - 'PNY', - ]; - - expect(brandFacetRefinements).toHaveLength(11); - - const brandSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - brand: brandSpy, - }, - }; - const searchState = { - refinementList: { - brand: brandFacetRefinements, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(warnSpy).toHaveBeenCalledTimes(1); - expect(warnSpy) - .toHaveBeenCalledWith(`The maximum number of \`ruleContexts\` is 10. They have been sliced to that limit. -Consider using \`transformRuleContexts\` to minimize the number of rules sent to Algolia.`); - - expect(brandSpy).toHaveBeenCalledTimes(1); - expect(brandSpy).toHaveBeenCalledWith([ - 'Insignia', - 'Canon', - 'Dynex', - 'LG', - 'Metra', - 'Sony', - 'HP', - 'Apple', - 'Samsung', - 'Speck', - 'PNY', - ]); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-brand-Insignia', - 'ais-brand-Canon', - 'ais-brand-Dynex', - 'ais-brand-LG', - 'ais-brand-Metra', - 'ais-brand-Sony', - 'ais-brand-HP', - 'ais-brand-Apple', - 'ais-brand-Samsung', - 'ais-brand-Speck', - ]); - - process.env.NODE_ENV = originalNodeEnv; - warnSpy.mockRestore(); - }); - }); - - describe('transformRuleContexts', () => { - it('transform rule contexts before adding them to search parameters', () => { - const priceSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsSingleIndex, - trackedFilters: { - price: priceSpy, - }, - transformRuleContexts: (rules) => - rules.map((rule) => rule.replace('ais-', 'transformed-')), - }; - const searchState = { - range: { - price: { - min: 20, - max: 3000, - }, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(priceSpy).toHaveBeenCalledTimes(1); - expect(priceSpy).toHaveBeenCalledWith([20, 3000]); - expect(searchParameters.ruleContexts).toEqual([ - 'transformed-price-20', - 'transformed-price-3000', - ]); - }); - }); - }); - - describe('multi index', () => { - const firstIndexName = 'firstIndex'; - const secondIndexName = 'secondIndex'; - - const contextValue: any = { mainTargetedIndex: firstIndexName }; - const indexContextValue: any = { targetedIndex: secondIndexName }; - - const defaultPropsMultiIndex = { - ...defaultProps, - contextValue, - indexContextValue, - }; - - it('without userData provides the correct props to the component', () => { - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - }; - const searchState = {}; - const searchResults = { - results: { [secondIndexName]: { userData: undefined } }, - }; - - expect( - connect.getProvidedProps(props, searchState, searchResults) - ).toEqual({ - items: [], - canRefine: false, - }); - }); - - it('with userData provides the correct props to the component', () => { - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - }; - const searchState = {}; - const searchResults = { - results: { - [secondIndexName]: { userData: [{ banner: 'image.png' }] }, - }, - }; - - expect( - connect.getProvidedProps(props, searchState, searchResults) - ).toEqual({ - items: [{ banner: 'image.png' }], - canRefine: true, - }); - }); - - describe('transformItems', () => { - it('transforms items before passing the props to the component', () => { - const transformItemsSpy = jest.fn(() => [ - { banner: 'image-transformed.png' }, - ]); - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - transformItems: transformItemsSpy, - }; - const searchState = {}; - const searchResults = { - results: { - [secondIndexName]: { userData: [{ banner: 'image.png' }] }, - }, - }; - expect( - connect.getProvidedProps(props, searchState, searchResults) - ).toEqual({ - items: [{ banner: 'image-transformed.png' }], - canRefine: true, - }); - expect(transformItemsSpy).toHaveBeenCalledTimes(1); - expect(transformItemsSpy).toHaveBeenCalledWith([ - { banner: 'image.png' }, - ]); - }); - }); - - describe('trackedFilters', () => { - it('does not set ruleContexts without search state and trackedFilters', () => { - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - }; - const searchState = {}; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(searchParameters.ruleContexts).toEqual(undefined); - }); - - it('does not set ruleContexts with search state but without tracked filters', () => { - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - }; - const searchState = { - indices: { - [secondIndexName]: { - range: { - price: { - min: 20, - max: 3000, - }, - }, - }, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(searchParameters.ruleContexts).toEqual(undefined); - }); - - it('sets ruleContexts based on range', () => { - const priceSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - trackedFilters: { - price: priceSpy, - }, - }; - const searchState = { - indices: { - [secondIndexName]: { - range: { - price: { - min: 20, - max: 3000, - }, - }, - }, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(priceSpy).toHaveBeenCalledTimes(1); - expect(priceSpy).toHaveBeenCalledWith([20, 3000]); - expect(searchParameters.ruleContexts).toEqual([ - 'ais-price-20', - 'ais-price-3000', - ]); - }); - - it('sets empty ruleContexts without search state', () => { - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - trackedFilters: { - price: (values) => values, - }, - }; - const searchState = {}; - - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(searchParameters.ruleContexts).toEqual([]); - }); - }); - - describe('transformRuleContexts', () => { - it('transform rule contexts before adding them to search parameters', () => { - const priceSpy = jest.fn((values) => values); - const props: ConnectedProps = { - ...defaultPropsMultiIndex, - trackedFilters: { - price: priceSpy, - }, - transformRuleContexts: (rules) => - rules.map((rule) => rule.replace('ais-', 'transformed-')), - }; - const searchState = { - indices: { - [secondIndexName]: { - range: { - price: { - min: 20, - max: 3000, - }, - }, - }, - }, - }; - const searchParameters = connect.getSearchParameters( - new SearchParameters(), - props, - searchState - ); - - expect(priceSpy).toHaveBeenCalledTimes(1); - expect(priceSpy).toHaveBeenCalledWith([20, 3000]); - expect(searchParameters.ruleContexts).toEqual([ - 'transformed-price-20', - 'transformed-price-3000', - ]); - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectRange.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectRange.js deleted file mode 100644 index af11a01f02..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectRange.js +++ /dev/null @@ -1,1739 +0,0 @@ -import { SearchParameters, SearchResults } from 'algoliasearch-helper'; - -import connect from '../connectRange'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectRange', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps.call( - {}, - { attribute: 'ok', min: 5, max: 10, precision: 2, contextValue }, - {}, - {} - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 5, max: 10 }, - count: [], - canRefine: false, - precision: 2, - }); - - let results = { - getFacetStats: () => ({ min: 5, max: 10 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 2, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 5, max: 10 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 2, - }); - - results = { - getFacetStats: () => ({ min: 0.1, max: 9.9 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 0, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: 0, - max: 10, - currentRefinement: { min: 0, max: 10 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 0, - }); - - results = { - getFacetStats: () => ({ min: 0.1, max: 9.9 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 2, - min: 0.1, - max: 9.9, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: 0.1, - max: 9.9, - currentRefinement: { min: 0.1, max: 9.9 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 2, - }); - - results = { - getFacetStats: () => ({ min: 0.1234, max: 9.5678 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 0, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: 0, - max: 10, - currentRefinement: { min: 0, max: 10 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 0, - }); - - results = { - getFacetStats: () => ({ min: 0.1234, max: 9.5678 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 1, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: 0.1, - max: 9.6, - currentRefinement: { min: 0.1, max: 9.6 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 1, - }); - - results = { - getFacetStats: () => ({ min: 0.1234, max: 9.5678 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 2, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: 0.12, - max: 9.57, - currentRefinement: { min: 0.12, max: 9.57 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 2, - }); - - results = { - getFacetStats: () => ({ min: 0.1234, max: 9.5678 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 3, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: 0.123, - max: 9.568, - currentRefinement: { min: 0.123, max: 9.568 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 3, - }); - - results = { - getFacetByName: () => false, - getFacetStats: () => {}, - getFacetValues: () => [], - hits: [], - }; - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 2, - contextValue, - }, - {}, - { results } - ); - expect(props).toEqual({ - min: undefined, - max: undefined, - currentRefinement: { - min: undefined, - max: undefined, - }, - count: [], - canRefine: false, - precision: 2, - }); - - props = connect.getProvidedProps.call( - {}, - { attribute: 'ok', precision: 2, contextValue }, - { ok: { min: 6, max: 9 } }, - {} - ); - expect(props).toEqual({ - min: undefined, - max: undefined, - currentRefinement: { - min: undefined, - max: undefined, - }, - count: [], - canRefine: false, - precision: 2, - }); - - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - min: 5, - max: 10, - precision: 2, - contextValue, - }, - { - range: { ok: { min: 6, max: 9 } }, - }, - {} - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 6, max: 9 }, - count: [], - canRefine: false, - precision: 2, - }); - - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - min: 5, - max: 10, - precision: 2, - contextValue, - }, - { - range: { ok: { min: '6', max: '9' } }, - }, - {} - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 6, max: 9 }, - count: [], - canRefine: false, - precision: 2, - }); - - // With a precision of 0 -> parseInt - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - min: 5, - max: 10, - precision: 0, - contextValue, - }, - { - range: { ok: { min: '6.9', max: '9.6' } }, - }, - {} - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 6, max: 9 }, - count: [], - canRefine: false, - precision: 0, - }); - - // With a precision of > 0 -> parseFloat - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - min: 5, - max: 10, - precision: 1, - contextValue, - }, - { - range: { ok: { min: '6.9', max: '9.6' } }, - }, - {} - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 6.9, max: 9.6 }, - count: [], - canRefine: false, - precision: 1, - }); - - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - min: 5, - max: 10, - defaultRefinement: { min: 6, max: 9 }, - precision: 2, - contextValue, - }, - {}, - {} - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 6, max: 9 }, - count: [], - canRefine: false, - precision: 2, - }); - - props = connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - precision: 2, - contextValue, - }, - {}, - { - results: new SearchResults(new SearchParameters(), [{ hits: [] }]), - } - ); - expect(props).toEqual({ - min: undefined, - max: undefined, - currentRefinement: { - min: undefined, - max: undefined, - }, - count: [], - canRefine: false, - precision: 2, - }); - - expect(() => - connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - min: 5, - max: 10, - defaultRefinement: { min: 4, max: 9 }, - precision: 2, - contextValue, - }, - {}, - {} - ) - ).toThrow("You can't provide min value lower than range."); - - expect(() => - connect.getProvidedProps.call( - {}, - { - attribute: 'ok', - min: 5, - max: 10, - defaultRefinement: { min: 6, max: 11 }, - precision: 2, - contextValue, - }, - {}, - {} - ) - ).toThrow("You can't provide max value greater than range."); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'ok', contextValue }, - { otherKey: 'val', range: { otherKey: { min: 1, max: 2 } } }, - { min: 3, max: 5 } - ); - expect(nextState).toEqual({ - page: 1, - otherKey: 'val', - range: { ok: { min: 3, max: 5 }, otherKey: { min: 1, max: 2 } }, - }); - }); - - it('calling refine with non finite values should fail', () => { - function shouldNotRefineWithNaN() { - connect.refine.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'ok', contextValue }, - { otherKey: 'val', range: { otherKey: { min: 1, max: 2 } } }, - { min: NaN, max: 5 } - ); - } - expect(shouldNotRefineWithNaN).toThrow( - "You can't provide non finite values to the range connector" - ); - - function shouldNotRefineWithNull() { - connect.refine.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'ok', contextValue }, - { otherKey: 'val', range: { otherKey: { min: 1, max: 2 } } }, - { min: null, max: 5 } - ); - } - expect(shouldNotRefineWithNull).toThrow( - "You can't provide non finite values to the range connector" - ); - }); - - it('refines the corresponding numeric facet', () => { - params = connect.getSearchParameters.call( - { - _currentRange: { - min: 10, - max: 30, - }, - }, - new SearchParameters(), - { attribute: 'facet', contextValue }, - { range: { facet: { min: 10, max: 30 } } } - ); - - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [10], - '<=': [30], - }); - - params = connect.getSearchParameters.call( - { - _currentRange: { - min: 10, - max: 30, - }, - }, - new SearchParameters(), - { attribute: 'facet', min: 10, max: 30, contextValue }, - {} - ); - - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [10], - '<=': [30], - }); - }); - - it('registers its filter in metadata', () => { - let metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'wot', contextValue }, - { range: { wot: { min: 5 } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [ - { - label: '5 <= wot', - attribute: 'wot', - currentRefinement: { min: 5, max: 100 }, - // Ignore clear, we test it later - value: metadata.items[0].value, - }, - ], - }); - - const searchState = metadata.items[0].value({ - range: { wot: { min: 5 } }, - }); - expect(searchState).toEqual({ - page: 1, - range: { - wot: { - min: undefined, - max: undefined, - }, - }, - }); - - metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'wot', contextValue }, - { range: { wot: { max: 10 } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [ - { - label: 'wot <= 10', - attribute: 'wot', - currentRefinement: { min: 0, max: 10 }, - value: metadata.items[0].value, - }, - ], - }); - - metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'wot', contextValue }, - { range: { wot: { min: 5, max: 10 } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [ - { - label: '5 <= wot <= 10', - attribute: 'wot', - currentRefinement: { min: 5, max: 10 }, - value: metadata.items[0].value, - }, - ], - }); - - metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'wot', contextValue }, - { range: { wot: { min: 0, max: 100 } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'one', contextValue }, - { range: { one: { min: 5 }, two: { max: 4 } } } - ); - - const searchState = metadata.items[0].value({ - range: { one: { min: 5 }, two: { max: 4 } }, - }); - - expect(searchState).toEqual({ - page: 1, - range: { - two: { - max: 4, - }, - one: { - min: undefined, - max: undefined, - }, - }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp.call( - {}, - { attribute: 'name', contextValue }, - { - range: { name: 'searchState', name2: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - range: { name2: 'searchState' }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp.call( - {}, - { attribute: 'name2', contextValue }, - searchState - ); - expect(searchState).toEqual({ - range: {}, - another: { searchState: 'searchState' }, - }); - }); - - describe('refine', () => { - it('expect to refine when values are in range', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - const nextRefinement = { - min: 10, - max: 90, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 10, - max: 90, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to refine when values are parsable integer', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - const nextRefinement = { - min: '10', - max: '90', - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 10, - max: 90, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to refine when values are parsable float', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - const nextRefinement = { - min: '10.15', - max: '90.85', - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 10.15, - max: 90.85, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to refine min at range bound when defined', () => { - const thisArgs = { - _currentRange: { min: 10, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - min: 10, - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - const nextRefinement = { - min: 10, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 10, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to refine max at range bound when defined', () => { - const thisArgs = { - _currentRange: { min: 0, max: 90 }, - }; - - const propsForRefine = { - attribute: 'ok', - max: 90, - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - const nextRefinement = { - max: 90, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - max: 90, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to refine min when user bound are set and value is nullable', () => { - const thisArgs = { - _currentRange: { min: 10, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - min: 10, - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 20, - max: 90, - }, - }, - }; - - const nextRefinement = { - min: undefined, - max: 90, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 10, - max: 90, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to refine max when user bound are set and value is nullable', () => { - const thisArgs = { - _currentRange: { min: 0, max: 90 }, - }; - - const propsForRefine = { - attribute: 'ok', - max: 90, - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 10, - max: 90, - }, - }, - }; - - const nextRefinement = { - min: 10, - max: undefined, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 10, - max: 90, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to reset min with undefined', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 5, - max: 10, - }, - }, - }; - - const nextRefinement = { - min: undefined, - max: 10, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: undefined, - max: 10, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to reset max with undefined', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 5, - max: 10, - }, - }, - }; - - const nextRefinement = { - min: undefined, - max: 10, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: undefined, - max: 10, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to reset min with empty string', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 5, - max: 10, - }, - }, - }; - - const nextRefinement = { - min: '', - max: 10, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: undefined, - max: 10, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to reset max with empty string', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 5, - max: 10, - }, - }, - }; - - const nextRefinement = { - min: 5, - max: '', - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 5, - max: undefined, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to reset min when value is at bound and no bound are defined', () => { - const thisArgs = { - _currentRange: { min: 10, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 20, - max: 90, - }, - }, - }; - - const nextRefinement = { - min: 10, - max: 90, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: undefined, - max: 90, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to reset max when value is at bound and no bound are defined', () => { - const thisArgs = { - _currentRange: { min: 0, max: 100 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = { - otherKey: 'val', - range: { - otherKey: { - min: 1, - max: 2, - }, - ok: { - min: 10, - max: 90, - }, - }, - }; - - const nextRefinement = { - min: 10, - max: 100, - }; - - const actual = connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ); - - const expectation = { - page: 1, - otherKey: 'val', - range: { - ok: { - min: 10, - max: undefined, - }, - otherKey: { - min: 1, - max: 2, - }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to throw an error when min is invalid', () => { - const thisArgs = { - _currentRange: { min: 0, max: 90 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = {}; - - const nextRefinement = { - min: 'kdsjhfkd', - }; - - expect(() => - connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ) - ).toThrow("You can't provide non finite values to the range connector"); - }); - - it('expect to throw an error when max is invalid', () => { - const thisArgs = { - _currentRange: { min: 0, max: 90 }, - }; - - const propsForRefine = { - attribute: 'ok', - }; - - const searchState = {}; - - const nextRefinement = { - max: 'kdsjhfkd', - }; - - expect(() => - connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ) - ).toThrow("You can't provide non finite values to the range connector"); - }); - - it('expect to throw an error when min is out of range', () => { - const thisArgs = { - _currentRange: { min: 10, max: 90 }, - }; - - const propsForRefine = { - attribute: 'ok', - min: 10, - }; - - const searchState = {}; - - const nextRefinement = { - min: 0, - }; - - expect(() => - connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ) - ).toThrow("You can't provide min value lower than range."); - }); - - it('expect to throw an error when max is out of range', () => { - const thisArgs = { - _currentRange: { min: 10, max: 90 }, - }; - - const propsForRefine = { - attribute: 'ok', - max: 100, - }; - - const searchState = {}; - - const nextRefinement = { - max: 110, - }; - - expect(() => - connect.refine.call( - thisArgs, - propsForRefine, - searchState, - nextRefinement - ) - ).toThrow("You can't provide max value greater than range."); - }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - const results = { - second: { - getFacetStats: () => ({ min: 5, max: 10 }), - getFacetValues: () => [ - { name: '5', count: 10 }, - { name: '2', count: 20 }, - ], - getFacetByName: () => true, - }, - }; - const instance = {}; - - props = connect.getProvidedProps.call( - instance, - { attribute: 'ok', precision: 2, contextValue, indexContextValue }, - {}, - { results } - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 5, max: 10 }, - count: [ - { value: '5', count: 10 }, - { value: '2', count: 20 }, - ], - canRefine: true, - precision: 2, - }); - - props = connect.getProvidedProps.call( - instance, - { - attribute: 'ok', - min: 5, - max: 10, - precision: 2, - contextValue, - indexContextValue, - }, - { - indices: { second: { range: { ok: { min: 6, max: 9 } } } }, - }, - {} - ); - expect(props).toEqual({ - min: 5, - max: 10, - currentRefinement: { min: 6, max: 9 }, - count: [], - canRefine: false, - precision: 2, - }); - - props = connect.getProvidedProps.call( - instance, - { - attribute: 'ok', - precision: 2, - contextValue, - indexContextValue, - }, - {}, - { - results: { - second: new SearchResults(new SearchParameters(), [{ hits: [] }]), - }, - } - ); - expect(props).toEqual({ - min: undefined, - max: undefined, - currentRefinement: { - min: undefined, - max: undefined, - }, - count: [], - canRefine: false, - precision: 2, - }); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'ok', contextValue, indexContextValue }, - { - otherKey: 'val', - indices: { second: { range: { otherKey: { min: 1, max: 2 } } } }, - }, - { min: 3, max: 5 } - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { - second: { - page: 1, - range: { ok: { min: 3, max: 5 }, otherKey: { min: 1, max: 2 } }, - }, - }, - }); - - nextState = connect.refine.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { - attribute: 'ok', - contextValue, - indexContextValue: { targetedIndex: 'first' }, - }, - { - otherKey: 'val', - indices: { second: { range: { otherKey: { min: 1, max: 2 } } } }, - }, - { min: 3, max: 5 } - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { - first: { page: 1, range: { ok: { min: 3, max: 5 } } }, - second: { range: { otherKey: { min: 1, max: 2 } } }, - }, - }); - }); - - it('refines the corresponding numeric facet', () => { - params = connect.getSearchParameters( - new SearchParameters(), - { attribute: 'facet', contextValue, indexContextValue }, - { indices: { second: { range: { facet: { min: 10, max: 30 } } } } } - ); - expect(params.getNumericRefinements('facet')).toEqual({ - '>=': [10], - '<=': [30], - }); - }); - - it('registers its filter in metadata', () => { - let metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'wot', contextValue, indexContextValue }, - { indices: { second: { range: { wot: { min: 5 } } } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'second', - items: [ - { - label: '5 <= wot', - attribute: 'wot', - currentRefinement: { min: 5, max: 100 }, - // Ignore clear, we test it later - value: metadata.items[0].value, - }, - ], - }); - - const searchState = metadata.items[0].value({ - indices: { second: { range: { wot: { min: 5 } } } }, - }); - expect(searchState).toEqual({ - indices: { - second: { - page: 1, - range: { - wot: { - min: undefined, - max: undefined, - }, - }, - }, - }, - }); - - metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'wot', contextValue, indexContextValue }, - { indices: { second: { range: { wot: { max: 10 } } } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'second', - items: [ - { - label: 'wot <= 10', - attribute: 'wot', - currentRefinement: { min: 0, max: 10 }, - value: metadata.items[0].value, - }, - ], - }); - - metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'wot', contextValue, indexContextValue }, - { indices: { second: { range: { wot: { max: 100 } } } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'second', - items: [], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata.call( - { - _currentRange: { min: 0, max: 100 }, - }, - { attribute: 'one', contextValue, indexContextValue }, - { indices: { second: { range: { one: { min: 5 }, two: { max: 4 } } } } } - ); - - const searchState = metadata.items[0].value({ - indices: { second: { range: { one: { min: 5 }, two: { max: 4 } } } }, - }); - - expect(searchState).toEqual({ - indices: { - second: { - page: 1, - range: { - one: { - min: undefined, - max: undefined, - }, - two: { - max: 4, - }, - }, - }, - }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue, indexContextValue }, - { - indices: { - second: { range: { name: 'searchState', name2: 'searchState' } }, - }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - indices: { second: { range: { name2: 'searchState' } } }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue, indexContextValue }, - searchState - ); - expect(searchState).toEqual({ - another: { searchState: 'searchState' }, - indices: { second: { range: {} } }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js deleted file mode 100644 index 3fd2b04dd4..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js +++ /dev/null @@ -1,865 +0,0 @@ -import { SearchResults, SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectRefinementList'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectRefinementList', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - const results = { - getFacetValues: jest.fn(() => []), - getFacetByName: () => true, - hits: [], - }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: [], - isFromSearch: false, - canRefine: false, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - {}, - {} - ); - expect(props).toEqual({ - items: [], - currentRefinement: [], - isFromSearch: false, - canRefine: false, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - { refinementList: { ok: ['wat'] } }, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: ['wat'], - isFromSearch: false, - canRefine: false, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', defaultRefinement: ['wat'], contextValue }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: ['wat'], - isFromSearch: false, - canRefine: false, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', searchable: true, contextValue }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: [], - isFromSearch: false, - canRefine: false, - searchable: true, - }); - - results.getFacetValues.mockClear(); - results.getFacetValues.mockImplementation(() => [ - { - name: 'wat', - escapedValue: 'wat', - isRefined: true, - count: 20, - }, - { - name: 'oy', - escapedValue: 'oy', - isRefined: false, - count: 10, - }, - ]); - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: ['wat'], - label: 'wat', - isRefined: true, - count: 20, - }, - { - value: ['oy'], - label: 'oy', - isRefined: false, - count: 10, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: ['wat'], - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue }, - {}, - { results }, - {}, - { - query: 'query', - ok: [ - { - value: 'wat', - escapedValue: 'wat', - count: 10, - highlighted: 'wat', - isRefined: false, - }, - ], - } - ); - expect(props.items).toEqual([ - { - value: ['wat'], - label: 'wat', - isRefined: false, - count: 10, - _highlightResult: { label: { value: 'wat' } }, - }, - ]); - - props = connect.getProvidedProps( - { attribute: 'ok', limit: 1, contextValue }, - {}, - { results }, - {}, - { query: '' } - ); - expect(props.items).toEqual([ - { - value: ['wat'], - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - props = connect.getProvidedProps( - { - attribute: 'ok', - showMore: true, - limit: 0, - showMoreLimit: 1, - contextValue, - }, - {}, - { results } - ); - expect(props.items).toEqual([ - { - value: ['wat'], - label: 'wat', - isRefined: true, - count: 20, - }, - ]); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { attribute: 'ok', transformItems, contextValue }, - {}, - { results } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { - value: ['wat'], - label: 'wat', - isRefined: true, - count: 20, - }, - { - value: ['oy'], - label: 'oy', - isRefined: false, - count: 10, - }, - ]); - expect(props.items).toEqual(['items']); - }); - - it('facetValues results should be provided as props if they exists', () => { - props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, - {}, - { results }, - {}, - { - ok: [ - { - value: 'wat', - escapedValue: 'wat', - label: 'wat', - isRefined: true, - count: 20, - highlighted: 'wat', - }, - ], - } - ); - expect(props.items).toEqual([ - { - _highlightResult: { label: { value: 'wat' } }, - count: 20, - isRefined: true, - label: 'wat', - value: ['wat'], - }, - ]); - expect(props.isFromSearch).toBe(true); - }); - - it('facetValues have facetOrdering by default', () => { - const userProps = { - ...connect.defaultProps, - attribute: 'ok', - contextValue, - }; - const searchState = { - refinementList: { ok: ['wat'] }, - }; - const parameters = connect.getSearchParameters( - new SearchParameters(), - userProps, - searchState - ); - - const searchResults = new SearchResults(parameters, [ - { - hits: [], - renderingContent: { - facetOrdering: { - values: { - ok: { - order: ['lol'], - }, - }, - }, - }, - facets: { - ok: { - wat: 20, - lol: 2000, - }, - }, - }, - ]); - - const providedProps = connect.getProvidedProps(userProps, searchState, { - results: searchResults, - }); - - expect(providedProps.items).toEqual([ - { - count: 2000, - isRefined: false, - label: 'lol', - value: ['wat', 'lol'], - }, - { - count: 20, - isRefined: true, - label: 'wat', - value: [], - }, - ]); - expect(providedProps.isFromSearch).toBe(false); - }); - - it('facetValues results does not use facetOrdering if disabled', () => { - const userProps = { attribute: 'ok', facetOrdering: false, contextValue }; - const searchState = { - refinementList: { ok: ['wat'] }, - }; - const parameters = connect.getSearchParameters( - new SearchParameters(), - userProps, - searchState - ); - - const searchResults = new SearchResults(parameters, [ - { - hits: [], - renderingContent: { - facetOrdering: { - values: { - ok: { - order: ['lol'], - }, - }, - }, - }, - facets: { - ok: { - wat: 20, - lol: 2000, - }, - }, - }, - ]); - - const providedProps = connect.getProvidedProps(userProps, searchState, { - results: searchResults, - }); - - expect(providedProps.items).toEqual([ - { - count: 20, - isRefined: true, - label: 'wat', - value: [], - }, - { - count: 2000, - isRefined: false, - label: 'lol', - value: ['wat', 'lol'], - }, - ]); - expect(providedProps.isFromSearch).toBe(false); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { attribute: 'ok', contextValue }, - { otherKey: 'val', refinementList: { otherKey: ['val'] } }, - ['yep'] - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - refinementList: { ok: ['yep'], otherKey: ['val'] }, - }); - }); - - it("increases maxValuesPerFacet when it isn't big enough", () => { - const initSP = new SearchParameters({ maxValuesPerFacet: 100 }); - - params = connect.getSearchParameters( - initSP, - { - limit: 101, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(101); - - params = connect.getSearchParameters( - initSP, - { - showMore: true, - showMoreLimit: 101, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(101); - - params = connect.getSearchParameters( - initSP, - { - limit: 99, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(100); - - params = connect.getSearchParameters( - initSP, - { - showMore: true, - showMoreLimit: 99, - contextValue, - }, - {} - ); - expect(params.maxValuesPerFacet).toBe(100); - }); - - it('correctly applies its state to search parameters', () => { - const initSP = new SearchParameters(); - - params = connect.getSearchParameters( - initSP, - { - attribute: 'ok', - operator: 'or', - limit: 1, - contextValue, - }, - { refinementList: { ok: ['wat'] } } - ); - expect(params).toEqual( - initSP - .addDisjunctiveFacet('ok') - .addDisjunctiveFacetRefinement('ok', 'wat') - .setQueryParameter('maxValuesPerFacet', 1) - ); - - params = connect.getSearchParameters( - initSP, - { - attribute: 'ok', - operator: 'and', - limit: 1, - contextValue, - }, - { refinementList: { ok: ['wat'] } } - ); - expect(params).toEqual( - initSP - .addFacet('ok') - .addFacetRefinement('ok', 'wat') - .setQueryParameter('maxValuesPerFacet', 1) - ); - }); - - describe('getMetadata', () => { - it('registers its id in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'ok', contextValue }, - {} - ); - expect(metadata).toEqual({ id: 'ok', index: 'index', items: [] }); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'wot', contextValue }, - { refinementList: { wot: ['wat', 'wut'] } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [ - { - label: 'wot: ', - attribute: 'wot', - currentRefinement: ['wat', 'wut'], - value: metadata.items[0].value, - items: [ - { - label: 'wat', - value: metadata.items[0].items[0].value, - }, - { - label: 'wut', - value: metadata.items[0].items[1].value, - }, - ], - // Ignore value, we test it later - }, - ], - }); - }); - - it('registers escaped filterd in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'wot', contextValue }, - { refinementList: { wot: ['\\-wat', 'wut'] } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'index', - items: [ - { - label: 'wot: ', - attribute: 'wot', - currentRefinement: ['\\-wat', 'wut'], - value: metadata.items[0].value, - items: [ - { - label: '-wat', - value: metadata.items[0].items[0].value, - }, - { - label: 'wut', - value: metadata.items[0].items[1].value, - }, - ], - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attribute: 'one', contextValue }, - { refinementList: { one: ['one1', 'one2'], two: ['two'] } } - ); - - let searchState = metadata.items[0].items[0].value({ - refinementList: { one: ['one1', 'one2'], two: ['two'] }, - }); - - expect(searchState).toEqual({ - page: 1, - refinementList: { one: ['one2'], two: ['two'] }, - }); - - searchState = metadata.items[0].items[1].value(searchState); - - expect(searchState).toEqual({ - page: 1, - refinementList: { one: '', two: ['two'] }, - }); - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue }, - { - refinementList: { name: 'searchState', name2: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - refinementList: { name2: 'searchState' }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue }, - searchState - ); - expect(searchState).toEqual({ - refinementList: {}, - another: { searchState: 'searchState' }, - }); - }); - - it('calling searchForItems return the right searchForItems parameters with limit', () => { - const parameters = connect.searchForFacetValues( - { - attribute: 'ok', - limit: 15, - showMoreLimit: 25, - showMore: false, - contextValue, - }, - {}, - 'yep' - ); - - expect(parameters).toEqual({ - facetName: 'ok', - query: 'yep', - maxFacetHits: 15, - }); - }); - - it('calling searchForItems return the right searchForItems parameters with showMoreLimit', () => { - const parameters = connect.searchForFacetValues( - { - attribute: 'ok', - limit: 15, - showMoreLimit: 25, - showMore: true, - contextValue, - }, - {}, - 'yep' - ); - - expect(parameters).toEqual({ - facetName: 'ok', - query: 'yep', - maxFacetHits: 25, - }); - }); - - it('computes canRefine based on the length of the transformed items list', () => { - const transformItems = () => []; - - props = connect.getProvidedProps( - { attribute: 'ok', transformItems, contextValue }, - {}, - { results } - ); - - expect(props.canRefine).toEqual(false); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - const results = { - first: { - getFacetValues: jest.fn(() => []), - getFacetByName: () => true, - }, - second: { - getFacetValues: jest.fn(() => []), - getFacetByName: () => true, - }, - }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - {}, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: [], - isFromSearch: false, - canRefine: false, - }); - - props = connect.getProvidedProps( - { attribute: 'ok', contextValue, indexContextValue }, - { indices: { second: { refinementList: { ok: ['wat'] } } } }, - { results } - ); - expect(props).toEqual({ - items: [], - currentRefinement: ['wat'], - isFromSearch: false, - canRefine: false, - }); - - results.second.getFacetValues.mockClear(); - results.second.getFacetValues.mockImplementation(() => [ - { - name: 'wat', - isRefined: true, - count: 20, - }, - { - name: 'oy', - isRefined: false, - count: 10, - }, - ]); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { attribute: 'ok', contextValue, indexContextValue }, - { - otherKey: 'val', - indices: { second: { refinementList: { otherKey: ['val'] } } }, - }, - ['yep'] - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { - second: { - page: 1, - refinementList: { ok: ['yep'], otherKey: ['val'] }, - }, - }, - }); - - nextState = connect.refine( - { - attribute: 'ok', - contextValue, - indexContextValue, - }, - { - otherKey: 'val', - indices: { second: { refinementList: { otherKey: ['val'] } } }, - }, - ['yep'] - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { - second: { - page: 1, - refinementList: { ok: ['yep'], otherKey: ['val'] }, - }, - }, - }); - }); - - it('correctly applies its state to search parameters', () => { - const initSP = new SearchParameters(); - - params = connect.getSearchParameters( - initSP, - { - attribute: 'ok', - operator: 'or', - limit: 1, - contextValue, - indexContextValue, - }, - { indices: { second: { refinementList: { ok: ['wat'] } } } } - ); - expect(params).toEqual( - initSP - .addDisjunctiveFacet('ok') - .addDisjunctiveFacetRefinement('ok', 'wat') - .setQueryParameter('maxValuesPerFacet', 1) - ); - - params = connect.getSearchParameters( - initSP, - { - attribute: 'ok', - operator: 'and', - limit: 1, - contextValue, - indexContextValue, - }, - { indices: { second: { refinementList: { ok: ['wat'] } } } } - ); - expect(params).toEqual( - initSP - .addFacet('ok') - .addFacetRefinement('ok', 'wat') - .setQueryParameter('maxValuesPerFacet', 1) - ); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 'wot', contextValue, indexContextValue }, - { indices: { second: { refinementList: { wot: ['wat', 'wut'] } } } } - ); - expect(metadata).toEqual({ - id: 'wot', - index: 'second', - items: [ - { - label: 'wot: ', - attribute: 'wot', - currentRefinement: ['wat', 'wut'], - value: metadata.items[0].value, - items: [ - { - label: 'wat', - value: metadata.items[0].items[0].value, - }, - { - label: 'wut', - value: metadata.items[0].items[1].value, - }, - ], - // Ignore value, we test it later - }, - ], - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attribute: 'one', contextValue, indexContextValue }, - { - indices: { - second: { refinementList: { one: ['one1', 'one2'], two: ['two'] } }, - }, - } - ); - - let searchState = metadata.items[0].items[0].value({ - indices: { - second: { refinementList: { one: ['one1', 'one2'], two: ['two'] } }, - }, - }); - - expect(searchState).toEqual({ - indices: { - second: { page: 1, refinementList: { one: ['one2'], two: ['two'] } }, - }, - }); - - searchState = metadata.items[0].items[1].value(searchState); - - expect(searchState).toEqual({ - indices: { - second: { page: 1, refinementList: { one: '', two: ['two'] } }, - }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue, indexContextValue }, - { - indices: { - second: { - refinementList: { name: 'searchState', name2: 'searchState' }, - }, - }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - indices: { second: { refinementList: { name2: 'searchState' } } }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue, indexContextValue }, - searchState - ); - expect(searchState).toEqual({ - indices: { second: { refinementList: {} } }, - another: { searchState: 'searchState' }, - }); - }); - - it('errors if searchable is used in a multi index context', () => { - expect(() => { - connect.getProvidedProps( - { - contextValue, - indexContextValue, - searchable: true, - }, - {}, - {} - ); - }).toThrowErrorMatchingInlineSnapshot( - `"react-instantsearch: searching in *List is not available when used inside a multi index context"` - ); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectRelevantSort.ts b/packages/react-instantsearch-core/src/connectors/__tests__/connectRelevantSort.ts deleted file mode 100644 index ec5bfb5f9d..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectRelevantSort.ts +++ /dev/null @@ -1,229 +0,0 @@ -import connectReal from '../connectRelevantSort'; - -import type { ConnectorDescription } from '../../core/createConnector'; - -jest.mock( - '../../core/createConnector', - () => (connector: ConnectorDescription) => connector -); -// our mock implementation is diverging from the regular createConnector, -// so we redefine it as `any` here, since we have no more information -// @TODO: refactor these tests to work better with TS -const connect: any = connectReal; - -describe('connectRelevantSort', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('returns false when results are null', () => { - const props = connect.getProvidedProps({ contextValue }, null, { - results: null, - }); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: false, - }); - }); - - it('returns correct props with defined appliedRelevancyStrictness', () => { - const props = connect.getProvidedProps({ contextValue }, null, { - results: { - hits: [], - nbHits: 300, - nbSortedHits: 1, - appliedRelevancyStrictness: 30, - }, - }); - - expect(props).toEqual({ - isRelevantSorted: true, - isVirtualReplica: true, - }); - }); - - it('returns correct props with undefined appliedRelevancyStrictness', () => { - const props = connect.getProvidedProps({ contextValue }, null, { - results: {}, - }); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: false, - }); - }); - - it('decide isRelevantSorted based on appliedRelevancyStrictness', () => { - const props = connect.getProvidedProps({ contextValue }, null, { - results: { - hits: [], - appliedRelevancyStrictness: 0, - }, - }); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: true, - }); - }); - - it('apply relevancyStrictness on refine', () => { - let props = connect.getProvidedProps({ contextValue }, null, { - results: { - hits: [], - nbHits: 300, - nbSortedHits: 1, - appliedRelevancyStrictness: 98, - }, - }); - - expect(props).toEqual({ - isRelevantSorted: true, - isVirtualReplica: true, - }); - - const searchState = connect.refine({}, { relevancyStrictness: 98 }, 0); - - expect(searchState).toEqual({ - relevancyStrictness: 0, - page: 1, - }); - - props = connect.getProvidedProps({ contextValue }, searchState, { - results: { - hits: [], - appliedRelevancyStrictness: 0, - }, - }); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: true, - }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('returns false when results are null', () => { - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { second: null }, - } - ); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: false, - }); - }); - - it('returns correct props with defined appliedRelevancyStrictness', () => { - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits: [], - nbHits: 300, - nbSortedHits: 1, - appliedRelevancyStrictness: 30, - }, - }, - } - ); - - expect(props).toEqual({ - isRelevantSorted: true, - isVirtualReplica: true, - }); - }); - - it('returns correct props with undefined appliedRelevancyStrictness', () => { - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { second: {} }, - } - ); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: false, - }); - }); - - it('decide isRelevantSorted based on appliedRelevancyStrictness', () => { - const props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits: [], - appliedRelevancyStrictness: 0, - }, - }, - } - ); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: true, - }); - }); - - it('apply relevancyStrictness on refine', () => { - let props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { - second: { - hits: [], - nbHits: 300, - nbSortedHits: 1, - appliedRelevancyStrictness: 30, - }, - }, - } - ); - - expect(props).toEqual({ - isRelevantSorted: true, - isVirtualReplica: true, - }); - - const searchState = connect.refine({}, { relevancyStrictness: 98 }, 0); - - expect(searchState).toEqual({ - relevancyStrictness: 0, - page: 1, - }); - - props = connect.getProvidedProps( - { contextValue, indexContextValue }, - searchState, - { - results: { - second: { - hits: [], - appliedRelevancyStrictness: 0, - }, - }, - } - ); - - expect(props).toEqual({ - isRelevantSorted: false, - isVirtualReplica: true, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectScrollTo.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectScrollTo.js deleted file mode 100644 index 4e1f57255a..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectScrollTo.js +++ /dev/null @@ -1,77 +0,0 @@ -import connect from '../connectScrollTo'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -describe('connectScrollTo', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - it('provides the correct props to the component', () => { - const instance = {}; - props = connect.getProvidedProps.call( - instance, - { scrollOn: 'p', contextValue }, - { p: 1, configure: 3, refinementList: 'ok' } - ); - expect(props).toEqual({ value: 1, hasNotChanged: false }); - - props = connect.getProvidedProps.call( - instance, - { scrollOn: 'p', contextValue }, - { p: 1, configure: 3, refinementList: 'not ok' } - ); - expect(props).toEqual({ value: 1, hasNotChanged: false }); - - props = connect.getProvidedProps.call( - instance, - { scrollOn: 'p', contextValue }, - { p: 2, configure: 3, refinementList: 'not ok' } - ); - expect(props).toEqual({ value: 2, hasNotChanged: true }); - - props = connect.getProvidedProps.call( - instance, - { scrollOn: 'anything', contextValue }, - { anything: 2 } - ); - expect(props).toEqual({ value: 2, hasNotChanged: false }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - it('provides the correct props to the component', () => { - const instance = {}; - const searchState = { indices: { second: { p: 1 } } }; - - props = connect.getProvidedProps.call( - instance, - { scrollOn: 'p', contextValue, indexContextValue }, - searchState - ); - expect(props).toEqual({ value: 1, hasNotChanged: true }); - - searchState.indices.second = { ...searchState.indices.second, p: 2 }; - - props = connect.getProvidedProps.call( - instance, - { scrollOn: 'p', contextValue, indexContextValue }, - searchState - ); - expect(props).toEqual({ value: 2, hasNotChanged: true }); - - searchState.indices.second = { - ...searchState.indices.second, - anything: 'ok', - }; - - props = connect.getProvidedProps.call( - instance, - { scrollOn: 'p', contextValue, indexContextValue }, - searchState - ); - expect(props).toEqual({ value: 2, hasNotChanged: false }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js deleted file mode 100644 index 2d6d35a408..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js +++ /dev/null @@ -1,155 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectSearchBox'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectSearchBox', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps({ contextValue }, {}, {}); - expect(props).toEqual({ currentRefinement: '' }); - - props = connect.getProvidedProps({ contextValue }, { query: 'yep' }, {}); - expect(props).toEqual({ currentRefinement: 'yep' }); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { contextValue }, - { otherKey: 'val' }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - query: 'yep', - }); - }); - - it('supports defaultRefinement', () => { - expect( - connect.getProvidedProps( - { defaultRefinement: 'yaw', contextValue }, - {}, - {} - ) - ).toEqual({ - currentRefinement: 'yaw', - }); - }); - - it('refines the query parameter', () => { - params = connect.getSearchParameters( - new SearchParameters(), - { contextValue }, - { query: 'bar' } - ); - expect(params.query).toBe('bar'); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { contextValue }, - { - query: { searchState: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ another: { searchState: 'searchState' } }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { contextValue, indexContextValue }, - {}, - {} - ); - expect(props).toEqual({ currentRefinement: '' }); - - props = connect.getProvidedProps( - { contextValue, indexContextValue }, - { indices: { second: { query: 'yep' } } }, - {} - ); - expect(props).toEqual({ currentRefinement: 'yep' }); - }); - - it("calling refine updates the widget's search state", () => { - let nextState = connect.refine( - { contextValue, indexContextValue }, - { otherKey: 'val' }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { second: { query: 'yep', page: 1 } }, - }); - - nextState = connect.refine( - { - contextValue: { mainTargetedIndex: 'first' }, - indexContextValue: { targetedIndex: 'first' }, - }, - { - indices: { - first: { query: 'yep' }, - }, - otherKey: 'val', - }, - 'yip' - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { - first: { query: 'yip', page: 1 }, - }, - }); - }); - - it('supports defaultRefinement', () => { - expect( - connect.getProvidedProps( - { defaultRefinement: 'yaw', contextValue, indexContextValue }, - {}, - {} - ) - ).toEqual({ - currentRefinement: 'yaw', - }); - }); - - it('refines the query parameter', () => { - params = connect.getSearchParameters( - new SearchParameters(), - { contextValue, indexContextValue }, - { indices: { second: { query: 'bar' } } } - ); - expect(params.query).toBe('bar'); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { contextValue, indexContextValue }, - { - indices: { second: { query: '' } }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - indices: { second: {} }, - another: { searchState: 'searchState' }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectSortBy.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectSortBy.js deleted file mode 100644 index 14cb0259bd..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectSortBy.js +++ /dev/null @@ -1,150 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectSortBy'; - -jest.mock('../../core/createConnector', () => (x) => x); - -let props; -let params; - -describe('connectSortBy', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { items: [{ value: 'yep' }, { value: 'yop' }], contextValue }, - { sortBy: 'yep' } - ); - expect(props).toEqual({ - items: [ - { value: 'yep', isRefined: true }, - { value: 'yop', isRefined: false }, - ], - currentRefinement: 'yep', - }); - - props = connect.getProvidedProps( - { items: [{ value: 'yep' }], defaultRefinement: 'yep', contextValue }, - {} - ); - expect(props).toEqual({ - items: [{ value: 'yep', isRefined: true }], - currentRefinement: 'yep', - }); - - const transformItems = jest.fn(() => ['items']); - props = connect.getProvidedProps( - { - items: [{ value: 'yep' }, { value: 'yop' }], - transformItems, - contextValue, - }, - { sortBy: 'yep' } - ); - expect(transformItems.mock.calls[0][0]).toEqual([ - { value: 'yep', isRefined: true }, - { value: 'yop', isRefined: false }, - ]); - expect(props.items).toEqual(['items']); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { contextValue }, - { otherKey: 'val' }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - page: 1, - sortBy: 'yep', - }); - }); - - it('refines the index parameter', () => { - params = connect.getSearchParameters( - new SearchParameters(), - { contextValue }, - { sortBy: 'yep' } - ); - expect(params.index).toBe('yep'); - }); - - it('registers its id in metadata', () => { - const metadata = connect.getMetadata({ contextValue }); - expect(metadata).toEqual({ id: 'sortBy' }); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { - contextValue, - }, - { - sortBy: { searchState: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ another: { searchState: 'searchState' } }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - props = connect.getProvidedProps( - { - items: [{ value: 'yep' }, { value: 'yop' }], - contextValue, - indexContextValue, - }, - { indices: { second: { sortBy: 'yep' } } } - ); - expect(props).toEqual({ - items: [ - { value: 'yep', isRefined: true }, - { value: 'yop', isRefined: false }, - ], - currentRefinement: 'yep', - }); - }); - - it("calling refine updates the widget's search state", () => { - const nextState = connect.refine( - { contextValue, indexContextValue }, - { otherKey: 'val' }, - 'yep' - ); - expect(nextState).toEqual({ - otherKey: 'val', - indices: { second: { page: 1, sortBy: 'yep' } }, - }); - }); - - it('refines the index parameter', () => { - params = connect.getSearchParameters( - new SearchParameters(), - { contextValue, indexContextValue }, - { indices: { second: { sortBy: 'yep' } } } - ); - expect(params.index).toBe('yep'); - }); - - it('should return the right searchState when clean up', () => { - const searchState = connect.cleanUp( - { contextValue, indexContextValue }, - { - indices: { second: { sortBy: { searchState: 'searchState' } } }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - indices: { second: {} }, - another: { searchState: 'searchState' }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectStateResults.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectStateResults.js deleted file mode 100644 index 039aa85400..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectStateResults.js +++ /dev/null @@ -1,85 +0,0 @@ -import connect from '../connectStateResults'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectStateResults', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - const searchState = { state: 'state' }; - const error = 'error'; - const searching = true; - const isSearchStalled = true; - const searchingForFacetValues = true; - const searchResults = { - results: { nbHits: 25, hits: [] }, - error, - searching, - isSearchStalled, - searchingForFacetValues, - }; - - const expectation = { - searchState, - searchResults: searchResults.results, - allSearchResults: searchResults.results, - props: { props: 'props', contextValue }, - error, - searching, - isSearchStalled, - searchingForFacetValues, - }; - - const actual = connect.getProvidedProps( - { props: 'props', contextValue }, - searchState, - searchResults - ); - - expect(actual).toEqual(expectation); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - const searchState = { state: 'state' }; - const error = 'error'; - const searching = true; - const isSearchStalled = true; - const searchingForFacetValues = true; - const searchResults = { - results: { - first: { nbHits: 25, hits: [] }, - second: { nbHits: 26, hits: [] }, - }, - error, - searching, - isSearchStalled, - searchingForFacetValues, - }; - - const expectation = { - searchState, - searchResults: searchResults.results.second, - allSearchResults: searchResults.results, - props: { props: 'props', contextValue, indexContextValue }, - error, - searching, - isSearchStalled, - searchingForFacetValues, - }; - - const actual = connect.getProvidedProps( - { props: 'props', contextValue, indexContextValue }, - searchState, - searchResults - ); - - expect(actual).toEqual(expectation); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectStats.ts b/packages/react-instantsearch-core/src/connectors/__tests__/connectStats.ts deleted file mode 100644 index c2d7a0b3bc..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectStats.ts +++ /dev/null @@ -1,74 +0,0 @@ -import connectReal from '../connectStats'; - -import type { ConnectorDescription } from '../../core/createConnector'; - -jest.mock( - '../../core/createConnector', - () => (connector: ConnectorDescription) => connector -); -// our mock implementation is diverging from the regular createConnector, -// so we redefine it as `any` here, since we have no more information -// @TODO: refactor these tests to work better with TS -const connect: any = connectReal; - -describe('connectStats', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - it('provides the correct props to the component', () => { - let props = connect.getProvidedProps({ contextValue }, null, {}); - expect(props).toBe(null); - - props = connect.getProvidedProps({ contextValue }, null, { - results: { - nbHits: 666, - processingTimeMS: 1, - hits: [], - nbSortedHits: undefined, - isRelevantSorted: false, - }, - }); - expect(props).toEqual({ - nbHits: 666, - processingTimeMS: 1, - nbSortedHits: undefined, - areHitsSorted: false, - }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - it('provides the correct props to the component', () => { - let props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - {} - ); - expect(props).toBe(null); - - props = connect.getProvidedProps( - { contextValue, indexContextValue }, - null, - { - results: { - second: { - nbHits: 666, - processingTimeMS: 1, - nbSortedHits: undefined, - isRelevantSorted: false, - }, - }, - } - ); - expect(props).toEqual({ - nbHits: 666, - processingTimeMS: 1, - nbSortedHits: undefined, - areHitsSorted: false, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectToggleRefinement.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectToggleRefinement.js deleted file mode 100644 index 83cf730020..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectToggleRefinement.js +++ /dev/null @@ -1,933 +0,0 @@ -import { SearchParameters, SearchResults } from 'algoliasearch-helper'; - -import connect from '../connectToggleRefinement'; - -jest.mock('../../core/createConnector', () => (x) => x); - -const createSearchResults = ({ disjunctiveFacets, facets }) => - new SearchResults( - new SearchParameters({ - disjunctiveFacets, - }), - [ - { - facets, - hits: [ - { objectID: '0123', name: 'Apple' }, - { objectID: '0123', name: 'Samsung' }, - { objectID: '0123', name: 'Microsoft' }, - ], - }, - ] - ); - -describe('connectToggleRefinement', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - const createSingleIndexSearchResults = (...args) => ({ - results: createSearchResults(...args), - }); - - it('expect `currentRefinement` to be `true` when the value is checked', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = { toggle: { shipping: true } }; - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(true); - }); - - it('expect `currentRefinement` to be `false` when the value is not checked', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(false); - }); - - it('expect `currentRefinement` to be `false` when searchState is a string considered falsy', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = { toggle: { shipping: 'false' } }; - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(false); - }); - - it('expect `currentRefinement` to be `defaultRefinement`', () => { - const props = { - defaultRefinement: true, - attribute: 'shipping', - value: true, - contextValue, - }; - const searchState = {}; - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(true); - }); - - it('expect `canRefine` to be computed to `true` from all the facet values when the value is checked', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = { toggle: { shipping: true } }; - const searchResults = createSingleIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 100, - false: 50, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(true); - }); - - it('expect `canRefine` to be computed to `true` from the selected facet value when the value is not checked', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 50, - false: 100, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(true); - }); - - it('expect `canRefine` to be `false` with a value count of 0', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 0, - false: 50, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `canRefine` to be `false` without the facet values', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: {}, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `canRefine` to be `false` without the facet', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - disjunctiveFacets: [], - facets: {}, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `canRefine` to be `false` without results', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `count` to match facet values with results', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 100, - false: 50, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.count).toEqual({ - checked: 150, - unchecked: 100, - }); - }); - - it('expect `count` to be null without the facet', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = createSingleIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: {}, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.count).toEqual({ - checked: null, - unchecked: null, - }); - }); - - it('expect `count` to be null without results', () => { - const props = { attribute: 'shipping', value: true, contextValue }; - const searchState = {}; - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.count).toEqual({ - checked: null, - unchecked: null, - }); - }); - - it("calling refine updates the widget's search state", () => { - let searchState = connect.refine( - { attribute: 't', contextValue }, - { otherKey: 'val', toggle: { otherKey: false } }, - true - ); - expect(searchState).toEqual({ - otherKey: 'val', - page: 1, - toggle: { t: true, otherKey: false }, - }); - - searchState = connect.refine( - { attribute: 't', contextValue }, - { otherKey: 'val' }, - false - ); - expect(searchState).toEqual({ - page: 1, - otherKey: 'val', - toggle: { t: false }, - }); - }); - - it('refines the corresponding facet with `true`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - value: 'val', - contextValue, - }, - { - toggle: { - facet: true, - }, - } - ); - - expect(params.getDisjunctiveRefinements('facet')).toEqual(['val']); - }); - - it('does not refine the corresponding facet with `false`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - value: 'val', - contextValue, - }, - { - toggle: { - facet: false, - }, - } - ); - - expect(params.getDisjunctiveRefinements('facet')).toEqual([]); - }); - - it('applies the provided filter with `true`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - filter: (sp) => sp.setQuery('yep'), - contextValue, - }, - { - toggle: { - facet: true, - }, - } - ); - - expect(params.query).toEqual('yep'); - }); - - it('does not apply the provided filter with `false`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - filter: (sp) => sp.setQuery('yep'), - contextValue, - }, - { - toggle: { - facet: false, - }, - } - ); - - expect(params.query).toBeUndefined(); - }); - - it('registers its filter in metadata', () => { - let metadata = connect.getMetadata({ attribute: 't', contextValue }, {}); - expect(metadata).toEqual({ - items: [], - id: 't', - index: 'index', - }); - - metadata = connect.getMetadata( - { attribute: 't', label: 'yep', contextValue }, - { toggle: { t: true } } - ); - expect(metadata).toEqual({ - items: [ - { - label: 'yep', - // Ignore clear, we test it later - value: metadata.items[0].value, - attribute: 't', - currentRefinement: true, - }, - ], - id: 't', - index: 'index', - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attribute: 'one', label: 'yep', contextValue }, - { toggle: { one: true, two: false } } - ); - - const searchState = metadata.items[0].value({ - toggle: { one: true, two: false }, - }); - - expect(searchState).toEqual({ - page: 1, - toggle: { one: false, two: false }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue }, - { - toggle: { name: 'searchState', name2: 'searchState' }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - toggle: { name2: 'searchState' }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue }, - searchState - ); - expect(searchState).toEqual({ - toggle: {}, - another: { searchState: 'searchState' }, - }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - const createMultiIndexSearchState = (state = {}) => ({ - indices: { - second: state, - }, - }); - - const createMultiIndexSearchResults = (...args) => ({ - results: { - second: createSearchResults(...args), - }, - }); - - it('expect `currentRefinement` to be `true` when the value is checked', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState({ - toggle: { shipping: true }, - }); - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(true); - }); - - it('expect `currentRefinement` to be `false` when the value is not checked', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(false); - }); - - it('expect `currentRefinement` to be `false` when searchState is a string considered falsy', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState({ - toggle: { shipping: 'false' }, - }); - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(false); - }); - - it('expect `currentRefinement` to be `defaultRefinement`', () => { - const props = { - defaultRefinement: true, - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.currentRefinement).toBe(true); - }); - - it('expect `canRefine` to be computed to `true` from all the facet values when the value is checked', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState({ - toggle: { shipping: true }, - }); - const searchResults = createMultiIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 100, - false: 50, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(true); - }); - - it('expect `canRefine` to be computed to `true` from the selected facet value when the value is not checked', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 50, - false: 100, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(true); - }); - - it('expect `canRefine` to be `false` with a value count of 0', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 0, - false: 50, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `canRefine` to be `false` without the facet values', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: {}, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `canRefine` to be `false` without the facet', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - disjunctiveFacets: [], - facets: {}, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `canRefine` to be `false` without results', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.canRefine).toBe(false); - }); - - it('expect `count` to match facet values with results', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: { - shipping: { - true: 100, - false: 50, - }, - }, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.count).toEqual({ - checked: 150, - unchecked: 100, - }); - }); - - it('expect `count` to be null without the facet', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = createMultiIndexSearchResults({ - disjunctiveFacets: ['shipping'], - facets: {}, - }); - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.count).toEqual({ - checked: null, - unchecked: null, - }); - }); - - it('expect `count` to be null without results', () => { - const props = { - attribute: 'shipping', - value: true, - contextValue, - indexContextValue, - }; - const searchState = createMultiIndexSearchState(); - const searchResults = {}; - - const actual = connect.getProvidedProps( - props, - searchState, - searchResults - ); - - expect(actual.count).toEqual({ - checked: null, - unchecked: null, - }); - }); - - it("calling refine updates the widget's search state", () => { - let searchState = connect.refine( - { attribute: 't', contextValue, indexContextValue }, - { - otherKey: 'val', - indices: { second: { toggle: { otherKey: false } } }, - }, - true - ); - expect(searchState).toEqual({ - otherKey: 'val', - indices: { second: { page: 1, toggle: { t: true, otherKey: false } } }, - }); - - searchState = connect.refine( - { - attribute: 't', - contextValue: { mainTargetedIndex: 'first' }, - indexContextValue: { targetedIndex: 'first' }, - }, - { indices: { first: { toggle: { t: true, otherKey: false } } } }, - false - ); - expect(searchState).toEqual({ - indices: { - first: { page: 1, toggle: { t: false, otherKey: false } }, - }, - }); - }); - - it('refines the corresponding facet with `true`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - value: 'val', - contextValue, - indexContextValue, - }, - { - indices: { - second: { - toggle: { - facet: true, - }, - }, - }, - } - ); - - expect(params.getDisjunctiveRefinements('facet')).toEqual(['val']); - }); - - it('does not refine the corresponding facet with `false`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - value: 'val', - contextValue, - indexContextValue, - }, - { - indices: { - second: { - toggle: { - facet: false, - }, - }, - }, - } - ); - - expect(params.getDisjunctiveRefinements('facet')).toEqual([]); - }); - - it('applies the provided filter with `true`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - filter: (sp) => sp.setQuery('yep'), - contextValue, - indexContextValue, - }, - { - indices: { - second: { - toggle: { - facet: true, - }, - }, - }, - } - ); - - expect(params.query).toEqual('yep'); - }); - - it('does not apply the provided filter with `false`', () => { - const params = connect.getSearchParameters( - new SearchParameters(), - { - attribute: 'facet', - filter: (sp) => sp.setQuery('yep'), - contextValue, - indexContextValue, - }, - { - indices: { - second: { - toggle: { - facet: false, - }, - }, - }, - } - ); - - expect(params.query).toBeUndefined(); - }); - - it('registers its filter in metadata', () => { - const metadata = connect.getMetadata( - { attribute: 't', label: 'yep', contextValue, indexContextValue }, - { indices: { second: { toggle: { t: true } } } } - ); - expect(metadata).toEqual({ - items: [ - { - label: 'yep', - // Ignore clear, we test it later - value: metadata.items[0].value, - attribute: 't', - currentRefinement: true, - }, - ], - id: 't', - index: 'second', - }); - }); - - it('items value function should clear it from the search state', () => { - const metadata = connect.getMetadata( - { attribute: 'one', label: 'yep', contextValue, indexContextValue }, - { indices: { second: { toggle: { one: true, two: false } } } } - ); - - const searchState = metadata.items[0].value({ - indices: { second: { toggle: { one: true, two: false } } }, - }); - - expect(searchState).toEqual({ - indices: { second: { page: 1, toggle: { one: false, two: false } } }, - }); - }); - - it('should return the right searchState when clean up', () => { - let searchState = connect.cleanUp( - { attribute: 'name', contextValue, indexContextValue }, - { - indices: { - second: { toggle: { name: 'searchState', name2: 'searchState' } }, - }, - another: { searchState: 'searchState' }, - } - ); - expect(searchState).toEqual({ - indices: { second: { toggle: { name2: 'searchState' } } }, - another: { searchState: 'searchState' }, - }); - - searchState = connect.cleanUp( - { attribute: 'name2', contextValue, indexContextValue }, - searchState - ); - expect(searchState).toEqual({ - indices: { second: { toggle: {} } }, - another: { searchState: 'searchState' }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectVoiceSearch.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectVoiceSearch.js deleted file mode 100644 index 087febfbd2..0000000000 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectVoiceSearch.js +++ /dev/null @@ -1,350 +0,0 @@ -import { SearchParameters } from 'algoliasearch-helper'; - -import connect from '../connectVoiceSearch'; - -jest.mock('../../core/createConnector', () => (x) => x); - -describe('connectVoiceSearch', () => { - describe('single index', () => { - const contextValue = { mainTargetedIndex: 'index' }; - - describe('getProvidedProps', () => { - it('provides the correct props to the component', () => { - expect(connect.getProvidedProps({ contextValue }, {}, {})).toEqual({ - currentRefinement: '', - }); - - expect( - connect.getProvidedProps({ contextValue }, { query: 'abc' }, {}) - ).toEqual({ - currentRefinement: 'abc', - }); - }); - }); - - describe('refine', () => { - it('calls refine and updates the state correctly', () => { - expect(connect.refine({ contextValue }, {}, 'abc')).toEqual({ - page: 1, - query: 'abc', - additionalVoiceParameters: {}, - }); - }); - - it('refines and get additionalVoiceParameters', () => { - const { additionalVoiceParameters } = connect.refine( - { contextValue }, - {}, - 'abc' - ); - expect(additionalVoiceParameters).toEqual(expect.objectContaining({})); - }); - - it('refines with additionalQueryParameters', () => { - const props = { - contextValue, - additionalQueryParameters: () => ({ additional: 'param' }), - }; - expect(connect.refine(props, {}, 'abc')).toEqual({ - page: 1, - query: 'abc', - additionalVoiceParameters: { - additional: 'param', - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: undefined, - removeStopWords: true, - }, - }); - }); - - it('refines with language', () => { - const props = { contextValue, language: 'en-US' }; - expect(connect.refine(props, {}, 'abc')).toEqual({ - page: 1, - query: 'abc', - additionalVoiceParameters: { - queryLanguages: ['en'], - }, - }); - }); - - it('refines with language (2)', () => { - const props = { - contextValue, - language: 'en-US', - additionalQueryParameters: () => ({}), - }; - expect(connect.refine(props, {}, 'abc')).toEqual({ - page: 1, - query: 'abc', - additionalVoiceParameters: { - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - }, - }); - }); - - it('overrides with additionalQueryParameters', () => { - const props = { - contextValue, - additionalQueryParameters: () => ({ - ignorePlurals: false, - optionalWords: 'something else', - removeStopWords: false, - }), - }; - expect(connect.refine(props, {}, 'abc')).toEqual({ - page: 1, - query: 'abc', - additionalVoiceParameters: { - ignorePlurals: false, - optionalWords: 'something else', - removeStopWords: false, - }, - }); - }); - }); - - describe('cleanUp', () => { - it('should return the right searchState when clean up', () => { - expect( - connect.cleanUp( - { - contextValue, - }, - { - query: 'abc', - additionalVoiceParameters: { - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - }, - } - ) - ).toEqual({}); - }); - }); - - describe('getSearchParameters', () => { - it('returns searchParameters with query', () => { - expect( - connect.getSearchParameters( - new SearchParameters(), - { - contextValue, - }, - { query: 'foo' } - ) - ).toEqual(expect.objectContaining({ query: 'foo' })); - }); - - it('returns searchParameters with additional params', () => { - expect( - connect.getSearchParameters( - new SearchParameters(), - { - contextValue, - }, - { - query: 'abc', - additionalVoiceParameters: { - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - }, - } - ) - ).toEqual( - expect.objectContaining({ - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - query: 'abc', - }) - ); - }); - }); - - describe('getMetadata', () => { - it('returns correct metadata', () => { - expect( - connect.getMetadata( - { - contextValue, - }, - { - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - query: 'abc', - } - ) - ).toEqual({ - id: 'query', - index: 'index', - items: [ - { - label: 'query: abc', - value: expect.any(Function), - currentRefinement: 'abc', - }, - ], - }); - }); - }); - }); - - describe('multi index', () => { - const contextValue = { mainTargetedIndex: 'first' }; - const indexContextValue = { targetedIndex: 'second' }; - - describe('getProvidedProps', () => { - it('provides the correct props to the component', () => { - expect( - connect.getProvidedProps({ contextValue, indexContextValue }, {}, {}) - ).toEqual({ - currentRefinement: '', - }); - - expect( - connect.getProvidedProps( - { contextValue, indexContextValue }, - { indices: { second: { query: 'yep' } } }, - {} - ) - ).toEqual({ - currentRefinement: 'yep', - }); - }); - }); - - describe('refine', () => { - it('refines with additionalQueryParameters', () => { - const props = { - contextValue, - indexContextValue, - additionalQueryParameters: () => ({ additional: 'param' }), - }; - expect(connect.refine(props, {}, 'abc')).toEqual({ - indices: { - second: { - page: 1, - query: 'abc', - additionalVoiceParameters: { - additional: 'param', - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: undefined, - removeStopWords: true, - }, - }, - }, - }); - }); - - it('refines with language', () => { - const props = { contextValue, indexContextValue, language: 'en-US' }; - expect(connect.refine(props, {}, 'abc')).toEqual({ - indices: { - second: { - page: 1, - query: 'abc', - additionalVoiceParameters: { - queryLanguages: ['en'], - }, - }, - }, - }); - }); - }); - - describe('getSearchParameters', () => { - it('returns searchParameters with query', () => { - expect( - connect.getSearchParameters( - new SearchParameters(), - { - contextValue, - indexContextValue, - }, - { indices: { second: { query: 'foo' } } } - ) - ).toEqual(expect.objectContaining({ query: 'foo' })); - }); - - it('returns searchParameters with additional params', () => { - expect( - connect.getSearchParameters( - new SearchParameters(), - { - contextValue, - indexContextValue, - }, - { - indices: { - second: { - query: 'abc', - additionalVoiceParameters: { - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - }, - }, - }, - } - ) - ).toEqual( - expect.objectContaining({ - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - query: 'abc', - }) - ); - }); - }); - - describe('getMetadata', () => { - it('returns correct metadata', () => { - expect( - connect.getMetadata( - { - contextValue, - indexContextValue, - }, - { - indices: { - second: { - ignorePlurals: true, - optionalWords: 'abc', - queryLanguages: ['en'], - removeStopWords: true, - query: 'abc', - }, - }, - } - ) - ).toEqual({ - id: 'query', - index: 'second', - items: [ - { - label: 'query: abc', - value: expect.any(Function), - currentRefinement: 'abc', - }, - ], - }); - }); - }); - }); -}); diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useBreadcrumb.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useBreadcrumb.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useBreadcrumb.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useBreadcrumb.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useClearRefinements.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useClearRefinements.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useClearRefinements.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useClearRefinements.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useConfigure.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useConfigure.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useConfigure.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useConfigure.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useCurrentRefinements.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useCurrentRefinements.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useCurrentRefinements.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useCurrentRefinements.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useDynamicWidgets.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useDynamicWidgets.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useDynamicWidgets.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useDynamicWidgets.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useGeoSearch.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useGeoSearch.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useGeoSearch.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useGeoSearch.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useHierarchicalMenu.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useHierarchicalMenu.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useHierarchicalMenu.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useHierarchicalMenu.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useHits.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useHits.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useHits.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useHits.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useHitsPerPage.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useHitsPerPage.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useHitsPerPage.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useHitsPerPage.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useInfiniteHits.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useInfiniteHits.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useInfiniteHits.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useInfiniteHits.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useMenu.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useMenu.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useMenu.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useMenu.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useNumericMenu.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useNumericMenu.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useNumericMenu.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useNumericMenu.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/usePagination.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/usePagination.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/usePagination.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/usePagination.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/usePoweredBy.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/usePoweredBy.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/usePoweredBy.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/usePoweredBy.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useQueryRules.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useQueryRules.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useQueryRules.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useQueryRules.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useRange.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useRange.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useRange.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useRange.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useRefinementList.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useRefinementList.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useRefinementList.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useRefinementList.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useSearchBox.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useSearchBox.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useSearchBox.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useSearchBox.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useSortBy.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useSortBy.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useSortBy.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useSortBy.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useStats.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useStats.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useStats.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useStats.test.tsx diff --git a/packages/react-instantsearch-hooks/src/connectors/__tests__/useToggleRefinement.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useToggleRefinement.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/__tests__/useToggleRefinement.test.tsx rename to packages/react-instantsearch-core/src/connectors/__tests__/useToggleRefinement.test.tsx diff --git a/packages/react-instantsearch-core/src/connectors/connectAutoComplete.js b/packages/react-instantsearch-core/src/connectors/connectAutoComplete.js deleted file mode 100644 index a1ce148706..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectAutoComplete.js +++ /dev/null @@ -1,124 +0,0 @@ -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - refineValue, - getCurrentRefinementValue, -} from '../core/indexUtils'; -import { addQueryID, addAbsolutePositions } from '../core/utils'; - -const getId = () => 'query'; - -function getCurrentRefinement(props, searchState, context) { - const id = getId(); - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - '' - ); - - if (currentRefinement) { - return currentRefinement; - } - return ''; -} - -function getHits(searchResults) { - if (searchResults.results) { - if ( - searchResults.results.hits && - Array.isArray(searchResults.results.hits) - ) { - return addAbsolutePositions( - addQueryID(searchResults.results.hits, searchResults.results.queryID), - searchResults.results.hitsPerPage, - searchResults.results.page - ); - } else { - return Object.keys(searchResults.results).reduce( - (hits, index) => [ - ...hits, - { - index, - hits: addAbsolutePositions( - addQueryID( - searchResults.results[index].hits, - searchResults.results[index].queryID - ), - searchResults.results[index].hitsPerPage, - searchResults.results[index].page - ), - }, - ], - [] - ); - } - } else { - return []; - } -} - -function refine(props, searchState, nextRefinement, context) { - const id = getId(); - const nextValue = { [id]: nextRefinement }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, getId()); -} - -/** - * connectAutoComplete connector provides the logic to create connected - * components that will render the results retrieved from - * Algolia. - * - * To configure the number of hits retrieved, use [HitsPerPage widget](widgets/HitsPerPage.html), - * [connectHitsPerPage connector](connectors/connectHitsPerPage.html) or pass the hitsPerPage - * prop to a [Configure](guide/Search_parameters.html) widget. - * @name connectAutoComplete - * @kind connector - * @propType {string} [defaultRefinement] - Provide a default value for the query - * @providedPropType {array.} hits - the records that matched the search state - * @providedPropType {function} refine - a function to change the query - * @providedPropType {string} currentRefinement - the query to search for - */ -export default createConnector({ - displayName: 'AlgoliaAutoComplete', - $$type: 'ais.autoComplete', - - getProvidedProps(props, searchState, searchResults) { - return { - hits: getHits(searchResults), - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - return searchParameters.setQuery( - getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectBreadcrumb.js b/packages/react-instantsearch-core/src/connectors/connectBreadcrumb.js deleted file mode 100644 index e83422c8d8..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectBreadcrumb.js +++ /dev/null @@ -1,130 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { refineValue, getResults } from '../core/indexUtils'; - -export const getId = (props) => props.attributes[0]; - -const namespace = 'hierarchicalMenu'; - -function refine(props, searchState, nextRefinement, context) { - const id = getId(props); - const nextValue = { [id]: nextRefinement || '' }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage, namespace); -} - -function transformValue(values) { - return values.reduce((acc, item) => { - if (item.isRefined) { - acc.push({ - label: item.name, - // If dealing with a nested "items", "value" is equal to the previous value concatenated with the current value - // If dealing with the first level, "value" is equal to the current value - value: item.escapedValue, - }); - // Create a variable in order to keep the same acc for the recursion, otherwise "reduce" returns a new one - if (item.data) { - acc = acc.concat(transformValue(item.data, acc)); - } - } - return acc; - }, []); -} - -/** - * The breadcrumb component is s a type of secondary navigation scheme that - * reveals the user’s location in a website or web application. - * - * @name connectBreadcrumb - * @requirements To use this widget, your attributes must be formatted in a specific way. - * If you want for example to have a Breadcrumb of categories, objects in your index - * should be formatted this way: - * - * ```json - * { - * "categories.lvl0": "products", - * "categories.lvl1": "products > fruits", - * "categories.lvl2": "products > fruits > citrus" - * } - * ``` - * - * It's also possible to provide more than one path for each level: - * - * ```json - * { - * "categories.lvl0": ["products", "goods"], - * "categories.lvl1": ["products > fruits", "goods > to eat"] - * } - * ``` - * - * All attributes passed to the `attributes` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * - * @kind connector - * @propType {array.} attributes - List of attributes to use to generate the hierarchy of the menu. See the example for the convention to follow. - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @providedPropType {function} refine - a function to toggle a refinement - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {array.<{items: object, count: number, isRefined: boolean, label: string, value: string}>} items - the list of items the Breadcrumb can display. - */ - -export default createConnector({ - displayName: 'AlgoliaBreadcrumb', - $$type: 'ais.breadcrumb', - - propTypes: { - attributes: (props, propName, componentName) => { - const isNotString = (val) => typeof val !== 'string'; - if ( - !Array.isArray(props[propName]) || - props[propName].some(isNotString) || - props[propName].length < 1 - ) { - return new Error( - `Invalid prop ${propName} supplied to ${componentName}. Expected an Array of Strings` - ); - } - return undefined; - }, - transformItems: PropTypes.func, - }, - - getProvidedProps(props, searchState, searchResults) { - const id = getId(props); - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const isFacetPresent = - Boolean(results) && Boolean(results.getFacetByName(id)); - - if (!isFacetPresent) { - return { - items: [], - canRefine: false, - }; - } - - const values = results.getFacetValues(id); - - const items = values.data ? transformValue(values.data) : []; - - const transformedItems = props.transformItems - ? props.transformItems(items) - : items; - - return { - canRefine: transformedItems.length > 0, - items: transformedItems, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectConfigure.js b/packages/react-instantsearch-core/src/connectors/connectConfigure.js deleted file mode 100644 index ad6cc410de..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectConfigure.js +++ /dev/null @@ -1,71 +0,0 @@ -import createConnector from '../core/createConnector'; -import { - refineValue, - getIndexId, - hasMultipleIndices, -} from '../core/indexUtils'; -import { omit } from '../core/utils'; - -function getId() { - return 'configure'; -} - -export default createConnector({ - displayName: 'AlgoliaConfigure', - $$type: 'ais.configure', - getProvidedProps() { - return {}; - }, - getSearchParameters(searchParameters, props) { - const { children, contextValue, indexContextValue, ...items } = props; - return searchParameters.setQueryParameters(items); - }, - transitionState(props, prevSearchState, nextSearchState) { - const id = getId(); - const { children, contextValue, indexContextValue, ...items } = props; - const propKeys = Object.keys(props); - const nonPresentKeys = this._props - ? Object.keys(this._props).filter((prop) => propKeys.indexOf(prop) === -1) - : []; - this._props = props; - const nextValue = { - [id]: { ...omit(nextSearchState[id], nonPresentKeys), ...items }, - }; - return refineValue(nextSearchState, nextValue, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - cleanUp(props, searchState) { - const id = getId(); - const indexId = getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const subState = - hasMultipleIndices({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) && searchState.indices - ? searchState.indices[indexId] - : searchState; - - const configureKeys = - subState && subState[id] ? Object.keys(subState[id]) : []; - - const configureState = configureKeys.reduce((acc, item) => { - if (!props[item]) { - acc[item] = subState[id][item]; - } - return acc; - }, {}); - - const nextValue = { [id]: configureState }; - - return refineValue(searchState, nextValue, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectConfigureRelatedItems.ts b/packages/react-instantsearch-core/src/connectors/connectConfigureRelatedItems.ts deleted file mode 100644 index 7ec8156066..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectConfigureRelatedItems.ts +++ /dev/null @@ -1,233 +0,0 @@ -import algoliasearchHelper from 'algoliasearch-helper'; - -import createConnector from '../core/createConnector'; -import { - refineValue, - getIndexId, - hasMultipleIndices, - // @ts-ignore -} from '../core/indexUtils'; -import { - omit, - getObjectType, - getPropertyByPath, - removeEmptyKey, - removeEmptyArraysFromObject, -} from '../core/utils'; - -import type { ConnectedProps } from '../core/createConnector'; -import type { - PlainSearchParameters, - SearchParameters, -} from 'algoliasearch-helper'; - -type Hit = any; - -export type MatchingPatterns = { - [attribute: string]: { - /** - * The score of the optional filter. - * - * @see https://www.algolia.com/doc/guides/managing-results/rules/merchandising-and-promoting/in-depth/optional-filters/ - */ - score: number; - }; -}; - -interface ConfigureRelatedItemsProps { - /** - * The reference hit to extract the filters from. - */ - hit: Hit; - /** - * The schema to create the optional filters. - * Each key represents an attribute from the hit. - */ - matchingPatterns: MatchingPatterns; - /** - * Function to transform the generated search parameters. - */ - transformSearchParameters?: ( - searchParameters: SearchParameters - ) => PlainSearchParameters; -} - -function createOptionalFilter({ - attributeName, - attributeValue, - attributeScore, -}: { - attributeName: string; - attributeValue: string; - attributeScore: number; -}) { - return `${attributeName}:${attributeValue}`; -} - -const defaultProps: Partial = { - transformSearchParameters: (x) => ({ ...x }), -}; - -function getId(): string { - // We store the search state of this widget in `configure`. - return 'configure'; -} - -type InternalConfigureRelatedItemsProps = ConfigureRelatedItemsProps & - Required; - -function getSearchParametersFromProps( - props: ConnectedProps -): PlainSearchParameters { - const optionalFilters = Object.keys(props.matchingPatterns).reduce< - Array - >((acc, attributeName) => { - const attributePattern = props.matchingPatterns[attributeName]; - const attributeValue = getPropertyByPath(props.hit, attributeName); - const attributeScore = attributePattern.score; - - if (Array.isArray(attributeValue)) { - return [ - ...acc, - attributeValue.map((attributeSubValue) => { - return createOptionalFilter({ - attributeName, - attributeValue: attributeSubValue, - attributeScore, - }); - }), - ]; - } - - if (typeof attributeValue === 'string') { - return [ - ...acc, - createOptionalFilter({ - attributeName, - attributeValue, - attributeScore, - }), - ]; - } - - if (process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - `The \`matchingPatterns\` option returned a value of type ${getObjectType( - attributeValue - )} for the "${attributeName}" key. This value was not sent to Algolia because \`optionalFilters\` only supports strings and array of strings. - -You can remove the "${attributeName}" key from the \`matchingPatterns\` option. - -See https://www.algolia.com/doc/api-reference/api-parameters/optionalFilters/` - ); - } - - return acc; - }, []); - - return props.transformSearchParameters( - new algoliasearchHelper.SearchParameters({ - // @ts-ignore @TODO algoliasearch-helper@3.0.1 will contain the type - // `sumOrFiltersScores`. - // See https://github.com/algolia/algoliasearch-helper-js/pull/753 - sumOrFiltersScores: true, - facetFilters: [`objectID:-${props.hit.objectID}`], - optionalFilters, - }) - ); -} - -interface ConnectorState { - _searchParameters: PlainSearchParameters; -} - -export default createConnector({ - displayName: 'AlgoliaConfigureRelatedItems', - $$type: 'ais.configureRelatedItems', - - defaultProps, - - getProvidedProps() { - return {}; - }, - - getSearchParameters( - searchParameters: SearchParameters, - props: ConnectedProps - ) { - return searchParameters.setQueryParameters( - getSearchParametersFromProps(props) - ); - }, - - transitionState( - this: ConnectorState, - props, - _prevSearchState, - nextSearchState - ) { - const id = getId(); - // We need to transform the exhaustive search parameters back to clean - // search parameters without the empty default keys so we don't pollute the - // `configure` search state. - const searchParameters = removeEmptyArraysFromObject( - removeEmptyKey(getSearchParametersFromProps(props)) - ); - - const searchParametersKeys = Object.keys(searchParameters); - const nonPresentKeys = this._searchParameters - ? Object.keys(this._searchParameters).filter( - (prop) => searchParametersKeys.indexOf(prop) === -1 - ) - : []; - this._searchParameters = searchParameters; - const nextValue: any = { - [id]: { - ...omit(nextSearchState[id], nonPresentKeys), - ...searchParameters, - }, - }; - - return refineValue(nextSearchState, nextValue, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(this: ConnectorState, props, searchState) { - const id = getId(); - const indexId = getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const subState = - hasMultipleIndices({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) && searchState.indices - ? searchState.indices[indexId] - : searchState; - - const configureKeys = - subState && subState[id] ? Object.keys(subState[id]) : []; - - const configureState = ( - configureKeys as Array - ).reduce((acc, item) => { - if (!this._searchParameters[item]) { - (acc as any)[item] = subState[id][item]; - } - - return acc; - }, {} as PlainSearchParameters); - - const nextValue = { [id]: configureState }; - - return refineValue(searchState, nextValue, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectCurrentRefinements.js b/packages/react-instantsearch-core/src/connectors/connectCurrentRefinements.js deleted file mode 100644 index 40f1e0d86d..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectCurrentRefinements.js +++ /dev/null @@ -1,66 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; - -/** - * connectCurrentRefinements connector provides the logic to build a widget that will - * give the user the ability to remove all or some of the filters that were - * set. - * @name connectCurrentRefinements - * @kind connector - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @propType {function} [clearsQuery=false] - Pass true to also clear the search query - * @providedPropType {function} refine - a function to remove a single filter - * @providedPropType {array.<{label: string, attribute: string, currentRefinement: string || object, items: array, value: function}>} items - all the filters, the `value` is to pass to the `refine` function for removing all currentrefinements, `label` is for the display. When existing several refinements for the same atribute name, then you get a nested `items` object that contains a `label` and a `value` function to use to remove a single filter. `attribute` and `currentRefinement` are metadata containing row values. - * @providedPropType {string} query - the search query - */ -export default createConnector({ - displayName: 'AlgoliaCurrentRefinements', - $$type: 'ais.currentRefinements', - - propTypes: { - transformItems: PropTypes.func, - }, - - getProvidedProps(props, searchState, searchResults, metadata) { - const items = metadata.reduce((res, meta) => { - if (typeof meta.items !== 'undefined') { - if (!props.clearsQuery && meta.id === 'query') { - return res; - } else { - if ( - props.clearsQuery && - meta.id === 'query' && - meta.items[0].currentRefinement === '' - ) { - return res; - } - return res.concat( - meta.items.map((item) => ({ - ...item, - id: meta.id, - index: meta.index, - })) - ); - } - } - return res; - }, []); - - const transformedItems = props.transformItems - ? props.transformItems(items) - : items; - - return { - items: transformedItems, - canRefine: transformedItems.length > 0, - }; - }, - - refine(props, searchState, items) { - // `value` corresponds to our internal clear function computed in each connector metadata. - const refinementsToClear = - items instanceof Array ? items.map((item) => item.value) : [items]; - return refinementsToClear.reduce((res, clear) => clear(res), searchState); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectDynamicWidgets.ts b/packages/react-instantsearch-core/src/connectors/connectDynamicWidgets.ts deleted file mode 100644 index b908e54b38..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectDynamicWidgets.ts +++ /dev/null @@ -1,90 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -// @ts-ignore -import { getResults } from '../core/indexUtils'; - -import type { SearchParameters } from 'algoliasearch-helper'; - -const MAX_WILDCARD_FACETS = 20; - -export default createConnector({ - displayName: 'AlgoliaDynamicWidgets', - $$type: 'ais.dynamicWidgets', - - defaultProps: { - transformItems: (items: any[]) => items, - maxValuesPerFacet: 20, - }, - - propTypes: { - transformItems: PropTypes.func, - facets: PropTypes.arrayOf(PropTypes.string), - maxValuesPerFacet: PropTypes.number, - }, - - getProvidedProps(props, _searchState, searchResults) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if ( - props.facets && - !( - Array.isArray(props.facets) && - props.facets.length <= 1 && - (props.facets[0] === '*' || props.facets[0] === undefined) - ) - ) { - throw new Error( - `The \`facets\` prop only accepts [] or ["*"], you passed ${JSON.stringify( - props.facets - )}` - ); - } - - if (!results) { - return { attributesToRender: [] }; - } - - const facetOrder = - (results.renderingContent && - results.renderingContent.facetOrdering && - results.renderingContent.facetOrdering.facets && - results.renderingContent.facetOrdering.facets.order) || - []; - - const attributesToRender = props.transformItems(facetOrder, { results }); - - if (attributesToRender.length > MAX_WILDCARD_FACETS && !props.facets) { - // eslint-disable-next-line no-console - console.warn( - `More than ${MAX_WILDCARD_FACETS} facets are requested to be displayed without explicitly setting which facets to retrieve. This could have a performance impact. Set "facets" to [] to do two smaller network requests, or explicitly to ['*'] to avoid this warning.` - ); - } - - if (props.maxValuesPerFacet < results._state.maxValuesPerFacet) { - // eslint-disable-next-line no-console - console.warn( - `The maxValuesPerFacet set by dynamic widgets (${props.maxValuesPerFacet}) is smaller than one of the limits set by a widget (${results._state.maxValuesPerFacet}). This causes a mismatch in query parameters and thus an extra network request when that widget is mounted.` - ); - } - - return { - attributesToRender, - }; - }, - - getSearchParameters(searchParameters, props) { - return (props.facets || ['*']).reduce( - (acc: SearchParameters, curr: string) => acc.addFacet(curr), - searchParameters.setQueryParameters({ - maxValuesPerFacet: Math.max( - props.maxValuesPerFacet || 0, - searchParameters.maxValuesPerFacet || 0 - ), - }) - ); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectGeoSearch.js b/packages/react-instantsearch-core/src/connectors/connectGeoSearch.js deleted file mode 100644 index e11a81d6a4..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectGeoSearch.js +++ /dev/null @@ -1,246 +0,0 @@ -import createConnector from '../core/createConnector'; -import { - getResults, - getCurrentRefinementValue, - getIndexId, - refineValue, - cleanUpValue, -} from '../core/indexUtils'; -import { objectHasKeys } from '../core/utils'; - -/** - * The GeoSearch connector provides the logic to build a widget that will display the results on a map. - * It also provides a way to search for results based on their position. The connector provides function to manage the search experience (search on map interaction). - * @name connectGeoSearch - * @kind connector - * @requirements Note that the GeoSearch connector uses the [geosearch](https://www.algolia.com/doc/guides/searching/geo-search) capabilities of Algolia. - * Your hits **must** have a `_geoloc` attribute in order to be passed to the rendering function. Currently, the feature is not compatible with multiple values in the `_geoloc` attribute - * (e.g. a restaurant with multiple locations). In that case you can duplicate your records and use the [distinct](https://www.algolia.com/doc/guides/ranking/distinct) feature of Algolia to only retrieve unique results. - * @propType {{ northEast: { lat: number, lng: number }, southWest: { lat: number, lng: number } }} [defaultRefinement] - Default search state of the widget containing the bounds for the map - * @providedPropType {function({ northEast: { lat: number, lng: number }, southWest: { lat: number, lng: number } })} refine - a function to toggle the refinement - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {array.} hits - the records that matched the search - * @providedPropType {boolean} isRefinedWithMap - true if the current refinement is set with the map bounds - * @providedPropType {{ northEast: { lat: number, lng: number }, southWest: { lat: number, lng: number } }} [currentRefinement] - the refinement currently applied - * @providedPropType {{ lat: number, lng: number }} [position] - the position of the search - */ - -// To control the map with an external widget the other widget -// **must** write the value in the attribute `aroundLatLng` -const getBoundingBoxId = () => 'boundingBox'; -const getAroundLatLngId = () => 'aroundLatLng'; -const getConfigureAroundLatLngId = () => 'configure.aroundLatLng'; - -const currentRefinementToString = (currentRefinement) => - [ - currentRefinement.northEast.lat, - currentRefinement.northEast.lng, - currentRefinement.southWest.lat, - currentRefinement.southWest.lng, - ].join(); - -const stringToCurrentRefinement = (value) => { - const values = value.split(','); - - return { - northEast: { - lat: parseFloat(values[0]), - lng: parseFloat(values[1]), - }, - southWest: { - lat: parseFloat(values[2]), - lng: parseFloat(values[3]), - }, - }; -}; - -const latLngRegExp = /^(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)$/; -const stringToPosition = (value) => { - const pattern = value.match(latLngRegExp); - - return { - lat: parseFloat(pattern[1]), - lng: parseFloat(pattern[2]), - }; -}; - -const getCurrentRefinement = (props, searchState, context) => { - const refinement = getCurrentRefinementValue( - props, - searchState, - context, - getBoundingBoxId(), - {} - ); - - if (!objectHasKeys(refinement)) { - return; - } - - // eslint-disable-next-line consistent-return - return { - northEast: { - lat: parseFloat(refinement.northEast.lat), - lng: parseFloat(refinement.northEast.lng), - }, - southWest: { - lat: parseFloat(refinement.southWest.lat), - lng: parseFloat(refinement.southWest.lng), - }, - }; -}; - -const getCurrentPosition = (props, searchState, context) => { - const { defaultRefinement, ...propsWithoutDefaultRefinement } = props; - - const aroundLatLng = getCurrentRefinementValue( - propsWithoutDefaultRefinement, - searchState, - context, - getAroundLatLngId() - ); - - if (!aroundLatLng) { - // Fallback on `configure.aroundLatLng` - const configureAroundLatLng = getCurrentRefinementValue( - propsWithoutDefaultRefinement, - searchState, - context, - getConfigureAroundLatLngId() - ); - - return configureAroundLatLng && stringToPosition(configureAroundLatLng); - } - - return aroundLatLng; -}; - -const refine = (searchState, nextValue, context) => { - const resetPage = true; - const nextRefinement = { - [getBoundingBoxId()]: nextValue, - }; - - return refineValue(searchState, nextRefinement, context, resetPage); -}; - -export default createConnector({ - displayName: 'AlgoliaGeoSearch', - $$type: 'ais.geoSearch', - - getProvidedProps(props, searchState, searchResults) { - const context = { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }; - - const results = getResults(searchResults, context); - - // We read it from both because the SearchParameters & the searchState are not always - // in sync. When we set the refinement the searchState is used but when we clear the refinement - // the SearchParameters is used. In the first case when we render, the results are not there - // so we can't find the value from the results. The most up to date value is the searchState. - // But when we clear the refinement the searchState is immediately cleared even when the items - // retrieved are still the one from the previous query with the bounding box. It leads to some - // issue with the position of the map. We should rely on 1 source of truth or at least always - // be sync. - - const currentRefinementFromSearchState = getCurrentRefinement( - props, - searchState, - context - ); - - const currentRefinementFromSearchParameters = - (results && - results._state.insideBoundingBox && - stringToCurrentRefinement(results._state.insideBoundingBox)) || - undefined; - - const currentPositionFromSearchState = getCurrentPosition( - props, - searchState, - context - ); - - const currentPositionFromSearchParameters = - (results && - results._state.aroundLatLng && - stringToPosition(results._state.aroundLatLng)) || - undefined; - - const currentRefinement = - currentRefinementFromSearchState || currentRefinementFromSearchParameters; - - const position = - currentPositionFromSearchState || currentPositionFromSearchParameters; - - return { - hits: !results ? [] : results.hits.filter((_) => Boolean(_._geoloc)), - isRefinedWithMap: Boolean(currentRefinement), - currentRefinement, - position, - }; - }, - - refine(props, searchState, nextValue) { - return refine(searchState, nextValue, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if (!currentRefinement) { - return searchParameters; - } - - return searchParameters.setQueryParameter( - 'insideBoundingBox', - currentRefinementToString(currentRefinement) - ); - }, - - cleanUp(props, searchState) { - return cleanUpValue( - searchState, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - getBoundingBoxId() - ); - }, - - getMetadata(props, searchState) { - const items = []; - const id = getBoundingBoxId(); - const context = { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }; - const index = getIndexId(context); - const nextRefinement = {}; - const currentRefinement = getCurrentRefinement(props, searchState, context); - - if (currentRefinement) { - items.push({ - label: `${id}: ${currentRefinementToString(currentRefinement)}`, - value: (nextState) => refine(nextState, nextRefinement, context), - currentRefinement, - }); - } - - return { - id, - index, - items, - }; - }, - - shouldComponentUpdate() { - return true; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js b/packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js deleted file mode 100644 index 738ddcab6d..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js +++ /dev/null @@ -1,312 +0,0 @@ -import algoliasearchHelper from 'algoliasearch-helper'; -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - getIndexId, - refineValue, - getCurrentRefinementValue, - getResults, -} from '../core/indexUtils'; -import { unescapeFacetValue } from '../core/utils'; - -export const getId = (props) => props.attributes[0]; - -const namespace = 'hierarchicalMenu'; - -function getCurrentRefinement(props, searchState, context) { - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - `${namespace}.${getId(props)}`, - null - ); - - if (currentRefinement === '') { - return null; - } - return currentRefinement; -} - -function getValue(value, props, searchState, context) { - const { id, attributes, separator, rootPath, showParentLevel } = props; - - const currentRefinement = getCurrentRefinement(props, searchState, context); - let nextRefinement; - - if (currentRefinement === null) { - nextRefinement = value; - } else { - const tmpSearchParameters = new algoliasearchHelper.SearchParameters({ - hierarchicalFacets: [ - { - name: id, - attributes, - separator, - rootPath, - showParentLevel, - }, - ], - }); - - nextRefinement = tmpSearchParameters - .toggleHierarchicalFacetRefinement(id, currentRefinement) - .toggleHierarchicalFacetRefinement(id, value) - .getHierarchicalRefinement(id)[0]; - } - - return nextRefinement; -} - -function transformValue(value, props, searchState, context) { - return value.map((v) => ({ - label: v.name, - value: getValue(v.escapedValue, props, searchState, context), - count: v.count, - isRefined: v.isRefined, - items: v.data && transformValue(v.data, props, searchState, context), - })); -} - -const truncate = (items = [], limit = 10) => - items.slice(0, limit).map((item = {}) => - Array.isArray(item.items) - ? { - ...item, - items: truncate(item.items, limit), - } - : item - ); - -function refine(props, searchState, nextRefinement, context) { - const id = getId(props); - const nextValue = { [id]: nextRefinement || '' }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage, namespace); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, `${namespace}.${getId(props)}`); -} - -const sortBy = ['name:asc']; - -/** - * connectHierarchicalMenu connector provides the logic to build a widget that will - * give the user the ability to explore a tree-like structure. - * This is commonly used for multi-level categorization of products on e-commerce - * websites. From a UX point of view, we suggest not displaying more than two levels deep. - * @name connectHierarchicalMenu - * @requirements To use this widget, your attributes must be formatted in a specific way. - * If you want for example to have a hierarchical menu of categories, objects in your index - * should be formatted this way: - * - * ```json - * { - * "categories.lvl0": "products", - * "categories.lvl1": "products > fruits", - * "categories.lvl2": "products > fruits > citrus" - * } - * ``` - * - * It's also possible to provide more than one path for each level: - * - * ```json - * { - * "categories.lvl0": ["products", "goods"], - * "categories.lvl1": ["products > fruits", "goods > to eat"] - * } - * ``` - * - * All attributes passed to the `attributes` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * - * @kind connector - * @propType {array.} attributes - List of attributes to use to generate the hierarchy of the menu. See the example for the convention to follow. - * @propType {string} [defaultRefinement] - the item value selected by default - * @propType {boolean} [showMore=false] - Flag to activate the show more button, for toggling the number of items between limit and showMoreLimit. - * @propType {number} [limit=10] - The maximum number of items displayed. - * @propType {number} [showMoreLimit=20] - The maximum number of items displayed when the user triggers the show more. Not considered if `showMore` is false. - * @propType {string} [separator='>'] - Specifies the level separator used in the data. - * @propType {string} [rootPath=null] - The path to use if the first level is not the root level. - * @propType {boolean} [showParentLevel=true] - Flag to set if the parent level should be displayed. - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @providedPropType {function} refine - a function to toggle a refinement - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {string} currentRefinement - the refinement currently applied - * @providedPropType {array.<{items: object, count: number, isRefined: boolean, label: string, value: string}>} items - the list of items the HierarchicalMenu can display. items has the same shape as parent items. - */ -export default createConnector({ - displayName: 'AlgoliaHierarchicalMenu', - $$type: 'ais.hierarchicalMenu', - - propTypes: { - attributes: (props, propName, componentName) => { - const isNotString = (val) => typeof val !== 'string'; - if ( - !Array.isArray(props[propName]) || - props[propName].some(isNotString) || - props[propName].length < 1 - ) { - return new Error( - `Invalid prop ${propName} supplied to ${componentName}. Expected an Array of Strings` - ); - } - return undefined; - }, - separator: PropTypes.string, - rootPath: PropTypes.string, - showParentLevel: PropTypes.bool, - defaultRefinement: PropTypes.string, - showMore: PropTypes.bool, - limit: PropTypes.number, - showMoreLimit: PropTypes.number, - transformItems: PropTypes.func, - facetOrdering: PropTypes.bool, - }, - - defaultProps: { - showMore: false, - limit: 10, - showMoreLimit: 20, - separator: ' > ', - rootPath: null, - showParentLevel: true, - facetOrdering: true, - }, - - getProvidedProps(props, searchState, searchResults) { - const { showMore, limit, showMoreLimit, facetOrdering } = props; - const id = getId(props); - - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const isFacetPresent = - Boolean(results) && Boolean(results.getFacetByName(id)); - - if (!isFacetPresent) { - return { - items: [], - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - canRefine: false, - }; - } - const itemsLimit = showMore ? showMoreLimit : limit; - const value = results.getFacetValues(id, { sortBy, facetOrdering }); - const items = value.data - ? transformValue(value.data, props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - : []; - const transformedItems = props.transformItems - ? props.transformItems(items) - : items; - return { - items: truncate(transformedItems, itemsLimit), - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - canRefine: transformedItems.length > 0, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - const { - attributes, - separator, - rootPath, - showParentLevel, - showMore, - limit, - showMoreLimit, - contextValue, - } = props; - - const id = getId(props); - const itemsLimit = showMore ? showMoreLimit : limit; - - searchParameters = searchParameters - .addHierarchicalFacet({ - name: id, - attributes, - separator, - rootPath, - showParentLevel, - }) - .setQueryParameters({ - maxValuesPerFacet: Math.max( - searchParameters.maxValuesPerFacet || 0, - itemsLimit - ), - }); - - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: contextValue, - multiIndexContext: props.indexContextValue, - }); - if (currentRefinement !== null) { - searchParameters = searchParameters.toggleHierarchicalFacetRefinement( - id, - currentRefinement - ); - } - - return searchParameters; - }, - - getMetadata(props, searchState) { - const rootAttribute = props.attributes[0]; - const id = getId(props); - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const items = !currentRefinement - ? [] - : [ - { - label: `${rootAttribute}: ${unescapeFacetValue(currentRefinement)}`, - attribute: rootAttribute, - value: (nextState) => - refine(props, nextState, '', { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - currentRefinement, - }, - ]; - - return { - id, - index: getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - items, - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectHighlight.js b/packages/react-instantsearch-core/src/connectors/connectHighlight.js deleted file mode 100644 index d44b6391fa..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectHighlight.js +++ /dev/null @@ -1,78 +0,0 @@ -import createConnector from '../core/createConnector'; -import { HIGHLIGHT_TAGS, parseAlgoliaHit } from '../core/highlight'; - -const highlight = ({ - attribute, - hit, - highlightProperty, - preTag = HIGHLIGHT_TAGS.highlightPreTag, - postTag = HIGHLIGHT_TAGS.highlightPostTag, -}) => - parseAlgoliaHit({ - attribute, - highlightProperty, - hit, - preTag, - postTag, - }); - -/** - * connectHighlight connector provides the logic to create an highlighter - * component that will retrieve, parse and render an highlighted attribute - * from an Algolia hit. - * @name connectHighlight - * @kind connector - * @category connector - * @providedPropType {function} highlight - function to retrieve and parse an attribute from a hit. It takes a configuration object with 3 attributes: `highlightProperty` which is the property that contains the highlight structure from the records, `attribute` which is the name of the attribute (it can be either a string or an array of strings) to look for and `hit` which is the hit from Algolia. It returns an array of objects `{value: string, isHighlighted: boolean}`. If the element that corresponds to the attribute is an array of strings, it will return a nested array of objects. - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, SearchBox, Hits, connectHighlight } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const CustomHighlight = connectHighlight( - * ({ highlight, attribute, hit, highlightProperty }) => { - * const highlights = highlight({ - * highlightProperty: '_highlightResult', - * attribute, - * hit - * }); - * - * return highlights.map(part => part.isHighlighted ? ( - * {part.value} - * ) : ( - * {part.value} - * )); - * } - * ); - * - * const Hit = ({ hit }) => ( - *

- * - *

- * ); - * - * const App = () => ( - * - * - * - * - * ); - */ -export default createConnector({ - displayName: 'AlgoliaHighlighter', - $$type: 'ais.highlighter', - - propTypes: {}, - - getProvidedProps() { - return { highlight }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectHitInsights.ts b/packages/react-instantsearch-core/src/connectors/connectHitInsights.ts deleted file mode 100644 index f281523f63..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectHitInsights.ts +++ /dev/null @@ -1,83 +0,0 @@ -import createConnector from '../core/createConnector'; -// @ts-ignore -import { getResults } from '../core/indexUtils'; - -type Results = { index: string }; -type Hit = { objectID: string; __position: number; __queryID: string }; - -type InsightsClient = ( - method: InsightsClientMethod, - payload: InsightsClientPayload -) => void; - -type InsightsClientMethod = - | 'clickedObjectIDsAfterSearch' - | 'convertedObjectIDsAfterSearch'; - -type InsightsClientPayload = { - index: string; - queryID: string; - eventName: string; - objectIDs: string[]; - positions?: number[]; -}; - -function inferPayload({ - method, - results, - currentHit, -}: { - method: InsightsClientMethod; - results: Results; - currentHit: Hit; -}): Omit { - const { index } = results; - const queryID = currentHit.__queryID; - const objectIDs = [currentHit.objectID]; - - if (!queryID) { - throw new Error(`Could not infer \`queryID\`. Ensure \`clickAnalytics: true\` was added with the Configure widget. -See: https://alg.li/VpPpLt`); - } - - switch (method) { - case 'clickedObjectIDsAfterSearch': { - const positions = [currentHit.__position]; - return { index, queryID, objectIDs, positions }; - } - - case 'convertedObjectIDsAfterSearch': - return { index, queryID, objectIDs }; - - default: - throw new Error( - `Unsupported method "${method}" passed to the insights function. The supported methods are: "clickedObjectIDsAfterSearch", "convertedObjectIDsAfterSearch".` - ); - } -} - -const wrapInsightsClient = - (aa: InsightsClient, results: Results, currentHit: Hit) => - (method: InsightsClientMethod, payload: Partial) => { - if (typeof aa !== 'function') { - throw new TypeError(`Expected insightsClient to be a Function`); - } - const inferredPayload = inferPayload({ method, results, currentHit }); - aa(method, { ...inferredPayload, ...payload } as any); - }; - -export default (insightsClient: InsightsClient) => - createConnector({ - displayName: 'AlgoliaInsights', - $$type: 'ais.insights', - - getProvidedProps(props, _, searchResults) { - const results: Results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const insights = wrapInsightsClient(insightsClient, results, props.hit); - return { insights }; - }, - }); diff --git a/packages/react-instantsearch-core/src/connectors/connectHits.js b/packages/react-instantsearch-core/src/connectors/connectHits.js deleted file mode 100644 index 3af94aa261..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectHits.js +++ /dev/null @@ -1,86 +0,0 @@ -import createConnector from '../core/createConnector'; -import { getResults } from '../core/indexUtils'; -import { addAbsolutePositions, addQueryID } from '../core/utils'; - -/** - * connectHits connector provides the logic to create connected - * components that will render the results retrieved from - * Algolia. - * - * To configure the number of hits retrieved, use [HitsPerPage widget](widgets/HitsPerPage.html), - * [connectHitsPerPage connector](connectors/connectHitsPerPage.html) or pass the hitsPerPage - * prop to a [Configure](guide/Search_parameters.html) widget. - * - * **Warning:** you will need to use the **objectID** property available on every hit as a key - * when iterating over them. This will ensure you have the best possible UI experience - * especially on slow networks. - * @name connectHits - * @kind connector - * @providedPropType {array.} hits - the records that matched the search state - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, Highlight, connectHits } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * const CustomHits = connectHits(({ hits }) => ( - *
- * {hits.map(hit => - *

- * - *

- * )} - *
- * )); - * - * const App = () => ( - * - * - * - * ); - */ -export default createConnector({ - displayName: 'AlgoliaHits', - $$type: 'ais.hits', - - getProvidedProps(props, searchState, searchResults) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - if (!results) { - return { hits: [] }; - } - const hitsWithPositions = addAbsolutePositions( - results.hits, - results.hitsPerPage, - results.page - ); - const hitsWithPositionsAndQueryID = addQueryID( - hitsWithPositions, - results.queryID - ); - return { hits: hitsWithPositionsAndQueryID }; - }, - - /* - * Hits needs to be considered as a widget to trigger a search, - * even if no other widgets are used. - * - * To be considered as a widget you need either: - * - getSearchParameters - * - getMetadata - * - transitionState - * - * See: createConnector.tsx - */ - getSearchParameters(searchParameters) { - return searchParameters; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectHitsPerPage.js b/packages/react-instantsearch-core/src/connectors/connectHitsPerPage.js deleted file mode 100644 index 3af521be40..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectHitsPerPage.js +++ /dev/null @@ -1,106 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - refineValue, - getCurrentRefinementValue, -} from '../core/indexUtils'; - -function getId() { - return 'hitsPerPage'; -} - -function getCurrentRefinement(props, searchState, context) { - const id = getId(); - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - null - ); - - if (typeof currentRefinement === 'string') { - return parseInt(currentRefinement, 10); - } - return currentRefinement; -} - -/** - * connectHitsPerPage connector provides the logic to create connected - * components that will allow a user to choose to display more or less results from Algolia. - * @name connectHitsPerPage - * @kind connector - * @propType {number} defaultRefinement - The number of items selected by default - * @propType {{value: number, label: string}[]} items - List of hits per page options. - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @providedPropType {function} refine - a function to remove a single filter - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {string} currentRefinement - the refinement currently applied - * @providedPropType {array.<{isRefined: boolean, label?: string, value: number}>} items - the list of items the HitsPerPage can display. If no label provided, the value will be displayed. - */ -export default createConnector({ - displayName: 'AlgoliaHitsPerPage', - $$type: 'ais.hitsPerPage', - - propTypes: { - defaultRefinement: PropTypes.number.isRequired, - items: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string, - value: PropTypes.number.isRequired, - }) - ).isRequired, - transformItems: PropTypes.func, - }, - - getProvidedProps(props, searchState) { - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const items = props.items.map((item) => - item.value === currentRefinement - ? { ...item, isRefined: true } - : { ...item, isRefined: false } - ); - return { - items: props.transformItems ? props.transformItems(items) : items, - currentRefinement, - }; - }, - - refine(props, searchState, nextRefinement) { - const id = getId(); - const nextValue = { [id]: nextRefinement }; - const resetPage = true; - return refineValue( - searchState, - nextValue, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - resetPage - ); - }, - - cleanUp(props, searchState) { - return cleanUpValue( - searchState, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - getId() - ); - }, - - getSearchParameters(searchParameters, props, searchState) { - return searchParameters.setHitsPerPage( - getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ); - }, - - getMetadata() { - return { id: getId() }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectInfiniteHits.js b/packages/react-instantsearch-core/src/connectors/connectInfiniteHits.js deleted file mode 100644 index b28177275d..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectInfiniteHits.js +++ /dev/null @@ -1,150 +0,0 @@ -import isEqual from 'react-fast-compare'; - -import createConnector from '../core/createConnector'; -import { - getCurrentRefinementValue, - refineValue, - getResults, -} from '../core/indexUtils'; -import { addAbsolutePositions, addQueryID } from '../core/utils'; - -function getId() { - return 'page'; -} - -function getCurrentRefinement(props, searchState, context) { - const id = getId(); - const page = 1; - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - page - ); - - if (typeof currentRefinement === 'string') { - return parseInt(currentRefinement, 10); - } - return currentRefinement; -} - -function getStateWithoutPage(state) { - const { page, ...rest } = state || {}; - return rest; -} - -function getInMemoryCache() { - let cachedHits = undefined; - let cachedState = undefined; - return { - read({ state }) { - return isEqual(cachedState, getStateWithoutPage(state)) - ? cachedHits - : null; - }, - write({ state, hits }) { - cachedState = getStateWithoutPage(state); - cachedHits = hits; - }, - }; -} - -function extractHitsFromCachedHits(cachedHits) { - return Object.keys(cachedHits) - .map(Number) - .sort((a, b) => a - b) - .reduce((acc, page) => { - return acc.concat(cachedHits[page]); - }, []); -} - -/** - * InfiniteHits connector provides the logic to create connected - * components that will render an continuous list of results retrieved from - * Algolia. This connector provides a function to load more results. - * @name connectInfiniteHits - * @kind connector - * @providedPropType {array.} hits - the records that matched the search state - * @providedPropType {boolean} hasMore - indicates if there are more pages to load - * @providedPropType {function} refine - call to load more results - */ -export default createConnector({ - displayName: 'AlgoliaInfiniteHits', - $$type: 'ais.infiniteHits', - - getProvidedProps(props, searchState, searchResults) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if (!results) { - return { - hits: [], - hasPrevious: false, - hasMore: false, - refine: () => {}, - refinePrevious: () => {}, - refineNext: () => {}, - }; - } - - const { page, hits, hitsPerPage, nbPages, _state: state } = results; - - this._cache = props.cache ? props.cache : this._cache || getInMemoryCache(); - const cachedHits = this._cache.read({ state }) || {}; - - const hitsWithPositions = addAbsolutePositions(hits, hitsPerPage, page); - const hitsWithPositionsAndQueryID = addQueryID( - hitsWithPositions, - results.queryID - ); - - cachedHits[page] = hitsWithPositionsAndQueryID; - this._cache.write({ state, hits: cachedHits }); - - /* - Math.min() and Math.max() returns Infinity or -Infinity when no argument is given. - But there is always something in this point because of `cachedHits[page]`. - */ - const firstReceivedPage = Math.min(...Object.keys(cachedHits).map(Number)); - const lastReceivedPage = Math.max(...Object.keys(cachedHits).map(Number)); - - const hasPrevious = firstReceivedPage > 0; - const lastPageIndex = nbPages - 1; - const hasMore = lastReceivedPage < lastPageIndex; - const refinePrevious = (event) => this.refine(event, firstReceivedPage - 1); - const refineNext = (event) => this.refine(event, lastReceivedPage + 1); - - return { - hits: extractHitsFromCachedHits(cachedHits), - hasPrevious, - hasMore, - refinePrevious, - refineNext, - }; - }, - - getSearchParameters(searchParameters, props, searchState) { - return searchParameters.setQueryParameters({ - page: - getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - 1, - }); - }, - - refine(props, searchState, event, index) { - const id = getId(); - const nextValue = { [id]: index + 1 }; - const resetPage = false; - return refineValue( - searchState, - nextValue, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - resetPage - ); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectMenu.js b/packages/react-instantsearch-core/src/connectors/connectMenu.js deleted file mode 100644 index 3e470abba7..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectMenu.js +++ /dev/null @@ -1,265 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - getIndexId, - cleanUpValue, - refineValue, - getCurrentRefinementValue, - getResults, -} from '../core/indexUtils'; -import { unescapeFacetValue } from '../core/utils'; - -const namespace = 'menu'; - -function getId(props) { - return props.attribute; -} - -function getCurrentRefinement(props, searchState, context) { - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - `${namespace}.${getId(props)}`, - null - ); - - if (currentRefinement === '') { - return null; - } - return currentRefinement; -} - -function getValue(value, props, searchState, context) { - const currentRefinement = getCurrentRefinement(props, searchState, context); - return value === currentRefinement ? '' : value; -} - -function getLimit({ showMore, limit, showMoreLimit }) { - return showMore ? showMoreLimit : limit; -} - -function refine(props, searchState, nextRefinement, context) { - const id = getId(props); - const nextValue = { [id]: nextRefinement ? nextRefinement : '' }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage, namespace); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, `${namespace}.${getId(props)}`); -} - -const defaultSortBy = ['count:desc', 'name:asc']; - -/** - * connectMenu connector provides the logic to build a widget that will - * give the user the ability to choose a single value for a specific facet. - * @name connectMenu - * @requirements The attribute passed to the `attribute` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * @kind connector - * @propType {string} attribute - the name of the attribute in the record - * @propType {boolean} [showMore=false] - true if the component should display a button that will expand the number of items - * @propType {number} [limit=10] - the minimum number of diplayed items - * @propType {number} [showMoreLimit=20] - the maximun number of displayed items. Only used when showMore is set to `true` - * @propType {string} [defaultRefinement] - the value of the item selected by default - * @propType {boolean} [searchable=false] - allow search inside values - * @providedPropType {function} refine - a function to toggle a refinement - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {string} currentRefinement - the refinement currently applied - * @providedPropType {array.<{count: number, isRefined: boolean, label: string, value: string}>} items - the list of items the Menu can display. - * @providedPropType {function} searchForItems - a function to toggle a search inside items values - * @providedPropType {boolean} isFromSearch - a boolean that says if the `items` props contains facet values from the global search or from the search inside items. - */ -export default createConnector({ - displayName: 'AlgoliaMenu', - $$type: 'ais.menu', - - propTypes: { - attribute: PropTypes.string.isRequired, - showMore: PropTypes.bool, - limit: PropTypes.number, - showMoreLimit: PropTypes.number, - defaultRefinement: PropTypes.string, - transformItems: PropTypes.func, - searchable: PropTypes.bool, - facetOrdering: PropTypes.bool, - }, - - defaultProps: { - showMore: false, - limit: 10, - showMoreLimit: 20, - facetOrdering: true, - }, - - getProvidedProps( - props, - searchState, - searchResults, - meta, - searchForFacetValuesResults - ) { - const { attribute, searchable, indexContextValue, facetOrdering } = props; - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const canRefine = - Boolean(results) && Boolean(results.getFacetByName(attribute)); - - const isFromSearch = Boolean( - searchForFacetValuesResults && - searchForFacetValuesResults[attribute] && - searchForFacetValuesResults.query !== '' - ); - - // Search For Facet Values is not available with derived helper (used for multi index search) - if (searchable && indexContextValue) { - throw new Error( - 'react-instantsearch: searching in *List is not available when used inside a' + - ' multi index context' - ); - } - - if (!canRefine) { - return { - items: [], - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - isFromSearch, - searchable, - canRefine, - }; - } - - let items; - if (isFromSearch) { - items = searchForFacetValuesResults[attribute].map((v) => ({ - label: v.value, - value: getValue(v.escapedValue, props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - _highlightResult: { label: { value: v.highlighted } }, - count: v.count, - isRefined: v.isRefined, - })); - } else { - items = results - .getFacetValues(attribute, { - sortBy: searchable ? undefined : defaultSortBy, - facetOrdering, - }) - .map((v) => ({ - label: v.name, - value: getValue(v.escapedValue, props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - count: v.count, - isRefined: v.isRefined, - })); - } - - const transformedItems = props.transformItems - ? props.transformItems(items) - : items; - - return { - items: transformedItems.slice(0, getLimit(props)), - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - isFromSearch, - searchable, - canRefine: transformedItems.length > 0, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - searchForFacetValues(props, searchState, nextRefinement) { - return { - facetName: props.attribute, - query: nextRefinement, - maxFacetHits: getLimit(props), - }; - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - const { attribute } = props; - - searchParameters = searchParameters.setQueryParameters({ - maxValuesPerFacet: Math.max( - searchParameters.maxValuesPerFacet || 0, - getLimit(props) - ), - }); - - searchParameters = searchParameters.addDisjunctiveFacet(attribute); - - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - if (currentRefinement !== null) { - searchParameters = searchParameters.addDisjunctiveFacetRefinement( - attribute, - currentRefinement - ); - } - - return searchParameters; - }, - - getMetadata(props, searchState) { - const id = getId(props); - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - return { - id, - index: getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - items: - currentRefinement === null - ? [] - : [ - { - label: `${props.attribute}: ${unescapeFacetValue( - currentRefinement - )}`, - attribute: props.attribute, - value: (nextState) => - refine(props, nextState, '', { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - currentRefinement, - }, - ], - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectNumericMenu.js b/packages/react-instantsearch-core/src/connectors/connectNumericMenu.js deleted file mode 100644 index 4cc353a599..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectNumericMenu.js +++ /dev/null @@ -1,252 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - refineValue, - getCurrentRefinementValue, - getResults, - getIndexId, -} from '../core/indexUtils'; -import { find } from '../core/utils'; - -function stringifyItem(item) { - if (typeof item.start === 'undefined' && typeof item.end === 'undefined') { - return ''; - } - const start = typeof item.start !== 'undefined' ? item.start : ''; - const end = typeof item.end !== 'undefined' ? item.end : ''; - return `${start}:${end}`; -} - -function parseItem(value) { - if (value.length === 0) { - return { start: null, end: null }; - } - const [startStr, endStr] = value.split(':'); - return { - start: startStr.length > 0 ? parseFloat(startStr) : null, - end: endStr.length > 0 ? parseFloat(endStr) : null, - }; -} - -const namespace = 'multiRange'; - -function getId(props) { - return props.attribute; -} - -function getCurrentRefinement(props, searchState, context) { - return getCurrentRefinementValue( - props, - searchState, - context, - `${namespace}.${getId(props)}`, - '', - (currentRefinement) => { - if (currentRefinement === '') { - return ''; - } - return currentRefinement; - } - ); -} - -function isRefinementsRangeIncludesInsideItemRange(stats, start, end) { - return ( - (stats.min >= start && stats.min <= end) || - (stats.max >= start && stats.max <= end) - ); -} - -function isItemRangeIncludedInsideRefinementsRange(stats, start, end) { - return ( - (start >= stats.min && start <= stats.max) || - (end >= stats.min && end <= stats.max) - ); -} - -function itemHasRefinement(attribute, results, value) { - const stats = results.getFacetByName(attribute) - ? results.getFacetStats(attribute) - : null; - const range = value.split(':'); - const start = - Number(range[0]) === 0 || value === '' - ? Number.NEGATIVE_INFINITY - : Number(range[0]); - const end = - Number(range[1]) === 0 || value === '' - ? Number.POSITIVE_INFINITY - : Number(range[1]); - return !( - Boolean(stats) && - (isRefinementsRangeIncludesInsideItemRange(stats, start, end) || - isItemRangeIncludedInsideRefinementsRange(stats, start, end)) - ); -} - -function refine(props, searchState, nextRefinement, context) { - const nextValue = { [getId(props, searchState)]: nextRefinement }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage, namespace); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, `${namespace}.${getId(props)}`); -} - -/** - * connectNumericMenu connector provides the logic to build a widget that will - * give the user the ability to select a range value for a numeric attribute. - * Ranges are defined statically. - * @name connectNumericMenu - * @requirements The attribute passed to the `attribute` prop must be holding numerical values. - * @kind connector - * @propType {string} attribute - the name of the attribute in the records - * @propType {{label: string, start: number, end: number}[]} items - List of options. With a text label, and upper and lower bounds. - * @propType {string} [defaultRefinement] - the value of the item selected by default, follow the shape of a `string` with a pattern of `'{start}:{end}'`. - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @providedPropType {function} refine - a function to select a range. - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {string} currentRefinement - the refinement currently applied. follow the shape of a `string` with a pattern of `'{start}:{end}'` which corresponds to the current selected item. For instance, when the selected item is `{start: 10, end: 20}`, the searchState of the widget is `'10:20'`. When `start` isn't defined, the searchState of the widget is `':{end}'`, and the same way around when `end` isn't defined. However, when neither `start` nor `end` are defined, the searchState is an empty string. - * @providedPropType {array.<{isRefined: boolean, label: string, value: string, isRefined: boolean, noRefinement: boolean}>} items - the list of ranges the NumericMenu can display. - */ -export default createConnector({ - displayName: 'AlgoliaNumericMenu', - $$type: 'ais.numericMenu', - - propTypes: { - id: PropTypes.string, - attribute: PropTypes.string.isRequired, - items: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node, - start: PropTypes.number, - end: PropTypes.number, - }) - ).isRequired, - transformItems: PropTypes.func, - }, - - getProvidedProps(props, searchState, searchResults) { - const attribute = props.attribute; - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const items = props.items.map((item) => { - const value = stringifyItem(item); - return { - label: item.label, - value, - isRefined: value === currentRefinement, - noRefinement: results - ? itemHasRefinement(getId(props), results, value) - : false, - }; - }); - - const stats = - results && results.getFacetByName(attribute) - ? results.getFacetStats(attribute) - : null; - const refinedItem = find(items, (item) => item.isRefined === true); - if (!items.some((item) => item.value === '')) { - items.push({ - value: '', - isRefined: refinedItem === undefined, - noRefinement: !stats, - label: 'All', - }); - } - - const transformedItems = props.transformItems - ? props.transformItems(items) - : items; - - return { - items: transformedItems, - currentRefinement, - canRefine: - transformedItems.length > 0 && - transformedItems.some((item) => item.noRefinement === false), - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - const { attribute } = props; - const { start, end } = parseItem( - getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ); - searchParameters = searchParameters.addDisjunctiveFacet(attribute); - - if (typeof start === 'number') { - searchParameters = searchParameters.addNumericRefinement( - attribute, - '>=', - start - ); - } - if (typeof end === 'number') { - searchParameters = searchParameters.addNumericRefinement( - attribute, - '<=', - end - ); - } - return searchParameters; - }, - - getMetadata(props, searchState) { - const id = getId(props); - const value = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const items = []; - const index = getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - if (value !== '') { - const { label } = find( - props.items, - (item) => stringifyItem(item) === value - ); - items.push({ - label: `${props.attribute}: ${label}`, - attribute: props.attribute, - currentRefinement: label, - value: (nextState) => - refine(props, nextState, '', { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - }); - } - return { id, index, items }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectPagination.js b/packages/react-instantsearch-core/src/connectors/connectPagination.js deleted file mode 100644 index 0879adb0a5..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectPagination.js +++ /dev/null @@ -1,105 +0,0 @@ -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - refineValue, - getCurrentRefinementValue, - getResults, -} from '../core/indexUtils'; - -function getId() { - return 'page'; -} - -function getCurrentRefinement(props, searchState, context) { - const id = getId(); - const page = 1; - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - page - ); - - if (typeof currentRefinement === 'string') { - return parseInt(currentRefinement, 10); - } - return currentRefinement; -} - -function refine(props, searchState, nextPage, context) { - const id = getId(); - const nextValue = { [id]: nextPage }; - const resetPage = false; - return refineValue(searchState, nextValue, context, resetPage); -} - -/** - * connectPagination connector provides the logic to build a widget that will - * let the user displays hits corresponding to a certain page. - * @name connectPagination - * @kind connector - * @propType {boolean} [showFirst=true] - Display the first page link. - * @propType {boolean} [showLast=false] - Display the last page link. - * @propType {boolean} [showPrevious=true] - Display the previous page link. - * @propType {boolean} [showNext=true] - Display the next page link. - * @propType {number} [padding=3] - How many page links to display around the current page. - * @propType {number} [totalPages=Infinity] - Maximum number of pages to display. - * @providedPropType {function} refine - a function to remove a single filter - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {number} nbPages - the total of existing pages - * @providedPropType {number} currentRefinement - the page refinement currently applied - */ -export default createConnector({ - displayName: 'AlgoliaPagination', - $$type: 'ais.pagination', - - getProvidedProps(props, searchState, searchResults) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if (!results) { - return null; - } - - const nbPages = results.nbPages; - return { - nbPages, - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - canRefine: nbPages > 1, - }; - }, - - refine(props, searchState, nextPage) { - return refine(props, searchState, nextPage, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUpValue( - searchState, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - getId() - ); - }, - - getSearchParameters(searchParameters, props, searchState) { - return searchParameters.setPage( - getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - 1 - ); - }, - - getMetadata() { - return { id: getId() }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectPoweredBy.js b/packages/react-instantsearch-core/src/connectors/connectPoweredBy.js deleted file mode 100644 index bb5d4e39e4..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectPoweredBy.js +++ /dev/null @@ -1,31 +0,0 @@ -import createConnector from '../core/createConnector'; - -/** - * connectPoweredBy connector provides the logic to build a widget that - * will display a link to algolia. - * @name connectPoweredBy - * @kind connector - * @providedPropType {string} url - the url to redirect to algolia - */ -export default createConnector({ - displayName: 'AlgoliaPoweredBy', - $$type: 'ais.poweredBy', - - getProvidedProps() { - const hostname = - typeof window === 'undefined' || typeof window.location === 'undefined' - ? '' - : window.location.hostname; - - const url = - 'https://www.algolia.com/?' + - 'utm_source=react-instantsearch&' + - 'utm_medium=website&' + - `utm_content=${hostname}&` + - 'utm_campaign=poweredby'; - - return { - url, - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectQueryRules.ts b/packages/react-instantsearch-core/src/connectors/connectQueryRules.ts deleted file mode 100644 index 4e8ec955cc..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectQueryRules.ts +++ /dev/null @@ -1,201 +0,0 @@ -import createConnector from '../core/createConnector'; -// @ts-ignore -import { getResults, getIndexId, hasMultipleIndices } from '../core/indexUtils'; - -import type { ConnectedProps } from '../core/createConnector'; - -type SearchState = any; - -type SearchParameters = any; - -export type CustomUserData = { - [key: string]: any; -}; - -type TrackedFilterRefinement = string | number | boolean; - -export type QueryRulesProps = { - trackedFilters: { - [facetName: string]: ( - facetValues: TrackedFilterRefinement[] - ) => TrackedFilterRefinement[]; - }; - transformRuleContexts: (ruleContexts: string[]) => string[]; - transformItems: (items: TItem[]) => TItem[]; -}; - -// A context rule must consist only of alphanumeric characters, hyphens, and underscores. -// See https://www.algolia.com/doc/guides/managing-results/refine-results/merchandising-and-promoting/in-depth/implementing-query-rules/#context -function escapeRuleContext(ruleName: string): string { - return ruleName.replace(/[^a-z0-9-_]+/gi, '_'); -} - -function getWidgetRefinements( - attribute: string, - widgetKey: string, - searchState: SearchState -): TrackedFilterRefinement[] { - const widgetState = searchState[widgetKey]; - - switch (widgetKey) { - case 'range': - return Object.keys(widgetState[attribute]).map( - (rangeKey) => widgetState[attribute][rangeKey] - ); - - case 'refinementList': - return widgetState[attribute]; - - case 'hierarchicalMenu': - return [widgetState[attribute]]; - - case 'menu': - return [widgetState[attribute]]; - - case 'multiRange': - return widgetState[attribute].split(':'); - - case 'toggle': - return [widgetState[attribute]]; - - default: - return []; - } -} - -function getRefinements( - attribute: string, - searchState: SearchState = {} -): TrackedFilterRefinement[] { - const refinements = Object.keys(searchState) - .filter( - (widgetKey) => - searchState[widgetKey] !== undefined && - searchState[widgetKey][attribute] !== undefined - ) - .map((widgetKey) => getWidgetRefinements(attribute, widgetKey, searchState)) - .reduce((acc, current) => acc.concat(current), []); // flatten the refinements - - return refinements; -} - -function getRuleContextsFromTrackedFilters({ - searchState, - trackedFilters, -}: { - searchState: SearchState; - trackedFilters: QueryRulesProps['trackedFilters']; -}) { - const ruleContexts = Object.keys(trackedFilters).reduce( - (facets, facetName) => { - const facetRefinements: TrackedFilterRefinement[] = getRefinements( - facetName, - searchState - ); - - const getTrackedFacetValues = trackedFilters[facetName]; - const trackedFacetValues = getTrackedFacetValues(facetRefinements); - - return [ - ...facets, - ...facetRefinements - .filter((facetRefinement) => - trackedFacetValues.includes(facetRefinement) - ) - .map((facetValue) => - escapeRuleContext(`ais-${facetName}-${facetValue}`) - ), - ]; - }, - [] - ); - - return ruleContexts; -} - -const defaultProps: QueryRulesProps = { - transformItems: (items) => items, - transformRuleContexts: (ruleContexts) => ruleContexts, - trackedFilters: {}, -}; - -export default createConnector({ - displayName: 'AlgoliaQueryRules', - $$type: 'ais.queryRules', - - defaultProps, - - getProvidedProps( - props: ConnectedProps, - _1: any, - searchResults: any - ) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if (results === null) { - return { - items: [], - canRefine: false, - }; - } - - const { userData = [] } = results; - const { transformItems } = props; - const transformedItems = transformItems(userData); - - return { - items: transformedItems, - canRefine: transformedItems.length > 0, - }; - }, - - getSearchParameters( - searchParameters: SearchParameters, - props: ConnectedProps, - searchState: SearchState - ) { - if (Object.keys(props.trackedFilters).length === 0) { - return searchParameters; - } - - const indexSearchState = - hasMultipleIndices({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) && searchState.indices - ? searchState.indices[ - getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ] - : searchState; - - const newRuleContexts = getRuleContextsFromTrackedFilters({ - searchState: indexSearchState, - trackedFilters: props.trackedFilters, - }); - - const initialRuleContexts = searchParameters.ruleContexts || []; - const nextRuleContexts = [...initialRuleContexts, ...newRuleContexts]; - - if (process.env.NODE_ENV === 'development') { - if (nextRuleContexts.length > 10) { - // eslint-disable-next-line no-console - console.warn( - `The maximum number of \`ruleContexts\` is 10. They have been sliced to that limit. -Consider using \`transformRuleContexts\` to minimize the number of rules sent to Algolia.` - ); - } - } - - const ruleContexts = props - .transformRuleContexts(nextRuleContexts) - .slice(0, 10); - - return searchParameters.setQueryParameter('ruleContexts', ruleContexts); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectRange.js b/packages/react-instantsearch-core/src/connectors/connectRange.js deleted file mode 100644 index c82223d54e..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectRange.js +++ /dev/null @@ -1,343 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - getIndexId, - refineValue, - getCurrentRefinementValue, - getResults, -} from '../core/indexUtils'; - -/** - * connectRange connector provides the logic to create connected - * components that will give the ability for a user to refine results using - * a numeric range. - * @name connectRange - * @kind connector - * @requirements The attribute passed to the `attribute` prop must be present in “attributes for faceting” - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * The values inside the attribute must be JavaScript numbers (not strings). - * @propType {string} attribute - Name of the attribute for faceting - * @propType {{min?: number, max?: number}} [defaultRefinement] - Default searchState of the widget containing the start and the end of the range. - * @propType {number} [min] - Minimum value. When this isn't set, the minimum value will be automatically computed by Algolia using the data in the index. - * @propType {number} [max] - Maximum value. When this isn't set, the maximum value will be automatically computed by Algolia using the data in the index. - * @propType {number} [precision=0] - Number of digits after decimal point to use. - * @providedPropType {function} refine - a function to select a range. - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {string} currentRefinement - the refinement currently applied - * @providedPropType {number} min - the minimum value available. - * @providedPropType {number} max - the maximum value available. - * @providedPropType {number} precision - Number of digits after decimal point to use. - */ - -function getId(props) { - return props.attribute; -} - -const namespace = 'range'; - -function getCurrentRange(boundaries, stats, precision) { - const pow = Math.pow(10, precision); - - let min; - if (typeof boundaries.min === 'number' && isFinite(boundaries.min)) { - min = boundaries.min; - } else if (typeof stats.min === 'number' && isFinite(stats.min)) { - min = stats.min; - } else { - min = undefined; - } - - let max; - if (typeof boundaries.max === 'number' && isFinite(boundaries.max)) { - max = boundaries.max; - } else if (typeof stats.max === 'number' && isFinite(stats.max)) { - max = stats.max; - } else { - max = undefined; - } - - return { - min: min !== undefined ? Math.floor(min * pow) / pow : min, - max: max !== undefined ? Math.ceil(max * pow) / pow : max, - }; -} - -function getCurrentRefinement(props, searchState, currentRange, context) { - const { min, max } = getCurrentRefinementValue( - props, - searchState, - context, - `${namespace}.${getId(props)}`, - {} - ); - - const isFloatPrecision = Boolean(props.precision); - - let nextMin = min; - if (typeof nextMin === 'string') { - nextMin = isFloatPrecision ? parseFloat(nextMin) : parseInt(nextMin, 10); - } - - let nextMax = max; - if (typeof nextMax === 'string') { - nextMax = isFloatPrecision ? parseFloat(nextMax) : parseInt(nextMax, 10); - } - - const refinement = { - min: nextMin, - max: nextMax, - }; - - const hasMinBound = props.min !== undefined; - const hasMaxBound = props.max !== undefined; - - const hasMinRefinment = refinement.min !== undefined; - const hasMaxRefinment = refinement.max !== undefined; - - if (hasMinBound && hasMinRefinment && refinement.min < currentRange.min) { - throw Error("You can't provide min value lower than range."); - } - - if (hasMaxBound && hasMaxRefinment && refinement.max > currentRange.max) { - throw Error("You can't provide max value greater than range."); - } - - if (hasMinBound && !hasMinRefinment) { - refinement.min = currentRange.min; - } - - if (hasMaxBound && !hasMaxRefinment) { - refinement.max = currentRange.max; - } - - return refinement; -} - -function getCurrentRefinementWithRange(refinement, range) { - return { - min: refinement.min !== undefined ? refinement.min : range.min, - max: refinement.max !== undefined ? refinement.max : range.max, - }; -} - -function nextValueForRefinement(hasBound, isReset, range, value) { - let next; - if (!hasBound && range === value) { - next = undefined; - } else if (hasBound && isReset) { - next = range; - } else { - next = value; - } - - return next; -} - -function refine(props, searchState, nextRefinement, currentRange, context) { - const { min: nextMin, max: nextMax } = nextRefinement; - const { min: currentMinRange, max: currentMaxRange } = currentRange; - - const isMinReset = nextMin === undefined || nextMin === ''; - const isMaxReset = nextMax === undefined || nextMax === ''; - - const nextMinAsNumber = !isMinReset ? parseFloat(nextMin) : undefined; - const nextMaxAsNumber = !isMaxReset ? parseFloat(nextMax) : undefined; - - const isNextMinValid = isMinReset || isFinite(nextMinAsNumber); - const isNextMaxValid = isMaxReset || isFinite(nextMaxAsNumber); - - if (!isNextMinValid || !isNextMaxValid) { - throw Error("You can't provide non finite values to the range connector."); - } - - if (nextMinAsNumber < currentMinRange) { - throw Error("You can't provide min value lower than range."); - } - - if (nextMaxAsNumber > currentMaxRange) { - throw Error("You can't provide max value greater than range."); - } - - const id = getId(props); - const resetPage = true; - const nextValue = { - [id]: { - min: nextValueForRefinement( - props.min !== undefined, - isMinReset, - currentMinRange, - nextMinAsNumber - ), - max: nextValueForRefinement( - props.max !== undefined, - isMaxReset, - currentMaxRange, - nextMaxAsNumber - ), - }, - }; - - return refineValue(searchState, nextValue, context, resetPage, namespace); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, `${namespace}.${getId(props)}`); -} - -export default createConnector({ - displayName: 'AlgoliaRange', - $$type: 'ais.range', - - propTypes: { - id: PropTypes.string, - attribute: PropTypes.string.isRequired, - defaultRefinement: PropTypes.shape({ - min: PropTypes.number, - max: PropTypes.number, - }), - min: PropTypes.number, - max: PropTypes.number, - precision: PropTypes.number, - header: PropTypes.node, - footer: PropTypes.node, - }, - - defaultProps: { - precision: 0, - }, - - getProvidedProps(props, searchState, searchResults) { - const { attribute, precision, min: minBound, max: maxBound } = props; - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const hasFacet = results && results.getFacetByName(attribute); - const stats = hasFacet ? results.getFacetStats(attribute) || {} : {}; - const facetValues = hasFacet ? results.getFacetValues(attribute) : []; - - const count = facetValues.map((v) => ({ - value: v.name, - count: v.count, - })); - - const { min: rangeMin, max: rangeMax } = getCurrentRange( - { min: minBound, max: maxBound }, - stats, - precision - ); - - // The searchState is not always in sync with the helper state. For example - // when we set boundaries on the first render the searchState don't have - // the correct refinement. If this behavior change in the upcoming version - // we could store the range inside the searchState instead of rely on `this`. - this._currentRange = { - min: rangeMin, - max: rangeMax, - }; - - const currentRefinement = getCurrentRefinement( - props, - searchState, - this._currentRange, - { ais: props.contextValue, multiIndexContext: props.indexContextValue } - ); - - return { - min: rangeMin, - max: rangeMax, - canRefine: count.length > 0, - currentRefinement: getCurrentRefinementWithRange( - currentRefinement, - this._currentRange - ), - count, - precision, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, this._currentRange, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(params, props, searchState) { - const { attribute } = props; - const { min, max } = getCurrentRefinement( - props, - searchState, - this._currentRange, - { ais: props.contextValue, multiIndexContext: props.indexContextValue } - ); - - params = params.addDisjunctiveFacet(attribute); - - if (min !== undefined) { - params = params.addNumericRefinement(attribute, '>=', min); - } - - if (max !== undefined) { - params = params.addNumericRefinement(attribute, '<=', max); - } - - return params; - }, - - getMetadata(props, searchState) { - const { min: minRange, max: maxRange } = this._currentRange; - const { min: minValue, max: maxValue } = getCurrentRefinement( - props, - searchState, - this._currentRange, - { ais: props.contextValue, multiIndexContext: props.indexContextValue } - ); - - const items = []; - const hasMin = minValue !== undefined; - const hasMax = maxValue !== undefined; - const shouldDisplayMinLabel = hasMin && minValue !== minRange; - const shouldDisplayMaxLabel = hasMax && maxValue !== maxRange; - - if (shouldDisplayMinLabel || shouldDisplayMaxLabel) { - const fragments = [ - hasMin ? `${minValue} <= ` : '', - props.attribute, - hasMax ? ` <= ${maxValue}` : '', - ]; - - items.push({ - label: fragments.join(''), - attribute: props.attribute, - value: (nextState) => - refine(props, nextState, {}, this._currentRange, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - currentRefinement: getCurrentRefinementWithRange( - { min: minValue, max: maxValue }, - { min: minRange, max: maxRange } - ), - }); - } - - return { - id: getId(props), - index: getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - items, - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectRefinementList.js b/packages/react-instantsearch-core/src/connectors/connectRefinementList.js deleted file mode 100644 index eb4efc27e5..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectRefinementList.js +++ /dev/null @@ -1,293 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - getIndexId, - refineValue, - getCurrentRefinementValue, - getResults, -} from '../core/indexUtils'; -import { unescapeFacetValue } from '../core/utils'; - -const namespace = 'refinementList'; - -function getId(props) { - return props.attribute; -} - -function getCurrentRefinement(props, searchState, context) { - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - `${namespace}.${getId(props)}`, - [] - ); - - if (typeof currentRefinement !== 'string') { - return currentRefinement; - } - - if (currentRefinement) { - return [currentRefinement]; - } - - return []; -} - -function getValue(value, props, searchState, context) { - const currentRefinement = getCurrentRefinement(props, searchState, context); - const isAnewValue = currentRefinement.indexOf(value) === -1; - const nextRefinement = isAnewValue - ? currentRefinement.concat([value]) // cannot use .push(), it mutates - : currentRefinement.filter((selectedValue) => selectedValue !== value); // cannot use .splice(), it mutates - return nextRefinement; -} - -function getLimit({ showMore, limit, showMoreLimit }) { - return showMore ? showMoreLimit : limit; -} - -function refine(props, searchState, nextRefinement, context) { - const id = getId(props); - // Setting the value to an empty string ensures that it is persisted in - // the URL as an empty value. - // This is necessary in the case where `defaultRefinement` contains one - // item and we try to deselect it. `nextSelected` would be an empty array, - // which would not be persisted to the URL. - // {foo: ['bar']} => "foo[0]=bar" - // {foo: []} => "" - const nextValue = { [id]: nextRefinement.length > 0 ? nextRefinement : '' }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage, namespace); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, `${namespace}.${getId(props)}`); -} -/** - * connectRefinementList connector provides the logic to build a widget that will - * give the user the ability to choose multiple values for a specific facet. - * @name connectRefinementList - * @kind connector - * @requirements The attribute passed to the `attribute` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * @propType {string} attribute - the name of the attribute in the record - * @propType {boolean} [searchable=false] - allow search inside values - * @propType {string} [operator=or] - How to apply the refinements. Possible values: 'or' or 'and'. - * @propType {boolean} [showMore=false] - true if the component should display a button that will expand the number of items - * @propType {number} [limit=10] - the minimum number of displayed items - * @propType {number} [showMoreLimit=20] - the maximun number of displayed items. Only used when showMore is set to `true` - * @propType {string[]} defaultRefinement - the values of the items selected by default. The searchState of this widget takes the form of a list of `string`s, which correspond to the values of all selected refinements. However, when there are no refinements selected, the value of the searchState is an empty string. - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @providedPropType {function} refine - a function to toggle a refinement - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {string[]} currentRefinement - the refinement currently applied - * @providedPropType {array.<{count: number, isRefined: boolean, label: string, value: string}>} items - the list of items the RefinementList can display. - * @providedPropType {function} searchForItems - a function to toggle a search inside items values - * @providedPropType {boolean} isFromSearch - a boolean that says if the `items` props contains facet values from the global search or from the search inside items. - * @providedPropType {boolean} canRefine - a boolean that says whether you can refine - */ - -const sortBy = ['isRefined', 'count:desc', 'name:asc']; -export default createConnector({ - displayName: 'AlgoliaRefinementList', - $$type: 'ais.refinementList', - - propTypes: { - id: PropTypes.string, - attribute: PropTypes.string.isRequired, - operator: PropTypes.oneOf(['and', 'or']), - showMore: PropTypes.bool, - limit: PropTypes.number, - showMoreLimit: PropTypes.number, - defaultRefinement: PropTypes.arrayOf( - PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - ), - searchable: PropTypes.bool, - transformItems: PropTypes.func, - facetOrdering: PropTypes.bool, - }, - - defaultProps: { - operator: 'or', - showMore: false, - limit: 10, - showMoreLimit: 20, - facetOrdering: true, - }, - - getProvidedProps( - props, - searchState, - searchResults, - metadata, - searchForFacetValuesResults - ) { - const { attribute, searchable, indexContextValue, facetOrdering } = props; - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const canRefine = - Boolean(results) && Boolean(results.getFacetByName(attribute)); - - const isFromSearch = Boolean( - searchForFacetValuesResults && - searchForFacetValuesResults[attribute] && - searchForFacetValuesResults.query !== '' - ); - - // Search For Facet Values is not available with derived helper (used for multi index search) - if (searchable && indexContextValue) { - throw new Error( - 'react-instantsearch: searching in *List is not available when used inside a' + - ' multi index context' - ); - } - - if (!canRefine) { - return { - items: [], - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - canRefine, - isFromSearch, - searchable, - }; - } - - const items = isFromSearch - ? searchForFacetValuesResults[attribute].map((v) => ({ - label: v.value, - value: getValue(v.escapedValue, props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - _highlightResult: { label: { value: v.highlighted } }, - count: v.count, - isRefined: v.isRefined, - })) - : results - .getFacetValues(attribute, { sortBy, facetOrdering }) - .map((v) => ({ - label: v.name, - value: getValue(v.escapedValue, props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - count: v.count, - isRefined: v.isRefined, - })); - - const transformedItems = props.transformItems - ? props.transformItems(items) - : items; - - return { - items: transformedItems.slice(0, getLimit(props)), - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - isFromSearch, - searchable, - canRefine: transformedItems.length > 0, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - searchForFacetValues(props, searchState, nextRefinement) { - return { - facetName: props.attribute, - query: nextRefinement, - maxFacetHits: getLimit(props), - }; - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - const { attribute, operator } = props; - - const addKey = operator === 'and' ? 'addFacet' : 'addDisjunctiveFacet'; - const addRefinementKey = `${addKey}Refinement`; - - searchParameters = searchParameters.setQueryParameters({ - maxValuesPerFacet: Math.max( - searchParameters.maxValuesPerFacet || 0, - getLimit(props) - ), - }); - - searchParameters = searchParameters[addKey](attribute); - - return getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }).reduce( - (res, val) => res[addRefinementKey](attribute, val), - searchParameters - ); - }, - - getMetadata(props, searchState) { - const id = getId(props); - const context = { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }; - return { - id, - index: getIndexId(context), - items: - getCurrentRefinement(props, searchState, context).length > 0 - ? [ - { - attribute: props.attribute, - label: `${props.attribute}: `, - currentRefinement: getCurrentRefinement( - props, - searchState, - context - ), - value: (nextState) => refine(props, nextState, [], context), - items: getCurrentRefinement(props, searchState, context).map( - (item) => ({ - label: unescapeFacetValue(`${item}`), - value: (nextState) => { - const nextSelectedItems = getCurrentRefinement( - props, - nextState, - context - ).filter((other) => other !== item); - return refine( - props, - searchState, - nextSelectedItems, - context - ); - }, - }) - ), - }, - ] - : [], - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectRelevantSort.ts b/packages/react-instantsearch-core/src/connectors/connectRelevantSort.ts deleted file mode 100644 index 1b2aa2b308..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectRelevantSort.ts +++ /dev/null @@ -1,79 +0,0 @@ -import createConnector from '../core/createConnector'; -import { - refineValue, - getCurrentRefinementValue, - getResults, - // @ts-ignore -} from '../core/indexUtils'; - -import type { ConnectedProps } from '../core/createConnector'; - -function getId() { - return 'relevancyStrictness'; -} - -function getCurrentRefinement( - props: ConnectedProps, - searchState: any, - context: any -) { - const id = getId(); - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id - ); - - return currentRefinement; -} - -export default createConnector({ - displayName: 'AlgoliaRelevantSort', - $$type: 'ais.relevantSort', - - getProvidedProps(props, _searchState, searchResults) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if (!results) { - return { - isVirtualReplica: false, - isRelevantSorted: false, - }; - } - - return { - isVirtualReplica: results.appliedRelevancyStrictness !== undefined, - isRelevantSorted: - results.appliedRelevancyStrictness !== undefined && - results.appliedRelevancyStrictness > 0, - }; - }, - - getSearchParameters(searchParameters, props, searchState) { - return searchParameters.setQueryParameter( - 'relevancyStrictness', - getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ); - }, - - refine(props, searchState, nextRefinement) { - const nextValue = { - [getId()]: nextRefinement, - }; - const resetPage = true; - - return refineValue( - searchState, - nextValue, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - resetPage - ); - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectScrollTo.js b/packages/react-instantsearch-core/src/connectors/connectScrollTo.js deleted file mode 100644 index e6cc357c7f..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectScrollTo.js +++ /dev/null @@ -1,84 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - getCurrentRefinementValue, - hasMultipleIndices, - getIndexId, -} from '../core/indexUtils'; -import { shallowEqual, omit } from '../core/utils'; - -/** - * connectScrollTo connector provides the logic to build a widget that will - * let the page scroll to a certain point. - * @name connectScrollTo - * @kind connector - * @propType {string} [scrollOn="page"] - Widget searchState key on which to listen for changes, default to the pagination widget. - * @providedPropType {any} value - the current refinement applied to the widget listened by scrollTo - * @providedPropType {boolean} hasNotChanged - indicates whether the refinement came from the scrollOn argument (for instance page by default) - */ -export default createConnector({ - displayName: 'AlgoliaScrollTo', - $$type: 'ais.scrollTo', - - propTypes: { - scrollOn: PropTypes.string, - }, - - defaultProps: { - scrollOn: 'page', - }, - - getProvidedProps(props, searchState) { - const id = props.scrollOn; - const value = getCurrentRefinementValue( - props, - searchState, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - id, - null - ); - - if (!this._prevSearchState) { - this._prevSearchState = {}; - } - - // Get the subpart of the state that interest us - if ( - hasMultipleIndices({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ) { - searchState = searchState.indices - ? searchState.indices[ - getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ] - : {}; - } - - // if there is a change in the app that has been triggered by another element - // than "props.scrollOn (id) or the Configure widget, we need to keep track of - // the search state to know if there's a change in the app that was not triggered - // by the props.scrollOn (id) or the Configure widget. This is useful when - // using ScrollTo in combination of Pagination. As pagination can be change - // by every widget, we want to scroll only if it cames from the pagination - // widget itself. We also remove the configure key from the search state to - // do this comparison because for now configure values are not present in the - // search state before a first refinement has been made and will false the results. - // See: https://github.com/algolia/react-instantsearch/issues/164 - const cleanedSearchState = omit(searchState, ['configure', id]); - - const hasNotChanged = shallowEqual( - this._prevSearchState, - cleanedSearchState - ); - - this._prevSearchState = cleanedSearchState; - - return { value, hasNotChanged }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectSearchBox.js b/packages/react-instantsearch-core/src/connectors/connectSearchBox.js deleted file mode 100644 index 2f7c9cbec7..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectSearchBox.js +++ /dev/null @@ -1,121 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - refineValue, - getCurrentRefinementValue, - getIndexId, -} from '../core/indexUtils'; - -function getId() { - return 'query'; -} - -function getCurrentRefinement(props, searchState, context) { - const id = getId(props); - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - '' - ); - - if (currentRefinement) { - return currentRefinement; - } - return ''; -} - -function refine(props, searchState, nextRefinement, context) { - const id = getId(); - const nextValue = { [id]: nextRefinement }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, getId()); -} - -/** - * connectSearchBox connector provides the logic to build a widget that will - * let the user search for a query - * @name connectSearchBox - * @kind connector - * @propType {string} [defaultRefinement] - Provide a default value for the query - * @providedPropType {function} refine - a function to change the current query - * @providedPropType {string} currentRefinement - the current query used - * @providedPropType {boolean} isSearchStalled - a flag that indicates if InstantSearch has detected that searches are stalled - */ -export default createConnector({ - displayName: 'AlgoliaSearchBox', - $$type: 'ais.searchBox', - - propTypes: { - defaultRefinement: PropTypes.string, - }, - - getProvidedProps(props, searchState, searchResults) { - return { - currentRefinement: getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - isSearchStalled: searchResults.isSearchStalled, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - return searchParameters.setQuery( - getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }) - ); - }, - - getMetadata(props, searchState) { - const id = getId(props); - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - return { - id, - index: getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - items: - currentRefinement === null - ? [] - : [ - { - label: `${id}: ${currentRefinement}`, - value: (nextState) => - refine(props, nextState, '', { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - currentRefinement, - }, - ], - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectSortBy.js b/packages/react-instantsearch-core/src/connectors/connectSortBy.js deleted file mode 100644 index 19f24603c6..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectSortBy.js +++ /dev/null @@ -1,107 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - refineValue, - getCurrentRefinementValue, -} from '../core/indexUtils'; - -function getId() { - return 'sortBy'; -} - -function getCurrentRefinement(props, searchState, context) { - const id = getId(props); - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - null - ); - - if (currentRefinement) { - return currentRefinement; - } - return null; -} - -/** - * The connectSortBy connector provides the logic to build a widget that will - * display a list of indices. This allows a user to change how the hits are being sorted. - * @name connectSortBy - * @requirements Algolia handles sorting by creating replica indices. [Read more about sorting](https://www.algolia.com/doc/guides/relevance/sorting/) on - * the Algolia website. - * @kind connector - * @propType {string} defaultRefinement - The default selected index. - * @propType {{value: string, label: string}[]} items - The list of indexes to search in. - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @providedPropType {function} refine - a function to remove a single filter - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - * @providedPropType {string[]} currentRefinement - the refinement currently applied - * @providedPropType {array.<{isRefined: boolean, label?: string, value: string}>} items - the list of items the HitsPerPage can display. If no label provided, the value will be displayed. - */ -export default createConnector({ - displayName: 'AlgoliaSortBy', - $$type: 'ais.sortBy', - - propTypes: { - defaultRefinement: PropTypes.string, - items: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string, - value: PropTypes.string.isRequired, - }) - ).isRequired, - transformItems: PropTypes.func, - }, - - getProvidedProps(props, searchState) { - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const items = props.items.map((item) => - item.value === currentRefinement - ? { ...item, isRefined: true } - : { ...item, isRefined: false } - ); - return { - items: props.transformItems ? props.transformItems(items) : items, - currentRefinement, - }; - }, - - refine(props, searchState, nextRefinement) { - const id = getId(); - const nextValue = { [id]: nextRefinement }; - const resetPage = true; - return refineValue( - searchState, - nextValue, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - resetPage - ); - }, - - cleanUp(props, searchState) { - return cleanUpValue( - searchState, - { ais: props.contextValue, multiIndexContext: props.indexContextValue }, - getId() - ); - }, - - getSearchParameters(searchParameters, props, searchState) { - const selectedIndex = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - return searchParameters.setIndex(selectedIndex); - }, - - getMetadata() { - return { id: getId() }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectStateResults.js b/packages/react-instantsearch-core/src/connectors/connectStateResults.js deleted file mode 100644 index 63e7f80a14..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectStateResults.js +++ /dev/null @@ -1,74 +0,0 @@ -import createConnector from '../core/createConnector'; -import { getResults } from '../core/indexUtils'; - -/** - * The `connectStateResults` connector provides a way to access the `searchState` and the `searchResults` - * of InstantSearch. - * For instance this connector allows you to create results/noResults or query/noQuery pages. - * @name connectStateResults - * @kind connector - * @providedPropType {object} searchState - The search state of the instant search component.

See: [Search state structure](https://community.algolia.com/react-instantsearch/guide/Search_state.html) - * @providedPropType {object} searchResults - The search results.

In case of multiple indices: if used under ``, results will be those of the corresponding index otherwise it'll be those of the root index See: [Search results structure](https://community.algolia.com/algoliasearch-helper-js/reference.html#searchresults) - * @providedPropType {object} allSearchResults - In case of multiple indices you can retrieve all the results - * @providedPropType {string} error - If the search failed, the error will be logged here. - * @providedPropType {boolean} searching - If there is a search in progress. - * @providedPropType {boolean} isSearchStalled - Flag that indicates if React InstantSearch has detected that searches are stalled. - * @providedPropType {boolean} searchingForFacetValues - If there is a search in a list in progress. - * @providedPropType {object} props - component props. - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, SearchBox, Hits, connectStateResults } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const Content = connectStateResults(({ searchState, searchResults }) => { - * const hasResults = searchResults && searchResults.nbHits !== 0; - * - * return ( - *
- * - * - *
- * ); - * }); - * - * const App = () => ( - * - * - * - * - * ); - */ -export default createConnector({ - displayName: 'AlgoliaStateResults', - $$type: 'ais.stateResults', - - getProvidedProps(props, searchState, searchResults) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - return { - searchState, - searchResults: results, - allSearchResults: searchResults.results, - searching: searchResults.searching, - isSearchStalled: searchResults.isSearchStalled, - error: searchResults.error, - searchingForFacetValues: searchResults.searchingForFacetValues, - props, - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectStats.ts b/packages/react-instantsearch-core/src/connectors/connectStats.ts deleted file mode 100644 index bc66ec36ce..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectStats.ts +++ /dev/null @@ -1,38 +0,0 @@ -import createConnector from '../core/createConnector'; -// @ts-ignore -import { getResults } from '../core/indexUtils'; - -/** - * connectStats connector provides the logic to build a widget that will - * displays algolia search statistics (hits number and processing time). - * @name connectStats - * @kind connector - * @providedPropType {number} nbHits - number of hits returned by Algolia. - * @providedPropType {number} nbSortedHits - number of sorted hits returned by Algolia. - * @providedPropType {number} processingTimeMS - the time in ms took by Algolia to search for results. - */ -export default createConnector({ - displayName: 'AlgoliaStats', - $$type: 'ais.stats', - - getProvidedProps(props, _searchState, searchResults) { - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if (!results) { - return null; - } - - return { - areHitsSorted: - results.appliedRelevancyStrictness !== undefined && - results.appliedRelevancyStrictness > 0 && - results.nbHits !== results.nbSortedHits, - nbHits: results.nbHits, - nbSortedHits: results.nbSortedHits, - processingTimeMS: results.processingTimeMS, - }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectToggleRefinement.js b/packages/react-instantsearch-core/src/connectors/connectToggleRefinement.js deleted file mode 100644 index 1b63fbcf98..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectToggleRefinement.js +++ /dev/null @@ -1,190 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - getIndexId, - getResults, - refineValue, - getCurrentRefinementValue, -} from '../core/indexUtils'; -import { find } from '../core/utils'; - -function getId(props) { - return props.attribute; -} - -const namespace = 'toggle'; - -const falsyStrings = ['0', 'false', 'null', 'undefined']; - -function getCurrentRefinement(props, searchState, context) { - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - `${namespace}.${getId(props)}`, - false - ); - - if (falsyStrings.indexOf(currentRefinement) !== -1) { - return false; - } - - return Boolean(currentRefinement); -} - -function refine(props, searchState, nextRefinement, context) { - const id = getId(props); - const nextValue = { [id]: nextRefinement ? nextRefinement : false }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage, namespace); -} - -function cleanUp(props, searchState, context) { - return cleanUpValue(searchState, context, `${namespace}.${getId(props)}`); -} - -/** - * connectToggleRefinement connector provides the logic to build a widget that will - * provides an on/off filtering feature based on an attribute value. - * @name connectToggleRefinement - * @kind connector - * @requirements To use this widget, you'll need an attribute to toggle on. - * - * You can't toggle on null or not-null values. If you want to address this particular use-case you'll need to compute an - * extra boolean attribute saying if the value exists or not. See this [thread](https://discourse.algolia.com/t/how-to-create-a-toggle-for-the-absence-of-a-string-attribute/2460) for more details. - * - * @propType {string} attribute - Name of the attribute on which to apply the `value` refinement. Required when `value` is present. - * @propType {string} label - Label for the toggle. - * @propType {string} value - Value of the refinement to apply on `attribute`. - * @propType {boolean} [defaultRefinement=false] - Default searchState of the widget. Should the toggle be checked by default? - * @providedPropType {boolean} currentRefinement - `true` when the refinement is applied, `false` otherwise - * @providedPropType {object} count - an object that contains the count for `checked` and `unchecked` state - * @providedPropType {function} refine - a function to toggle a refinement - * @providedPropType {function} createURL - a function to generate a URL for the corresponding search state - */ -export default createConnector({ - displayName: 'AlgoliaToggle', - $$type: 'ais.toggle', - - propTypes: { - label: PropTypes.string.isRequired, - attribute: PropTypes.string.isRequired, - value: PropTypes.any.isRequired, - filter: PropTypes.func, - defaultRefinement: PropTypes.bool, - }, - - getProvidedProps(props, searchState, searchResults) { - const { attribute, value } = props; - const results = getResults(searchResults, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const currentRefinement = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - const allFacetValues = - results && results.getFacetByName(attribute) - ? results.getFacetValues(attribute) - : null; - - const facetValue = - // Use null to always be consistent with type of the value - // count: number | null - allFacetValues && allFacetValues.length - ? find(allFacetValues, (item) => item.name === value.toString()) - : null; - - const facetValueCount = facetValue && facetValue.count; - const allFacetValuesCount = - // Use null to always be consistent with type of the value - // count: number | null - allFacetValues && allFacetValues.length - ? allFacetValues.reduce((acc, item) => acc + item.count, 0) - : null; - - const canRefine = currentRefinement - ? allFacetValuesCount !== null && allFacetValuesCount > 0 - : facetValueCount !== null && facetValueCount > 0; - - const count = { - checked: allFacetValuesCount, - unchecked: facetValueCount, - }; - - return { - currentRefinement, - canRefine, - count, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - const { attribute, value, filter } = props; - const checked = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - let nextSearchParameters = searchParameters.addDisjunctiveFacet(attribute); - - if (checked) { - nextSearchParameters = nextSearchParameters.addDisjunctiveFacetRefinement( - attribute, - value - ); - - if (filter) { - nextSearchParameters = filter(nextSearchParameters); - } - } - - return nextSearchParameters; - }, - - getMetadata(props, searchState) { - const id = getId(props); - const checked = getCurrentRefinement(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const items = []; - const index = getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - - if (checked) { - items.push({ - label: props.label, - currentRefinement: checked, - attribute: props.attribute, - value: (nextState) => - refine(props, nextState, false, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - }); - } - - return { id, index, items }; - }, -}); diff --git a/packages/react-instantsearch-core/src/connectors/connectVoiceSearch.js b/packages/react-instantsearch-core/src/connectors/connectVoiceSearch.js deleted file mode 100644 index ee6ce4abf9..0000000000 --- a/packages/react-instantsearch-core/src/connectors/connectVoiceSearch.js +++ /dev/null @@ -1,161 +0,0 @@ -import PropTypes from 'prop-types'; - -import createConnector from '../core/createConnector'; -import { - cleanUpValue, - refineValue, - getCurrentRefinementValue, - getIndexId, -} from '../core/indexUtils'; - -function getId() { - return 'query'; -} - -function getAdditionalId() { - return 'additionalVoiceParameters'; -} - -function getCurrentRefinementQuery(props, searchState, context) { - const id = getId(); - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - '' - ); - - if (currentRefinement) { - return currentRefinement; - } - return ''; -} - -function getCurrentRefinementAdditional(props, searchState, context) { - const id = getAdditionalId(); - const currentRefinement = getCurrentRefinementValue( - props, - searchState, - context, - id, - '' - ); - - if (currentRefinement) { - return currentRefinement; - } - return {}; -} - -function refine(props, searchState, nextRefinement, context) { - const id = getId(); - const voiceParams = getAdditionalId(); - const queryLanguages = props.language - ? { queryLanguages: [props.language.split('-')[0]] } - : {}; - const additionalQueryParameters = - typeof props.additionalQueryParameters === 'function' - ? { - ignorePlurals: true, - removeStopWords: true, - optionalWords: nextRefinement, - ...props.additionalQueryParameters({ query: nextRefinement }), - } - : {}; - const nextValue = { - [id]: nextRefinement, - [voiceParams]: { - ...queryLanguages, - ...additionalQueryParameters, - }, - }; - const resetPage = true; - return refineValue(searchState, nextValue, context, resetPage); -} - -function cleanUp(props, searchState, context) { - const interimState = cleanUpValue(searchState, context, getId()); - return cleanUpValue(interimState, context, getAdditionalId()); -} - -export default createConnector({ - displayName: 'AlgoliaVoiceSearch', - $$type: 'ais.voiceSearch', - - propTypes: { - defaultRefinement: PropTypes.string, - }, - - getProvidedProps(props, searchState, searchResults) { - return { - currentRefinement: getCurrentRefinementQuery(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - isSearchStalled: searchResults.isSearchStalled, - }; - }, - - refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - cleanUp(props, searchState) { - return cleanUp(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - }, - - getSearchParameters(searchParameters, props, searchState) { - const query = getCurrentRefinementQuery(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - const additionalParams = getCurrentRefinementAdditional( - props, - searchState, - { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - } - ); - - return searchParameters - .setQuery(query) - .setQueryParameters(additionalParams); - }, - - getMetadata(props, searchState) { - const id = getId(); - const currentRefinement = getCurrentRefinementQuery(props, searchState, { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }); - return { - id, - index: getIndexId({ - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - items: - currentRefinement === null - ? [] - : [ - { - label: `${id}: ${currentRefinement}`, - value: (nextState) => - refine(props, nextState, '', { - ais: props.contextValue, - multiIndexContext: props.indexContextValue, - }), - currentRefinement, - }, - ], - }; - }, -}); diff --git a/packages/react-instantsearch-hooks/src/connectors/useBreadcrumb.ts b/packages/react-instantsearch-core/src/connectors/useBreadcrumb.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useBreadcrumb.ts rename to packages/react-instantsearch-core/src/connectors/useBreadcrumb.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useClearRefinements.ts b/packages/react-instantsearch-core/src/connectors/useClearRefinements.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useClearRefinements.ts rename to packages/react-instantsearch-core/src/connectors/useClearRefinements.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useConfigure.ts b/packages/react-instantsearch-core/src/connectors/useConfigure.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useConfigure.ts rename to packages/react-instantsearch-core/src/connectors/useConfigure.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useCurrentRefinements.ts b/packages/react-instantsearch-core/src/connectors/useCurrentRefinements.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useCurrentRefinements.ts rename to packages/react-instantsearch-core/src/connectors/useCurrentRefinements.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useDynamicWidgets.ts b/packages/react-instantsearch-core/src/connectors/useDynamicWidgets.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useDynamicWidgets.ts rename to packages/react-instantsearch-core/src/connectors/useDynamicWidgets.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useGeoSearch.ts b/packages/react-instantsearch-core/src/connectors/useGeoSearch.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useGeoSearch.ts rename to packages/react-instantsearch-core/src/connectors/useGeoSearch.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useHierarchicalMenu.ts b/packages/react-instantsearch-core/src/connectors/useHierarchicalMenu.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useHierarchicalMenu.ts rename to packages/react-instantsearch-core/src/connectors/useHierarchicalMenu.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useHits.ts b/packages/react-instantsearch-core/src/connectors/useHits.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useHits.ts rename to packages/react-instantsearch-core/src/connectors/useHits.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useHitsPerPage.ts b/packages/react-instantsearch-core/src/connectors/useHitsPerPage.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useHitsPerPage.ts rename to packages/react-instantsearch-core/src/connectors/useHitsPerPage.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useInfiniteHits.ts b/packages/react-instantsearch-core/src/connectors/useInfiniteHits.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useInfiniteHits.ts rename to packages/react-instantsearch-core/src/connectors/useInfiniteHits.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useMenu.ts b/packages/react-instantsearch-core/src/connectors/useMenu.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useMenu.ts rename to packages/react-instantsearch-core/src/connectors/useMenu.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useNumericMenu.ts b/packages/react-instantsearch-core/src/connectors/useNumericMenu.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useNumericMenu.ts rename to packages/react-instantsearch-core/src/connectors/useNumericMenu.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/usePagination.ts b/packages/react-instantsearch-core/src/connectors/usePagination.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/usePagination.ts rename to packages/react-instantsearch-core/src/connectors/usePagination.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/usePoweredBy.ts b/packages/react-instantsearch-core/src/connectors/usePoweredBy.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/usePoweredBy.ts rename to packages/react-instantsearch-core/src/connectors/usePoweredBy.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useQueryRules.ts b/packages/react-instantsearch-core/src/connectors/useQueryRules.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useQueryRules.ts rename to packages/react-instantsearch-core/src/connectors/useQueryRules.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useRange.ts b/packages/react-instantsearch-core/src/connectors/useRange.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useRange.ts rename to packages/react-instantsearch-core/src/connectors/useRange.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useRefinementList.ts b/packages/react-instantsearch-core/src/connectors/useRefinementList.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useRefinementList.ts rename to packages/react-instantsearch-core/src/connectors/useRefinementList.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useSearchBox.ts b/packages/react-instantsearch-core/src/connectors/useSearchBox.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useSearchBox.ts rename to packages/react-instantsearch-core/src/connectors/useSearchBox.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useSortBy.ts b/packages/react-instantsearch-core/src/connectors/useSortBy.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useSortBy.ts rename to packages/react-instantsearch-core/src/connectors/useSortBy.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useStats.ts b/packages/react-instantsearch-core/src/connectors/useStats.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useStats.ts rename to packages/react-instantsearch-core/src/connectors/useStats.ts diff --git a/packages/react-instantsearch-hooks/src/connectors/useToggleRefinement.ts b/packages/react-instantsearch-core/src/connectors/useToggleRefinement.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/connectors/useToggleRefinement.ts rename to packages/react-instantsearch-core/src/connectors/useToggleRefinement.ts diff --git a/packages/react-instantsearch-core/src/core/__tests__/createConnector.js b/packages/react-instantsearch-core/src/core/__tests__/createConnector.js deleted file mode 100644 index f8fa419152..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/createConnector.js +++ /dev/null @@ -1,1422 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { wait } from '@instantsearch/testutils'; -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount, shallow } from 'enzyme'; -import React from 'react'; - -import { InstantSearchProvider } from '../context'; -import createConnector, { - createConnectorWithoutContext, -} from '../createConnector'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('createConnector', () => { - const createFakeState = (props) => ({ - widgets: {}, - results: {}, - resultsFacetValues: {}, - searching: false, - searchingForFacetValues: false, - isSearchStalled: false, - metadata: [], - error: new Error(), - ...props, - }); - - const createFakeStore = (props) => ({ - getState: () => createFakeState(), - setState() {}, - subscribe() {}, - ...props, - }); - - const createFakeWidgetManager = (props) => ({ - registerWidget() {}, - getWidgets() {}, - update() {}, - ...props, - }); - - const createFakeContext = (props) => ({ - onInternalStateUpdate() {}, - createHrefForState() {}, - onSearchForFacetValues() {}, - onSearchStateChange() {}, - onSearchParameters() {}, - store: createFakeStore(), - widgetsManager: createFakeWidgetManager(), - ...props, - }); - - describe('state', () => { - it('computes provided props', () => { - const getProvidedProps = jest.fn((props) => ({ - providedProps: props, - })); - - const Fake = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps, - })(Fake); - - const state = createFakeState(); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - }), - }); - - const wrapper = shallow(); - - expect(getProvidedProps).toHaveBeenCalledTimes(1); - expect(getProvidedProps).toHaveBeenCalledWith( - { hello: 'there', contextValue: context }, - state.widgets, - { - results: state.results, - searching: state.searching, - searchingForFacetValues: state.searchingForFacetValues, - isSearchStalled: state.isSearchStalled, - error: state.error, - }, - state.metadata, - state.resultsFacetValues - ); - - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'there', - providedProps: { - hello: 'there', - contextValue: context, - }, - }); - }); - - it('computes provided props on props change', () => { - const getProvidedProps = jest.fn((props) => ({ - providedProps: props, - })); - - const Fake = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps, - })(Fake); - - const props = { - hello: 'there', - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - expect(getProvidedProps).toHaveBeenCalledTimes(1); - - wrapper.setProps({ - hello: 'again', - }); - - expect(getProvidedProps).toHaveBeenCalledTimes(2); - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'again', - providedProps: { - hello: 'again', - contextValue: context, - }, - }); - }); - - it('computes provided props with the correct value for `canRender` on props change', () => { - const getProvidedProps = jest.fn((props) => ({ - providedProps: props, - })); - - const Fake = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps, - })(Fake); - - const props = { - hello: 'there', - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - // Simulate props change before mount - wrapper.setProps({ hello: 'again' }); - - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'again', - providedProps: { - hello: 'again', - contextValue: context, - }, - }); - - // Simulate mount lifecycle - wrapper.instance().componentDidMount(); - - // Simulate props change after mount - wrapper.setProps({ hello: 'once again' }); - - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'once again', - providedProps: { - hello: 'once again', - contextValue: context, - }, - }); - }); - - it('computes provided props on search state change', () => { - const getProvidedProps = jest.fn((_, state) => state); - - const Fake = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps, - })(Fake); - - const subscribe = jest.fn(); - - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: jest.fn(() => state), - subscribe, - }), - }); - - const wrapper = shallow(); - - expect(getProvidedProps).toHaveBeenCalledTimes(1); - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'there', - query: 'hello', - }); - - // Simulate a search state change - context.store.getState.mockImplementation(() => ({ - ...state, - widgets: { - query: 'hello World', - }, - })); - - // Simulate a dispatch on search state change - context.store.subscribe.mock.calls[0][0](); - - expect(getProvidedProps).toHaveBeenCalledTimes(2); - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'there', - query: 'hello World', - }); - }); - - it('computes provided props with latest props on search state change', () => { - const getProvidedProps = jest.fn((_, state) => state); - - const Fake = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps, - })(Fake); - - const subscribe = jest.fn(); - - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: jest.fn(() => state), - subscribe, - }), - }); - - const wrapper = shallow(); - - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'there', - query: 'hello', - }); - - // Simulate a search state change - context.store.getState.mockImplementation(() => ({ - ...state, - widgets: { - query: 'hello world', - }, - })); - - // Simulate a props change - wrapper.setProps({ - hello: 'again', - }); - - // Simulate a dispatch on search state change - context.store.subscribe.mock.calls[0][0](); - - expect(wrapper.find(Fake).props()).toEqual({ - hello: 'again', - query: 'hello world', - }); - }); - - it('does not compute provided props when props do not change', () => { - const getProvidedProps = jest.fn((props) => ({ - providedProps: props, - })); - - const Fake = jest.fn(() => null); - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps, - })(Fake); - - const props = { - hello: 'again', - another: ['one', 'two'], - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - expect(getProvidedProps).toHaveBeenCalledTimes(1); - - wrapper.setProps({ - hello: 'again', - another: ['one', 'two'], - }); - - expect(getProvidedProps).toHaveBeenCalledTimes(1); - - wrapper.setProps({ - hello: 'again', - another: ['one', 'two'], - }); - - expect(getProvidedProps).toHaveBeenCalledTimes(1); - }); - - it('use shouldComponentUpdate when provided', () => { - const shouldComponentUpdate = jest.fn(() => true); - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: (props) => props, - getMetadata: () => null, - shouldComponentUpdate, - })(() => null); - - const props = { hello: 'there' }; - const context = createFakeContext(); - const wrapper = mount(); - - expect(shouldComponentUpdate).toHaveBeenCalledTimes(0); - - wrapper.setProps({ hello: 'here' }); - - expect(shouldComponentUpdate).toHaveBeenCalledTimes(2); - expect(shouldComponentUpdate).toHaveBeenCalledWith( - { - hello: 'there', - contextValue: context, - }, - { - hello: 'here', - contextValue: context, - }, - { - providedProps: { - hello: 'there', - contextValue: context, - }, - }, - { - providedProps: { - hello: 'there', - contextValue: context, - }, - } - ); - - expect(shouldComponentUpdate).toHaveBeenCalledWith( - { - hello: 'here', - contextValue: context, - }, - { - hello: 'here', - contextValue: context, - }, - { - providedProps: { - hello: 'there', - contextValue: context, - }, - }, - { - providedProps: { - hello: 'here', - contextValue: context, - }, - } - ); - }); - - it('subscribes to the store once mounted', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const subscribe = jest.fn(); - - const context = createFakeContext({ - store: createFakeStore({ - subscribe, - }), - }); - - const wrapper = shallow(, { - disableLifecycleMethods: true, - }); - - expect(subscribe).toHaveBeenCalledTimes(0); - - // Simulate didMount - wrapper.instance().componentDidMount(); - - expect(subscribe).toHaveBeenCalledTimes(1); - }); - - it('unsubscribes from the store on unmount', async () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const unsubscribe = jest.fn(); - - const context = createFakeContext({ - store: createFakeStore({ - subscribe: () => unsubscribe, - }), - }); - - const wrapper = shallow(); - - expect(unsubscribe).toHaveBeenCalledTimes(0); - - wrapper.unmount(); - - await wait(0); - - expect(unsubscribe).toHaveBeenCalledTimes(1); - }); - - it('does not throw an error on unmount before mount', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const context = createFakeContext({ - store: createFakeStore({ - subscribe() { - return () => { - // unsubscribe - }; - }, - }), - }); - - const wrapper = shallow(, { - disableLifecycleMethods: true, - }); - - expect(() => wrapper.unmount()).not.toThrow(); - }); - - it('does not throw an error on dispatch after unmount', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const unsubscribe = () => {}; - const subscribe = jest.fn(() => unsubscribe); - - const context = createFakeContext({ - store: createFakeStore({ - subscribe, - }), - }); - - const wrapper = shallow(); - - expect(() => () => { - wrapper.unmount(); - - // Simulate a dispatch - subscribe.mock.calls[0][0](); - }).not.toThrow(); - }); - }); - - describe('widget', () => { - it('registers itself as a widget with getMetadata', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - })(() => null); - - const registerWidget = jest.fn(); - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - registerWidget, - }), - }); - - const wrapper = shallow(); - - expect(registerWidget).toHaveBeenCalledTimes(1); - expect(registerWidget).toHaveBeenCalledWith(wrapper.instance()); - }); - - it('registers itself as a widget with getSearchParameters', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getSearchParameters: () => null, - })(() => null); - - const registerWidget = jest.fn(); - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - registerWidget, - }), - }); - - const wrapper = shallow(); - - expect(registerWidget).toHaveBeenCalledTimes(1); - expect(registerWidget).toHaveBeenCalledWith(wrapper.instance()); - }); - - it('registers itself as a widget once mounted', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getSearchParameters: () => null, - })(() => null); - - const registerWidget = jest.fn(); - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - registerWidget, - }), - }); - - const wrapper = shallow(, { - disableLifecycleMethods: true, - }); - - expect(registerWidget).toHaveBeenCalledTimes(0); - - // Simulate didMount - wrapper.instance().componentDidMount(); - - expect(registerWidget).toHaveBeenCalledTimes(1); - expect(registerWidget).toHaveBeenCalledWith(wrapper.instance()); - }); - - it('does not register itself as a widget without getMetadata nor getSearchParameters', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const registerWidget = jest.fn(); - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - registerWidget, - }), - }); - - shallow(); - - expect(registerWidget).not.toHaveBeenCalled(); - }); - - it('calls onSearchParameters on mount with getSearchParameters', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'CoolConnector', - getProvidedProps: () => {}, - getSearchParameters: () => null, - getMetadata: () => null, - })(() => null); - - const onSearchParameters = jest.fn(); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - onSearchParameters, - }); - - shallow(); - - expect(onSearchParameters).toHaveBeenCalledTimes(1); - expect(onSearchParameters).toHaveBeenCalledWith( - expect.any(Function), - { ais: context }, - { ...props, contextValue: context }, - expect.any(Function), - 'CoolConnector' - ); - }); - - it('does not call onSearchParameters on mount without getSearchParameters', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'CoolConnector', - getProvidedProps: () => {}, - })(() => null); - - const onSearchParameters = jest.fn(); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - onSearchParameters, - }); - - shallow(); - - expect(onSearchParameters).not.toHaveBeenCalled(); - }); - - it('binds getSearchParameters to the connector instance with onSearchParameters', () => { - const getSearchParameters = jest.fn(function () { - return this; - }); - - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getSearchParameters, - })(() => null); - - const onSearchParameters = jest.fn(); - - const context = createFakeContext({ - onSearchParameters, - }); - - const wrapper = shallow(); - - expect(onSearchParameters.mock.calls[0][0]()).toBe(wrapper.instance()); - }); - - it('triggers a widgetManager update on props change', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - })(() => null); - - const update = jest.fn(); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - update, - }), - }); - - const wrapper = shallow(); - - expect(update).toHaveBeenCalledTimes(0); - - wrapper.setProps({ - hello: 'again', - }); - - expect(update).toHaveBeenCalledTimes(1); - }); - - it('does not trigger a widgetManager update when props do not change', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - })(() => null); - - const update = jest.fn(); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - update, - }), - }); - - const wrapper = shallow(); - - expect(update).toHaveBeenCalledTimes(0); - - wrapper.setProps({ - hello: 'there', - }); - - expect(update).toHaveBeenCalledTimes(0); - }); - - it('triggers an onSearchStateChange on props change with transitionState', () => { - const transitionState = jest.fn(function () { - return this.props; - }); - - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - transitionState, - })(() => null); - - const state = createFakeState(); - const onSearchStateChange = jest.fn(); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - }), - onSearchStateChange, - }); - - const wrapper = shallow(); - - expect(onSearchStateChange).toHaveBeenCalledTimes(0); - expect(transitionState).toHaveBeenCalledTimes(0); - - wrapper.setProps({ - hello: 'again', - }); - - expect(onSearchStateChange).toHaveBeenCalledTimes(1); - expect(onSearchStateChange).toHaveBeenCalledWith({ - hello: 'again', - contextValue: context, - }); - - expect(transitionState).toHaveBeenCalledTimes(1); - expect(transitionState).toHaveBeenCalledWith( - { hello: 'again', contextValue: context }, - state.widgets, - state.widgets - ); - }); - - it('does not trigger an onSearchStateChange on props change without transitionState', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - })(() => null); - - const onSearchStateChange = jest.fn(); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - onSearchStateChange, - }); - - const wrapper = shallow(); - - expect(onSearchStateChange).not.toHaveBeenCalled(); - - wrapper.setProps({ - hello: 'again', - }); - - expect(onSearchStateChange).not.toHaveBeenCalled(); - }); - - it('unregisters itself on unmount', async () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - })(() => null); - - const unregisterWidget = jest.fn(); - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - registerWidget: () => unregisterWidget, - }), - }); - - const wrapper = shallow(); - - expect(unregisterWidget).toHaveBeenCalledTimes(0); - - wrapper.unmount(); - - await wait(0); - - expect(unregisterWidget).toHaveBeenCalledTimes(1); - }); - - it('calls onSearchStateChange with cleanUp on unmount', async () => { - const cleanUp = jest.fn(function (props, searchState) { - return { - instanceProps: this.props, - providedProps: props, - searchState, - }; - }); - - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - cleanUp, - })(() => null); - - const onSearchStateChange = jest.fn(); - const setState = jest.fn(); - - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - setState, - }), - widgetsManager: createFakeWidgetManager({ - registerWidget: () => () => {}, - }), - onSearchStateChange, - }); - - const wrapper = shallow(); - - expect(cleanUp).toHaveBeenCalledTimes(0); - expect(onSearchStateChange).toHaveBeenCalledTimes(0); - - wrapper.unmount(); - - await wait(0); - - expect(cleanUp).toHaveBeenCalledTimes(1); - expect(onSearchStateChange).toHaveBeenCalledTimes(1); - expect(onSearchStateChange).toHaveBeenCalledWith({ - providedProps: { - hello: 'there', - contextValue: context, - }, - instanceProps: { - hello: 'there', - contextValue: context, - }, - searchState: { - query: 'hello', - }, - }); - }); - - it('calls onSearchStateChange with cleanUp without empty keys on unmount', async () => { - const cleanUp = jest.fn((_, searchState) => searchState); - - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - cleanUp, - })(() => null); - - const onSearchStateChange = jest.fn(); - const setState = jest.fn(); - - const state = createFakeState({ - widgets: { - query: 'hello', - hello: {}, - }, - }); - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - setState, - }), - widgetsManager: createFakeWidgetManager({ - registerWidget: () => () => {}, - }), - onSearchStateChange, - }); - - const wrapper = shallow(); - - wrapper.unmount(); - - await wait(0); - - expect(onSearchStateChange).toHaveBeenCalledWith({ - query: 'hello', - }); - }); - - it('does not throw an error on unmount before mount', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata: () => null, - })(() => null); - - const unregisterWidget = jest.fn(); - - const context = createFakeContext({ - widgetsManager: createFakeWidgetManager({ - registerWidget: () => unregisterWidget, - }), - }); - - const wrapper = shallow(, { - disableLifecycleMethods: true, - }); - - const trigger = () => wrapper.unmount(); - - expect(() => trigger()).not.toThrow(); - }); - }); - - describe('getSearchParameters', () => { - it('returns the widget search parameters when getSearchParameters is provided', () => { - const getSearchParameters = function ( - searchParameters, - props, - searchState - ) { - return { - instanceProps: this.props, - providedProps: props, - searchParameters, - searchState, - }; - }; - - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getSearchParameters, - })(() => null); - - const searchParameters = { - query: '', - hitsPerPage: 10, - }; - - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - }), - }); - - const wrapper = shallow(); - - const widgetSearchParameters = wrapper - .instance() - .getSearchParameters(searchParameters); - - expect(widgetSearchParameters).toEqual({ - searchParameters: { - query: '', - hitsPerPage: 10, - }, - instanceProps: { - hello: 'there', - contextValue: context, - }, - providedProps: { - hello: 'there', - contextValue: context, - }, - searchState: { - query: 'hello', - }, - }); - }); - - it('returns null when getSearchParameters is not provided', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const searchParameters = { - query: '', - hitsPerPage: 10, - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - const widgetSearchParamameters = wrapper - .instance() - .getSearchParameters(searchParameters); - - expect(widgetSearchParamameters).toBe(null); - }); - }); - - describe('getMetadata', () => { - it('returns the widget metadata when getMetadata is provided', () => { - const getMetadata = function (props, searchState) { - return { - instanceProps: this.props, - providedProps: props, - searchState, - }; - }; - - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - getMetadata, - })(() => null); - - const props = { - hello: 'there', - }; - - const searchState = { - query: 'hello', - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - const widgetMetadata = wrapper.instance().getMetadata(searchState); - - expect(widgetMetadata).toEqual({ - instanceProps: { - hello: 'there', - contextValue: context, - }, - providedProps: { - hello: 'there', - contextValue: context, - }, - searchState: { - query: 'hello', - }, - }); - }); - - it('returns an empty object when getMetadata is not provided', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const searchState = { - query: 'hello', - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - const widgetMetadata = wrapper.instance().getMetadata(searchState); - - expect(widgetMetadata).toEqual({}); - }); - }); - - describe('transitionState', () => { - it('returns the widget transitionState when transitionState is provided', () => { - const transitionState = function ( - props, - previousSearchState, - nextSearchState - ) { - return { - instanceProps: this.props, - providedProps: props, - previousSearchState, - nextSearchState, - }; - }; - - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - transitionState, - })(() => null); - - const props = { - hello: 'there', - }; - - const previousSearchState = { - query: 'hello', - }; - - const nextSearchState = { - query: 'hello again', - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - const widgetTransitionState = wrapper - .instance() - .transitionState(previousSearchState, nextSearchState); - - expect(widgetTransitionState).toEqual({ - instanceProps: { - hello: 'there', - contextValue: context, - }, - providedProps: { - hello: 'there', - contextValue: context, - }, - previousSearchState: { - query: 'hello', - }, - nextSearchState: { - query: 'hello again', - }, - }); - }); - - it('returns the given next state when transitionState is not provided', () => { - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - })(() => null); - - const props = { - hello: 'there', - }; - - const previousSearchState = { - query: 'hello', - }; - - const nextSearchState = { - query: 'hello again', - }; - - const context = createFakeContext(); - - const wrapper = shallow(); - - const widgetTransitionState = wrapper - .instance() - .transitionState(previousSearchState, nextSearchState); - - expect(widgetTransitionState).toEqual({ - query: 'hello again', - }); - }); - }); - - describe('refine', () => { - it('passes a refine method to the component', () => { - const refine = (props, searchState, next) => ({ - props, - searchState, - next, - }); - - const Dummy = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - refine, - })(Dummy); - - const onInternalStateUpdate = jest.fn(); - - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - }), - onInternalStateUpdate, - }); - - const wrapper = shallow(); - - expect(onInternalStateUpdate).toHaveBeenCalledTimes(0); - - wrapper.find(Dummy).props().refine({ query: 'hello world' }); - - expect(onInternalStateUpdate).toHaveBeenCalledTimes(1); - expect(onInternalStateUpdate).toHaveBeenCalledWith({ - props: { - hello: 'there', - contextValue: context, - }, - searchState: { - query: 'hello', - }, - next: { - query: 'hello world', - }, - }); - }); - }); - - describe('createURL', () => { - it('passes a createURL method to the component', () => { - const refine = (props, searchState, next) => ({ - props, - searchState, - next, - }); - - const Dummy = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - refine, - })(Dummy); - - const createHrefForState = jest.fn(); - - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const props = { - hello: 'there', - }; - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - }), - createHrefForState, - }); - - const wrapper = shallow(); - - expect(createHrefForState).toHaveBeenCalledTimes(0); - - wrapper.find(Dummy).props().createURL({ query: 'hello world' }); - - expect(createHrefForState).toHaveBeenCalledTimes(1); - expect(createHrefForState).toHaveBeenCalledWith({ - props: { - hello: 'there', - contextValue: context, - }, - searchState: { - query: 'hello', - }, - next: { - query: 'hello world', - }, - }); - }); - }); - - describe('searchForFacetValues', () => { - it('passes a searchForItems method to the component', () => { - const searchForFacetValues = (props, searchState, next) => ({ - props, - searchState, - next, - }); - - const Dummy = () => null; - const Connected = createConnectorWithoutContext({ - displayName: 'Connector', - getProvidedProps: () => {}, - searchForFacetValues, - })(Dummy); - - const onSearchForFacetValues = jest.fn(); - - const props = { - hello: 'there', - }; - - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - }), - onSearchForFacetValues, - }); - - const wrapper = shallow(); - - expect(onSearchForFacetValues).toHaveBeenCalledTimes(0); - - wrapper.find(Dummy).props().searchForItems({ - facetName: 'brand', - query: 'apple', - maxFacetHits: 10, - }); - - expect(onSearchForFacetValues).toHaveBeenCalledTimes(1); - expect(onSearchForFacetValues).toHaveBeenCalledWith({ - props: { - hello: 'there', - contextValue: context, - }, - searchState: { - query: 'hello', - }, - next: { - facetName: 'brand', - query: 'apple', - maxFacetHits: 10, - }, - }); - }); - }); - - describe('wrapped with InstantSearchProvider', () => { - it('default export reads from context', () => { - const state = createFakeState({ - widgets: { - query: 'hello', - }, - }); - - const context = createFakeContext({ - store: createFakeStore({ - getState: () => state, - }), - }); - - const Dummy = (props) => JSON.stringify(props, null, 2).replace(/"/g, ''); - - const Connected = createConnector({ - displayName: 'Connector', - getProvidedProps: (props) => ({ providedProps: props }), - })(Dummy); - - const props = { - message: 'hello', - }; - - const wrapper = mount( - - - - ); - - expect(wrapper.text()).toMatchInlineSnapshot(` - "{ - message: hello, - providedProps: { - contextValue: { - store: {}, - widgetsManager: {} - }, - message: hello - } - }" - `); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.derived.js b/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.derived.js deleted file mode 100644 index d2779a05cb..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.derived.js +++ /dev/null @@ -1,699 +0,0 @@ -import { wait } from '@instantsearch/testutils'; - -import createInstantSearchManager from '../createInstantSearchManager'; - -const createSearchClient = () => ({ - search: jest.fn((requests) => - Promise.resolve({ - results: requests.map( - ({ indexName, params: { page, query, hitsPerPage } }) => ({ - index: indexName, - query, - page, - hitsPerPage, - hits: [], - }) - ), - }) - ), - searchForFacetValues() { - return Promise.resolve([ - { - facetHits: [], - }, - ]); - }, -}); - -describe('createInstantSearchManager with multi index', () => { - it('updates the store and searches', async () => { - // - // - // - // - // - // - // - // - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'first', - initialState: {}, - searchParameters: {}, - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('first query 1'), - props: {}, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setPage(3), - props: { - indexContextValue: { - targetedIndex: 'first', - }, - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('second'), - props: { - indexName: 'second', - indexId: 'second', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('second query 1'), - props: { - indexContextValue: { - targetedIndex: 'second', - }, - }, - }); - - expect(ism.store.getState().results).toBe(null); - - await wait(0); - - expect(ism.store.getState().results.first).toEqual( - expect.objectContaining({ - query: 'first query 1', - index: 'first', - page: 3, - }) - ); - - expect(ism.store.getState().results.second).toEqual( - expect.objectContaining({ - query: 'second query 1', - index: 'second', - }) - ); - - await wait(0); - - // - ism.widgetsManager.getWidgets()[0].getSearchParameters = (params) => - params.setQuery('first query 2'); - - // - // - // - ism.widgetsManager.getWidgets()[4].getSearchParameters = (params) => - params.setQuery('second query 2'); - - ism.widgetsManager.update(); - - await wait(0); - - expect(ism.store.getState().results.first).toEqual( - expect.objectContaining({ - query: 'first query 2', - index: 'first', - page: 3, - }) - ); - - expect(ism.store.getState().results.second).toEqual( - expect.objectContaining({ - query: 'second query 2', - index: 'second', - }) - ); - }); - - // https://github.com/algolia/react-instantsearch/issues/2875 - it('update resultsState without mutate object', async () => { - const ism = createInstantSearchManager({ - indexName: 'first', - initialState: {}, - searchParameters: {}, - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('first query 1'), - props: {}, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setPage(3), - props: { - indexContextValue: { - targetedIndex: 'first', - }, - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('second'), - props: { - indexName: 'second', - indexId: 'second', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('second query 1'), - props: { - indexContextValue: { - targetedIndex: 'second', - }, - }, - }); - - expect(ism.store.getState().results).toBe(null); - - await wait(0); - - const resultsWithFirstAndSecond = ism.store.getState().results; - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('thrid'), - props: { - indexName: 'thrid', - indexId: 'thrid', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('thrid query 1'), - props: { - indexContextValue: { - targetedIndex: 'thrid', - }, - }, - }); - - await wait(0); - - expect( - Object.is(resultsWithFirstAndSecond, ism.store.getState().results) - ).toBe(false); - }); - - it('searches with duplicate Index & SortBy', async () => { - // - // - // - // - // - // - // - // - // ; - - const ism = createInstantSearchManager({ - indexName: 'first', - initialState: {}, - searchParameters: {}, - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('query'), - props: {}, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('third'), - props: { - indexContextValue: { - targetedIndex: 'first', - }, - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('second'), - props: { - indexName: 'second', - indexId: 'second', - }, - }); - - expect(ism.store.getState().results).toBe(null); - - await wait(0); - - expect(ism.store.getState().results.first).toEqual( - expect.objectContaining({ - index: 'third', - query: 'query', - }) - ); - - expect(ism.store.getState().results.second).toEqual( - expect.objectContaining({ - index: 'second', - query: 'query', - }) - ); - }); - - it('searches with N queries for N Index widgets', async () => { - // - // - // - // - // - // ; - - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'first', - initialState: {}, - searchParameters: {}, - searchClient, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first', - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('second'), - props: { - indexName: 'second', - indexId: 'second', - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('third'), - props: { - indexName: 'third', - indexId: 'third', - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('four'), - props: { - indexName: 'four', - indexId: 'four', - }, - }); - - await wait(0); - - expect(searchClient.search.mock.calls[0][0]).toHaveLength(4); - - ism.widgetsManager.update(); - - await wait(0); - - expect(searchClient.search.mock.calls[1][0]).toHaveLength(4); - }); - - it('searches with same index but different params', async () => { - // - // - // - // - // - // - // - // - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'first', - initialState: {}, - searchParameters: {}, - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('first query'), - context: {}, - props: {}, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first_5_hits', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => - params.setQueryParameter('hitsPerPage', 5), - props: { - indexContextValue: { - targetedIndex: 'first_5_hits', - }, - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first_10_hits', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => - params.setQueryParameter('hitsPerPage', 10), - props: { - indexContextValue: { - targetedIndex: 'first_10_hits', - }, - }, - }); - - expect(ism.store.getState().results).toBe(null); - - await wait(0); - - expect(ism.store.getState().results.first_5_hits).toEqual( - expect.objectContaining({ - query: 'first query', - index: 'first', - hitsPerPage: 5, - }) - ); - - expect(ism.store.getState().results.first_10_hits).toEqual( - expect.objectContaining({ - query: 'first query', - index: 'first', - hitsPerPage: 10, - }) - ); - }); - - it('switching from mono to multi index', async () => { - // - // - // - // - // - // - // - // - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'first', - initialState: {}, - searchParameters: {}, - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('first query 1'), - props: {}, - }); - - // - const unregisterFirstIndexWidget = ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first', - }, - }); - - // - // - // - const unregisterPaginationWidget = ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setPage(3), - props: { - indexContextValue: { - targetedIndex: 'first', - }, - }, - }); - - // - const unregisterSecondIndexWidget = ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('second'), - props: { - indexName: 'second', - indexId: 'second', - }, - }); - - // - // - // - const unregisterSecondSearchBoxWidget = ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('second query 1'), - props: { - indexContextValue: { - targetedIndex: 'second', - }, - }, - }); - - expect(ism.store.getState().results).toBe(null); - - await wait(0); - - expect(ism.store.getState().results.first).toEqual( - expect.objectContaining({ - index: 'first', - query: 'first query 1', - page: 3, - }) - ); - - expect(ism.store.getState().results.second).toEqual( - expect.objectContaining({ - index: 'second', - query: 'second query 1', - }) - ); - - ism.widgetsManager.getWidgets()[0].getSearchParameters = (params) => - params.setQuery('first query 2'); - - unregisterFirstIndexWidget(); - unregisterPaginationWidget(); - unregisterSecondIndexWidget(); - unregisterSecondSearchBoxWidget(); - - await wait(0); - - expect(ism.store.getState().results).toEqual( - expect.objectContaining({ - index: 'first', - query: 'first query 2', - }) - ); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setPage(3), - props: { - indexContextValue: { - targetedIndex: 'first', - }, - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setIndex('second'), - props: { - indexName: 'second', - indexId: 'second', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('second query 2'), - props: { - indexContextValue: { - targetedIndex: 'second', - }, - }, - }); - - await wait(0); - - expect(ism.store.getState().results.first).toEqual( - expect.objectContaining({ - index: 'first', - query: 'first query 2', - page: 3, - }) - ); - - expect(ism.store.getState().results.second).toEqual( - expect.objectContaining({ - index: 'second', - query: 'second query 2', - }) - ); - }); - - it('sets state with correct value for `searching` property', async () => { - // - // - // - // - // ; - - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'first', - initialState: {}, - searchParameters: {}, - searchClient, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('first query 1'), - props: {}, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('first'), - props: { - indexName: 'first', - indexId: 'first', - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('second'), - props: { - indexName: 'second', - indexId: 'second', - }, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters: (x) => x.setIndex('third'), - props: { - indexName: 'third', - indexId: 'third', - }, - }); - - const setStateSpy = jest.spyOn(ism.store, 'setState'); - - await wait(0); - - expect(setStateSpy.mock.calls).toHaveLength(4); - expect(setStateSpy.mock.calls[0][0]).toEqual( - expect.objectContaining({ searching: true }) - ); - expect(setStateSpy.mock.calls[1][0]).toEqual( - expect.objectContaining({ searching: true }) - ); - expect(setStateSpy.mock.calls[2][0]).toEqual( - expect.objectContaining({ searching: true }) - ); - expect(setStateSpy.mock.calls[3][0]).toEqual( - expect.objectContaining({ searching: false }) - ); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.error.js b/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.error.js deleted file mode 100644 index bbc2cbbb37..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.error.js +++ /dev/null @@ -1,209 +0,0 @@ -import { wait } from '@instantsearch/testutils'; - -import createInstantSearchManager from '../createInstantSearchManager'; - -const createSearchClient = () => ({ - search: jest.fn(), - searchForFacetValues: jest.fn(), -}); - -describe('createInstantSearchManager with errors', () => { - describe('on search', () => { - it('updates the store on widget lifecycle', async () => { - const searchClient = createSearchClient({}); - - searchClient.search.mockImplementation(() => - Promise.reject(new Error('API_ERROR_1')) - ); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('search'), - context: {}, - props: {}, - }); - - expect(ism.store.getState().error).toBe(null); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(1); - expect(ism.store.getState().error).toEqual(new Error('API_ERROR_1')); - expect(ism.store.getState().results).toEqual(null); - - searchClient.search.mockImplementation(() => - Promise.reject(new Error('API_ERROR_2')) - ); - - ism.widgetsManager.update(); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(2); - expect(ism.store.getState().error).toEqual(new Error('API_ERROR_2')); - expect(ism.store.getState().results).toEqual(null); - }); - - it('updates the store on external updates', async () => { - const searchClient = createSearchClient({}); - - searchClient.search.mockImplementation(() => - Promise.reject(new Error('API_ERROR_1')) - ); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.onExternalStateUpdate({}); - - expect(ism.store.getState().error).toBe(null); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(1); - expect(ism.store.getState().error).toEqual(new Error('API_ERROR_1')); - expect(ism.store.getState().results).toEqual(null); - - searchClient.search.mockImplementation(() => - Promise.reject(new Error('API_ERROR_2')) - ); - - ism.onExternalStateUpdate({}); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(2); - expect(ism.store.getState().error).toEqual(new Error('API_ERROR_2')); - expect(ism.store.getState().results).toEqual(null); - }); - - it('reset the error after a successful search', async () => { - const searchClient = createSearchClient({}); - - searchClient.search.mockImplementation(() => - Promise.reject(new Error('API_ERROR')) - ); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('search'), - context: {}, - props: {}, - }); - - expect(ism.store.getState().error).toBe(null); - - await wait(0); - - expect(ism.store.getState().error).toEqual(new Error('API_ERROR')); - expect(ism.store.getState().results).toEqual(null); - - searchClient.search.mockImplementation(() => - Promise.resolve({ - results: [ - { - hits: [], - }, - ], - }) - ); - - ism.widgetsManager.update(); - - await wait(0); - - expect(ism.store.getState().error).toEqual(null); - expect(ism.store.getState().results).toEqual( - expect.objectContaining({ - hits: [], - }) - ); - }); - }); - - describe('on search for facet values', () => { - it('updates the store on function call', async () => { - const searchClient = createSearchClient({}); - - searchClient.searchForFacetValues.mockImplementation(() => - Promise.reject(new Error('API_ERROR')) - ); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.onSearchForFacetValues({ - facetName: 'facetName', - query: 'query', - }); - - expect(ism.store.getState().searchingForFacetValues).toBe(true); - expect(ism.store.getState().error).toBe(null); - - await wait(0); - - expect(ism.store.getState().searchingForFacetValues).toBe(false); - expect(ism.store.getState().error).toEqual(new Error('API_ERROR')); - }); - - it('reset the error after a successful search', async () => { - const searchClient = createSearchClient({}); - - searchClient.searchForFacetValues.mockImplementation(() => - Promise.reject(new Error('API_ERROR')) - ); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.onSearchForFacetValues({ - facetName: 'facetName', - query: 'query', - }); - - expect(ism.store.getState().error).toBe(null); - - await wait(0); - - expect(ism.store.getState().error).toEqual(new Error('API_ERROR')); - expect(ism.store.getState().resultsFacetValues).toBeUndefined(); - - searchClient.searchForFacetValues.mockImplementation(() => - Promise.resolve([ - { - facetHits: [], - }, - ]) - ); - - ism.onSearchForFacetValues({ - facetName: 'facetName', - query: 'query', - }); - - await wait(0); - - expect(ism.store.getState().error).toBe(null); - expect(ism.store.getState().resultsFacetValues).toEqual( - expect.objectContaining({ - facetName: [], - query: 'query', - }) - ); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.js b/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.js deleted file mode 100644 index a27a2c3b80..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.js +++ /dev/null @@ -1,997 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { runAllMicroTasks } from '@instantsearch/testutils'; -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import { SearchResults } from 'algoliasearch-helper'; -import algoliasearch from 'algoliasearch/lite'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import { - InstantSearch, - Index, - SortBy, - Configure, -} from 'react-instantsearch-dom'; - -import createInstantSearchManager from '../createInstantSearchManager'; - -Enzyme.configure({ adapter: new Adapter() }); - -jest.useFakeTimers(); - -const runOnlyNextMicroTask = () => Promise.resolve(); - -const createSearchClient = () => ({ - search: jest.fn(() => - Promise.resolve({ - results: [ - { - params: 'query=&hitsPerPage=10&page=0&facets=%5B%5D&tagFilters=', - page: 0, - hits: [], - hitsPerPage: 10, - nbPages: 0, - processingTimeMS: 4, - query: '', - nbHits: 0, - index: 'index', - }, - ], - }) - ), -}); - -describe('createInstantSearchManager', () => { - it('initializes the manager with an empty state', () => { - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - expect(ism.store.getState()).toEqual({ - error: null, - isSearchStalled: true, - metadata: [], - results: null, - searching: false, - searchingForFacetValues: false, - widgets: {}, - }); - - expect(ism.widgetsManager.getWidgets()).toEqual([]); - - expect(ism.transitionState({})).toEqual({}); - - expect(ism.getWidgetsIds()).toEqual([]); - }); - - describe('client hydratation', () => { - it('hydrates the `searchClient` for a single index results', () => { - const searchClient = algoliasearch('appId', 'apiKey', { - _cache: true, // cache is not enabled by default inside Node - }); - - // Skip this test with Algoliasearch API Client >= v4 - // (cache is handled by the client ifself) - if (searchClient.transporter) { - return; - } - - const resultsState = { - metadata: [], - rawResults: [ - { - index: 'index', - query: 'query', - }, - ], - state: { - index: 'index', - query: 'query', - }, - }; - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - - createInstantSearchManager({ - indexName: 'index', - searchClient, - resultsState, - }); - - expect(Object.keys(searchClient.cache)).toHaveLength(1); - Object.keys(searchClient.cache).forEach((key) => { - expect(typeof searchClient.cache[key]).toBe('string'); - expect(JSON.parse(searchClient.cache[key])).toEqual({ - results: [ - { - index: 'index', - query: 'query', - }, - ], - }); - }); - }); - - it('hydrates the `searchClient` for a multi index results', () => { - const searchClient = algoliasearch('appId', 'apiKey', { - _cache: true, // cache is not enabled by default inside Node - }); - - // Skip this test with Algoliasearch API Client >= v4 - // (cache is handled by the client ifself) - if (searchClient.transporter) { - return; - } - - const resultsState = { - metadata: [], - results: [ - { - _internalIndexId: 'index1', - rawResults: [ - { - index: 'index1', - query: 'query1', - }, - ], - state: { - index: 'index1', - query: 'query1', - }, - }, - { - _internalIndexId: 'index2', - rawResults: [ - { - index: 'index2', - query: 'query2', - }, - ], - state: { - index: 'index2', - query: 'query2', - }, - }, - ], - }; - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - - createInstantSearchManager({ - indexName: 'index', - searchClient, - resultsState, - }); - - expect(Object.keys(searchClient.cache)).toHaveLength(1); - Object.keys(searchClient.cache).forEach((key) => { - expect(typeof searchClient.cache[key]).toBe('string'); - expect(JSON.parse(searchClient.cache[key])).toEqual({ - results: [ - { - index: 'index1', - query: 'query1', - }, - { - index: 'index2', - query: 'query2', - }, - ], - }); - }); - }); - - it('does not hydrate the `searchClient` without results', () => { - const searchClient = algoliasearch('appId', 'apiKey'); - - // Skip this test with Algoliasearch API Client >= v4 - // (cache is handled by the client ifself) - if (searchClient.transporter) { - return; - } - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - - createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - }); - - it("does not hydrate the `searchClient` if it's not an Algolia client", () => { - const searchClient = { - _useCache: true, - cache: {}, - }; - - // Skip this test with Algoliasearch API Client >= v4 - // (cache is handled by the client ifself) - if (searchClient.transporter) { - return; - } - - const resultsState = { - metadata: [], - rawResults: [ - { - index: 'indexName', - query: 'query', - }, - ], - state: { - index: 'indexName', - query: 'query', - }, - }; - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - - createInstantSearchManager({ - indexName: 'index', - searchClient, - resultsState, - }); - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - }); - - it('does not hydrate the `searchClient` without cache enabled', () => { - const searchClient = algoliasearch('appId', 'apiKey', { - _cache: false, - }); - - // Skip this test with Algoliasearch API Client >= v4 - // (cache is handled by the client ifself) - if (searchClient.transporter) { - return; - } - - const resultsState = { - metadata: [], - rawResults: [ - { - index: 'indexName', - query: 'query', - }, - ], - state: { - index: 'indexName', - query: 'query', - }, - }; - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - - createInstantSearchManager({ - indexName: 'index', - searchClient, - resultsState, - }); - - expect(Object.keys(searchClient.cache)).toHaveLength(0); - }); - - it('when using algoliasearch@v4, it overrides search only once', () => { - const searchClient = algoliasearch('appId', 'apiKey', { - _cache: true, - }); - - // Skip this test with Algoliasearch API Client < v4, as - // search does not need to be overridden. - if (!searchClient.transporter) { - return; - } - - const resultsState = { - metadata: [], - rawResults: [ - { - index: 'indexName', - query: 'query', - }, - ], - state: { - index: 'indexName', - query: 'query', - }, - }; - - const originalSearch = algoliasearch.search; - - createInstantSearchManager({ - indexName: 'index', - searchClient, - resultsState, - }); - - expect(searchClient.search).not.toBe(originalSearch); - - const alreadyOverridden = jest.fn(); - searchClient.search = alreadyOverridden; - - createInstantSearchManager({ - indexName: 'index', - searchClient, - resultsState, - }); - - expect(searchClient.search).toBe(alreadyOverridden); - }); - }); - - describe('results hydration', () => { - it('initializes the manager with a single index hydrated results', () => { - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - resultsState: { - metadata: [], - rawResults: [ - { - index: 'indexName', - query: 'query', - }, - ], - state: { - index: 'indexName', - query: 'query', - }, - }, - }); - - expect(ism.store.getState().results).toBeInstanceOf(SearchResults); - expect(ism.store.getState().results.query).toEqual('query'); - }); - - it('initializes the manager with a multi index hydrated results', () => { - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - resultsState: { - metadata: [], - results: [ - { - _internalIndexId: 'index1', - rawResults: [ - { - index: 'index1', - query: 'query1', - }, - ], - state: { - index: 'index1', - query: 'query1', - }, - }, - { - _internalIndexId: 'index2', - rawResults: [ - { - index: 'index2', - query: 'query2', - }, - ], - state: { - index: 'index2', - query: 'query2', - }, - }, - ], - }, - }); - - expect(ism.store.getState().results.index1.query).toBe('query1'); - expect(ism.store.getState().results.index1).toBeInstanceOf(SearchResults); - expect(ism.store.getState().results.index2.query).toBe('query2'); - expect(ism.store.getState().results.index2).toBeInstanceOf(SearchResults); - }); - }); - - describe('metadata hydration', () => { - test('replaces value with a function returning empty search state', () => { - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - resultsState: { - metadata: [ - { stuff: 1, items: [{ stuff: 2, items: [{ stuff: 3 }] }] }, - ], - rawResults: [ - { - index: 'indexName', - query: 'query', - }, - ], - state: { - index: 'indexName', - query: 'query', - }, - }, - }); - - const hydratedMetadata = ism.store.getState().metadata; - - expect(hydratedMetadata).toEqual([ - { - value: expect.any(Function), - stuff: 1, - items: [ - { - value: expect.any(Function), - stuff: 2, - items: [ - { - value: expect.any(Function), - stuff: 3, - }, - ], - }, - ], - }, - ]); - - expect(hydratedMetadata[0].value()).toEqual({}); - expect(hydratedMetadata[0].items[0].value()).toEqual({}); - expect(hydratedMetadata[0].items[0].items[0].value()).toEqual({}); - }); - }); - - describe('widget manager', () => { - it('triggers a search when a widget is added', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.widgetsManager.registerWidget({ - getSearchParameters: () => ({}), - props: {}, - context: {}, - }); - - expect(ism.store.getState().searching).toBe(false); - - await runOnlyNextMicroTask(); - - expect(ism.store.getState().searching).toBe(true); - - await runAllMicroTasks(); - - expect(ism.store.getState().searching).toBe(false); - }); - }); - - describe('transitionState', () => { - it('executes widgets hook', () => { - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - ism.widgetsManager.registerWidget({ - transitionState: (next, current) => { - expect(next).toEqual({}); - - return { - ...current, - a: 1, - }; - }, - }); - - ism.widgetsManager.registerWidget({ - transitionState: (next, current) => { - expect(next).toEqual({}); - - return { - ...current, - b: 2, - }; - }, - }); - - expect(ism.transitionState()).toEqual({ - a: 1, - b: 2, - }); - }); - }); - - describe('getWidgetsIds', () => { - it('returns the list of ids of all registered widgets', async () => { - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - expect(ism.getWidgetsIds()).toEqual([]); - - ism.widgetsManager.registerWidget({ getMetadata: () => ({ id: 'a' }) }); - ism.widgetsManager.registerWidget({ getMetadata: () => ({ id: 'b' }) }); - ism.widgetsManager.registerWidget({ getMetadata: () => ({ id: 'c' }) }); - ism.widgetsManager.registerWidget({ getMetadata: () => ({ id: 'd' }) }); - - await runAllMicroTasks(); - - expect(ism.getWidgetsIds()).toEqual(['a', 'b', 'c', 'd']); - }); - }); - - describe('getSearchParameters', () => { - it('expects a widget top level to be shared between main and derived parameters', () => { - // - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setQuery('shared'); - }, - context: {}, - props: {}, - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setIndex('main'); - }, - props: { - indexId: 'main', - }, - }); - - const { mainParameters, derivedParameters } = ism.getSearchParameters(); - - expect(mainParameters).toEqual( - expect.objectContaining({ - index: 'index', - query: 'shared', - }) - ); - - expect(derivedParameters).toEqual([ - { - indexId: 'main', - parameters: expect.objectContaining({ - index: 'main', - query: 'shared', - }), - }, - ]); - }); - - it('expects a widget with the same id than the indexName to be a main parameters', () => { - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setIndex('main'); - }, - context: {}, - props: { - indexId: 'index', - }, - }); - - const { mainParameters, derivedParameters } = ism.getSearchParameters(); - - expect(mainParameters).toEqual( - expect.objectContaining({ - index: 'main', - }) - ); - - expect(derivedParameters).toEqual([]); - }); - - it('expects a widget with a different id than the indexName to be a derived parameters', () => { - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setIndex('main'); - }, - context: {}, - props: { - indexId: 'index_main', - }, - }); - - const { mainParameters, derivedParameters } = ism.getSearchParameters(); - - expect(mainParameters).toEqual( - expect.objectContaining({ - index: 'index', - }) - ); - - expect(derivedParameters).toEqual([ - { - indexId: 'index_main', - parameters: expect.objectContaining({ - index: 'main', - }), - }, - ]); - }); - - it('expects a widget within a mutli index context with the same id than the indexName to be a main parameters', () => { - // - // - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setIndex('index'); - }, - context: {}, - props: { - indexId: 'index', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setQuery('main'); - }, - context: { - multiIndexContext: { - targetedIndex: 'index', - }, - }, - props: {}, - }); - - const { mainParameters, derivedParameters } = ism.getSearchParameters(); - - expect(mainParameters).toEqual( - expect.objectContaining({ - index: 'index', - query: 'main', - }) - ); - - expect(derivedParameters).toEqual([]); - }); - - it('expects a widget within a mutli index context with a different id than the indexName to be a derived parameters', () => { - // - // - // - // - // - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient: createSearchClient({}), - }); - - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setIndex('index'); - }, - props: { - indexId: 'index_with_refinement', - }, - }); - - // - // - // - ism.widgetsManager.registerWidget({ - getSearchParameters(state) { - return state.setQuery('derived'); - }, - props: { - indexContextValue: { - targetedIndex: 'index_with_refinement', - }, - }, - }); - - const { mainParameters, derivedParameters } = ism.getSearchParameters(); - - expect(mainParameters).toEqual( - expect.objectContaining({ - index: 'index', - }) - ); - - expect(derivedParameters).toEqual([ - { - indexId: 'index_with_refinement', - parameters: expect.objectContaining({ - index: 'index', - query: 'derived', - }), - }, - ]); - }); - - it('expects widgets main parameters and derived parameters to be correctly calculated within a multi index context', () => { - const wrapper = mount( - - - - - - - - - - - - - - - - - ); - - const { mainParameters, derivedParameters } = wrapper - .instance() - .state.instantSearchManager.getSearchParameters(); - - expect(mainParameters).toEqual( - expect.objectContaining({ - index: 'index1', - }) - ); - - expect(derivedParameters).toEqual([ - expect.objectContaining({ - indexId: 'bestbuy', - parameters: expect.objectContaining({ - index: 'bestbuy', - }), - }), - expect.objectContaining({ - indexId: 'instant_search', - parameters: expect.objectContaining({ - index: 'instant_search', - }), - }), - expect.objectContaining({ - indexId: 'instant_search_apple', - parameters: expect.objectContaining({ - index: 'instant_search', - filters: 'brand:Apple', - }), - }), - expect.objectContaining({ - indexId: 'instant_search_samsung', - parameters: expect.objectContaining({ - index: 'instant_search', - filters: 'brand:Samsung', - }), - }), - expect.objectContaining({ - indexId: 'instant_search_microsoft', - parameters: expect.objectContaining({ - index: 'instant_search', - filters: 'brand:Microsoft', - }), - }), - ]); - }); - - it('expects widgets main parameters and derived parameters to be correctly calculated with SortBy within a multi index context', () => { - const wrapper = mount( - - - - - - - - - - ); - - const { mainParameters, derivedParameters } = wrapper - .instance() - .state.instantSearchManager.getSearchParameters(); - - expect(mainParameters).toEqual( - expect.objectContaining({ - index: 'index1', - }) - ); - - expect(derivedParameters).toEqual([ - expect.objectContaining({ - indexId: 'categories', - parameters: expect.objectContaining({ - index: 'bestbuy', - }), - }), - expect.objectContaining({ - indexId: 'products', - parameters: expect.objectContaining({ - index: 'brands', - }), - }), - ]); - }); - }); - - describe('searchStalled', () => { - it('should be updated if search is stalled', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.widgetsManager.registerWidget({ - getMetadata: () => {}, - transitionState: () => {}, - }); - - expect(searchClient.search).not.toHaveBeenCalled(); - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: true, - }); - - await runOnlyNextMicroTask(); - - expect(searchClient.search).toHaveBeenCalledTimes(1); - - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: true, - }); - - jest.runAllTimers(); - - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: true, - }); - - await runOnlyNextMicroTask(); - - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: false, - }); - - ism.widgetsManager.update(); - - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: false, - }); - - await runOnlyNextMicroTask(); - - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: false, - }); - - jest.runAllTimers(); - - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: true, - }); - - await runOnlyNextMicroTask(); - - expect(ism.store.getState()).toMatchObject({ - isSearchStalled: false, - }); - }); - }); - - describe('client.search', () => { - it('should be called when there is a new widget', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.widgetsManager.registerWidget({ - getMetadata: () => {}, - transitionState: () => {}, - }); - - expect(searchClient.search).toHaveBeenCalledTimes(0); - - await runAllMicroTasks(); - - expect(searchClient.search).toHaveBeenCalledTimes(1); - }); - - it('should be called when there is a new client', () => { - const searchClient = createSearchClient({}); - const nextSearchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - expect(searchClient.search).toHaveBeenCalledTimes(0); - expect(nextSearchClient.search).toHaveBeenCalledTimes(0); - - ism.updateClient(nextSearchClient); - - expect(searchClient.search).toHaveBeenCalledTimes(0); - expect(nextSearchClient.search).toHaveBeenCalledTimes(1); - }); - - it('should not be called when the search is skipped', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.skipSearch(); - - ism.widgetsManager.registerWidget({ - getMetadata: () => {}, - transitionState: () => {}, - }); - - await runAllMicroTasks(); - - expect(searchClient.search).toHaveBeenCalledTimes(0); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.result.js b/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.result.js deleted file mode 100644 index 487ae7207b..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/createInstantSearchManager.result.js +++ /dev/null @@ -1,244 +0,0 @@ -import { wait } from '@instantsearch/testutils'; - -import createInstantSearchManager from '../createInstantSearchManager'; - -const createSearchClient = () => ({ - search: jest.fn(() => - Promise.resolve({ - results: [ - { - hits: [{ value: 'results' }], - }, - ], - }) - ), - searchForFacetValues: jest.fn(() => - Promise.resolve([ - { - facetHits: [{ value: 'results' }], - }, - ]) - ), -}); - -describe('createInstantSearchManager with results', () => { - describe('on search', () => { - it('updates the store on widget lifecycle', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.setQuery('search'), - props: {}, - context: {}, - }); - - expect(ism.store.getState().results).toBe(null); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(1); - expect(ism.store.getState().results.hits).toEqual([{ value: 'results' }]); - expect(ism.store.getState().error).toBe(null); - - ism.widgetsManager.update(); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(2); - expect(ism.store.getState().results.hits).toEqual([{ value: 'results' }]); - expect(ism.store.getState().error).toBe(null); - }); - - it('updates the store on external updates', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - ism.onExternalStateUpdate({}); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(1); - expect(ism.store.getState().results.hits).toEqual([{ value: 'results' }]); - expect(ism.store.getState().error).toBe(null); - - ism.onExternalStateUpdate({}); - - await wait(0); - - expect(searchClient.search).toHaveBeenCalledTimes(2); - expect(ism.store.getState().results.hits).toEqual([{ value: 'results' }]); - expect(ism.store.getState().error).toBe(null); - }); - }); - - describe('on search for facet values', () => { - // We should avoid to rely on such mock, we mostly do an integration tests rather than - // a unit ones for the manager. We have to simulate a real helper environement (facet, - // etc, ...) to have something that don't throw errors. An easier way would be to provide - // the helper to the manager with the real implementation by default. With this, we can easily - // pass a custom helper (mocked or not) and don't rely on the helper + client. - - it('updates the store and searches', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - // We have to register the facet to be able to search on it - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.addFacet('facetName'), - props: {}, - context: {}, - }); - - await wait(0); - - ism.onSearchForFacetValues({ - facetName: 'facetName', - query: 'query', - }); - - expect(ism.store.getState().searchingForFacetValues).toBe(true); - - await wait(0); - - expect(ism.store.getState().searchingForFacetValues).toBe(false); - - expect(searchClient.searchForFacetValues).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - params: expect.objectContaining({ - facetName: 'facetName', - facetQuery: 'query', - maxFacetHits: 10, - }), - }), - ]) - ); - - expect(ism.store.getState().resultsFacetValues).toEqual({ - facetName: [expect.objectContaining({ value: 'results' })], - query: 'query', - }); - }); - - it('updates the store and searches with maxFacetHits', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - // We have to register the facet to be able to search on it - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.addFacet('facetName'), - props: {}, - context: {}, - }); - - await wait(0); - - ism.onSearchForFacetValues({ - facetName: 'facetName', - query: 'query', - maxFacetHits: 25, - }); - - await wait(0); - - expect(searchClient.searchForFacetValues).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - params: expect.objectContaining({ - maxFacetHits: 25, - }), - }), - ]) - ); - }); - - it('updates the store and searches with maxFacetHits out of range (higher)', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - // We have to register the facet to be able to search on it - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.addFacet('facetName'), - props: {}, - context: {}, - }); - - await wait(0); - - ism.onSearchForFacetValues({ - facetName: 'facetName', - query: 'query', - maxFacetHits: 125, - }); - - await wait(0); - - expect(searchClient.searchForFacetValues).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - params: expect.objectContaining({ - maxFacetHits: 100, - }), - }), - ]) - ); - }); - - it('updates the store and searches with maxFacetHits out of range (lower)', async () => { - const searchClient = createSearchClient({}); - - const ism = createInstantSearchManager({ - indexName: 'index', - searchClient, - }); - - // We have to register the facet to be able to search on it - ism.widgetsManager.registerWidget({ - getSearchParameters: (params) => params.addFacet('facetName'), - props: {}, - context: {}, - }); - - await wait(0); - - ism.onSearchForFacetValues({ - facetName: 'facetName', - query: 'query', - maxFacetHits: 0, - }); - - await wait(0); - - expect(searchClient.searchForFacetValues).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - params: expect.objectContaining({ - maxFacetHits: 1, - }), - }), - ]) - ); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/createStore.js b/packages/react-instantsearch-core/src/core/__tests__/createStore.js deleted file mode 100644 index 741926aea6..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/createStore.js +++ /dev/null @@ -1,49 +0,0 @@ -import createStore from '../createStore'; - -describe('createStore', () => { - describe('getState', () => { - it('retrieves the current state of the store', () => { - const initialState = {}; - const store = createStore(initialState); - expect(store.getState()).toBe(initialState); - }); - }); - - describe('setState', () => { - it('sets a new state', () => { - const initialState = {}; - const store = createStore(initialState); - const newState = {}; - store.setState(newState); - expect(store.getState()).toBe(newState); - }); - }); - - describe('subscribe', () => { - it('subscribes to new states', () => { - const initialState = {}; - const store = createStore(initialState); - const listener = jest.fn(); - store.subscribe(listener); - const newState = {}; - expect(listener.mock.calls).toHaveLength(0); - store.setState(newState); - expect(listener.mock.calls).toHaveLength(1); - }); - - it('returns a method to unsubscribe', () => { - const initialState = {}; - const store = createStore(initialState); - const listener = jest.fn(); - const unsubscribe = store.subscribe(listener); - const newState = {}; - expect(listener.mock.calls).toHaveLength(0); - store.setState(newState); - expect(listener.mock.calls).toHaveLength(1); - unsubscribe(); - const newerState = {}; - store.setState(newerState); - expect(listener.mock.calls).toHaveLength(1); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/createWidgetsManager.js b/packages/react-instantsearch-core/src/core/__tests__/createWidgetsManager.js deleted file mode 100644 index bef1cd171d..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/createWidgetsManager.js +++ /dev/null @@ -1,39 +0,0 @@ -import createWidgetsManager from '../createWidgetsManager'; - -describe('createWidgetsManager', () => { - describe('registerWidget', () => { - it('adds the widget to the widgets list', () => { - const wm = createWidgetsManager(() => null); - const widget = {}; - wm.registerWidget(widget); - expect(wm.getWidgets()[0]).toBe(widget); - }); - - it('returns an unregister method', () => { - const wm = createWidgetsManager(() => null); - const unregister = wm.registerWidget({}); - unregister(); - expect(wm.getWidgets()).toHaveLength(0); - }); - - it('schedules an update', () => { - const onUpdate = jest.fn(); - const wm = createWidgetsManager(onUpdate); - wm.registerWidget({}); - return Promise.resolve().then(() => { - expect(onUpdate.mock.calls).toHaveLength(1); - }); - }); - }); - - describe('update', () => { - it('schedules an update', () => { - const onUpdate = jest.fn(); - const wm = createWidgetsManager(onUpdate); - wm.update(); - return Promise.resolve().then(() => { - expect(onUpdate.mock.calls).toHaveLength(1); - }); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/highlight.js b/packages/react-instantsearch-core/src/core/__tests__/highlight.js deleted file mode 100644 index 832070fb5d..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/highlight.js +++ /dev/null @@ -1,148 +0,0 @@ -import { parseAlgoliaHit } from '../highlight'; - -describe('parseAlgoliaHit()', () => { - it('does not break when there is a missing attribute', () => { - const attribute = 'attr'; - const out = parseAlgoliaHit({ - attribute, - hit: {}, - highlightProperty: '_highlightResult', - }); - expect(out).toEqual([]); - }); - - it('creates a single element when there is no tag', () => { - const value = 'foo bar baz'; - const attribute = 'attr'; - const out = parseAlgoliaHit({ - attribute, - hit: createHit(attribute, value), - highlightProperty: '_highlightResult', - }); - expect(out).toEqual([{ isHighlighted: false, value }]); - }); - - it('creates a single element when there is only a tag', () => { - const textValue = 'foo bar baz'; - const value = `${textValue}`; - const attribute = 'attr'; - const out = parseAlgoliaHit({ - attribute, - hit: createHit(attribute, value), - highlightProperty: '_highlightResult', - }); - expect(out).toEqual([{ value: textValue, isHighlighted: true }]); - }); - - it('fetches and parses a deep attribute', () => { - const textValue = 'foo bar baz'; - const value = `${textValue}`; - const hit = { - lvl0: { lvl1: { lvl2: value } }, - _highlightResult: { - lvl0: { lvl1: { lvl2: { value } } }, - }, - }; - const out = parseAlgoliaHit({ - attribute: 'lvl0.lvl1.lvl2', - hit, - highlightProperty: '_highlightResult', - }); - expect(out).toEqual([{ value: textValue, isHighlighted: true }]); - }); - - it('parses the string and returns the part that are highlighted - 1 big highlight', () => { - const str = 'like algolia does algolia'; - const hit = createHit('attr', str); - const parsed = parseAlgoliaHit({ - attribute: 'attr', - hit, - highlightProperty: '_highlightResult', - }); - expect(parsed).toEqual([ - { value: 'like ', isHighlighted: false }, - { value: 'al', isHighlighted: true }, - { value: 'golia does ', isHighlighted: false }, - { value: 'al', isHighlighted: true }, - { value: 'golia', isHighlighted: false }, - ]); - }); - - it('supports the array format, parses it and returns the part that is highlighted', () => { - const hit = { - tags: ['litterature', 'biology', 'photography'], - _highlightResult: { - tags: [ - { value: 'litterature' }, - { value: 'biology' }, - { value: 'photography' }, - ], - }, - }; - - const actual = parseAlgoliaHit({ - attribute: 'tags', - hit, - highlightProperty: '_highlightResult', - }); - - const exepectation = [ - [{ value: 'litterature', isHighlighted: false }], - [{ value: 'biology', isHighlighted: false }], - [ - { value: 'photo', isHighlighted: true }, - { value: 'graphy', isHighlighted: false }, - ], - ]; - - expect(actual).toEqual(exepectation); - }); - - it('parses the string and returns the part that are highlighted - same pre and post tag', () => { - const str = 'surpise **lo**l mouhahah roflmao **lo**utre'; - const hit = createHit('attr', str); - const parsed = parseAlgoliaHit({ - preTag: '**', - postTag: '**', - attribute: 'attr', - hit, - highlightProperty: '_highlightResult', - }); - expect(parsed).toEqual([ - { value: 'surpise ', isHighlighted: false }, - { value: 'lo', isHighlighted: true }, - { value: 'l mouhahah roflmao ', isHighlighted: false }, - { value: 'lo', isHighlighted: true }, - { value: 'utre', isHighlighted: false }, - ]); - }); - - it('throws when hit is `null`', () => { - expect( - parseAlgoliaHit.bind(null, { - attribute: 'unknownattribute', - hit: null, - highlightProperty: '_highlightResult', - }) - ).toThrow('`hit`, the matching record, must be provided'); - }); - - it('throws when hit is `undefined`', () => { - expect( - parseAlgoliaHit.bind(null, { - attribute: 'unknownAttribute', - hit: undefined, - highlightProperty: '_highlightResult', - }) - ).toThrow('`hit`, the matching record, must be provided'); - }); -}); - -function createHit(attribute, value) { - return { - [attribute]: value, - _highlightResult: { - [attribute]: { value }, - }, - }; -} diff --git a/packages/react-instantsearch-core/src/core/__tests__/indexUtils.js b/packages/react-instantsearch-core/src/core/__tests__/indexUtils.js deleted file mode 100644 index a0c564d6a7..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/indexUtils.js +++ /dev/null @@ -1,756 +0,0 @@ -import { - refineValue, - getCurrentRefinementValue, - cleanUpValue, - getResults, -} from '../indexUtils'; - -describe('utility method for manipulating the search state', () => { - describe('when there is a single index', () => { - const context = { ais: { mainTargetedIndex: 'index' } }; - it('refine with no namespace', () => { - let searchState = {}; - let nextRefinement = { refinement: 'refinement' }; - let resetPage = false; - - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage - ); - - expect(searchState).toEqual({ refinement: 'refinement' }); - - nextRefinement = { another: 'another' }; - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage - ); - - expect(searchState).toEqual({ - refinement: 'refinement', - another: 'another', - }); - - nextRefinement = { last: 'last' }; - resetPage = true; - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage - ); - - expect(searchState).toEqual({ - page: 1, - last: 'last', - refinement: 'refinement', - another: 'another', - }); - }); - - it('refine with namespace', () => { - let searchState = {}; - let nextRefinement = { refinement: 'refinement' }; - let resetPage = false; - - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage, - 'namespace' - ); - - expect(searchState).toEqual({ namespace: { refinement: 'refinement' } }); - - nextRefinement = { another: 'another' }; - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage, - 'namespace' - ); - - expect(searchState).toEqual({ - namespace: { refinement: 'refinement', another: 'another' }, - }); - - nextRefinement = { another: 'another' }; - resetPage = true; - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage, - 'namespace' - ); - - expect(searchState).toEqual({ - page: 1, - namespace: { refinement: 'refinement', another: 'another' }, - }); - }); - - describe('getCurrentRefinementValue', () => { - it('retrieves the current refinement value', () => { - const searchState = { - page: 1, - last: 'last', - refinement: 'refinement', - another: 'another', - namespace: { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }, - }; - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'refinement', - null - ) - ).toEqual('refinement'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.another', - null - ) - ).toEqual('another'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.nested.another', - null - ) - ).toEqual('nested.another'); - }); - - it('retrieves default value', () => { - expect( - getCurrentRefinementValue( - {}, - {}, - context, - 'refinement', - 'defaultValue' - ) - ).toEqual('defaultValue'); - - expect( - getCurrentRefinementValue( - { defaultRefinement: 'defaultRefinement' }, - {}, - context, - 'refinement', - null - ) - ).toEqual('defaultRefinement'); - }); - - it('retrieves from objects without prototype', () => { - const searchState = Object.create(null); - searchState.page = 1; - searchState.last = 'last'; - searchState.refinement = 'refinement'; - searchState.another = 'another'; - searchState.namespace = { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }; - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'refinement', - null - ) - ).toEqual('refinement'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.another', - null - ) - ).toEqual('another'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.nested.another', - null - ) - ).toEqual('nested.another'); - }); - }); - - it('clean up values', () => { - let searchState = { - page: 1, - last: 'last', - refinement: 'refinement', - another: 'another', - namespace: { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }, - }; - - searchState = cleanUpValue(searchState, context, 'refinement'); - - expect(searchState).toEqual({ - page: 1, - last: 'last', - another: 'another', - namespace: { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }, - }); - - searchState = cleanUpValue(searchState, context, 'namespace.another'); - - expect(searchState).toEqual({ - page: 1, - last: 'last', - another: 'another', - namespace: { - refinement: 'refinement', - 'nested.another': 'nested.another', - }, - }); - - searchState = cleanUpValue(searchState, context, 'namespace.refinement'); - - expect(searchState).toEqual({ - page: 1, - last: 'last', - another: 'another', - namespace: { 'nested.another': 'nested.another' }, - }); - - searchState = cleanUpValue( - searchState, - context, - 'namespace.nested.another' - ); - - expect(searchState).toEqual({ - page: 1, - last: 'last', - another: 'another', - namespace: {}, - }); - }); - - it('get results', () => { - const searchResults = { results: { hits: ['some'] } }; - - const results = getResults(searchResults, context); - - expect(results).toEqual({ hits: ['some'] }); - }); - }); - - describe('when there are multiple index', () => { - let context = { multiIndexContext: { targetedIndex: 'first' } }; - it('refine with no namespace', () => { - let searchState = {}; - let nextRefinement = { refinement: 'refinement' }; - - searchState = refineValue(searchState, nextRefinement, context); - - expect(searchState).toEqual({ - indices: { first: { refinement: 'refinement' } }, - }); - - nextRefinement = { another: 'another' }; - searchState = refineValue(searchState, nextRefinement, context); - - expect(searchState).toEqual({ - indices: { first: { refinement: 'refinement', another: 'another' } }, - }); - - nextRefinement = { last: 'last' }; - const resetPage = true; - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage - ); - - expect(searchState).toEqual({ - indices: { - first: { - page: 1, - refinement: 'refinement', - another: 'another', - last: 'last', - }, - }, - }); - }); - - it('refine with namespace', () => { - let searchState = {}; - let nextRefinement = { refinement: 'refinement' }; - let resetPage = false; - - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage, - 'namespace' - ); - - expect(searchState).toEqual({ - indices: { first: { namespace: { refinement: 'refinement' } } }, - }); - - nextRefinement = { another: 'another' }; - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage, - 'namespace' - ); - - expect(searchState).toEqual({ - indices: { - first: { - page: 1, - namespace: { refinement: 'refinement', another: 'another' }, - }, - }, - }); - - nextRefinement = { another: 'another' }; - resetPage = true; - searchState = refineValue( - searchState, - nextRefinement, - context, - resetPage, - 'namespace' - ); - - expect(searchState).toEqual({ - indices: { - first: { - page: 1, - namespace: { refinement: 'refinement', another: 'another' }, - }, - }, - }); - }); - - describe('getCurrentRefinementValue', () => { - it('retrieves the current refinement value', () => { - const searchState = { - page: 1, - refinement: 'refinement', - indices: { - first: { - refinement: 'refinement', - namespace: { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }, - }, - }, - }; - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'refinement', - null - ) - ).toEqual('refinement'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.refinement', - null - ) - ).toEqual('refinement'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.another', - null - ) - ).toEqual('another'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.nested.another', - null - ) - ).toEqual('nested.another'); - - expect( - getCurrentRefinementValue( - {}, - {}, - context, - 'refinement', - 'defaultValue' - ) - ).toEqual('defaultValue'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'anotherNamespace.refinement.top', - 'defaultValue' - ) - ).toEqual('defaultValue'); - - expect( - getCurrentRefinementValue( - { defaultRefinement: 'defaultRefinement' }, - {}, - context, - 'refinement', - null - ) - ).toEqual('defaultRefinement'); - }); - - it('retrieves default value', () => { - const searchState = { - page: 1, - refinement: 'refinement', - indices: {}, - }; - - const defaultRefinement = null; - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'refinement', - defaultRefinement - ) - ).toBe(defaultRefinement); - }); - - it('retrieves from objects without prototype', () => { - const searchState = Object.create(null); - - searchState.page = 1; - searchState.refinement = 'refinement'; - searchState.indices = { - first: { - refinement: 'refinement', - namespace: { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }, - }, - }; - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'refinement', - null - ) - ).toEqual('refinement'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.refinement', - null - ) - ).toEqual('refinement'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.another', - null - ) - ).toEqual('another'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'namespace.nested.another', - null - ) - ).toEqual('nested.another'); - - expect( - getCurrentRefinementValue( - {}, - {}, - context, - 'refinement', - 'defaultValue' - ) - ).toEqual('defaultValue'); - - expect( - getCurrentRefinementValue( - {}, - searchState, - context, - 'anotherNamespace.refinement.top', - 'defaultValue' - ) - ).toEqual('defaultValue'); - - expect( - getCurrentRefinementValue( - { defaultRefinement: 'defaultRefinement' }, - {}, - context, - 'refinement', - null - ) - ).toEqual('defaultRefinement'); - }); - }); - - it('clean up values', () => { - let searchState = { - page: 1, - indices: { - first: { - refinement: 'refinement', - namespace: { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }, - }, - }, - }; - - searchState = cleanUpValue(searchState, context, 'refinement'); - - expect(searchState).toEqual({ - page: 1, - indices: { - first: { - namespace: { - refinement: 'refinement', - another: 'another', - 'nested.another': 'nested.another', - }, - }, - }, - }); - - searchState = cleanUpValue(searchState, context, 'namespace.another'); - - expect(searchState).toEqual({ - page: 1, - indices: { - first: { - namespace: { - refinement: 'refinement', - 'nested.another': 'nested.another', - }, - }, - }, - }); - - searchState = cleanUpValue(searchState, context, 'namespace.refinement'); - - expect(searchState).toEqual({ - page: 1, - indices: { - first: { - namespace: { - 'nested.another': 'nested.another', - }, - }, - }, - }); - - searchState = cleanUpValue( - searchState, - context, - 'namespace.nested.another' - ); - - expect(searchState).toEqual({ - page: 1, - indices: { - first: { - namespace: {}, - }, - }, - }); - - // When nothing is refine the searchState doesn't have the indices - // attribute even if we are in a context with . This should - // not happen, the searchState should always be in sync (indices + no values). - searchState = { - page: 1, - }; - - searchState = cleanUpValue(searchState, context, 'namespace.refinement'); - - expect(searchState).toEqual({ - page: 1, - namespace: {}, - }); - - // It might happen that we try to cleanUp an index that is not - // present on the searchState. We should not throw an error in - // that case, just return the searchState as it is. - searchState = { - page: 1, - indices: { - second: { - page: 1, - }, - }, - }; - - searchState = cleanUpValue(searchState, context, 'menu.category'); - - expect(searchState).toEqual({ - page: 1, - indices: { - second: { - page: 1, - }, - }, - }); - }); - - it('get results', () => { - let searchResults = { results: { first: { some: 'results' } } }; - - let results = getResults(searchResults, context); - - expect(results).toEqual({ some: 'results' }); - - searchResults = { results: { first: { some: 'results' } } }; - - results = getResults(searchResults, { - ais: { mainTargetedIndex: 'first' }, - }); - - expect(results).toEqual({ some: 'results' }); - }); - - it('refine shared widgets should reset indices page to 1 with resetPage', () => { - context = {}; - const resetPage = true; - const nextRefinement = { query: 'new' }; - const searchState = { - indices: { - first: { page: 3 }, - second: { page: 3 }, - }, - }; - - const actual = refineValue( - searchState, - nextRefinement, - context, - resetPage - ); - - const expectation = { - query: 'new', - page: 1, - indices: { - first: { page: 1 }, - second: { page: 1 }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('refine shared widgets should not reset indices page to 1 without resetPage', () => { - context = {}; - const resetPage = false; - const nextRefinement = { query: 'new' }; - const searchState = { - indices: { - first: { page: 3 }, - second: { page: 3 }, - }, - }; - - const actual = refineValue( - searchState, - nextRefinement, - context, - resetPage - ); - - const expectation = { - query: 'new', - indices: { - first: { page: 3 }, - second: { page: 3 }, - }, - }; - - expect(actual).toEqual(expectation); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/metadata.tsx b/packages/react-instantsearch-core/src/core/__tests__/metadata.tsx deleted file mode 100644 index 719ae17f5c..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/metadata.tsx +++ /dev/null @@ -1,259 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { render } from '@testing-library/react'; -import algoliasearch from 'algoliasearch/lite'; -import React from 'react'; - -import { connectSearchBox } from '../..'; -import { InstantSearchProvider } from '../context'; -import createStore from '../createStore'; -import createWidgetsManager from '../createWidgetsManager'; -import { isMetadataEnabled, getMetadataPayload } from '../metadata'; - -import type { SearchClient } from '../../widgets/InstantSearch'; - -const { window: originalWindow } = global; - -Object.defineProperty( - window.navigator, - 'userAgent', - ((value) => ({ - get() { - return value; - }, - set(v: string) { - value = v; - }, - }))(window.navigator.userAgent) -); - -const defaultUserAgent = - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15'; -const algoliaUserAgent = 'Algolia Crawler 5.3.2'; - -function setUserAgent(userAgent: string) { - // casting to any, as userAgent is set as readonly by TypeScript - (global.navigator as any).userAgent = userAgent; -} - -describe('isMetadataEnabled', () => { - afterEach(() => { - setUserAgent(defaultUserAgent); - global.window = originalWindow; - }); - - it('does not enable on normal user agent', () => { - setUserAgent(defaultUserAgent); - - expect(isMetadataEnabled()).toBe(false); - }); - - it("does not enable when there's no window", () => { - setUserAgent(algoliaUserAgent); - - // @ts-expect-error - delete global.window; - - expect(isMetadataEnabled()).toBe(false); - }); - - it("does not enable when there's no navigator (react native)", () => { - setUserAgent(algoliaUserAgent); - - // @ts-expect-error - delete global.window; - // @ts-expect-error - global.window = {}; - - expect(isMetadataEnabled()).toBe(false); - }); - - it('metadata enabled returns true', () => { - setUserAgent(algoliaUserAgent); - - expect(isMetadataEnabled()).toBe(true); - }); -}); - -describe('getMetadataPayload', () => { - describe('user agent', () => { - test('extracts user agent from algoliasearch', () => { - const widgetsManager = createWidgetsManager(() => {}); - const searchClient = algoliasearch( - 'appId', - 'apiKey' - ) as unknown as SearchClient; - - const { ua } = getMetadataPayload( - widgetsManager.getWidgets(), - searchClient - ); - - expect(ua).toEqual( - expect.stringMatching( - /^Algolia for JavaScript \((\d+\.?)+\); Node\.js \((\d+\.?)+\)$/ - ) - ); - }); - - test('extracts user agent from algoliasearch v3', () => { - const widgetsManager = createWidgetsManager(() => {}); - const searchClient = { - search() { - return Promise.resolve({}); - }, - searchForFacetValues() { - return Promise.resolve({}); - }, - _ua: 'v3 style user agent', - }; - - const { ua } = getMetadataPayload( - widgetsManager.getWidgets(), - searchClient - ); - - expect(ua).toEqual('v3 style user agent'); - }); - - test('extracts nothing if absent', () => { - const widgetsManager = createWidgetsManager(() => {}); - const searchClient = { - search() { - return Promise.resolve({}); - }, - searchForFacetValues() { - return Promise.resolve({}); - }, - }; - - const { ua } = getMetadataPayload( - widgetsManager.getWidgets(), - searchClient - ); - - expect(ua).toBe(undefined); - }); - }); - - describe('widgets', () => { - test('detects empty widgets', () => { - const widgetsManager = createWidgetsManager(() => {}); - const searchClient = algoliasearch( - 'appId', - 'apiKey' - ) as unknown as SearchClient; - - const { widgets } = getMetadataPayload( - widgetsManager.getWidgets(), - searchClient - ); - - expect(widgets).toEqual([]); - }); - - test('detects a widget', () => { - const widgetsManager = createWidgetsManager(() => {}); - const searchClient = algoliasearch( - 'appId', - 'apiKey' - ) as unknown as SearchClient; - - const RawSearchBox = () => null; - - const SearchBox = connectSearchBox(RawSearchBox); - - render( - - - - ); - - const { widgets } = getMetadataPayload( - widgetsManager.getWidgets(), - searchClient - ); - - expect(widgets).toEqual([ - { - $$type: 'ais.searchBox', - $$widgetType: undefined, - displayName: 'AlgoliaSearchBox', - params: [], - }, - ]); - }); - - test('detects $$widgetType', () => { - const widgetsManager = createWidgetsManager(() => {}); - const searchClient = algoliasearch( - 'appId', - 'apiKey' - ) as unknown as SearchClient; - - const RawSearchBox = () => null; - - const SearchBox = connectSearchBox(RawSearchBox, { - $$widgetType: 'ais.searchBox', - }); - - render( - - - - ); - - const { widgets } = getMetadataPayload( - widgetsManager.getWidgets(), - searchClient - ); - - expect(widgets).toEqual([ - { - $$type: 'ais.searchBox', - $$widgetType: 'ais.searchBox', - displayName: 'AlgoliaSearchBox', - params: [], - }, - ]); - }); - }); -}); - -function FakeProvider({ - children, - widgetsManager, -}: { - children: React.ReactNode; - widgetsManager: any; -}) { - const createFakeState = () => ({ - widgets: {}, - results: {}, - resultsFacetValues: {}, - searching: false, - searchingForFacetValues: false, - isSearchStalled: false, - metadata: [], - error: new Error(), - }); - - return ( - '', - mainTargetedIndex: '', - onInternalStateUpdate: () => {}, - onSearchForFacetValues: () => {}, - onSearchParameters: () => {}, - onSearchStateChange: () => {}, - store: createStore(createFakeState()), - widgetsManager, - }} - > - {children} - - ); -} diff --git a/packages/react-instantsearch-core/src/core/__tests__/translatable.js b/packages/react-instantsearch-core/src/core/__tests__/translatable.js deleted file mode 100644 index f102f16277..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/translatable.js +++ /dev/null @@ -1,43 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import translatable from '../translatable'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('translatable', () => { - it('provides a translate prop to the composed component', () => { - const Dummy = () => null; - const defaultTranslations = { - sup: 'hey', - thing: (n) => `${n} things`, - }; - const Translated = translatable(defaultTranslations)(Dummy); - const { translate } = shallow() - .find(Dummy) - .props(); - expect(translate('sup')).toBe('hey'); - expect(translate('thing', 20)).toBe('20 things'); - }); - - it('uses the translations passed as props before the default', () => { - const Dummy = () => null; - const defaultTranslations = { - sup: 'hey', - thing: (n) => `${n} things`, - fallbackThing: 'hi', - }; - const translations = { - sup: 'hoy', - fallbackThing: undefined, - }; - const Translated = translatable(defaultTranslations)(Dummy); - const { translate } = shallow() - .find(Dummy) - .props(); - expect(translate('sup')).toBe('hoy'); - expect(translate('thing', 20)).toBe('20 things'); - expect(translate('fallbackThing')).toBe(undefined); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/__tests__/utils.js b/packages/react-instantsearch-core/src/core/__tests__/utils.js deleted file mode 100644 index 9415cf0d45..0000000000 --- a/packages/react-instantsearch-core/src/core/__tests__/utils.js +++ /dev/null @@ -1,364 +0,0 @@ -import { Component } from 'react'; - -import * as utils from '../utils'; - -describe('utils', () => { - describe('getDisplayName', () => { - it('gets the right displayName from classes', () => { - class SuperComponent extends Component { - render() { - return null; - } - } - - expect(utils.getDisplayName(SuperComponent)).toBe('SuperComponent'); - }); - - // this works because babel turns arrows functions to named function expressions - it('gets the right displayName from stateless components', () => { - const SuperComponent = () => null; // => var SuperComponent = function SuperComponent() {} - expect(utils.getDisplayName(SuperComponent)).toBe('SuperComponent'); - }); - - it('sets a default displayName when not able to find one', () => { - expect(utils.getDisplayName(() => null)).toBe('UnknownComponent'); - }); - }); - - describe('defer', () => { - it('calling a function asynchronously, should be done as soon as possible.', () => { - let count = 0; - - utils.defer(() => { - count = 1; - }); - - return Promise.resolve().then(() => { - expect(count).toEqual(1); - }); - }); - }); - - describe('remove empty key', () => { - it('empty key should be removed', () => { - const state = { - query: '', - page: 2, - sortBy: 'mostPopular', - range: { - price: { - min: 20, - max: 3000, - }, - }, - refinementList: {}, - indices: { - index1: { - configure: { - hitsPerPage: 3, - refinementList: {}, - }, - }, - index2: { - configure: { - hitsPerPage: 10, - }, - refinementList: { - fruits: ['lemon', 'orange'], - }, - }, - }, - }; - - const newState = utils.removeEmptyKey(state); - - expect(newState).toEqual({ - query: '', - page: 2, - sortBy: 'mostPopular', - range: { - price: { - min: 20, - max: 3000, - }, - }, - indices: { - index1: { - configure: { - hitsPerPage: 3, - }, - }, - index2: { - configure: { - hitsPerPage: 10, - }, - refinementList: { - fruits: ['lemon', 'orange'], - }, - }, - }, - }); - }); - - it('does not do anything on empty root', () => { - expect(utils.removeEmptyKey({})).toEqual({}); - }); - - it('does empty out objects', () => { - expect(utils.removeEmptyKey({ test: {} })).toEqual({}); - expect(utils.removeEmptyKey({ test: { dog: {} } })).toEqual({ - // this one stays, because we have no multipass algorithm - test: {}, - }); - }); - - it('does not empty out arrays', () => { - expect(utils.removeEmptyKey({ test: [] })).toEqual({ test: [] }); - expect(utils.removeEmptyKey({ test: { dog: [] } })).toEqual({ - test: { dog: [] }, - }); - }); - }); - - describe('removeEmptyArraysFromObject', () => { - it('removes empty arrays from objects', () => { - expect( - utils.removeEmptyArraysFromObject({ - disjunctiveFacets: [], - facetFilters: ['categories'], - facets: [], - filters: 'NOT objectID:1', - hierarchicalFacets: [], - optionalFilters: ['brand:Amazon'], - sumOrFiltersScores: true, - tagRefinements: [], - }) - ).toEqual({ - facetFilters: ['categories'], - filters: 'NOT objectID:1', - optionalFilters: ['brand:Amazon'], - sumOrFiltersScores: true, - }); - }); - }); - - describe('addAbsolutePositions', () => { - const allHits = [ - { objectID: '1' }, - { objectID: '2' }, - { objectID: '3' }, - { objectID: '4' }, - { objectID: '5' }, - { objectID: '6' }, - ]; - const hitsPerPage = 2; - it('should add __positions 1 and 2 on page 0', () => { - const hits = allHits.slice(0, 2); - const page = 0; - expect(utils.addAbsolutePositions(hits, hitsPerPage, page)).toEqual([ - { objectID: '1', __position: 1 }, - { objectID: '2', __position: 2 }, - ]); - }); - it('should add __positions 5 and 6 on page 2', () => { - const hits = allHits.slice(4, 6); - const page = 2; - expect(utils.addAbsolutePositions(hits, hitsPerPage, page)).toEqual([ - { objectID: '5', __position: 5 }, - { objectID: '6', __position: 6 }, - ]); - }); - }); - - describe('addQueryID', () => { - const hits = [{ objectID: '1' }, { objectID: '2' }]; - it('should passed __queryID to hits', () => { - expect(utils.addQueryID(hits, 'theQueryID')).toEqual([ - { objectID: '1', __queryID: 'theQueryID' }, - { objectID: '2', __queryID: 'theQueryID' }, - ]); - }); - }); - - describe('getPropertyByPath', () => { - it('returns undefined on non-object root', () => { - expect(utils.getPropertyByPath(false, 'fake')).toBeUndefined(); - expect(utils.getPropertyByPath(undefined, 'fake')).toBeUndefined(); - expect(utils.getPropertyByPath(null, 'fake.nested')).toBeUndefined(); - }); - - it('returns path if exists', () => { - expect(utils.getPropertyByPath({ dog: true }, 'dog')).toBe(true); - expect( - utils.getPropertyByPath( - { i: { like: { properties: false } } }, - 'i.like.properties' - ) - ).toBe(false); - expect( - utils.getPropertyByPath({ true: { nested: 'ok' } }, 'true.nested') - ).toBe('ok'); - }); - - it('accepts a pre-split path as array', () => { - expect(utils.getPropertyByPath({ dog: true }, ['dog'])).toBe(true); - expect( - utils.getPropertyByPath({ i: { like: { properties: false } } }, [ - 'i', - 'like', - 'properties', - ]) - ).toBe(false); - expect( - utils.getPropertyByPath({ true: { nested: 'ok' } }, ['true', 'nested']) - ).toBe('ok'); - }); - - it('does not split a pre-split path as array', () => { - expect(utils.getPropertyByPath({ dog: true }, ['dog'])).toBe(true); - expect( - utils.getPropertyByPath({ i: { like: { properties: false } } }, [ - 'i', - 'like.properties', - ]) - ).toBeUndefined(); - expect( - utils.getPropertyByPath({ true: { nested: 'ok' } }, ['true.nested']) - ).toBeUndefined(); - }); - - it('returns undefined if does not exist', () => { - expect( - utils.getPropertyByPath( - { name: { known: { value: '' } } }, - 'name.unkown' - ) - ).toBeUndefined(); - - expect(utils.getPropertyByPath({ name: false }, 'name.unkown')).toBe( - undefined - ); - }); - - it('returns indexed path if exists', () => { - expect( - utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array.2') - ).toBe('c'); - expect( - utils.getPropertyByPath( - { array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] }, - 'array.2.letter' - ) - ).toBe('c'); - - expect( - utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array[2]') - ).toBe('c'); - expect( - utils.getPropertyByPath( - { array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] }, - 'array[2].letter' - ) - ).toBe('c'); - }); - - it('returns undefined if indexed path does not exist', () => { - expect( - utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array.4') - ).toBeUndefined(); - expect( - utils.getPropertyByPath( - { array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] }, - 'array.5.letter' - ) - ).toBeUndefined(); - - expect( - utils.getPropertyByPath({ array: ['a', 'b', 'c'] }, 'array[4]') - ).toBeUndefined(); - expect( - utils.getPropertyByPath( - { array: [{ letter: 'a' }, { letter: 'b' }, { letter: 'c' }] }, - 'array[5].letter' - ) - ).toBeUndefined(); - }); - }); - - describe('find', () => { - test('returns the first match based on the comparator', () => { - expect( - utils.find([1], () => { - return true; - }) - ).toBe(1); - expect( - utils.find([1, 2], () => { - return true; - }) - ).toBe(1); - - expect( - utils.find([{ nice: false }, { nice: true }], function (el) { - return el.nice; - }) - ).toEqual({ nice: true }); - }); - - test('returns undefined in non-found cases', () => { - expect( - utils.find([], () => { - return false; - }) - ).toBeUndefined(); - expect( - utils.find(undefined, () => { - return false; - }) - ).toBeUndefined(); - - expect(() => { - utils.find([1, 2, 3], undefined); - }).toThrow(); - }); - }); - - describe('getObjectType', () => { - test('returns the type of a string', () => { - expect(utils.getObjectType('string')).toEqual('String'); - }); - - test('returns the type of a number', () => { - expect(utils.getObjectType(2)).toEqual('Number'); - }); - - test('returns the type of a boolean', () => { - expect(utils.getObjectType(true)).toEqual('Boolean'); - expect(utils.getObjectType(false)).toEqual('Boolean'); - }); - - test('returns the type of an object', () => { - expect(utils.getObjectType({})).toEqual('Object'); - }); - - test('returns the type of an array', () => { - expect(utils.getObjectType([])).toEqual('Array'); - }); - - test('returns the type of a date', () => { - expect(utils.getObjectType(new Date())).toEqual('Date'); - }); - - test('returns the type of a function', () => { - expect(utils.getObjectType(() => {})).toEqual('Function'); - }); - - test('returns the type of undefined', () => { - expect(utils.getObjectType(undefined)).toEqual('Undefined'); - }); - - test('returns the type of null', () => { - expect(utils.getObjectType(null)).toEqual('Null'); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/core/context.ts b/packages/react-instantsearch-core/src/core/context.ts deleted file mode 100644 index 6cbfebbe73..0000000000 --- a/packages/react-instantsearch-core/src/core/context.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { createContext } from 'react'; - -import type { Store } from '../core/createStore'; -import type InstantSearch from '../widgets/InstantSearch'; - -export type InstantSearchContext = { - onInternalStateUpdate: InstantSearch['onWidgetsInternalStateUpdate']; - createHrefForState: InstantSearch['createHrefForState']; - onSearchForFacetValues: InstantSearch['onSearchForFacetValues']; - onSearchStateChange: InstantSearch['onSearchStateChange']; - onSearchParameters: InstantSearch['onSearchParameters']; - store: Store; - widgetsManager: any; - mainTargetedIndex: string; -}; - -export const instantSearchContext = createContext({ - onInternalStateUpdate: () => undefined, - createHrefForState: () => '#', - onSearchForFacetValues: () => undefined, - onSearchStateChange: () => undefined, - onSearchParameters: () => undefined, - store: {} as Store, - widgetsManager: {}, - mainTargetedIndex: '', -}); - -export const { - Consumer: InstantSearchConsumer, - Provider: InstantSearchProvider, -} = instantSearchContext; - -export type IndexContext = - | { - targetedIndex: string; - } - | undefined; - -export const { Consumer: IndexConsumer, Provider: IndexProvider } = - createContext(undefined); diff --git a/packages/react-instantsearch-core/src/core/createConnector.tsx b/packages/react-instantsearch-core/src/core/createConnector.tsx deleted file mode 100644 index 6c0ca934bf..0000000000 --- a/packages/react-instantsearch-core/src/core/createConnector.tsx +++ /dev/null @@ -1,403 +0,0 @@ -import React, { Component } from 'react'; -import isEqual from 'react-fast-compare'; - -import { InstantSearchConsumer, IndexConsumer } from './context'; -import { shallowEqual, getDisplayName, removeEmptyKey } from './utils'; - -import type { InstantSearchContext, IndexContext } from './context'; -import type { ElementType } from 'react'; - -export type ConnectorDescription = { - displayName: string; - $$type: string; - /** - * a function to filter the local state - */ - refine?: (...args: any[]) => any; - /** - * function transforming the local state to a SearchParameters - */ - getSearchParameters?: (...args: any[]) => any; - /** - * metadata of the widget (for current refinements) - */ - getMetadata?: (...args: any[]) => any; - /** - * hook after the state has changed - */ - transitionState?: (...args: any[]) => any; - /** - * transform the state into props passed to the wrapped component. - * Receives (props, widgetStates, searchState, metadata) and returns the local state. - */ - getProvidedProps: (...args: any[]) => any; - /** - * Receives props and return the id that will be used to identify the widget - */ - getId?: (...args: any[]) => string; - /** - * hook when the widget will unmount. Receives (props, searchState) and return a cleaned state. - */ - cleanUp?: (...args: any[]) => any; - searchForFacetValues?: (...args: any[]) => any; - shouldComponentUpdate?: (...args: any[]) => boolean; - /** - * PropTypes forwarded to the wrapped component. - */ - propTypes?: Record; // I can't find a definition for a propTypes object - defaultProps?: Record; -}; - -export type AdditionalWidgetProperties = { - $$widgetType?: string; -}; - -type ConnectorProps = { - contextValue: InstantSearchContext; - indexContextValue?: IndexContext; -}; - -export type ConnectedProps = TWidgetProps & ConnectorProps; - -type ConnectorState = { - providedProps: {}; -}; - -/** - * Connectors are the HOC used to transform React components - * into InstantSearch widgets. - * In order to simplify the construction of such connectors - * `createConnector` takes a description and transform it into - * a connector. - * @param {ConnectorDescription} connectorDesc the description of the connector - * @return {Connector} a function that wraps a component into - * an instantsearch connected one. - */ -export function createConnectorWithoutContext( - connectorDesc: ConnectorDescription -) { - if (!connectorDesc.displayName) { - throw new Error( - '`createConnector` requires you to provide a `displayName` property.' - ); - } - - const isWidget = - typeof connectorDesc.getSearchParameters === 'function' || - typeof connectorDesc.getMetadata === 'function' || - typeof connectorDesc.transitionState === 'function'; - - return ( - Composed: ElementType, - additionalWidgetProperties: AdditionalWidgetProperties = {} - ) => { - class Connector extends Component { - static displayName = `${connectorDesc.displayName}(${getDisplayName( - Composed - )})`; - static $$type = connectorDesc.$$type; - static $$widgetType = additionalWidgetProperties.$$widgetType; - static propTypes = connectorDesc.propTypes; - static defaultProps = connectorDesc.defaultProps; - static _connectorDesc = connectorDesc; - - unsubscribe?: () => void; - unregisterWidget?: () => void; - - cleanupTimerRef: ReturnType | null = null; - isUnmounting = false; - - state: ConnectorState = { - providedProps: this.getProvidedProps(this.props), - }; - - constructor(props: ConnectorProps) { - super(props); - - if (connectorDesc.getSearchParameters) { - this.props.contextValue.onSearchParameters( - connectorDesc.getSearchParameters.bind(this), - { - ais: this.props.contextValue, - multiIndexContext: this.props.indexContextValue, - }, - this.props, - connectorDesc.getMetadata && connectorDesc.getMetadata.bind(this), - connectorDesc.displayName - ); - } - } - - componentDidMount() { - if (this.cleanupTimerRef) { - clearTimeout(this.cleanupTimerRef); - this.cleanupTimerRef = null; - } - - this.unsubscribe = this.props.contextValue.store.subscribe(() => { - if (!this.isUnmounting) { - this.setState({ - providedProps: this.getProvidedProps(this.props), - }); - } - }); - - if (isWidget) { - this.unregisterWidget = - this.props.contextValue.widgetsManager.registerWidget(this); - } - } - - shouldComponentUpdate(nextProps: any, nextState: any) { - if (typeof connectorDesc.shouldComponentUpdate === 'function') { - return connectorDesc.shouldComponentUpdate.call( - this, - this.props, - nextProps, - this.state, - nextState - ); - } - - const propsEqual = shallowEqual(this.props, nextProps); - - if ( - this.state.providedProps === null || - nextState.providedProps === null - ) { - if (this.state.providedProps === nextState.providedProps) { - return !propsEqual; - } - return true; - } - - return ( - !propsEqual || - !shallowEqual(this.state.providedProps, nextState.providedProps) - ); - } - - componentDidUpdate(prevProps: any) { - if (!isEqual(prevProps, this.props)) { - this.setState({ - providedProps: this.getProvidedProps(this.props), - }); - - if (isWidget) { - this.props.contextValue.widgetsManager.update(); - - if (typeof connectorDesc.transitionState === 'function') { - this.props.contextValue.onSearchStateChange( - connectorDesc.transitionState.call( - this, - this.props, - this.props.contextValue.store.getState().widgets, - this.props.contextValue.store.getState().widgets - ) - ); - } - } - } - } - - componentWillUnmount() { - this.cleanupTimerRef = setTimeout(() => { - this.isUnmounting = true; - - if (this.unsubscribe) { - this.unsubscribe(); - } - - if (this.unregisterWidget) { - this.unregisterWidget(); - - if (typeof connectorDesc.cleanUp === 'function') { - const nextState = connectorDesc.cleanUp.call( - this, - this.props, - this.props.contextValue.store.getState().widgets - ); - - this.props.contextValue.store.setState({ - ...this.props.contextValue.store.getState(), - widgets: nextState, - }); - - this.props.contextValue.onSearchStateChange( - removeEmptyKey(nextState) - ); - } - } - }); - } - - getProvidedProps(props: any) { - const { - widgets, - results, - resultsFacetValues, - searching, - searchingForFacetValues, - isSearchStalled, - metadata, - error, - } = this.props.contextValue.store.getState(); - - const searchResults = { - results, - searching, - searchingForFacetValues, - isSearchStalled, - error, - }; - - return connectorDesc.getProvidedProps.call( - this, - props, - widgets, - searchResults, - metadata, - // @MAJOR: move this attribute on the `searchResults` it doesn't - // makes sense to have it into a separate argument. The search - // flags are on the object why not the results? - resultsFacetValues - ); - } - - getSearchParameters(searchParameters: any) { - if (typeof connectorDesc.getSearchParameters === 'function') { - return connectorDesc.getSearchParameters.call( - this, - searchParameters, - this.props, - this.props.contextValue.store.getState().widgets - ); - } - - return null; - } - - getMetadata(nextWidgetsState: any) { - if (typeof connectorDesc.getMetadata === 'function') { - return connectorDesc.getMetadata.call( - this, - this.props, - nextWidgetsState - ); - } - - return {}; - } - - transitionState(prevWidgetsState: any, nextWidgetsState: any) { - if (typeof connectorDesc.transitionState === 'function') { - return connectorDesc.transitionState.call( - this, - this.props, - prevWidgetsState, - nextWidgetsState - ); - } - - return nextWidgetsState; - } - - refine = (...args: any[]) => { - this.props.contextValue.onInternalStateUpdate( - // refine will always be defined here because the prop is only given conditionally - connectorDesc.refine!.call( - this, - this.props, - this.props.contextValue.store.getState().widgets, - ...args - ) - ); - }; - - createURL = (...args: any[]) => - this.props.contextValue.createHrefForState( - // refine will always be defined here because the prop is only given conditionally - connectorDesc.refine!.call( - this, - this.props, - this.props.contextValue.store.getState().widgets, - ...args - ) - ); - - searchForFacetValues = (...args: any[]) => { - this.props.contextValue.onSearchForFacetValues( - // searchForFacetValues will always be defined here because the prop is only given conditionally - connectorDesc.searchForFacetValues!.call( - this, - this.props, - this.props.contextValue.store.getState().widgets, - ...args - ) - ); - }; - - render() { - const { contextValue, ...props } = this.props; - const { providedProps } = this.state; - - if (providedProps === null) { - return null; - } - - const refineProps = - typeof connectorDesc.refine === 'function' - ? { refine: this.refine, createURL: this.createURL } - : {}; - - const searchForFacetValuesProps = - typeof connectorDesc.searchForFacetValues === 'function' - ? { searchForItems: this.searchForFacetValues } - : {}; - - return ( - - ); - } - } - - return Connector; - }; -} - -const createConnectorWithContext = - (connectorDesc: ConnectorDescription) => - ( - Composed: ElementType, - additionalWidgetProperties?: AdditionalWidgetProperties - ) => { - const Connector = createConnectorWithoutContext(connectorDesc)( - Composed, - additionalWidgetProperties - ); - - const ConnectorWrapper: React.FC = (props) => ( - - {(contextValue) => ( - - {(indexContextValue) => ( - - )} - - )} - - ); - - return ConnectorWrapper; - }; - -export default createConnectorWithContext; diff --git a/packages/react-instantsearch-core/src/core/createInstantSearchManager.d.ts b/packages/react-instantsearch-core/src/core/createInstantSearchManager.d.ts deleted file mode 100644 index 6f9b35a2f8..0000000000 --- a/packages/react-instantsearch-core/src/core/createInstantSearchManager.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default function createInstantSearchManager(props: { - indexName: string; - initialState: object; - searchClient: object; - resultsState?: object; - stalledSearchDelay?: number; -}): any; diff --git a/packages/react-instantsearch-core/src/core/createInstantSearchManager.js b/packages/react-instantsearch-core/src/core/createInstantSearchManager.js deleted file mode 100644 index c402519023..0000000000 --- a/packages/react-instantsearch-core/src/core/createInstantSearchManager.js +++ /dev/null @@ -1,644 +0,0 @@ -import algoliasearchHelper from 'algoliasearch-helper'; -import { version as ReactVersion } from 'react'; - -import createStore from './createStore'; -import createWidgetsManager from './createWidgetsManager'; -import { HIGHLIGHT_TAGS } from './highlight'; -import { hasMultipleIndices } from './indexUtils'; -import version from './version'; - -function addAlgoliaAgents(searchClient) { - if (typeof searchClient.addAlgoliaAgent === 'function') { - searchClient.addAlgoliaAgent(`react (${ReactVersion})`); - searchClient.addAlgoliaAgent(`react-instantsearch (${version})`); - } -} - -const isMultiIndexContext = (widget) => - hasMultipleIndices({ - ais: widget.props.contextValue, - multiIndexContext: widget.props.indexContextValue, - }); -const isTargetedIndexEqualIndex = (widget, indexId) => - widget.props.indexContextValue.targetedIndex === indexId; - -// Relying on the `indexId` is a bit brittle to detect the `Index` widget. -// Since it's a class we could rely on `instanceof` or similar. We never -// had an issue though. Works for now. -const isIndexWidget = (widget) => Boolean(widget.props.indexId); -const isIndexWidgetEqualIndex = (widget, indexId) => - widget.props.indexId === indexId; - -const sortIndexWidgetsFirst = (firstWidget, secondWidget) => { - const isFirstWidgetIndex = isIndexWidget(firstWidget); - const isSecondWidgetIndex = isIndexWidget(secondWidget); - - if (isFirstWidgetIndex && !isSecondWidgetIndex) { - return -1; - } - if (!isFirstWidgetIndex && isSecondWidgetIndex) { - return 1; - } - return 0; -}; - -// This function is copied from the algoliasearch v4 API Client. If modified, -// consider updating it also in `serializeQueryParameters` from `@algolia/transporter`. -function serializeQueryParameters(parameters) { - const isObjectOrArray = (value) => - Object.prototype.toString.call(value) === '[object Object]' || - Object.prototype.toString.call(value) === '[object Array]'; - - const encode = (format, ...args) => { - let i = 0; - return format.replace(/%s/g, () => encodeURIComponent(args[i++])); - }; - - return Object.keys(parameters) - .map((key) => - encode( - '%s=%s', - key, - isObjectOrArray(parameters[key]) - ? JSON.stringify(parameters[key]) - : parameters[key] - ) - ) - .join('&'); -} - -/** - * Creates a new instance of the InstantSearchManager which controls the widgets and - * trigger the search when the widgets are updated. - * @param {string} indexName - the main index name - * @param {object} initialState - initial widget state - * @param {object} SearchParameters - optional additional parameters to send to the algolia API - * @param {number} stalledSearchDelay - time (in ms) after the search is stalled - * @return {InstantSearchManager} a new instance of InstantSearchManager - */ -export default function createInstantSearchManager({ - indexName, - initialState = {}, - searchClient, - resultsState, - stalledSearchDelay, -}) { - const helper = algoliasearchHelper(searchClient, indexName, { - ...HIGHLIGHT_TAGS, - }); - - addAlgoliaAgents(searchClient); - - helper - .on('search', handleNewSearch) - .on('result', handleSearchSuccess({ indexId: indexName })) - .on('error', handleSearchError); - - let skip = false; - let stalledSearchTimer = null; - let initialSearchParameters = helper.state; - let searchCounter; - - const widgetsManager = createWidgetsManager(onWidgetsUpdate); - - hydrateSearchClient(searchClient, resultsState); - - const store = createStore({ - widgets: initialState, - metadata: hydrateMetadata(resultsState), - results: hydrateResultsState(resultsState), - error: null, - searching: false, - isSearchStalled: true, - searchingForFacetValues: false, - }); - - function skipSearch() { - skip = true; - } - - function updateClient(client) { - addAlgoliaAgents(client); - helper.setClient(client); - search(); - } - - function clearCache() { - helper.clearCache(); - search(); - } - - function getMetadata(state) { - return widgetsManager - .getWidgets() - .filter((widget) => Boolean(widget.getMetadata)) - .map((widget) => widget.getMetadata(state)); - } - - function getSearchParameters() { - const sharedParameters = widgetsManager - .getWidgets() - .filter((widget) => Boolean(widget.getSearchParameters)) - .filter( - (widget) => !isMultiIndexContext(widget) && !isIndexWidget(widget) - ) - .reduce( - (res, widget) => widget.getSearchParameters(res), - initialSearchParameters - ); - - const mainParameters = widgetsManager - .getWidgets() - .filter((widget) => Boolean(widget.getSearchParameters)) - .filter((widget) => { - const targetedIndexEqualMainIndex = - isMultiIndexContext(widget) && - isTargetedIndexEqualIndex(widget, indexName); - - const subIndexEqualMainIndex = - isIndexWidget(widget) && isIndexWidgetEqualIndex(widget, indexName); - - return targetedIndexEqualMainIndex || subIndexEqualMainIndex; - }) - // We have to sort the `Index` widgets first so the `index` parameter - // is correctly set in the `reduce` function for the following widgets - .sort(sortIndexWidgetsFirst) - .reduce( - (res, widget) => widget.getSearchParameters(res), - sharedParameters - ); - - const derivedIndices = widgetsManager - .getWidgets() - .filter((widget) => Boolean(widget.getSearchParameters)) - .filter((widget) => { - const targetedIndexNotEqualMainIndex = - isMultiIndexContext(widget) && - !isTargetedIndexEqualIndex(widget, indexName); - - const subIndexNotEqualMainIndex = - isIndexWidget(widget) && !isIndexWidgetEqualIndex(widget, indexName); - - return targetedIndexNotEqualMainIndex || subIndexNotEqualMainIndex; - }) - // We have to sort the `Index` widgets first so the `index` parameter - // is correctly set in the `reduce` function for the following widgets - .sort(sortIndexWidgetsFirst) - .reduce((indices, widget) => { - const indexId = isMultiIndexContext(widget) - ? widget.props.indexContextValue.targetedIndex - : widget.props.indexId; - - const widgets = indices[indexId] || []; - - return { - ...indices, - [indexId]: widgets.concat(widget), - }; - }, {}); - - const derivedParameters = Object.keys(derivedIndices).map((indexId) => ({ - parameters: derivedIndices[indexId].reduce( - (res, widget) => widget.getSearchParameters(res), - sharedParameters - ), - indexId, - })); - - return { - mainParameters, - derivedParameters, - }; - } - - function search() { - if (!skip) { - const { mainParameters, derivedParameters } = getSearchParameters( - helper.state - ); - - searchCounter = derivedParameters.length + 1; - - // We have to call `slice` because the method `detach` on the derived - // helpers mutates the value `derivedHelpers`. The `forEach` loop does - // not iterate on each value and we're not able to correctly clear the - // previous derived helpers (memory leak + useless requests). - helper.derivedHelpers.slice().forEach((derivedHelper) => { - // Since we detach the derived helpers on **every** new search they - // won't receive intermediate results in case of a stalled search. - // Only the last result is dispatched by the derived helper because - // they are not detached yet: - // - // - a -> main helper receives results - // - ap -> main helper receives results - // - app -> main helper + derived helpers receive results - // - // The quick fix is to avoid to detach them on search but only once they - // received the results. But it means that in case of a stalled search - // all the derived helpers not detached yet register a new search inside - // the helper. The number grows fast in case of a bad network and it's - // not deterministic. - derivedHelper.detach(); - }); - - derivedParameters.forEach(({ indexId, parameters }) => { - const derivedHelper = helper.derive(() => parameters); - - derivedHelper - .on('result', handleSearchSuccess({ indexId })) - .on('error', handleSearchError); - }); - - helper.setState(mainParameters); - - helper.search(); - } - } - - function handleSearchSuccess({ indexId }) { - return (event) => { - searchCounter--; - - const state = store.getState(); - const isDerivedHelpersEmpty = !helper.derivedHelpers.length; - - let results = state.results ? state.results : {}; - - // Switching from mono index to multi index and vice versa must reset the - // results to an empty object, otherwise we keep reference of stalled and - // unused results. - results = !isDerivedHelpersEmpty && results.getFacetByName ? {} : results; - - if (!isDerivedHelpersEmpty) { - results = { ...results, [indexId]: event.results }; - } else { - results = event.results; - } - - const currentState = store.getState(); - let nextIsSearchStalled = currentState.isSearchStalled; - if (!helper.hasPendingRequests()) { - clearTimeout(stalledSearchTimer); - stalledSearchTimer = null; - nextIsSearchStalled = false; - } - - const { resultsFacetValues, ...partialState } = currentState; - - store.setState({ - ...partialState, - results, - isSearchStalled: nextIsSearchStalled, - searching: searchCounter > 0, - error: null, - }); - }; - } - - function handleSearchError({ error }) { - const currentState = store.getState(); - - let nextIsSearchStalled = currentState.isSearchStalled; - if (!helper.hasPendingRequests()) { - clearTimeout(stalledSearchTimer); - nextIsSearchStalled = false; - } - - const { resultsFacetValues, ...partialState } = currentState; - - store.setState({ - ...partialState, - isSearchStalled: nextIsSearchStalled, - error, - searching: false, - }); - } - - function handleNewSearch() { - if (!stalledSearchTimer) { - stalledSearchTimer = setTimeout(() => { - const { resultsFacetValues, ...partialState } = store.getState(); - - store.setState({ - ...partialState, - isSearchStalled: true, - }); - }, stalledSearchDelay); - } - } - - function hydrateSearchClient(client, results) { - if (!results) { - return; - } - - // Disable cache hydration on: - // - Algoliasearch API Client < v4 with cache disabled - // - Third party clients (detected by the `addAlgoliaAgent` function missing) - - if ( - (!client.transporter || client._cacheHydrated) && - (!client._useCache || typeof client.addAlgoliaAgent !== 'function') - ) { - return; - } - - // Algoliasearch API Client >= v4 - // To hydrate the client we need to populate the cache with the data from - // the server (done in `hydrateSearchClientWithMultiIndexRequest` or - // `hydrateSearchClientWithSingleIndexRequest`). But since there is no way - // for us to compute the key the same way as `algoliasearch-client` we need - // to populate it on a custom key and override the `search` method to - // search on it first. - if (client.transporter && !client._cacheHydrated) { - client._cacheHydrated = true; - - const baseMethod = client.search; - client.search = (requests, ...methodArgs) => { - const requestsWithSerializedParams = requests.map((request) => ({ - ...request, - params: serializeQueryParameters(request.params), - })); - - return client.transporter.responsesCache.get( - { - method: 'search', - args: [requestsWithSerializedParams, ...methodArgs], - }, - () => { - return baseMethod(requests, ...methodArgs); - } - ); - }; - } - - if (Array.isArray(results.results)) { - hydrateSearchClientWithMultiIndexRequest(client, results.results); - return; - } - - hydrateSearchClientWithSingleIndexRequest(client, results); - } - - function hydrateSearchClientWithMultiIndexRequest(client, results) { - // Algoliasearch API Client >= v4 - // Populate the cache with the data from the server - if (client.transporter) { - client.transporter.responsesCache.set( - { - method: 'search', - args: [ - results.reduce( - (acc, result) => - acc.concat( - result.rawResults.map((request) => ({ - indexName: request.index, - params: request.params, - })) - ), - [] - ), - ], - }, - { - results: results.reduce( - (acc, result) => acc.concat(result.rawResults), - [] - ), - } - ); - return; - } - - // Algoliasearch API Client < v4 - // Prior to client v4 we didn't have a proper API to hydrate the client - // cache from the outside. The following code populates the cache with - // a single-index result. You can find more information about the - // computation of the key inside the client (see link below). - // https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240 - const key = `/1/indexes/*/queries_body_${JSON.stringify({ - requests: results.reduce( - (acc, result) => - acc.concat( - result.rawResults.map((request) => ({ - indexName: request.index, - params: request.params, - })) - ), - [] - ), - })}`; - - client.cache = { - ...client.cache, - [key]: JSON.stringify({ - results: results.reduce( - (acc, result) => acc.concat(result.rawResults), - [] - ), - }), - }; - } - - function hydrateSearchClientWithSingleIndexRequest(client, results) { - // Algoliasearch API Client >= v4 - // Populate the cache with the data from the server - if (client.transporter) { - client.transporter.responsesCache.set( - { - method: 'search', - args: [ - results.rawResults.map((request) => ({ - indexName: request.index, - params: request.params, - })), - ], - }, - { - results: results.rawResults, - } - ); - return; - } - // Algoliasearch API Client < v4 - // Prior to client v4 we didn't have a proper API to hydrate the client - // cache from the outside. The following code populates the cache with - // a single-index result. You can find more information about the - // computation of the key inside the client (see link below). - // https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240 - const key = `/1/indexes/*/queries_body_${JSON.stringify({ - requests: results.rawResults.map((request) => ({ - indexName: request.index, - params: request.params, - })), - })}`; - - client.cache = { - ...client.cache, - [key]: JSON.stringify({ - results: results.rawResults, - }), - }; - } - - function hydrateResultsState(results) { - if (!results) { - return null; - } - - if (Array.isArray(results.results)) { - return results.results.reduce( - (acc, result) => ({ - ...acc, - [result._internalIndexId]: new algoliasearchHelper.SearchResults( - new algoliasearchHelper.SearchParameters(result.state), - result.rawResults - ), - }), - {} - ); - } - - return new algoliasearchHelper.SearchResults( - new algoliasearchHelper.SearchParameters(results.state), - results.rawResults - ); - } - - // Called whenever a widget has been rendered with new props. - function onWidgetsUpdate() { - const metadata = getMetadata(store.getState().widgets); - - store.setState({ - ...store.getState(), - metadata, - searching: true, - }); - - // Since the `getSearchParameters` method of widgets also depends on props, - // the result search parameters might have changed. - search(); - } - - function transitionState(nextSearchState) { - const searchState = store.getState().widgets; - - return widgetsManager - .getWidgets() - .filter((widget) => Boolean(widget.transitionState)) - .reduce( - (res, widget) => widget.transitionState(searchState, res), - nextSearchState - ); - } - - function onExternalStateUpdate(nextSearchState) { - const metadata = getMetadata(nextSearchState); - - store.setState({ - ...store.getState(), - widgets: nextSearchState, - metadata, - searching: true, - }); - - search(); - } - - function onSearchForFacetValues({ facetName, query, maxFacetHits = 10 }) { - // The values 1, 100 are the min / max values that the engine accepts. - // see: https://www.algolia.com/doc/api-reference/api-parameters/maxFacetHits - const maxFacetHitsWithinRange = Math.max(1, Math.min(maxFacetHits, 100)); - - store.setState({ - ...store.getState(), - searchingForFacetValues: true, - }); - - helper - .searchForFacetValues(facetName, query, maxFacetHitsWithinRange) - .then( - (content) => { - store.setState({ - ...store.getState(), - error: null, - searchingForFacetValues: false, - resultsFacetValues: { - ...store.getState().resultsFacetValues, - [facetName]: content.facetHits, - query, - }, - }); - }, - (error) => { - store.setState({ - ...store.getState(), - searchingForFacetValues: false, - error, - }); - } - ) - .catch((error) => { - // Since setState is synchronous, any error that occurs in the render of a - // component will be swallowed by this promise. - // This is a trick to make the error show up correctly in the console. - // See http://stackoverflow.com/a/30741722/969302 - setTimeout(() => { - throw error; - }); - }); - } - - function updateIndex(newIndex) { - initialSearchParameters = initialSearchParameters.setIndex(newIndex); - // No need to trigger a new search here as the widgets will also update and trigger it if needed. - } - - function getWidgetsIds() { - return store - .getState() - .metadata.reduce( - (res, meta) => - typeof meta.id !== 'undefined' ? res.concat(meta.id) : res, - [] - ); - } - - return { - store, - widgetsManager, - getWidgetsIds, - getSearchParameters, - onSearchForFacetValues, - onExternalStateUpdate, - transitionState, - updateClient, - updateIndex, - clearCache, - skipSearch, - }; -} - -function hydrateMetadata(resultsState) { - if (!resultsState) { - return []; - } - - // add a value noop, which gets replaced once the widgets are mounted - return resultsState.metadata.map((datum) => ({ - value: () => ({}), - ...datum, - items: - datum.items && - datum.items.map((item) => ({ - value: () => ({}), - ...item, - items: - item.items && - item.items.map((nestedItem) => ({ - value: () => ({}), - ...nestedItem, - })), - })), - })); -} diff --git a/packages/react-instantsearch-core/src/core/createStore.ts b/packages/react-instantsearch-core/src/core/createStore.ts deleted file mode 100644 index 11bcf1a37c..0000000000 --- a/packages/react-instantsearch-core/src/core/createStore.ts +++ /dev/null @@ -1,35 +0,0 @@ -type SearchState = any; -type ResultsState = any; -type ResultsFacetsValues = any; -type Listener = () => void; -type State = { - widgets: SearchState; - metadata: any[]; - results: ResultsState | null; - resultsFacetValues: ResultsFacetsValues | null; - error: Error | null; - searching: boolean; - isSearchStalled: boolean; - searchingForFacetValues: boolean; -}; -export default function createStore(initialState: State) { - let state = initialState; - const listeners: Listener[] = []; - return { - getState() { - return state; - }, - setState(nextState: State) { - state = nextState; - listeners.forEach((listener) => listener()); - }, - subscribe(listener: Listener) { - listeners.push(listener); - return function unsubscribe() { - listeners.splice(listeners.indexOf(listener), 1); - }; - }, - }; -} - -export type Store = ReturnType; diff --git a/packages/react-instantsearch-core/src/core/createWidgetsManager.ts b/packages/react-instantsearch-core/src/core/createWidgetsManager.ts deleted file mode 100644 index be0f3634ab..0000000000 --- a/packages/react-instantsearch-core/src/core/createWidgetsManager.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { defer } from './utils'; - -import type { ConnectorDescription } from './createConnector'; -import type { Component } from 'react'; - -export type Widget = Component & { - constructor: { - displayName?: string; - $$type?: string; - $$widgetType?: string; - _connectorDesc?: ConnectorDescription; - }; -}; - -export type WidgetsManager = ReturnType; - -export default function createWidgetsManager(onWidgetsUpdate: () => void) { - const widgets: Widget[] = []; - // Is an update scheduled? - let scheduled = false; - - // The state manager's updates need to be batched since more than one - // component can register or unregister widgets during the same tick. - function scheduleUpdate() { - if (scheduled) { - return; - } - scheduled = true; - defer(() => { - scheduled = false; - onWidgetsUpdate(); - }); - } - - return { - registerWidget(widget: Widget) { - widgets.push(widget); - scheduleUpdate(); - return function unregisterWidget() { - widgets.splice(widgets.indexOf(widget), 1); - scheduleUpdate(); - }; - }, - update: scheduleUpdate, - getWidgets() { - return widgets; - }, - }; -} diff --git a/packages/react-instantsearch-core/src/core/highlight.js b/packages/react-instantsearch-core/src/core/highlight.js deleted file mode 100644 index d76adb7051..0000000000 --- a/packages/react-instantsearch-core/src/core/highlight.js +++ /dev/null @@ -1,93 +0,0 @@ -import { getPropertyByPath } from './utils'; - -export const HIGHLIGHT_TAGS = { - highlightPreTag: ``, - highlightPostTag: ``, -}; - -/** - * Parses an highlighted attribute into an array of objects with the string value, and - * a boolean that indicated if this part is highlighted. - * - * @param {string} preTag - string used to identify the start of an highlighted value - * @param {string} postTag - string used to identify the end of an highlighted value - * @param {string} highlightedValue - highlighted attribute as returned by Algolia highlight feature - * @return {object[]} - An array of {value: string, isHighlighted: boolean}. - */ -function parseHighlightedAttribute({ preTag, postTag, highlightedValue = '' }) { - const splitByPreTag = highlightedValue.split(preTag); - const firstValue = splitByPreTag.shift(); - const elements = - firstValue === '' ? [] : [{ value: firstValue, isHighlighted: false }]; - - if (postTag === preTag) { - let isHighlighted = true; - splitByPreTag.forEach((split) => { - elements.push({ value: split, isHighlighted }); - isHighlighted = !isHighlighted; - }); - } else { - splitByPreTag.forEach((split) => { - const splitByPostTag = split.split(postTag); - - elements.push({ - value: splitByPostTag[0], - isHighlighted: true, - }); - - if (splitByPostTag[1] !== '') { - elements.push({ - value: splitByPostTag[1], - isHighlighted: false, - }); - } - }); - } - - return elements; -} - -/** - * Find an highlighted attribute given an `attribute` and an `highlightProperty`, parses it, - * and provided an array of objects with the string value and a boolean if this - * value is highlighted. - * - * In order to use this feature, highlight must be activated in the configuration of - * the index. The `preTag` and `postTag` attributes are respectively highlightPreTag and - * highlightPostTag in Algolia configuration. - * - * @param {string} preTag - string used to identify the start of an highlighted value - * @param {string} postTag - string used to identify the end of an highlighted value - * @param {string} highlightProperty - the property that contains the highlight structure in the results - * @param {string} attribute - the highlighted attribute to look for - * @param {object} hit - the actual hit returned by Algolia. - * @return {object[]} - An array of {value: string, isHighlighted: boolean}. - */ -export function parseAlgoliaHit({ - preTag = '', - postTag = '', - highlightProperty, - attribute, - hit, -}) { - if (!hit) throw new Error('`hit`, the matching record, must be provided'); - - const highlightObject = - getPropertyByPath(hit[highlightProperty], attribute) || {}; - - if (Array.isArray(highlightObject)) { - return highlightObject.map((item) => - parseHighlightedAttribute({ - preTag, - postTag, - highlightedValue: item.value, - }) - ); - } - - return parseHighlightedAttribute({ - preTag, - postTag, - highlightedValue: highlightObject.value, - }); -} diff --git a/packages/react-instantsearch-core/src/core/indexUtils.js b/packages/react-instantsearch-core/src/core/indexUtils.js deleted file mode 100644 index b7a467800d..0000000000 --- a/packages/react-instantsearch-core/src/core/indexUtils.js +++ /dev/null @@ -1,327 +0,0 @@ -import { omit } from './utils'; - -export function getIndexId(context) { - return hasMultipleIndices(context) - ? context.multiIndexContext.targetedIndex - : context.ais.mainTargetedIndex; -} - -// eslint-disable-next-line valid-jsdoc -/** - * @returns {import('algoliasearch-helper').SearchResults} results - */ -export function getResults(searchResults, context) { - if (searchResults.results) { - if (searchResults.results.hits) { - return searchResults.results; - } - - const indexId = getIndexId(context); - if (searchResults.results[indexId]) { - return searchResults.results[indexId]; - } - } - - return null; -} - -export function hasMultipleIndices(context) { - return context && context.multiIndexContext; -} - -export function refineValue( - searchState, - nextRefinement, - context, - resetPage, - namespace -) { - if (hasMultipleIndices(context)) { - const indexId = getIndexId(context); - return namespace - ? refineMultiIndexWithNamespace( - searchState, - nextRefinement, - indexId, - resetPage, - namespace - ) - : refineMultiIndex(searchState, nextRefinement, indexId, resetPage); - } else { - // When we have a multi index page with shared widgets we should also - // reset their page to 1 if the resetPage is provided. Otherwise the - // indices will always be reset - // see: https://github.com/algolia/react-instantsearch/issues/310 - // see: https://github.com/algolia/react-instantsearch/issues/637 - if (searchState.indices && resetPage) { - Object.keys(searchState.indices).forEach((targetedIndex) => { - searchState = refineValue( - searchState, - { page: 1 }, - { multiIndexContext: { targetedIndex } }, - true, - namespace - ); - }); - } - return namespace - ? refineSingleIndexWithNamespace( - searchState, - nextRefinement, - resetPage, - namespace - ) - : refineSingleIndex(searchState, nextRefinement, resetPage); - } -} - -function refineMultiIndex(searchState, nextRefinement, indexId, resetPage) { - const page = resetPage ? { page: 1 } : undefined; - const state = - searchState.indices && searchState.indices[indexId] - ? { - ...searchState.indices, - [indexId]: { - ...searchState.indices[indexId], - ...nextRefinement, - ...page, - }, - } - : { - ...searchState.indices, - [indexId]: { - ...nextRefinement, - ...page, - }, - }; - - return { - ...searchState, - indices: state, - }; -} - -function refineSingleIndex(searchState, nextRefinement, resetPage) { - const page = resetPage ? { page: 1 } : undefined; - return { ...searchState, ...nextRefinement, ...page }; -} - -function refineMultiIndexWithNamespace( - searchState, - nextRefinement, - indexId, - resetPage, - namespace -) { - const page = resetPage ? { page: 1 } : undefined; - const state = - searchState.indices && searchState.indices[indexId] - ? { - ...searchState.indices, - [indexId]: { - ...searchState.indices[indexId], - [namespace]: { - ...searchState.indices[indexId][namespace], - ...nextRefinement, - }, - page: 1, - }, - } - : { - ...searchState.indices, - [indexId]: { - [namespace]: nextRefinement, - ...page, - }, - }; - - return { - ...searchState, - indices: state, - }; -} - -function refineSingleIndexWithNamespace( - searchState, - nextRefinement, - resetPage, - namespace -) { - const page = resetPage ? { page: 1 } : undefined; - return { - ...searchState, - [namespace]: { ...searchState[namespace], ...nextRefinement }, - ...page, - }; -} - -function getNamespaceAndAttributeName(id) { - const parts = id.match(/^([^.]*)\.(.*)/); - const namespace = parts && parts[1]; - const attributeName = parts && parts[2]; - - return { namespace, attributeName }; -} - -function hasRefinements({ - multiIndex, - indexId, - namespace, - attributeName, - id, - searchState, -}) { - if (multiIndex && namespace) { - return ( - searchState.indices && - searchState.indices[indexId] && - searchState.indices[indexId][namespace] && - Object.hasOwnProperty.call( - searchState.indices[indexId][namespace], - attributeName - ) - ); - } - - if (multiIndex) { - return ( - searchState.indices && - searchState.indices[indexId] && - Object.hasOwnProperty.call(searchState.indices[indexId], id) - ); - } - - if (namespace) { - return ( - searchState[namespace] && - Object.hasOwnProperty.call(searchState[namespace], attributeName) - ); - } - - return Object.hasOwnProperty.call(searchState, id); -} - -function getRefinements({ - multiIndex, - indexId, - namespace, - attributeName, - id, - searchState, -}) { - if (multiIndex && namespace) { - return searchState.indices[indexId][namespace][attributeName]; - } - if (multiIndex) { - return searchState.indices[indexId][id]; - } - if (namespace) { - return searchState[namespace][attributeName]; - } - - return searchState[id]; -} - -export function getCurrentRefinementValue( - props, - searchState, - context, - id, - defaultValue -) { - const indexId = getIndexId(context); - const { namespace, attributeName } = getNamespaceAndAttributeName(id); - const multiIndex = hasMultipleIndices(context); - const args = { - multiIndex, - indexId, - namespace, - attributeName, - id, - searchState, - }; - const hasRefinementsValue = hasRefinements(args); - - if (hasRefinementsValue) { - return getRefinements(args); - } - - if (props.defaultRefinement) { - return props.defaultRefinement; - } - - return defaultValue; -} - -export function cleanUpValue(searchState, context, id) { - const indexId = getIndexId(context); - const { namespace, attributeName } = getNamespaceAndAttributeName(id); - - if (hasMultipleIndices(context) && Boolean(searchState.indices)) { - return cleanUpValueWithMultiIndex({ - attribute: attributeName, - searchState, - indexId, - id, - namespace, - }); - } - - return cleanUpValueWithSingleIndex({ - attribute: attributeName, - searchState, - id, - namespace, - }); -} - -function cleanUpValueWithSingleIndex({ - searchState, - id, - namespace, - attribute, -}) { - if (namespace) { - return { - ...searchState, - [namespace]: omit(searchState[namespace], [attribute]), - }; - } - - return omit(searchState, [id]); -} - -function cleanUpValueWithMultiIndex({ - searchState, - indexId, - id, - namespace, - attribute, -}) { - const indexSearchState = searchState.indices[indexId]; - - if (namespace && indexSearchState) { - return { - ...searchState, - indices: { - ...searchState.indices, - [indexId]: { - ...indexSearchState, - [namespace]: omit(indexSearchState[namespace], [attribute]), - }, - }, - }; - } - - if (indexSearchState) { - return { - ...searchState, - indices: { - ...searchState.indices, - [indexId]: omit(indexSearchState, [id]), - }, - }; - } - - return searchState; -} diff --git a/packages/react-instantsearch-core/src/core/metadata.ts b/packages/react-instantsearch-core/src/core/metadata.ts deleted file mode 100644 index 7b5b0a9d26..0000000000 --- a/packages/react-instantsearch-core/src/core/metadata.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { SearchClient } from '../widgets/InstantSearch'; -import type { Widget } from './createWidgetsManager'; - -export function isMetadataEnabled() { - return ( - typeof window === 'object' && - typeof window.navigator === 'object' && - typeof window.navigator.userAgent === 'string' && - window.navigator.userAgent.includes('Algolia Crawler') && - typeof window.document === 'object' - ); -} - -export function getMetadataPayload( - widgets: Widget[], - searchClient: SearchClient -) { - const internalProps = ['contextValue', 'indexContextValue']; - - const widgetsPayload = widgets.map(({ props, constructor }) => { - const { defaultProps = {}, displayName = constructor.displayName } = - constructor._connectorDesc || {}; - - return { - displayName, - $$type: constructor.$$type, - $$widgetType: constructor.$$widgetType, - params: Object.keys(props).filter( - (prop) => - !internalProps.includes(prop) && - defaultProps[prop] !== (props as any)[prop] && - (props as any)[prop] !== undefined - ), - }; - }); - - const client = searchClient as Record; - const ua = - client.transporter && client.transporter.userAgent - ? client.transporter.userAgent.value - : client._ua; - - return { - ua, - widgets: widgetsPayload, - }; -} - -export function injectMetadata(widgets: Widget[], searchClient: SearchClient) { - const payloadContainer = document.createElement('meta'); - const refNode = document.querySelector('head')!; - payloadContainer.name = 'algolia:metadata'; - - const payload = getMetadataPayload(widgets, searchClient); - - payloadContainer.content = JSON.stringify(payload); - refNode.appendChild(payloadContainer); -} diff --git a/packages/react-instantsearch-core/src/core/translatable.js b/packages/react-instantsearch-core/src/core/translatable.js deleted file mode 100644 index 13d483c76c..0000000000 --- a/packages/react-instantsearch-core/src/core/translatable.js +++ /dev/null @@ -1,52 +0,0 @@ -import React, { Component } from 'react'; - -const withKeysPropType = (keys) => (props, propName, componentName) => { - const prop = props[propName]; - if (prop) { - // eslint-disable-next-line no-restricted-syntax - for (const key of Object.keys(prop)) { - if (keys.indexOf(key) === -1) { - return new Error( - `Unknown \`${propName}\` key \`${key}\`. Check the render method ` + - `of \`${componentName}\`.` - ); - } - } - } - return undefined; -}; - -export default function translatable(defaultTranslations) { - return (Composed) => { - class Translatable extends Component { - translate = (key, ...params) => { - const { translations } = this.props; - - const translation = - translations && translations.hasOwnProperty(key) - ? translations[key] - : defaultTranslations[key]; - - if (typeof translation === 'function') { - return translation(...params); - } - - return translation; - }; - - render() { - return ; - } - } - - const name = Composed.displayName || Composed.name || 'UnknownComponent'; - - Translatable.displayName = `Translatable(${name})`; - - Translatable.propTypes = { - translations: withKeysPropType(Object.keys(defaultTranslations)), - }; - - return Translatable; - }; -} diff --git a/packages/react-instantsearch-core/src/core/utils.ts b/packages/react-instantsearch-core/src/core/utils.ts deleted file mode 100644 index 283615ed90..0000000000 --- a/packages/react-instantsearch-core/src/core/utils.ts +++ /dev/null @@ -1,162 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/unbound-method -const hasOwn = Object.prototype.hasOwnProperty; - -// From https://github.com/reactjs/react-redux/blob/master/src/utils/shallowEqual.js -export const shallowEqual = (objA: any, objB: any) => { - if (objA === objB) { - return true; - } - - const keysA = Object.keys(objA); - const keysB = Object.keys(objB); - - if (keysA.length !== keysB.length) { - return false; - } - - // Test for A's keys different from B. - for (let i = 0; i < keysA.length; i++) { - if (!hasOwn.call(objB, keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) { - return false; - } - } - - return true; -}; - -export const getDisplayName = (Component: any) => - Component.displayName || Component.name || 'UnknownComponent'; - -const resolved = Promise.resolve(); -export const defer = (f: any) => { - resolved.then(f); -}; - -const isPlainObject = (value: unknown): value is object => - typeof value === 'object' && value !== null && !Array.isArray(value); - -export const removeEmptyKey = (obj: Record) => { - Object.keys(obj).forEach((key) => { - const value = obj[key]; - - if (!isPlainObject(value)) { - return; - } - - if (!objectHasKeys(value)) { - delete obj[key]; - } else { - removeEmptyKey(value); - } - }); - - return obj; -}; - -export const removeEmptyArraysFromObject = (obj: Record) => { - Object.keys(obj).forEach((key) => { - const value = obj[key]; - - if (Array.isArray(value) && value.length === 0) { - delete obj[key]; - } - }); - - return obj; -}; - -export function addAbsolutePositions( - hits: any[], - hitsPerPage: number, - page: number -) { - return hits.map((hit, index) => ({ - ...hit, - __position: hitsPerPage * page + index + 1, - })); -} - -export function addQueryID(hits: any[], queryID: string) { - if (!queryID) { - return hits; - } - return hits.map((hit) => ({ - ...hit, - __queryID: queryID, - })); -} - -export function find( - array: TItem[], - comparator: (item: TItem) => boolean -): TItem | undefined { - if (!Array.isArray(array)) { - return undefined; - } - - for (let i = 0; i < array.length; i++) { - if (comparator(array[i])) { - return array[i]; - } - } - return undefined; -} - -export function objectHasKeys(object: object | undefined) { - return object && Object.keys(object).length > 0; -} - -// https://github.com/babel/babel/blob/3aaafae053fa75febb3aa45d45b6f00646e30ba4/packages/babel-helpers/src/helpers.js#L604-L620 -export function omit( - source: Record, - excluded: string[] -): Record { - if (source === null || source === undefined) { - return {}; - } - const target: Record = {}; - const sourceKeys = Object.keys(source); - for (let i = 0; i < sourceKeys.length; i++) { - const key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) { - // eslint-disable-next-line no-continue - continue; - } - target[key] = source[key]; - } - return target; -} - -/** - * Retrieve the value at a path of the object: - * - * @example - * getPropertyByPath( - * { test: { this: { function: [{ now: { everyone: true } }] } } }, - * 'test.this.function[0].now.everyone' - * ); // true - * - * getPropertyByPath( - * { test: { this: { function: [{ now: { everyone: true } }] } } }, - * ['test', 'this', 'function', 0, 'now', 'everyone'] - * ); // true - * - * @param object Source object to query - * @param path either an array of properties, or a string form of the properties, separated by . - */ -export const getPropertyByPath = ( - object: Record, - path: string[] | string -): any => - (Array.isArray(path) - ? path - : path.replace(/\[(\d+)]/g, '.$1').split('.') - ).reduce((current, key) => (current ? current[key] : undefined), object); - -export function getObjectType(object: unknown): string { - return Object.prototype.toString.call(object).slice(8, -1); -} - -export function unescapeFacetValue(value: string): string { - return value.replace(/^\\-/, '-'); -} diff --git a/packages/react-instantsearch-core/src/core/version.js b/packages/react-instantsearch-core/src/core/version.js deleted file mode 100644 index ecdab9c92b..0000000000 --- a/packages/react-instantsearch-core/src/core/version.js +++ /dev/null @@ -1 +0,0 @@ -export default '6.40.4'; diff --git a/packages/react-instantsearch-hooks/src/hooks/__tests__/useConnector.test.tsx b/packages/react-instantsearch-core/src/hooks/__tests__/useConnector.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/hooks/__tests__/useConnector.test.tsx rename to packages/react-instantsearch-core/src/hooks/__tests__/useConnector.test.tsx diff --git a/packages/react-instantsearch-hooks/src/hooks/__tests__/useInstantSearch.test.tsx b/packages/react-instantsearch-core/src/hooks/__tests__/useInstantSearch.test.tsx similarity index 91% rename from packages/react-instantsearch-hooks/src/hooks/__tests__/useInstantSearch.test.tsx rename to packages/react-instantsearch-core/src/hooks/__tests__/useInstantSearch.test.tsx index 165bc1e7dc..e3dd06b3cf 100644 --- a/packages/react-instantsearch-hooks/src/hooks/__tests__/useInstantSearch.test.tsx +++ b/packages/react-instantsearch-core/src/hooks/__tests__/useInstantSearch.test.tsx @@ -5,7 +5,7 @@ import { createAlgoliaSearchClient } from '@instantsearch/mocks'; import { createInstantSearchTestWrapper, - InstantSearchHooksTestWrapper, + InstantSearchTestWrapper, wait, } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; @@ -13,7 +13,7 @@ import { renderHook } from '@testing-library/react-hooks'; import userEvent from '@testing-library/user-event'; import { AlgoliaSearchHelper, SearchResults } from 'algoliasearch-helper'; import React, { useEffect } from 'react'; -import { SearchBox } from 'react-instantsearch-hooks-web'; +import { SearchBox } from 'react-instantsearch'; import { useInstantSearch } from '../useInstantSearch'; @@ -28,7 +28,7 @@ describe('useInstantSearch', () => { expect(result.error?.message).toMatchInlineSnapshot(` "[InstantSearch] Hooks must be used inside the component. - They are not compatible with the \`react-instantsearch-core\` and \`react-instantsearch-dom\` packages, so make sure to use the component from \`react-instantsearch-hooks\`." + They are not compatible with the \`react-instantsearch-core@6.x\` and \`react-instantsearch-dom\` packages, so make sure to use the component from \`react-instantsearch-core@7.x\`." `); }); }); @@ -114,9 +114,9 @@ describe('useInstantSearch', () => { } const { findByTestId } = render( - + - + ); const button = await findByTestId('button'); @@ -153,9 +153,9 @@ describe('useInstantSearch', () => { } const { findByTestId } = render( - + - + ); const button = await findByTestId('button'); @@ -192,10 +192,10 @@ describe('useInstantSearch', () => { function App() { return ( - + - + ); } @@ -262,10 +262,10 @@ describe('useInstantSearch', () => { } const { getByTestId } = render( - + - + ); const refreshButton = getByTestId('refresh-button'); @@ -297,10 +297,10 @@ describe('useInstantSearch', () => { describe('status', () => { test('initial status: idle', () => { const App = () => ( - + - + ); const { getByTestId } = render(); @@ -310,12 +310,10 @@ describe('useInstantSearch', () => { test('turns to loading and idle when searching', async () => { const App = () => ( - + - + ); const { getByTestId, getByPlaceholderText } = render(); @@ -335,13 +333,13 @@ describe('useInstantSearch', () => { test('turns to loading, stalled and idle when searching slowly', async () => { const App = () => ( - - + ); const { getByTestId, getByPlaceholderText } = render(); @@ -371,11 +369,11 @@ describe('useInstantSearch', () => { }); const App = () => ( - + {/* has catchError, as the real error can not be asserted upon */} - + ); const { getByTestId, getByPlaceholderText } = render(); diff --git a/packages/react-instantsearch-hooks/src/hooks/useConnector.ts b/packages/react-instantsearch-core/src/hooks/useConnector.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/hooks/useConnector.ts rename to packages/react-instantsearch-core/src/hooks/useConnector.ts diff --git a/packages/react-instantsearch-hooks/src/hooks/useInstantSearch.ts b/packages/react-instantsearch-core/src/hooks/useInstantSearch.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/hooks/useInstantSearch.ts rename to packages/react-instantsearch-core/src/hooks/useInstantSearch.ts diff --git a/packages/react-instantsearch-core/src/index.ts b/packages/react-instantsearch-core/src/index.ts index 7f99b00341..67f24e3b87 100644 --- a/packages/react-instantsearch-core/src/index.ts +++ b/packages/react-instantsearch-core/src/index.ts @@ -1,93 +1,31 @@ -// Core -export { default as createConnector } from './core/createConnector'; -export { - instantSearchContext, - InstantSearchConsumer, - InstantSearchProvider, -} from './core/context'; - -// Utils -// @ts-ignore not yet typed -export { HIGHLIGHT_TAGS } from './core/highlight'; -// @ts-ignore not yet typed -export { default as version } from './core/version'; -// @ts-ignore not yet typed -export { default as translatable } from './core/translatable'; - -// Widgets -// @ts-ignore not yet typed -export { default as Configure } from './widgets/Configure'; -export { default as ExperimentalConfigureRelatedItems } from './widgets/ConfigureRelatedItems'; - -import { default as connectDynamicWidgets } from './connectors/connectDynamicWidgets'; -import { default as DynamicWidgets } from './widgets/DynamicWidgets'; -/** @deprecated use DynamicWidgets */ -const ExperimentalDynamicWidgets = DynamicWidgets; -export { ExperimentalDynamicWidgets, DynamicWidgets }; - -export { default as QueryRuleContext } from './widgets/QueryRuleContext'; -export { default as Index } from './widgets/Index'; -export { default as InstantSearch } from './widgets/InstantSearch'; - -// Connectors -// @ts-ignore not yet typed -export { default as connectAutoComplete } from './connectors/connectAutoComplete'; -// @ts-ignore not yet typed -export { default as connectBreadcrumb } from './connectors/connectBreadcrumb'; -// @ts-ignore not yet typed -export { default as connectConfigure } from './connectors/connectConfigure'; -export { default as EXPERIMENTAL_connectConfigureRelatedItems } from './connectors/connectConfigureRelatedItems'; -// @ts-ignore not yet typed -export { default as connectCurrentRefinements } from './connectors/connectCurrentRefinements'; - -/** @deprecated use connectDynamicWidgets */ -const EXPERIMENTAL_connectDynamicWidgets = connectDynamicWidgets; -export { connectDynamicWidgets, EXPERIMENTAL_connectDynamicWidgets }; - -// @ts-ignore not yet typed -export { default as connectGeoSearch } from './connectors/connectGeoSearch'; -// @ts-ignore not yet typed -export { default as connectHierarchicalMenu } from './connectors/connectHierarchicalMenu'; -// @ts-ignore not yet typed -export { default as connectHighlight } from './connectors/connectHighlight'; -// @ts-ignore not yet typed -export { default as connectHits } from './connectors/connectHits'; -// @ts-ignore not yet typed -export { default as connectHitsPerPage } from './connectors/connectHitsPerPage'; -// @ts-ignore not yet typed -export { default as connectInfiniteHits } from './connectors/connectInfiniteHits'; -// @ts-ignore not yet typed -export { default as connectMenu } from './connectors/connectMenu'; -// @ts-ignore not yet typed -export { default as connectNumericMenu } from './connectors/connectNumericMenu'; -// @ts-ignore not yet typed -export { default as connectPagination } from './connectors/connectPagination'; -// @ts-ignore not yet typed -export { default as connectPoweredBy } from './connectors/connectPoweredBy'; -// @ts-ignore not yet typed -export { default as connectQueryRules } from './connectors/connectQueryRules'; -// @ts-ignore not yet typed -export { default as connectRange } from './connectors/connectRange'; -// @ts-ignore not yet typed -export { default as connectRefinementList } from './connectors/connectRefinementList'; -// @ts-ignore not yet typed -export { default as connectScrollTo } from './connectors/connectScrollTo'; -// @ts-ignore not yet typed -export { default as connectSearchBox } from './connectors/connectSearchBox'; -// @ts-ignore not yet typed -export { default as connectRelevantSort } from './connectors/connectRelevantSort'; -// @ts-ignore not yet typed -export { default as connectSortBy } from './connectors/connectSortBy'; -// @ts-ignore not yet typed -export { default as connectStateResults } from './connectors/connectStateResults'; -// @ts-ignore not yet typed -export { default as connectStats } from './connectors/connectStats'; -// @ts-ignore not yet typed -export { default as connectToggleRefinement } from './connectors/connectToggleRefinement'; -// @ts-ignore not yet typed -export { default as connectHitInsights } from './connectors/connectHitInsights'; -// @ts-ignore not yet typed -export { default as connectVoiceSearch } from './connectors/connectVoiceSearch'; - -// Types -export * from './types'; +export { default as version } from './version'; +export * from './components/Configure'; +export * from './components/DynamicWidgets'; +export * from './components/Index'; +export * from './components/InstantSearch'; +export * from './components/InstantSearchServerContext'; +export * from './components/InstantSearchSSRProvider'; +export * from './connectors/useBreadcrumb'; +export * from './connectors/useClearRefinements'; +export * from './connectors/useConfigure'; +export * from './connectors/useCurrentRefinements'; +export * from './connectors/useDynamicWidgets'; +export * from './connectors/useGeoSearch'; +export * from './connectors/useHierarchicalMenu'; +export * from './connectors/useHits'; +export * from './connectors/useHitsPerPage'; +export * from './connectors/useInfiniteHits'; +export * from './connectors/useMenu'; +export * from './connectors/useNumericMenu'; +export * from './connectors/usePagination'; +export * from './connectors/usePoweredBy'; +export * from './connectors/useQueryRules'; +export * from './connectors/useRange'; +export * from './connectors/useRefinementList'; +export * from './connectors/useSearchBox'; +export * from './connectors/useSortBy'; +export * from './connectors/useStats'; +export * from './connectors/useToggleRefinement'; +export * from './hooks/useConnector'; +export * from './hooks/useInstantSearch'; +export * from './server'; diff --git a/packages/react-instantsearch-hooks/src/lib/IndexContext.ts b/packages/react-instantsearch-core/src/lib/IndexContext.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/IndexContext.ts rename to packages/react-instantsearch-core/src/lib/IndexContext.ts diff --git a/packages/react-instantsearch-hooks/src/lib/InstantSearchContext.ts b/packages/react-instantsearch-core/src/lib/InstantSearchContext.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/InstantSearchContext.ts rename to packages/react-instantsearch-core/src/lib/InstantSearchContext.ts diff --git a/packages/react-instantsearch-hooks/src/lib/InstantSearchSSRContext.ts b/packages/react-instantsearch-core/src/lib/InstantSearchSSRContext.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/InstantSearchSSRContext.ts rename to packages/react-instantsearch-core/src/lib/InstantSearchSSRContext.ts diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/IndexContext.test.tsx b/packages/react-instantsearch-core/src/lib/__tests__/IndexContext.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/__tests__/IndexContext.test.tsx rename to packages/react-instantsearch-core/src/lib/__tests__/IndexContext.test.tsx diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/InstantSearchContext.test.tsx b/packages/react-instantsearch-core/src/lib/__tests__/InstantSearchContext.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/__tests__/InstantSearchContext.test.tsx rename to packages/react-instantsearch-core/src/lib/__tests__/InstantSearchContext.test.tsx diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/InstantSearchSSRContext.test.tsx b/packages/react-instantsearch-core/src/lib/__tests__/InstantSearchSSRContext.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/__tests__/InstantSearchSSRContext.test.tsx rename to packages/react-instantsearch-core/src/lib/__tests__/InstantSearchSSRContext.test.tsx diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/createSearchResults.test.ts b/packages/react-instantsearch-core/src/lib/__tests__/createSearchResults.test.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/__tests__/createSearchResults.test.ts rename to packages/react-instantsearch-core/src/lib/__tests__/createSearchResults.test.ts diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/dequal.test.ts b/packages/react-instantsearch-core/src/lib/__tests__/dequal.test.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/__tests__/dequal.test.ts rename to packages/react-instantsearch-core/src/lib/__tests__/dequal.test.ts diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/invariant.test.ts b/packages/react-instantsearch-core/src/lib/__tests__/invariant.test.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/__tests__/invariant.test.ts rename to packages/react-instantsearch-core/src/lib/__tests__/invariant.test.ts diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/useSearchResults.test.tsx b/packages/react-instantsearch-core/src/lib/__tests__/useSearchResults.test.tsx similarity index 97% rename from packages/react-instantsearch-hooks/src/lib/__tests__/useSearchResults.test.tsx rename to packages/react-instantsearch-core/src/lib/__tests__/useSearchResults.test.tsx index f8d5dada08..6870ee9689 100644 --- a/packages/react-instantsearch-hooks/src/lib/__tests__/useSearchResults.test.tsx +++ b/packages/react-instantsearch-core/src/lib/__tests__/useSearchResults.test.tsx @@ -6,7 +6,7 @@ import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; import { renderHook } from '@testing-library/react-hooks'; import { AlgoliaSearchHelper, SearchResults } from 'algoliasearch-helper'; import React from 'react'; -import { SearchBox } from 'react-instantsearch-hooks-web'; +import { SearchBox } from 'react-instantsearch'; import { useSearchResults } from '../useSearchResults'; diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/useSearchState.test.tsx b/packages/react-instantsearch-core/src/lib/__tests__/useSearchState.test.tsx similarity index 95% rename from packages/react-instantsearch-hooks/src/lib/__tests__/useSearchState.test.tsx rename to packages/react-instantsearch-core/src/lib/__tests__/useSearchState.test.tsx index acc0cf479b..5021e7bb4e 100644 --- a/packages/react-instantsearch-hooks/src/lib/__tests__/useSearchState.test.tsx +++ b/packages/react-instantsearch-core/src/lib/__tests__/useSearchState.test.tsx @@ -4,7 +4,7 @@ import { createInstantSearchTestWrapper, - InstantSearchHooksTestWrapper, + InstantSearchTestWrapper, } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; @@ -91,9 +91,9 @@ describe('useSearchState', () => { } const { getByRole, getByTestId } = render( - + - + ); const button = getByRole('button'); const uiState = getByTestId('uiState'); @@ -140,9 +140,9 @@ describe('useSearchState', () => { } const { getByRole, getByTestId } = render( - + - + ); const button = getByRole('button'); const uiState = getByTestId('uiState'); @@ -192,9 +192,9 @@ describe('useSearchState', () => { } const { getByRole, getByTestId } = render( - + - + ); const button = getByRole('button'); const indexUiState = getByTestId('indexUiState'); @@ -238,9 +238,9 @@ describe('useSearchState', () => { } const { getByRole, getByTestId } = render( - + - + ); const button = getByRole('button'); const indexUiState = getByTestId('indexUiState'); diff --git a/packages/react-instantsearch-hooks/src/lib/__tests__/warn.test.ts b/packages/react-instantsearch-core/src/lib/__tests__/warn.test.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/__tests__/warn.test.ts rename to packages/react-instantsearch-core/src/lib/__tests__/warn.test.ts diff --git a/packages/react-instantsearch-hooks/src/lib/createSearchResults.ts b/packages/react-instantsearch-core/src/lib/createSearchResults.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/createSearchResults.ts rename to packages/react-instantsearch-core/src/lib/createSearchResults.ts diff --git a/packages/react-instantsearch-hooks/src/lib/dequal.ts b/packages/react-instantsearch-core/src/lib/dequal.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/dequal.ts rename to packages/react-instantsearch-core/src/lib/dequal.ts diff --git a/packages/react-instantsearch-hooks/src/lib/getIndexSearchResults.ts b/packages/react-instantsearch-core/src/lib/getIndexSearchResults.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/getIndexSearchResults.ts rename to packages/react-instantsearch-core/src/lib/getIndexSearchResults.ts diff --git a/packages/react-instantsearch-hooks/src/lib/invariant.ts b/packages/react-instantsearch-core/src/lib/invariant.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/invariant.ts rename to packages/react-instantsearch-core/src/lib/invariant.ts diff --git a/packages/react-instantsearch-hooks/src/lib/noop.ts b/packages/react-instantsearch-core/src/lib/noop.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/noop.ts rename to packages/react-instantsearch-core/src/lib/noop.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useForceUpdate.ts b/packages/react-instantsearch-core/src/lib/useForceUpdate.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useForceUpdate.ts rename to packages/react-instantsearch-core/src/lib/useForceUpdate.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useIndex.ts b/packages/react-instantsearch-core/src/lib/useIndex.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useIndex.ts rename to packages/react-instantsearch-core/src/lib/useIndex.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useIndexContext.ts b/packages/react-instantsearch-core/src/lib/useIndexContext.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useIndexContext.ts rename to packages/react-instantsearch-core/src/lib/useIndexContext.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useInstantSearchApi.ts b/packages/react-instantsearch-core/src/lib/useInstantSearchApi.ts similarity index 97% rename from packages/react-instantsearch-hooks/src/lib/useInstantSearchApi.ts rename to packages/react-instantsearch-core/src/lib/useInstantSearchApi.ts index 77b3439765..5506e48ea1 100644 --- a/packages/react-instantsearch-hooks/src/lib/useInstantSearchApi.ts +++ b/packages/react-instantsearch-core/src/lib/useInstantSearchApi.ts @@ -18,7 +18,7 @@ import type { const defaultUserAgents = [ `react (${ReactVersion})`, `react-instantsearch (${version})`, - `react-instantsearch-hooks (${version})`, + `react-instantsearch-core (${version})`, ]; const serverUserAgent = `react-instantsearch-server (${version})`; const nextUserAgent = (nextVersion?: string) => @@ -137,7 +137,7 @@ export function useInstantSearchApi( if (prevProps.searchClient !== props.searchClient) { warn( false, - 'The `searchClient` prop of `` changed between renders, which may cause more search requests than necessary. If this is an unwanted behavior, please provide a stable reference: https://www.algolia.com/doc/api-reference/widgets/instantsearch/react-hooks/#widget-param-searchclient' + 'The `searchClient` prop of `` changed between renders, which may cause more search requests than necessary. If this is an unwanted behavior, please provide a stable reference: https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/#widget-param-searchclient' ); addAlgoliaAgents(props.searchClient, [ @@ -257,9 +257,9 @@ function warnNextRouter( warn( isUsingNextRouter, ` -You are using Next.js with InstantSearch without the "react-instantsearch-hooks-router-nextjs" package. +You are using Next.js with InstantSearch without the "react-instantsearch-router-nextjs" package. This package is recommended to make the routing work correctly with Next.js. -Please check its usage instructions: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch-hooks-router-nextjs +Please check its usage instructions: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch-router-nextjs You can ignore this warning if you are using a custom router that suits your needs, it won't be outputted in production builds.` ); diff --git a/packages/react-instantsearch-hooks/src/lib/useInstantSearchContext.ts b/packages/react-instantsearch-core/src/lib/useInstantSearchContext.ts similarity index 84% rename from packages/react-instantsearch-hooks/src/lib/useInstantSearchContext.ts rename to packages/react-instantsearch-core/src/lib/useInstantSearchContext.ts index 8b4e18c9dc..af1d102139 100644 --- a/packages/react-instantsearch-hooks/src/lib/useInstantSearchContext.ts +++ b/packages/react-instantsearch-core/src/lib/useInstantSearchContext.ts @@ -22,7 +22,7 @@ export function useInstantSearchContext< invariant( search !== null, 'Hooks must be used inside the component.\n\n' + - 'They are not compatible with the `react-instantsearch-core` and `react-instantsearch-dom` packages, so make sure to use the component from `react-instantsearch-hooks`.' + 'They are not compatible with the `react-instantsearch-core@6.x` and `react-instantsearch-dom` packages, so make sure to use the component from `react-instantsearch-core@7.x`.' ); return search; diff --git a/packages/react-instantsearch-hooks/src/lib/useInstantSearchSSRContext.ts b/packages/react-instantsearch-core/src/lib/useInstantSearchSSRContext.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useInstantSearchSSRContext.ts rename to packages/react-instantsearch-core/src/lib/useInstantSearchSSRContext.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useInstantSearchServerContext.ts b/packages/react-instantsearch-core/src/lib/useInstantSearchServerContext.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useInstantSearchServerContext.ts rename to packages/react-instantsearch-core/src/lib/useInstantSearchServerContext.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useIsomorphicLayoutEffect.ts b/packages/react-instantsearch-core/src/lib/useIsomorphicLayoutEffect.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useIsomorphicLayoutEffect.ts rename to packages/react-instantsearch-core/src/lib/useIsomorphicLayoutEffect.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useSearchResults.ts b/packages/react-instantsearch-core/src/lib/useSearchResults.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useSearchResults.ts rename to packages/react-instantsearch-core/src/lib/useSearchResults.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useSearchState.ts b/packages/react-instantsearch-core/src/lib/useSearchState.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useSearchState.ts rename to packages/react-instantsearch-core/src/lib/useSearchState.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useStableValue.ts b/packages/react-instantsearch-core/src/lib/useStableValue.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useStableValue.ts rename to packages/react-instantsearch-core/src/lib/useStableValue.ts diff --git a/packages/react-instantsearch-hooks/src/lib/useWidget.ts b/packages/react-instantsearch-core/src/lib/useWidget.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/useWidget.ts rename to packages/react-instantsearch-core/src/lib/useWidget.ts diff --git a/packages/react-instantsearch-hooks/src/lib/warn.ts b/packages/react-instantsearch-core/src/lib/warn.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/lib/warn.ts rename to packages/react-instantsearch-core/src/lib/warn.ts diff --git a/packages/react-instantsearch-hooks-server/src/__tests__/__snapshots__/getServerState.test.tsx.snap b/packages/react-instantsearch-core/src/server/__tests__/__snapshots__/getServerState.test.tsx.snap similarity index 100% rename from packages/react-instantsearch-hooks-server/src/__tests__/__snapshots__/getServerState.test.tsx.snap rename to packages/react-instantsearch-core/src/server/__tests__/__snapshots__/getServerState.test.tsx.snap diff --git a/packages/react-instantsearch-hooks-server/src/__tests__/getServerState.test.tsx b/packages/react-instantsearch-core/src/server/__tests__/getServerState.test.tsx similarity index 98% rename from packages/react-instantsearch-hooks-server/src/__tests__/getServerState.test.tsx rename to packages/react-instantsearch-core/src/server/__tests__/getServerState.test.tsx index e2a916dd8c..728e461769 100644 --- a/packages/react-instantsearch-hooks-server/src/__tests__/getServerState.test.tsx +++ b/packages/react-instantsearch-core/src/server/__tests__/getServerState.test.tsx @@ -11,6 +11,7 @@ import { } from '@instantsearch/mocks'; import React, { version as ReactVersion } from 'react'; import { renderToString } from 'react-dom/server'; +import { Hits, RefinementList } from 'react-instantsearch'; import { InstantSearch, InstantSearchSSRProvider, @@ -18,8 +19,7 @@ import { DynamicWidgets, version, useSearchBox, -} from 'react-instantsearch-hooks'; -import { Hits, RefinementList } from 'react-instantsearch-hooks-web'; +} from 'react-instantsearch-core'; import { getServerState } from '../getServerState'; @@ -28,7 +28,7 @@ import type { Hit as AlgoliaHit } from 'instantsearch.js'; import type { InstantSearchServerState, InstantSearchProps, -} from 'react-instantsearch-hooks'; +} from 'react-instantsearch-core'; function SearchBox() { const { query } = useSearchBox(); @@ -204,7 +204,7 @@ describe('getServerState', () => { `react-instantsearch (${version})` ); expect(searchClient.addAlgoliaAgent).toHaveBeenCalledWith( - `react-instantsearch-hooks (${version})` + `react-instantsearch-core (${version})` ); expect(searchClient.addAlgoliaAgent).toHaveBeenCalledWith( `react-instantsearch-server (${version})` diff --git a/packages/react-instantsearch-hooks-server/src/getServerState.tsx b/packages/react-instantsearch-core/src/server/getServerState.tsx similarity index 96% rename from packages/react-instantsearch-hooks-server/src/getServerState.tsx rename to packages/react-instantsearch-core/src/server/getServerState.tsx index 84591db904..d2c18defa2 100644 --- a/packages/react-instantsearch-hooks-server/src/getServerState.tsx +++ b/packages/react-instantsearch-core/src/server/getServerState.tsx @@ -4,17 +4,15 @@ import { } from 'instantsearch.js/es/lib/server'; import { walkIndex } from 'instantsearch.js/es/lib/utils'; import React from 'react'; -import { - InstantSearchServerContext, - InstantSearchSSRProvider, -} from 'react-instantsearch-hooks'; -import type { InstantSearch, UiState } from 'instantsearch.js'; -import type { ReactNode } from 'react'; +import { InstantSearchServerContext, InstantSearchSSRProvider } from '..'; + import type { InstantSearchServerContextApi, InstantSearchServerState, -} from 'react-instantsearch-hooks'; +} from '..'; +import type { InstantSearch, UiState } from 'instantsearch.js'; +import type { ReactNode } from 'react'; type SearchRef = { current: InstantSearch | undefined }; diff --git a/packages/react-instantsearch-hooks-server/src/index.ts b/packages/react-instantsearch-core/src/server/index.ts similarity index 100% rename from packages/react-instantsearch-hooks-server/src/index.ts rename to packages/react-instantsearch-core/src/server/index.ts diff --git a/packages/react-instantsearch-core/src/types/algoliasearch.ts b/packages/react-instantsearch-core/src/types/algoliasearch.ts deleted file mode 100644 index bbf2b3c567..0000000000 --- a/packages/react-instantsearch-core/src/types/algoliasearch.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Custom types to support both algoliasearch -// `v3` and algoliasearch `v4` clients. - -import type * as ClientSearch from '@algolia/client-search'; -import type algoliasearch from 'algoliasearch/lite'; -import type * as AlgoliaSearch from 'algoliasearch/lite'; -/** @ts-ignore */ - -/** @ts-ignore */ -type SearchResponseV3 = AlgoliaSearch.Response; -/** @ts-ignore */ -type SearchResponseV4 = ClientSearch.SearchResponse; - -type DummySearchClientV4 = { - readonly addAlgoliaAgent: (segment: string, version?: string) => void; -}; - -type SearchResponse = ReturnType< - typeof algoliasearch -> extends DummySearchClientV4 - ? SearchResponseV4 - : SearchResponseV3; - -export interface MultiResponse { - results: Array>; -} diff --git a/packages/react-instantsearch-core/src/types/index.ts b/packages/react-instantsearch-core/src/types/index.ts deleted file mode 100644 index 0274a75363..0000000000 --- a/packages/react-instantsearch-core/src/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './translatable'; diff --git a/packages/react-instantsearch-core/src/types/translatable.ts b/packages/react-instantsearch-core/src/types/translatable.ts deleted file mode 100644 index 03f8f7c34b..0000000000 --- a/packages/react-instantsearch-core/src/types/translatable.ts +++ /dev/null @@ -1 +0,0 @@ -export type Translate = (key: string, ...params: any[]) => string; diff --git a/packages/react-instantsearch-hooks/src/version.ts b/packages/react-instantsearch-core/src/version.ts similarity index 100% rename from packages/react-instantsearch-hooks/src/version.ts rename to packages/react-instantsearch-core/src/version.ts diff --git a/packages/react-instantsearch-core/src/widgets/Configure.js b/packages/react-instantsearch-core/src/widgets/Configure.js deleted file mode 100644 index 84314ed150..0000000000 --- a/packages/react-instantsearch-core/src/widgets/Configure.js +++ /dev/null @@ -1,43 +0,0 @@ -import connectConfigure from '../connectors/connectConfigure'; - -/** - * Configure is a widget that lets you provide raw search parameters - * to the Algolia API. - * - * Any of the props added to this widget will be forwarded to Algolia. For more information - * on the different parameters that can be set, have a look at the - * [reference](https://www.algolia.com/doc/api-client/javascript/search#search-parameters). - * - * This widget can be used either with react-dom and react-native. It will not render anything - * on screen, only configure some parameters. - * - * Read more in the [Search parameters](guide/Search_parameters.html) guide. - * @name Configure - * @kind widget - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, Configure, Hits } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * - * ); - */ - -export default connectConfigure( - function Configure() { - return null; - }, - { $$widgetType: 'ais.configure' } -); diff --git a/packages/react-instantsearch-core/src/widgets/ConfigureRelatedItems.tsx b/packages/react-instantsearch-core/src/widgets/ConfigureRelatedItems.tsx deleted file mode 100644 index aa8ffe7484..0000000000 --- a/packages/react-instantsearch-core/src/widgets/ConfigureRelatedItems.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import PropTypes from 'prop-types'; - -import connectConfigureRelatedItems from '../connectors/connectConfigureRelatedItems'; - -function ConfigureRelatedItems() { - return null; -} - -ConfigureRelatedItems.propTypes = { - hit: PropTypes.object.isRequired, - matchingPatterns: PropTypes.object.isRequired, - transformSearchParameters: PropTypes.func, -}; - -export default connectConfigureRelatedItems(ConfigureRelatedItems, { - $$widgetType: 'ais.configureRelatedItems', -}); diff --git a/packages/react-instantsearch-core/src/widgets/DynamicWidgets.tsx b/packages/react-instantsearch-core/src/widgets/DynamicWidgets.tsx deleted file mode 100644 index e3a72fc457..0000000000 --- a/packages/react-instantsearch-core/src/widgets/DynamicWidgets.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { Fragment } from 'react'; - -import connectDynamicWidgets from '../connectors/connectDynamicWidgets'; -import { getDisplayName } from '../core/utils'; - -import type { ComponentType, ReactElement, ReactNode } from 'react'; - -function isReactElement(element: any): element is ReactElement { - return typeof element === 'object' && element.props; -} - -function getAttribute(element: ReactNode): string | undefined { - if (!isReactElement(element)) { - return undefined; - } - - if (element.props.attribute) { - return element.props.attribute; - } - if (Array.isArray(element.props.attributes)) { - return element.props.attributes[0]; - } - if (element.props.children) { - return getAttribute(React.Children.only(element.props.children)); - } - - return undefined; -} - -type DynamicWidgetsProps = { - children: ReactNode; - attributesToRender: string[]; - fallbackComponent?: ComponentType<{ attribute: string }>; -}; - -function DynamicWidgets({ - children, - attributesToRender, - fallbackComponent: Fallback = () => null, -}: DynamicWidgetsProps) { - const widgets: Map = new Map(); - - React.Children.forEach(children, (child) => { - const attribute = getAttribute(child); - if (!attribute) { - throw new Error( - `Could not find "attribute" prop for ${getDisplayName(child)}.` - ); - } - widgets.set(attribute, child); - }); - - // on initial render this will be empty, but React InstantSearch keeps - // search state for unmounted components in place, so routing works. - return ( - <> - {attributesToRender.map((attribute) => ( - - {widgets.get(attribute) || } - - ))} - - ); -} - -export default connectDynamicWidgets(DynamicWidgets, { - $$widgetType: 'ais.dynamicWidgets', -}); diff --git a/packages/react-instantsearch-core/src/widgets/Index.tsx b/packages/react-instantsearch-core/src/widgets/Index.tsx deleted file mode 100644 index 77066c9e43..0000000000 --- a/packages/react-instantsearch-core/src/widgets/Index.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component, Children } from 'react'; - -import { InstantSearchConsumer, IndexProvider } from '../core/context'; - -import type { InstantSearchContext, IndexContext } from '../core/context'; -import type { SearchParameters } from 'algoliasearch-helper'; - -function getIndexContext(props: Props): IndexContext { - return { - targetedIndex: props.indexId, - }; -} - -type Props = { - indexName: string; - indexId: string; - children?: React.ReactNode; -}; - -type InnerProps = Props & { contextValue: InstantSearchContext }; - -type State = { - indexContext: IndexContext; -}; - -/** - * The component that allows you to apply widgets to a dedicated index. It's - * useful if you want to build an interface that targets multiple indices. - * - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, Index, SearchBox, Hits, Configure } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * - * - * - * - * - * - * - * ); - */ -class Index extends Component { - static propTypes = { - indexName: PropTypes.string.isRequired, - indexId: PropTypes.string.isRequired, - children: PropTypes.node, - }; - - static displayName = 'AlgoliaIndex'; - static $$type = 'ais.index'; - static $$widgetType = 'ais.index'; - - static getDerivedStateFromProps(props: InnerProps) { - return { - indexContext: getIndexContext(props), - }; - } - - state = { - indexContext: getIndexContext(this.props), - }; - - unregisterWidget?: () => void; - - constructor(props: InnerProps) { - super(props); - - this.props.contextValue.onSearchParameters( - this.getSearchParameters.bind(this), - { - ais: this.props.contextValue, - multiIndexContext: this.state.indexContext, - }, - this.props, - undefined, - Index.displayName - ); - } - - componentDidMount() { - this.unregisterWidget = - this.props.contextValue.widgetsManager.registerWidget(this); - } - - componentDidUpdate(prevProps: InnerProps) { - if (this.props.indexName !== prevProps.indexName) { - this.props.contextValue.widgetsManager.update(); - } - } - - componentWillUnmount() { - if (typeof this.unregisterWidget === 'function') { - this.unregisterWidget(); - } - } - - getSearchParameters(searchParameters: SearchParameters, props: InnerProps) { - return searchParameters.setIndex( - this.props ? this.props.indexName : props.indexName - ); - } - - render() { - const childrenCount = Children.count(this.props.children); - if (childrenCount === 0) { - return null; - } - return ( - - {this.props.children} - - ); - } -} - -type IndexWrapperProps = { - indexName: string; - indexId?: string; -}; - -const IndexWrapper: React.FC = (props) => { - const inferredIndexId = props.indexName; - return ( - - {(contextValue) => ( - - )} - - ); -}; - -IndexWrapper.propTypes = { - indexName: PropTypes.string.isRequired, - indexId: PropTypes.string, -}; - -export const IndexComponentWithoutContext = Index; -export default IndexWrapper; diff --git a/packages/react-instantsearch-core/src/widgets/InstantSearch.tsx b/packages/react-instantsearch-core/src/widgets/InstantSearch.tsx deleted file mode 100644 index 27f8fd1721..0000000000 --- a/packages/react-instantsearch-core/src/widgets/InstantSearch.tsx +++ /dev/null @@ -1,337 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component, Children } from 'react'; -import isEqual from 'react-fast-compare'; - -import { InstantSearchProvider } from '../core/context'; -import createInstantSearchManager from '../core/createInstantSearchManager'; -import { isMetadataEnabled, injectMetadata } from '../core/metadata'; - -import type { InstantSearchContext, IndexContext } from '../core/context'; -import type { ConnectorDescription } from '../core/createConnector'; -import type { Store } from '../core/createStore'; -import type { WidgetsManager } from '../core/createWidgetsManager'; -import type { MultiResponse } from '../types/algoliasearch'; -import type { - PlainSearchParameters, - SearchParameters, -} from 'algoliasearch-helper'; - -type ResultsState = { - metadata: never[]; - state: PlainSearchParameters; - rawResults: MultiResponse; -}; - -// @TODO: move to createInstantSearchManager when it's TS -type InstantSearchManager = { - store: Store; - widgetsManager: WidgetsManager; - getWidgetsIds: () => any; - getSearchParameters: (...args: any[]) => { - mainParameters: SearchParameters; - derivedParameters: SearchParameters; - }; - onSearchForFacetValues: (...args: any[]) => any; - onExternalStateUpdate: (...args: any[]) => any; - transitionState: any; - updateClient: any; - updateIndex: any; - clearCache: () => void; - skipSearch: (...args: any[]) => any; -}; - -export type SearchClient = { - search: (requests: Array<{}>) => Promise<{}>; - searchForFacetValues: (requests: Array<{}>) => Promise<{}>; -}; - -type SearchState = any; - -type Props = { - refresh: boolean; - indexName: string; - searchClient: SearchClient; - createURL?: (searchState: SearchState, knownKeys: any) => string; - onSearchStateChange?: (searchState: SearchState) => void; - searchState?: SearchState; - onSearchParameters?: ( - getSearchParameters: ConnectorDescription['getSearchParameters'], - context: { ais: InstantSearchContext; multiIndexContext: IndexContext }, - props: object, - searchState: SearchState - ) => void; - widgetsCollector?: (args: { - getSearchParameters: ConnectorDescription['getSearchParameters']; - getMetadata: ConnectorDescription['getMetadata']; - searchState: SearchState; - context: { ais: InstantSearchContext; multiIndexContext: IndexContext }; - props: object; - displayName: string; - }) => void; - stalledSearchDelay?: number; - resultsState?: ResultsState | { [indexId: string]: ResultsState }; - children?: React.ReactNode; -}; - -type State = { - isControlled: boolean; - instantSearchManager: InstantSearchManager; - contextValue: InstantSearchContext; -}; - -function isControlled(props: Props) { - return Boolean(props.searchState); -} - -/** - * @description - * `` is the root component of all React InstantSearch implementations. - * It provides all the connected components (aka widgets) a means to interact - * with the searchState. - * @kind widget - * @name - * @requirements You will need to have an Algolia account to be able to use this widget. - * [Create one now](https://www.algolia.com/users/sign_up). - * @propType {string} indexName - Main index in which to search. - * @propType {boolean} [refresh=false] - Flag to activate when the cache needs to be cleared so that the front-end is updated when a change occurs in the index. - * @propType {object} [searchClient] - Provide a custom search client. - * @propType {func} [onSearchStateChange] - Function to be called everytime a new search is done. Useful for [URL Routing](guide/Routing.html). - * @propType {object} [searchState] - Object to inject some search state. Switches the InstantSearch component in controlled mode. Useful for [URL Routing](guide/Routing.html). - * @propType {func} [createURL] - Function to call when creating links, useful for [URL Routing](guide/Routing.html). - * @propType {SearchResults|SearchResults[]} [resultsState] - Use this to inject the results that will be used at first rendering. Those results are found by using the `findResultsState` function. Useful for [Server Side Rendering](guide/Server-side_rendering.html). - * @propType {number} [stalledSearchDelay=200] - The amount of time before considering that the search takes too much time. The time is expressed in milliseconds. - * @propType {{ Root: string|function, props: object }} [root] - Use this to customize the root element. Default value: `{ Root: 'div' }` - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, SearchBox, Hits } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * - * ); - */ -class InstantSearch extends Component { - static defaultProps = { - stalledSearchDelay: 200, - refresh: false, - }; - - static propTypes = { - // @TODO: These props are currently constant. - indexName: PropTypes.string.isRequired, - - searchClient: PropTypes.shape({ - search: PropTypes.func.isRequired, - searchForFacetValues: PropTypes.func, - addAlgoliaAgent: PropTypes.func, - clearCache: PropTypes.func, - }).isRequired, - - createURL: PropTypes.func, - - refresh: PropTypes.bool, - - searchState: PropTypes.object, - onSearchStateChange: PropTypes.func, - - onSearchParameters: PropTypes.func, - widgetsCollector: PropTypes.func, - resultsState: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), - - children: PropTypes.node, - stalledSearchDelay: PropTypes.number, - }; - - static getDerivedStateFromProps( - nextProps: Props, - prevState: State - ): Partial { - const nextIsControlled = isControlled(nextProps); - const previousSearchState = - prevState.instantSearchManager.store.getState().widgets; - const nextSearchState = nextProps.searchState; - - if (nextIsControlled && !isEqual(previousSearchState, nextSearchState)) { - prevState.instantSearchManager.onExternalStateUpdate( - nextProps.searchState - ); - } - - return { - isControlled: nextIsControlled, - contextValue: { - ...prevState.contextValue, - mainTargetedIndex: nextProps.indexName, - }, - }; - } - - cleanupTimerRef: ReturnType | null = null; - isUnmounting: boolean = false; - - constructor(props: Props) { - super(props); - - const instantSearchManager = createInstantSearchManager({ - indexName: this.props.indexName, - searchClient: this.props.searchClient, - initialState: this.props.searchState || {}, - resultsState: this.props.resultsState, - stalledSearchDelay: this.props.stalledSearchDelay, - }); - - const contextValue = { - store: instantSearchManager.store, - widgetsManager: instantSearchManager.widgetsManager, - mainTargetedIndex: this.props.indexName, - onInternalStateUpdate: this.onWidgetsInternalStateUpdate.bind(this), - createHrefForState: this.createHrefForState.bind(this), - onSearchForFacetValues: this.onSearchForFacetValues.bind(this), - onSearchStateChange: this.onSearchStateChange.bind(this), - onSearchParameters: this.onSearchParameters.bind(this), - }; - - this.state = { - isControlled: isControlled(this.props), - instantSearchManager, - contextValue, - }; - } - - componentDidUpdate(prevProps: Props) { - const prevIsControlled = isControlled(prevProps); - - if (prevIsControlled && !this.state.isControlled) { - throw new Error( - "You can't switch from being controlled to uncontrolled" - ); - } - - if (!prevIsControlled && this.state.isControlled) { - throw new Error( - "You can't switch from being uncontrolled to controlled" - ); - } - - if (this.props.refresh !== prevProps.refresh && this.props.refresh) { - this.state.instantSearchManager.clearCache(); - } - - if (prevProps.indexName !== this.props.indexName) { - this.state.instantSearchManager.updateIndex(this.props.indexName); - } - - if (prevProps.searchClient !== this.props.searchClient) { - this.state.instantSearchManager.updateClient(this.props.searchClient); - } - } - - componentDidMount() { - if (this.cleanupTimerRef) { - clearTimeout(this.cleanupTimerRef); - this.cleanupTimerRef = null; - } - - if (isMetadataEnabled()) { - injectMetadata( - this.state.instantSearchManager.widgetsManager.getWidgets(), - this.props.searchClient - ); - } - } - - componentWillUnmount() { - this.cleanupTimerRef = setTimeout(() => { - this.isUnmounting = true; - this.state.instantSearchManager.skipSearch(); - }); - } - - createHrefForState(searchState: SearchState) { - searchState = this.state.instantSearchManager.transitionState(searchState); - return this.state.isControlled && this.props.createURL - ? this.props.createURL(searchState, this.getKnownKeys()) - : '#'; - } - - onWidgetsInternalStateUpdate(searchState: SearchState) { - searchState = this.state.instantSearchManager.transitionState(searchState); - - this.onSearchStateChange(searchState); - - if (!this.state.isControlled) { - this.state.instantSearchManager.onExternalStateUpdate(searchState); - } - } - - onSearchStateChange(searchState: any) { - if (this.props.onSearchStateChange && !this.isUnmounting) { - this.props.onSearchStateChange(searchState); - } - } - - onSearchParameters( - getSearchParameters: ConnectorDescription['getMetadata'], - context: { - ais: InstantSearchContext; - multiIndexContext: IndexContext; - }, - props: object, - getMetadata: ConnectorDescription['getMetadata'], - displayName: string - ) { - if (this.props.onSearchParameters) { - const searchState = this.props.searchState ? this.props.searchState : {}; - this.props.onSearchParameters( - getSearchParameters, - context, - props, - searchState - ); - } - if (this.props.widgetsCollector) { - const searchState = this.props.searchState ? this.props.searchState : {}; - this.props.widgetsCollector({ - getSearchParameters, - getMetadata, - context, - props, - searchState, - displayName, - }); - } - } - - onSearchForFacetValues(searchState: any) { - this.state.instantSearchManager.onSearchForFacetValues(searchState); - } - - getKnownKeys() { - return this.state.instantSearchManager.getWidgetsIds(); - } - - render() { - if (Children.count(this.props.children) === 0) { - return null; - } - - return ( - - {this.props.children} - - ); - } -} - -export default InstantSearch; diff --git a/packages/react-instantsearch-core/src/widgets/QueryRuleContext.ts b/packages/react-instantsearch-core/src/widgets/QueryRuleContext.ts deleted file mode 100644 index ad9578ead2..0000000000 --- a/packages/react-instantsearch-core/src/widgets/QueryRuleContext.ts +++ /dev/null @@ -1,8 +0,0 @@ -import connectQueryRules from '../connectors/connectQueryRules'; - -export default connectQueryRules( - function QueryRuleContext() { - return null; - }, - { $$widgetType: 'ais.queryRuleContext' } -); diff --git a/packages/react-instantsearch-core/src/widgets/__tests__/DynamicWidgets.test.tsx b/packages/react-instantsearch-core/src/widgets/__tests__/DynamicWidgets.test.tsx deleted file mode 100644 index 4ee0cd7565..0000000000 --- a/packages/react-instantsearch-core/src/widgets/__tests__/DynamicWidgets.test.tsx +++ /dev/null @@ -1,439 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { render } from '@testing-library/react'; -import React from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; -import { Panel } from 'react-instantsearch-dom'; - -import { - connectHierarchicalMenu, - connectMenu, - connectPagination, - connectRefinementList, -} from '../..'; -import DynamicWidgets from '../DynamicWidgets'; -import InstantSearch from '../InstantSearch'; - -const EMPTY_RESPONSE = { - results: [ - { - hits: [], - nbHits: 0, - page: 0, - nbPages: 0, - hitsPerPage: 20, - exhaustiveNbHits: true, - query: '', - queryAfterRemoval: '', - params: - 'highlightPreTag=%3Cais-highlight-0000000000%3E&highlightPostTag=%3C%2Fais-highlight-0000000000%3E&query=&facets=%5B%5D&tagFilters=', - index: 'instant_search', - processingTimeMS: 2, - }, - ], -}; - -const createSearchClient = () => ({ - search: jest.fn(() => Promise.resolve(EMPTY_RESPONSE)), - searchForFacetValues: jest.fn(() => Promise.resolve({})), -}); - -const RefinementList = connectRefinementList( - ({ attribute }: { attribute: string }) => `RefinementList(${attribute})` -); - -const HierarchicalMenu = connectHierarchicalMenu( - ({ attributes }: { attributes: string[] }) => - `HierarchicalMenu([${attributes.join(', ')}])` -); - -const Menu = connectMenu( - ({ attribute, otherProp }: { attribute: string; otherProp: any }) => - `Menu(${attribute})${otherProp ? ` ${JSON.stringify({ otherProp })}` : ''}` -); - -const Pagination = connectPagination(() => { - return
pagination
; -}); - -describe('DynamicWidgets', () => { - describe('before results', () => { - test('does not render the result of transformItems', () => { - const searchClient = createSearchClient(); - - const { container } = render( - - ['test1']}> - - - - ); - - expect(container).toMatchInlineSnapshot(`
`); - }); - }); - - describe('with results', () => { - const resultsState = { - metadata: [], - rawResults: EMPTY_RESPONSE.results, - state: { - index: 'instant_search', - query: '', - }, - }; - - test('default items is empty', () => { - const searchClient = createSearchClient(); - - const { container } = render( - - items}> - - - - ); - - expect(container).toMatchInlineSnapshot(`
`); - }); - - test('renders return of transformItems', () => { - const searchClient = createSearchClient(); - - const { container } = render( - - ['test1']}> - - - - - ); - - expect(container).toMatchInlineSnapshot(` -
- RefinementList(test1) -
- `); - }); - - test('renders from results', () => { - const searchClient = createSearchClient(); - - const RESULTS = [ - { - ...resultsState, - rawResults: [ - { - ...resultsState.rawResults[0], - renderingContent: { - facetOrdering: { facets: { order: ['test1', 'test2'] } }, - }, - }, - ], - }, - - { - ...resultsState, - rawResults: [ - { - ...resultsState.rawResults[0], - renderingContent: { - facetOrdering: { facets: { order: ['test2', 'test1'] } }, - }, - }, - ], - }, - - { - ...resultsState, - rawResults: [ - { - ...resultsState.rawResults[0], - renderingContent: { - facetOrdering: { facets: { order: ['test1'] } }, - }, - }, - ], - }, - ]; - - const Component = ({ - result, - }: { - result: React.ComponentProps['resultsState']; - }) => ( - - - - - - - ); - { - const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- RefinementList(test1) - HierarchicalMenu([test2, test3]) -
- `); - } - { - const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- HierarchicalMenu([test2, test3]) - RefinementList(test1) -
- `); - } - { - const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- RefinementList(test1) -
- `); - } - }); - - test('renders items in panel', () => { - const searchClient = createSearchClient(); - - const { container } = render( - - ['test1', 'test3', 'test5']}> - - - - - - - - - - - - - - - - - ); - - expect(container).toMatchInlineSnapshot(` -
- RefinementList(test1) -
-
- RefinementList(test3) -
-
-
-
- HierarchicalMenu([test5, test5.1]) -
-
-
- `); - }); - - test("does not render items that aren't directly in children", () => { - const fallbackRender = jest.fn(() => null); - - // prevent duplicate console errors still showing up - const spy = jest.spyOn(console, 'error'); - spy.mockImplementation(() => {}); - - const searchClient = createSearchClient(); - - const Wrapped = ({ attr }: { attr: string }) => ( -
- -
- ); - - render( - - - ['test1']}> - - - - - ); - - // note: this test now expects a failure, but if we ever implement deep - // fetching of attribute, we expect this to render Wrapped - expect( - (fallbackRender.mock.calls[0] as any)[0].error - ).toMatchInlineSnapshot( - `[Error: Could not find "attribute" prop for UnknownComponent.]` - ); - }); - - test('does not render items non-attribute widgets', () => { - const fallbackRender = jest.fn(() => null); - - // prevent duplicate console errors still showing up - const spy = jest.spyOn(console, 'error'); - spy.mockImplementation(() => {}); - - const searchClient = createSearchClient(); - - render( - - - ['test1']}> - - - - - ); - - expect( - (fallbackRender.mock.calls[0] as any)[0].error - ).toMatchInlineSnapshot( - `[Error: Could not find "attribute" prop for UnknownComponent.]` - ); - }); - - test('does not render attributes without widget by default', () => { - const searchClient = createSearchClient(); - - const { container } = render( - - ['test1', 'test2', 'test3']}> - - - - ); - - expect(container).toMatchInlineSnapshot(` -
- RefinementList(test1) -
- `); - }); - - test("uses fallbackComponent component to create widgets that aren't explicitly declared", () => { - const searchClient = createSearchClient(); - - const { container } = render( - - ['test1', 'test2', 'test3']} - fallbackComponent={Menu} - > - - - - ); - - expect(container).toMatchInlineSnapshot(` -
- RefinementList(test1) - Menu(test2) - Menu(test3) -
- `); - }); - - test("uses fallbackComponent callback to create widgets that aren't explicitly declared", () => { - const searchClient = createSearchClient(); - - const { container } = render( - - ['test1', 'test2', 'test3']} - fallbackComponent={({ attribute }: { attribute: string }) => ( - - )} - > - - - - ); - - expect(container).toMatchInlineSnapshot(` -
- RefinementList(test1) - Menu(test2) {"otherProp":true} - Menu(test3) {"otherProp":true} -
- `); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/widgets/__tests__/Index.js b/packages/react-instantsearch-core/src/widgets/__tests__/Index.js deleted file mode 100644 index 344ea5ab5d..0000000000 --- a/packages/react-instantsearch-core/src/widgets/__tests__/Index.js +++ /dev/null @@ -1,216 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import { SearchParameters } from 'algoliasearch-helper'; -import Enzyme, { shallow, mount } from 'enzyme'; -import React from 'react'; - -import { IndexConsumer, InstantSearchProvider } from '../../core/context'; -import createConnector from '../../core/createConnector'; -import Index, { IndexComponentWithoutContext } from '../Index'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('Index', () => { - const createContext = () => ({ - onSearchParameters: jest.fn(), - widgetsManager: { - registerWidget: jest.fn(), - update: jest.fn(), - }, - }); - - const requiredProps = { - indexName: 'indexName', - indexId: 'indexId', - root: { - Root: 'div', - }, - }; - - it('registers itself on mount', () => { - const context = createContext(); - - const wrapper = shallow( - -
- - ); - - expect(context.widgetsManager.registerWidget).toHaveBeenCalledTimes(1); - expect(context.widgetsManager.registerWidget).toHaveBeenCalledWith( - wrapper.instance() - ); - }); - - it('calls onSearchParameters on mount', () => { - const context = createContext(); - - shallow( - -
- - ); - - expect(context.onSearchParameters).toHaveBeenCalledTimes(1); - }); - - it('calls update if indexName prop changes', () => { - const context = createContext(); - - // componentDidUpdate wasn't called on `shallow` - const wrapper = mount( - -
- - ); - - expect(context.widgetsManager.update).toHaveBeenCalledTimes(0); - - wrapper.setProps({ indexName: 'newIndexName' }); - - expect(context.widgetsManager.update).toHaveBeenCalledTimes(1); - }); - - it('unregisters itself on unmount', () => { - const unregister = jest.fn(); - const context = createContext(); - - context.widgetsManager.registerWidget.mockImplementation(() => unregister); - - const wrapper = shallow( - -
- - ); - - expect(unregister).toHaveBeenCalledTimes(0); - - wrapper.unmount(); - - expect(unregister).toHaveBeenCalledTimes(1); - }); - - it('exposes multi index context', () => { - const context = createContext(); - - const wrapper = mount( - - - {(multiIndexContext) => ( -
{multiIndexContext.targetedIndex}
- )} -
-
- ); - - expect(wrapper.find('.inner').text()).toBe('indexId'); - }); - - it('provides search parameters from instance props', () => { - const context = createContext(); - - const wrapper = shallow( - -
- - ); - - const parameters = wrapper - .instance() - .getSearchParameters(new SearchParameters()); - - expect(parameters.index).toBe('indexName'); - }); - - it('provides search parameters from argument props when instance props are not available', () => { - const context = createContext(); - - const wrapper = shallow( - -
- - ); - - const parameters = wrapper - .instance() - .getSearchParameters.call({}, new SearchParameters(), { - indexName: 'otherIndexName', - }); - - expect(parameters.index).toBe('otherIndexName'); - }); - - it('wrapped with InstantSearchProvider: sets correct props', () => { - const state = { - results: {}, - resultsFacetValues: {}, - searching: false, - searchingForFacetValues: false, - isSearchStalled: false, - metadata: {}, - error: {}, - widgets: { - query: 'hello', - }, - }; - - const context = { - onInternalStateUpdate() {}, - createHrefForState() {}, - onSearchForFacetValues() {}, - onSearchStateChange() {}, - onSearchParameters() {}, - widgetsManager: { - registerWidget() {}, - getWidgets() {}, - update() {}, - }, - store: { - setState() {}, - subscribe() {}, - getState: () => state, - }, - }; - - const Dummy = (props) => JSON.stringify(props, null, 2).replace(/"/g, ''); - - const Connected = createConnector({ - displayName: 'Connector', - getProvidedProps: (props) => ({ providedProps: props }), - })(Dummy); - - const props = { - message: 'hello', - }; - - const wrapper = mount( - - - - - - ); - - expect(wrapper.text()).toMatchInlineSnapshot(` -"{ - indexContextValue: { - targetedIndex: indexId - }, - message: hello, - providedProps: { - contextValue: { - widgetsManager: {}, - store: {} - }, - indexContextValue: { - targetedIndex: indexId - }, - message: hello - } -}" -`); - }); -}); diff --git a/packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.integration.js b/packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.integration.js deleted file mode 100644 index ce7aa4b47a..0000000000 --- a/packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.integration.js +++ /dev/null @@ -1,187 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { createAlgoliaSearchClient } from '@instantsearch/mocks'; -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import algoliasearch from 'algoliasearch'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; - -import { connectHits, connectRefinementList, Index } from '../../index'; -import InstantSearch from '../InstantSearch'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('metadata', () => { - const defaultUserAgent = - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15'; - const algoliaUserAgent = 'Algolia Crawler 5.3.2'; - - Object.defineProperty( - window.navigator, - 'userAgent', - ((value) => ({ - get() { - return value; - }, - set(v) { - value = v; - }, - }))(window.navigator.userAgent) - ); - - afterEach(() => { - navigator.userAgent = defaultUserAgent; - document.head.innerHTML = ''; - }); - - test('adds no metadata to if not on crawler', () => { - const RefinementList = connectRefinementList(() => null); - const Hits = connectHits(() => null); - - mount( - - - - - - - ); - - expect(document.head).toMatchInlineSnapshot(``); - }); - - test('exposes widgets', () => { - navigator.userAgent = algoliaUserAgent; - - const RefinementList = connectRefinementList(() => null); - const Hits = connectHits(() => null); - - mount( - - - - - - - ); - - expect(document.head.children).toHaveLength(1); - - const meta = document.head.querySelector('meta'); - - expect(meta).toEqual( - expect.objectContaining({ - name: 'algolia:metadata', - content: expect.any(String), - }) - ); - - expect(JSON.parse(meta.content)).toMatchInlineSnapshot(` - { - "ua": "Algolia for JavaScript (test)", - "widgets": [ - { - "$$type": "ais.refinementList", - "displayName": "AlgoliaRefinementList", - "params": [ - "attribute", - ], - }, - { - "$$type": "ais.hits", - "displayName": "AlgoliaHits", - "params": [], - }, - { - "$$type": "ais.index", - "$$widgetType": "ais.index", - "displayName": "AlgoliaIndex", - "params": [ - "indexId", - "indexName", - "children", - ], - }, - ], - } - `); - }); - - describe('algoliaAgent', () => { - test('extracts user agent from algoliasearch v3', () => { - navigator.userAgent = algoliaUserAgent; - - mount( - - ); - - const meta = document.head.querySelector('meta'); - - expect(meta).toEqual( - expect.objectContaining({ - name: 'algolia:metadata', - content: expect.any(String), - }) - ); - - expect(JSON.parse(meta.content)).toMatchInlineSnapshot(` - { - "ua": "user agent v3", - "widgets": [], - } - `); - }); - - test('extracts user agent from algoliasearch', () => { - navigator.userAgent = algoliaUserAgent; - - mount( - - ); - - expect(document.head.children).toHaveLength(1); - expect(document.head.children[0]).toEqual(expect.any(HTMLMetaElement)); - - expect(JSON.parse(document.head.querySelector('meta').content)).toEqual({ - ua: expect.stringMatching( - /^Algolia for JavaScript \((\d+\.?)+\); Node\.js \((\d+\.?)+\); JS Helper \(3\.(\d+\.?)+\); react \((\d+\.?)+\); react-instantsearch \(6\.(\d+\.?)+\)$/ - ), - widgets: [], - }); - }); - - test('extracts user agent from custom client', () => { - navigator.userAgent = algoliaUserAgent; - - mount( - - ); - - expect(document.head).toMatchInlineSnapshot(` - - - - `); - }); - }); -}); diff --git a/packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.js b/packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.js deleted file mode 100644 index fe4405e459..0000000000 --- a/packages/react-instantsearch-core/src/widgets/__tests__/InstantSearch.js +++ /dev/null @@ -1,598 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { wait } from '@instantsearch/testutils'; -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow, mount } from 'enzyme'; -import React from 'react'; - -import { InstantSearchConsumer } from '../../core/context'; -import createInstantSearchManager from '../../core/createInstantSearchManager'; -import InstantSearch from '../InstantSearch'; - -Enzyme.configure({ adapter: new Adapter() }); - -jest.mock('../../core/createInstantSearchManager'); - -const createFakeStore = (rest = {}) => ({ - getState: jest.fn(() => ({})), - setState: jest.fn(), - subscribe: jest.fn(), - ...rest, -}); - -const createFakeInstantSearchManager = (rest = {}) => ({ - context: {}, - store: createFakeStore(), - clearCache: jest.fn(), - updateIndex: jest.fn(), - updateClient: jest.fn(), - skipSearch: jest.fn(), - getWidgetsIds: jest.fn(), - onExternalStateUpdate: jest.fn(), - onSearchForFacetValues: jest.fn(), - transitionState: jest.fn(), - ...rest, -}); - -const createFakeSearchClient = (rest = {}) => ({ - search: jest.fn(), - ...rest, -}); - -const DEFAULT_PROPS = { - appId: 'foo', - apiKey: 'bar', - indexName: 'foobar', - searchClient: createFakeSearchClient(), - root: { - Root: 'div', - }, - refresh: false, -}; - -describe('InstantSearch', () => { - beforeEach(() => { - createInstantSearchManager.mockImplementation(() => - createFakeInstantSearchManager() - ); - }); - - afterEach(() => { - createInstantSearchManager.mockClear(); - }); - - it('validates its props', () => { - expect(() => { - shallow( - -
- - ); - }).not.toThrow(); - - expect(() => { - shallow(); - }).not.toThrow(); - - expect(() => { - shallow( - -
-
- - ); - }).not.toThrow(); - - expect(() => { - const wrapper = shallow( - null} - createURL={() => null} - refresh={false} - > -
- - ); - wrapper.setProps({ - searchState: undefined, - }); - }).toThrow( - "You can't switch from being controlled to uncontrolled" - ); - - expect(() => { - const wrapper = shallow( - -
- - ); - wrapper.setProps({ - searchState: {}, - onSearchStateChange: () => null, - createURL: () => null, - }); - }).toThrow( - "You can't switch from being uncontrolled to controlled" - ); - - expect(() => { - const wrapper = shallow( - null} - createURL={() => null} - > -
- - ); - wrapper.setProps({ - searchState: undefined, - onSearchStateChange: undefined, - createURL: undefined, - }); - }).toThrow( - "You can't switch from being controlled to uncontrolled" - ); - }); - - it('correctly instantiates the isManager', () => { - mount( - -
- - ); - expect(createInstantSearchManager.mock.calls[0][0]).toEqual({ - indexName: DEFAULT_PROPS.indexName, - initialState: {}, - searchClient: DEFAULT_PROPS.searchClient, - stalledSearchDelay: 200, - }); - }); - - it('updates Algolia client when new one is given in props', () => { - const ism = createFakeInstantSearchManager(); - - createInstantSearchManager.mockImplementation(() => ism); - - const wrapper = mount( - -
- - ); - - expect(ism.updateClient).toHaveBeenCalledTimes(0); - - wrapper.setProps({ - ...DEFAULT_PROPS, - searchClient: createFakeSearchClient(), - }); - - expect(ism.updateClient).toHaveBeenCalledTimes(1); - }); - - it('works as a controlled input', () => { - const ism = createFakeInstantSearchManager({ - transitionState: (searchState) => ({ - ...searchState, - transitioned: true, - }), - }); - createInstantSearchManager.mockImplementation(() => ism); - const initialState = { a: 0 }; - const onSearchStateChange = jest.fn((searchState) => { - // eslint-disable-next-line @typescript-eslint/no-use-before-define - wrapper.setProps({ - searchState: { a: searchState.a + 1 }, - }); - }); - const wrapper = mount( - '#'} - > - - {(contextValue) => ( - - )} -
-); - -Control.propTypes = { - ...ControlPropTypes, - isRefineOnMapMove: PropTypes.bool.isRequired, - toggleRefineOnMapMove: PropTypes.func.isRequired, - hasMapMoveSinceLastRefine: PropTypes.bool.isRequired, - refineWithInstance: PropTypes.func.isRequired, -}; - -const ControlWrapper = (props) => ( - - {({ - isRefineOnMapMove, - hasMapMoveSinceLastRefine, - toggleRefineOnMapMove, - refineWithInstance, - }) => ( - - )} - -); - -ControlWrapper.propTypes = ControlPropTypes; - -export default translatable({ - control: 'Search as I move the map', - redo: 'Redo search here', -})(withGoogleMaps(ControlWrapper)); diff --git a/packages/react-instantsearch-dom-maps/src/CustomMarker.js b/packages/react-instantsearch-dom-maps/src/CustomMarker.js deleted file mode 100644 index b891131e11..0000000000 --- a/packages/react-instantsearch-dom-maps/src/CustomMarker.js +++ /dev/null @@ -1,112 +0,0 @@ -import PropTypes from 'prop-types'; -import { Component } from 'react'; -import ReactDOM from 'react-dom'; - -import createHTMLMarker from './elements/createHTMLMarker'; -import { GeolocHitPropType } from './propTypes'; -import { registerEvents, createListenersPropTypes } from './utils'; -import withGoogleMaps from './withGoogleMaps'; - -const eventTypes = { - onClick: 'click', - onDoubleClick: 'dblclick', - onMouseDown: 'mousedown', - onMouseEnter: 'mouseenter', - onMouseLeave: 'mouseleave', - onMouseMove: 'mousemove', - onMouseOut: 'mouseout', - onMouseOver: 'mouseover', - onMouseUp: 'mouseup', -}; - -export class CustomMarker extends Component { - static propTypes = { - ...createListenersPropTypes(eventTypes), - hit: GeolocHitPropType.isRequired, - children: PropTypes.node.isRequired, - google: PropTypes.object.isRequired, - googleMapsInstance: PropTypes.object.isRequired, - className: PropTypes.string, - anchor: PropTypes.shape({ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }), - }; - - static defaultProps = { - className: '', - anchor: { - x: 0, - y: 0, - }, - }; - - static isReact16() { - return typeof ReactDOM.createPortal === 'function'; - } - - state = { - marker: null, - }; - - componentDidMount() { - const { hit, google, googleMapsInstance, className, anchor } = this.props; - // Not the best way to create the reference of the CustomMarker - // but since the Google object is required didn't find another - // solution. Ideas? - const Marker = createHTMLMarker(google); - - const marker = new Marker({ - map: googleMapsInstance, - position: hit._geoloc, - className, - anchor, - }); - - this.removeListeners = registerEvents(eventTypes, this.props, marker); - - this.setState(() => ({ - marker, - })); - } - - componentDidUpdate() { - const { children } = this.props; - const { marker } = this.state; - - this.removeListeners(); - - this.removeListeners = registerEvents(eventTypes, this.props, marker); - - if (!CustomMarker.isReact16()) { - ReactDOM.unstable_renderSubtreeIntoContainer( - this, - children, - marker.element - ); - } - } - - componentWillUnmount() { - const { marker } = this.state; - - if (!CustomMarker.isReact16()) { - ReactDOM.unmountComponentAtNode(marker.element); - } - - marker.setMap(null); - } - - render() { - const { children } = this.props; - const { marker } = this.state; - - if (!marker || !CustomMarker.isReact16()) { - return null; - } - - return ReactDOM.createPortal(children, marker.element); - } -} - -export default withGoogleMaps(CustomMarker); diff --git a/packages/react-instantsearch-dom-maps/src/GeoSearch.js b/packages/react-instantsearch-dom-maps/src/GeoSearch.js deleted file mode 100644 index eae2918898..0000000000 --- a/packages/react-instantsearch-dom-maps/src/GeoSearch.js +++ /dev/null @@ -1,90 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import Connector from './Connector'; -import GoogleMaps from './GoogleMaps'; -import { LatLngPropType, BoundingBoxPropType } from './propTypes'; -import Provider from './Provider'; - -class GeoSearch extends Component { - static propTypes = { - google: PropTypes.object.isRequired, - children: PropTypes.func.isRequired, - initialZoom: PropTypes.number, - initialPosition: LatLngPropType, - enableRefine: PropTypes.bool, - enableRefineOnMapMove: PropTypes.bool, - defaultRefinement: BoundingBoxPropType, - }; - - static defaultProps = { - initialZoom: 1, - initialPosition: { lat: 0, lng: 0 }, - enableRefine: true, - enableRefineOnMapMove: true, - defaultRefinement: null, - }; - - renderChildrenWithBoundFunction = ({ hits, position, ...rest }) => { - const { - google, - children, - initialZoom, - initialPosition, - enableRefine, - enableRefineOnMapMove, - defaultRefinement, - ...mapOptions - } = this.props; - - return ( - - {({ - boundingBox, - boundingBoxPadding, - onChange, - onIdle, - shouldUpdate, - }) => ( - - {children({ hits })} - - )} - - ); - }; - - render() { - const { enableRefineOnMapMove, defaultRefinement } = this.props; - - return ( - - {this.renderChildrenWithBoundFunction} - - ); - } -} - -export default GeoSearch; diff --git a/packages/react-instantsearch-dom-maps/src/GeoSearchContext.ts b/packages/react-instantsearch-dom-maps/src/GeoSearchContext.ts deleted file mode 100644 index 6161da51ab..0000000000 --- a/packages/react-instantsearch-dom-maps/src/GeoSearchContext.ts +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -type GeoSearchContextState = { - isRefineOnMapMove: boolean; - hasMapMoveSinceLastRefine: boolean; - toggleRefineOnMapMove: () => void; - setMapMoveSinceLastRefine: (value: boolean) => void; - refineWithInstance: (value: google.maps.Map) => void; -}; - -const GeoSearchContext = React.createContext({ - // The actual default value comes from the prop of the component - // wrapping the `Provider`. - isRefineOnMapMove: true, - hasMapMoveSinceLastRefine: false, - toggleRefineOnMapMove: () => {}, - setMapMoveSinceLastRefine: () => {}, - refineWithInstance: () => {}, -}); - -export default GeoSearchContext; diff --git a/packages/react-instantsearch-dom-maps/src/GoogleMaps.js b/packages/react-instantsearch-dom-maps/src/GoogleMaps.js deleted file mode 100644 index bf3ed386cd..0000000000 --- a/packages/react-instantsearch-dom-maps/src/GoogleMaps.js +++ /dev/null @@ -1,155 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component, createRef } from 'react'; -import { createClassNames } from 'react-instantsearch-dom'; - -import GoogleMapsContext from './GoogleMapsContext'; -import { LatLngPropType, BoundingBoxPropType } from './propTypes'; - -const cx = createClassNames('GeoSearch'); - -class GoogleMaps extends Component { - static propTypes = { - google: PropTypes.object.isRequired, - initialZoom: PropTypes.number.isRequired, - initialPosition: LatLngPropType.isRequired, - mapOptions: PropTypes.object.isRequired, - onChange: PropTypes.func.isRequired, - onIdle: PropTypes.func.isRequired, - shouldUpdate: PropTypes.func.isRequired, - boundingBox: BoundingBoxPropType, - boundingBoxPadding: PropTypes.number, - children: PropTypes.node, - }; - - isUserInteraction = true; - isPendingRefine = false; - listeners = []; - mapRef = createRef(); - - state = { - isMapReady: false, - }; - - componentDidMount() { - const { google, mapOptions } = this.props; - - this.instance = new google.maps.Map(this.mapRef.current, { - mapTypeControl: false, - fullscreenControl: false, - streetViewControl: false, - clickableIcons: false, - zoomControlOptions: { - position: google.maps.ControlPosition.LEFT_TOP, - }, - ...mapOptions, - }); - - this.listeners.push( - google.maps.event.addListenerOnce( - this.instance, - 'idle', - this.setupListenersWhenMapIsReady - ) - ); - } - - componentDidUpdate() { - const { - google, - initialZoom, - initialPosition, - boundingBox, - boundingBoxPadding, - shouldUpdate, - } = this.props; - - if (!shouldUpdate()) { - return; - } - - if (boundingBox) { - this.lockUserInteraction(() => { - const oldBounds = this.instance.getBounds(); - - // south west and north east are swapped - const newBounds = new google.maps.LatLngBounds( - boundingBox.southWest, - boundingBox.northEast - ); - - if (!newBounds.equals(oldBounds)) { - this.instance.fitBounds(newBounds, boundingBoxPadding); - } - }); - } else { - this.lockUserInteraction(() => { - this.instance.setZoom(initialZoom); - this.instance.setCenter(initialPosition); - }); - } - } - - componentWillUnmount() { - this.listeners.forEach((listener) => { - listener.remove(); - }); - - this.listeners = []; - } - - setupListenersWhenMapIsReady = () => { - this.listeners = []; - - this.setState({ - isMapReady: true, - value: { - google: this.props.google, - instance: this.instance, - }, - }); - - const onChange = () => { - if (this.isUserInteraction) { - this.props.onChange(); - } - }; - - this.listeners.push(this.instance.addListener('center_changed', onChange)); - this.listeners.push(this.instance.addListener('zoom_changed', onChange)); - this.listeners.push(this.instance.addListener('dragstart', onChange)); - - this.listeners.push( - this.instance.addListener('idle', () => { - if (this.isUserInteraction) { - this.props.onIdle({ - instance: this.instance, - }); - } - }) - ); - }; - - lockUserInteraction(functionThatAltersTheMapPosition) { - this.isUserInteraction = false; - functionThatAltersTheMapPosition(); - this.isUserInteraction = true; - } - - render() { - const { children } = this.props; - const { isMapReady } = this.state; - - return ( -
-
- {isMapReady && ( - - {children} - - )} -
- ); - } -} - -export default GoogleMaps; diff --git a/packages/react-instantsearch-dom-maps/src/GoogleMapsContext.ts b/packages/react-instantsearch-dom-maps/src/GoogleMapsContext.ts deleted file mode 100644 index 0093341562..0000000000 --- a/packages/react-instantsearch-dom-maps/src/GoogleMapsContext.ts +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; - -export type GoogleMapsContextState = { - google: typeof google; - instance: google.maps.Map; -}; - -const GoogleMapsContext = React.createContext({ - // We mount the context only once the map is created. Thus, we can assume - // that the value provided through the context is never `undefined`. We can't - // create an instance at that point, hence the cast. - google: {} as unknown as typeof google, - instance: {} as unknown as google.maps.Map, -}); - -export default GoogleMapsContext; diff --git a/packages/react-instantsearch-dom-maps/src/GoogleMapsLoader.js b/packages/react-instantsearch-dom-maps/src/GoogleMapsLoader.js deleted file mode 100644 index 2766ae2cdf..0000000000 --- a/packages/react-instantsearch-dom-maps/src/GoogleMapsLoader.js +++ /dev/null @@ -1,53 +0,0 @@ -import PropTypes from 'prop-types'; -import { Component } from 'react'; - -class GoogleMapsLoader extends Component { - static propTypes = { - apiKey: PropTypes.string.isRequired, - children: PropTypes.func.isRequired, - endpoint: PropTypes.string, - }; - - static defaultProps = { - endpoint: 'https://maps.googleapis.com/maps/api/js?v=quarterly', - }; - - state = { - google: null, - }; - - isUnmounting = false; - - componentDidMount() { - // Inline the import to avoid to run the module on the server (rely on `document`) - // Under the hood we use `dynamic-import-node` to transpile the `import` to `require` - // see: https://github.com/algolia/react-instantsearch/issues/1425 - return import('scriptjs').then(({ default: injectScript }) => { - const { apiKey, endpoint } = this.props; - const operator = endpoint.indexOf('?') !== -1 ? '&' : '?'; - const endpointWithCredentials = `${endpoint}${operator}key=${apiKey}`; - - injectScript(endpointWithCredentials, () => { - if (!this.isUnmounting) { - this.setState(() => ({ - google: window.google, - })); - } - }); - }); - } - - componentWillUnmount() { - this.isUnmounting = true; - } - - render() { - if (!this.state.google) { - return null; - } - - return this.props.children(this.state.google); - } -} - -export default GoogleMapsLoader; diff --git a/packages/react-instantsearch-dom-maps/src/Marker.js b/packages/react-instantsearch-dom-maps/src/Marker.js deleted file mode 100644 index ff9e8ed25a..0000000000 --- a/packages/react-instantsearch-dom-maps/src/Marker.js +++ /dev/null @@ -1,67 +0,0 @@ -import PropTypes from 'prop-types'; -import { Component } from 'react'; - -import { GeolocHitPropType } from './propTypes'; -import { - registerEvents, - createListenersPropTypes, - createFilterProps, -} from './utils'; -import withGoogleMaps from './withGoogleMaps'; - -const eventTypes = { - onClick: 'click', - onDoubleClick: 'dblclick', - onMouseDown: 'mousedown', - onMouseOut: 'mouseout', - onMouseOver: 'mouseover', - onMouseUp: 'mouseup', -}; - -const excludes = ['children'].concat(Object.keys(eventTypes)); -const filterProps = createFilterProps(excludes); - -export class Marker extends Component { - static propTypes = { - ...createListenersPropTypes(eventTypes), - google: PropTypes.object.isRequired, - googleMapsInstance: PropTypes.object.isRequired, - hit: GeolocHitPropType.isRequired, - }; - - componentDidMount() { - const { google, googleMapsInstance, hit, ...props } = this.props; - - this.instance = new google.maps.Marker({ - ...filterProps(props), - map: googleMapsInstance, - position: hit._geoloc, - }); - - this.removeEventsListeners = registerEvents( - eventTypes, - this.props, - this.instance - ); - } - - componentDidUpdate() { - this.removeEventsListeners(); - - this.removeEventsListeners = registerEvents( - eventTypes, - this.props, - this.instance - ); - } - - componentWillUnmount() { - this.instance.setMap(null); - } - - render() { - return null; - } -} - -export default withGoogleMaps(Marker); diff --git a/packages/react-instantsearch-dom-maps/src/Provider.js b/packages/react-instantsearch-dom-maps/src/Provider.js deleted file mode 100644 index c752205aee..0000000000 --- a/packages/react-instantsearch-dom-maps/src/Provider.js +++ /dev/null @@ -1,131 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import GeoSearchContext from './GeoSearchContext'; -import { LatLngPropType, BoundingBoxPropType } from './propTypes'; - -class Provider extends Component { - static propTypes = { - google: PropTypes.object.isRequired, - hits: PropTypes.arrayOf(PropTypes.object).isRequired, - isRefineOnMapMove: PropTypes.bool.isRequired, - hasMapMoveSinceLastRefine: PropTypes.bool.isRequired, - isRefineEnable: PropTypes.bool.isRequired, - refine: PropTypes.func.isRequired, - toggleRefineOnMapMove: PropTypes.func.isRequired, - setMapMoveSinceLastRefine: PropTypes.func.isRequired, - children: PropTypes.func.isRequired, - position: LatLngPropType, - currentRefinement: BoundingBoxPropType, - }; - - isPendingRefine = false; - boundingBox = null; - previousBoundingBox = null; - - refineWithInstance = (instance) => { - const { refine } = this.props; - - const bounds = instance.getBounds(); - - refine({ - northEast: bounds.getNorthEast().toJSON(), - southWest: bounds.getSouthWest().toJSON(), - }); - }; - - mapValue = { - isRefineOnMapMove: this.props.isRefineOnMapMove, - hasMapMoveSinceLastRefine: this.props.hasMapMoveSinceLastRefine, - toggleRefineOnMapMove: this.props.toggleRefineOnMapMove, - setMapMoveSinceLastRefine: this.props.setMapMoveSinceLastRefine, - refineWithInstance: this.refineWithInstance, - }; - - getMapValue = () => { - const haveContextValuesChanges = - this.mapValue.isRefineOnMapMove !== this.props.isRefineOnMapMove || - this.mapValue.hasMapMoveSinceLastRefine !== - this.props.hasMapMoveSinceLastRefine; - - if (haveContextValuesChanges) { - this.mapValue = { - ...this.mapValue, - isRefineOnMapMove: this.props.isRefineOnMapMove, - hasMapMoveSinceLastRefine: this.props.hasMapMoveSinceLastRefine, - }; - } - - return this.mapValue; - }; - - createBoundingBoxFromHits(hits) { - const { google } = this.props; - - const latLngBounds = hits.reduce( - (acc, hit) => acc.extend(hit._geoloc), - new google.maps.LatLngBounds() - ); - - return { - northEast: latLngBounds.getNorthEast().toJSON(), - southWest: latLngBounds.getSouthWest().toJSON(), - }; - } - - onChange = () => { - const { isRefineOnMapMove, isRefineEnable, setMapMoveSinceLastRefine } = - this.props; - - if (isRefineEnable) { - setMapMoveSinceLastRefine(true); - - if (isRefineOnMapMove) { - this.isPendingRefine = true; - } - } - }; - - onIdle = ({ instance }) => { - if (this.isPendingRefine) { - this.isPendingRefine = false; - - this.refineWithInstance(instance); - } - }; - - shouldUpdate = () => { - const { hasMapMoveSinceLastRefine } = this.props; - - return !this.isPendingRefine && !hasMapMoveSinceLastRefine; - }; - - render() { - const { hits, currentRefinement, children } = this.props; - - // We use this value for differentiate the padding to apply during - // fitBounds. When we don't have a currenRefinement (boundingBox) - // we let GoogleMaps compute the automatic padding. But when we - // provide the currentRefinement we explicitly set the padding - // to `0` otherwise the map will decrease the zoom on each refine. - const boundingBoxPadding = !currentRefinement ? undefined : 0; - const boundingBox = - !currentRefinement && Boolean(hits.length) - ? this.createBoundingBoxFromHits(hits) - : currentRefinement; - - return ( - - {children({ - onChange: this.onChange, - onIdle: this.onIdle, - shouldUpdate: this.shouldUpdate, - boundingBox, - boundingBoxPadding, - })} - - ); - } -} - -export default Provider; diff --git a/packages/react-instantsearch-dom-maps/src/Redo.js b/packages/react-instantsearch-dom-maps/src/Redo.js deleted file mode 100644 index 5d441081dc..0000000000 --- a/packages/react-instantsearch-dom-maps/src/Redo.js +++ /dev/null @@ -1,53 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { createClassNames, translatable } from 'react-instantsearch-dom'; - -import GeoSearchContext from './GeoSearchContext'; -import withGoogleMaps from './withGoogleMaps'; - -const cx = createClassNames('GeoSearch'); -const RedoPropTypes = { - googleMapsInstance: PropTypes.object.isRequired, - translate: PropTypes.func.isRequired, -}; - -export const Redo = ({ - googleMapsInstance, - translate, - hasMapMoveSinceLastRefine, - refineWithInstance, -}) => ( -
- -
-); - -Redo.propTypes = { - ...RedoPropTypes, - hasMapMoveSinceLastRefine: PropTypes.bool.isRequired, - refineWithInstance: PropTypes.func.isRequired, -}; - -const RedoWrapper = (props) => ( - - {({ hasMapMoveSinceLastRefine, refineWithInstance }) => ( - - )} - -); - -RedoWrapper.propTypes = RedoPropTypes; - -export default translatable({ - redo: 'Redo search here', -})(withGoogleMaps(RedoWrapper)); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/Connector.js b/packages/react-instantsearch-dom-maps/src/__tests__/Connector.js deleted file mode 100644 index c2c7663a9d..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/Connector.js +++ /dev/null @@ -1,241 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import { Connector } from '../Connector'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('Connector', () => { - const defaultProps = { - hits: [], - position: null, - currentRefinement: null, - isRefinedWithMap: false, - enableRefineOnMapMove: true, - refine: () => {}, - }; - - const lastRenderArgs = (fn) => fn.mock.calls[fn.mock.calls.length - 1][0]; - - it('expect to call children with props', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - shallow({children}); - - expect(children).toHaveBeenCalledTimes(1); - expect(children).toHaveBeenCalledWith({ - hits: [], - position: null, - currentRefinement: null, - isRefinedWithMap: false, - isRefineOnMapMove: true, - toggleRefineOnMapMove: expect.any(Function), - hasMapMoveSinceLastRefine: false, - setMapMoveSinceLastRefine: expect.any(Function), - refine: expect.any(Function), - }); - }); - - it('expect to call children with refine on map move disabled', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - enableRefineOnMapMove: false, - }; - - shallow({children}); - - expect(children).toHaveBeenCalledTimes(1); - expect(children).toHaveBeenCalledWith( - expect.objectContaining({ - isRefineOnMapMove: false, - }) - ); - }); - - describe('getDerivedStateFromProps', () => { - it('expect reset hasMapMoveSinceLastRefine when position change', () => { - const nextProps = { - ...defaultProps, - position: { lat: 12, lng: 14 }, - currentRefinement: null, - }; - - const state = { - hasMapMoveSinceLastRefine: true, - previousPosition: { lat: 10, lng: 12 }, - previousCurrentRefinement: null, - }; - - const actual = Connector.getDerivedStateFromProps(nextProps, state); - - const expectation = { - hasMapMoveSinceLastRefine: false, - previousPosition: { lat: 12, lng: 14 }, - previousCurrentRefinement: null, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect reset hasMapMoveSinceLastRefine when currentRefinement change', () => { - const nextProps = { - ...defaultProps, - position: null, - currentRefinement: { - northEast: { lat: 12, lng: 14 }, - southWest: { lat: 14, lng: 16 }, - }, - }; - - const state = { - hasMapMoveSinceLastRefine: true, - previousPosition: null, - previousCurrentRefinement: { - northEast: { lat: 10, lng: 12 }, - southWest: { lat: 12, lng: 14 }, - }, - }; - - const actual = Connector.getDerivedStateFromProps(nextProps, state); - - const expectation = { - hasMapMoveSinceLastRefine: false, - previousPosition: null, - previousCurrentRefinement: { - northEast: { lat: 12, lng: 14 }, - southWest: { lat: 14, lng: 16 }, - }, - }; - - expect(actual).toEqual(expectation); - }); - - it('expect to not reset hasMapMoveSinceLastRefine when nothing change', () => { - const nextProps = { - ...defaultProps, - position: { lat: 10, lng: 12 }, - currentRefinement: { - northEast: { lat: 10, lng: 12 }, - southWest: { lat: 12, lng: 14 }, - }, - }; - - const state = { - hasMapMoveSinceLastRefine: true, - previousPosition: { lat: 10, lng: 12 }, - previousCurrentRefinement: { - northEast: { lat: 10, lng: 12 }, - southWest: { lat: 12, lng: 14 }, - }, - }; - - const actual = Connector.getDerivedStateFromProps(nextProps, state); - - const expectation = { - previousPosition: { lat: 10, lng: 12 }, - previousCurrentRefinement: { - northEast: { lat: 10, lng: 12 }, - southWest: { lat: 12, lng: 14 }, - }, - }; - - expect(actual).toEqual(expectation); - }); - }); - - describe('setMapMoveSinceLastRefine', () => { - it('expect to update the state with the given value', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - const wrapper = shallow({children}); - - lastRenderArgs(children).setMapMoveSinceLastRefine(true); - - expect(wrapper.state().hasMapMoveSinceLastRefine).toBe(true); - }); - - it('expect to only update the state when the given is different', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - shallow({children}); - - expect(children).toHaveBeenCalledTimes(1); - expect(children).toHaveBeenLastCalledWith( - expect.objectContaining({ - hasMapMoveSinceLastRefine: false, - }) - ); - - lastRenderArgs(children).setMapMoveSinceLastRefine(true); - - expect(children).toHaveBeenCalledTimes(2); - expect(children).toHaveBeenLastCalledWith( - expect.objectContaining({ - hasMapMoveSinceLastRefine: true, - }) - ); - - lastRenderArgs(children).setMapMoveSinceLastRefine(true); - - expect(children).toHaveBeenCalledTimes(2); - expect(children).toHaveBeenLastCalledWith( - expect.objectContaining({ - hasMapMoveSinceLastRefine: true, - }) - ); - }); - }); - - describe('toggleRefineOnMapMove', () => { - it('expect to update the state with the invert of previous value (true)', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - const wrapper = shallow({children}); - - expect(wrapper.state().isRefineOnMapMove).toBe(true); - - lastRenderArgs(children).toggleRefineOnMapMove(); - - expect(wrapper.state().isRefineOnMapMove).toBe(false); - }); - - it('expect to update the state with the invert of previous value (false)', () => { - const children = jest.fn(); - - const props = { - ...defaultProps, - }; - - const wrapper = shallow({children}); - - wrapper.setState({ - isRefineOnMapMove: false, - }); - - expect(wrapper.state().isRefineOnMapMove).toBe(false); - - lastRenderArgs(children).toggleRefineOnMapMove(); - - expect(wrapper.state().isRefineOnMapMove).toBe(true); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/Control.js b/packages/react-instantsearch-dom-maps/src/__tests__/Control.js deleted file mode 100644 index 0f64ade1b0..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/Control.js +++ /dev/null @@ -1,90 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import { createFakeMapInstance } from '../../test/mockGoogleMaps'; -import { Control } from '../Control'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('Control', () => { - const defaultProps = { - googleMapsInstance: createFakeMapInstance(), - translate: (x) => x, - isRefineOnMapMove: true, - hasMapMoveSinceLastRefine: false, - toggleRefineOnMapMove: () => {}, - refineWithInstance: () => {}, - }; - - it('expect to render correctly with refine on map move', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('input').props().checked).toBe(true); - }); - - it('expect to render correctly without refine on map move', () => { - const props = { - ...defaultProps, - isRefineOnMapMove: false, - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('input').props().checked).toBe(false); - }); - - it('expect to render correctly without refine on map move when the map has moved', () => { - const props = { - ...defaultProps, - isRefineOnMapMove: false, - hasMapMoveSinceLastRefine: true, - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to call toggleRefineOnMapMove on input change', () => { - const props = { - ...defaultProps, - toggleRefineOnMapMove: jest.fn(), - }; - - const wrapper = shallow(); - - expect(props.toggleRefineOnMapMove).toHaveBeenCalledTimes(0); - - wrapper.find('input').simulate('change'); - - expect(props.toggleRefineOnMapMove).toHaveBeenCalledTimes(1); - }); - - it('expect to call refineWithInstance on button click', () => { - const mapInstance = createFakeMapInstance(); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - isRefineOnMapMove: false, - hasMapMoveSinceLastRefine: true, - refineWithInstance: jest.fn(), - }; - - const wrapper = shallow(); - - expect(props.refineWithInstance).toHaveBeenCalledTimes(0); - - wrapper.find('button').simulate('click'); - - expect(props.refineWithInstance).toHaveBeenCalledTimes(1); - expect(props.refineWithInstance).toHaveBeenCalledWith(mapInstance); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/CustomMarker.js b/packages/react-instantsearch-dom-maps/src/__tests__/CustomMarker.js deleted file mode 100644 index b24d0a0f87..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/CustomMarker.js +++ /dev/null @@ -1,602 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount, shallow } from 'enzyme'; -import { createSerializer } from 'enzyme-to-json'; -import React from 'react'; -import ReactDOM from 'react-dom'; - -import { - createFakeGoogleReference, - createFakeMapInstance, - createFakeHTMLMarkerInstance, -} from '../../test/mockGoogleMaps'; -import Connected, { CustomMarker } from '../CustomMarker'; -import createHTMLMarker from '../elements/createHTMLMarker'; -import GoogleMapsContext from '../GoogleMapsContext'; -import * as utils from '../utils'; - -expect.addSnapshotSerializer(createSerializer()); -Enzyme.configure({ adapter: new Adapter() }); - -jest.mock('../elements/createHTMLMarker', () => jest.fn()); - -jest.mock('../utils'); - -describe('CustomMarker', () => { - const defaultProps = { - hit: { - _geoloc: { - lat: 10, - lng: 12, - }, - }, - }; - - beforeEach(() => { - utils.registerEvents.mockClear(); - utils.registerEvents.mockReset(); - - // Register default implementation - utils.registerEvents.mockImplementation(() => jest.fn()); - }); - - describe('creation', () => { - it('expect to create the marker on didMount with default options', () => { - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow( - - This is the children. - - ); - - expect(createHTMLMarker).toHaveBeenCalledWith(google); - expect(wrapper.state('marker')).toBe(marker); - - expect(factory).toHaveBeenCalledTimes(1); - expect(factory).toHaveBeenCalledWith( - expect.objectContaining({ - map: mapInstance, - position: { - lat: 10, - lng: 12, - }, - }) - ); - }); - - it('expect to create the marker on didMount with given options', () => { - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - className: 'my-marker', - googleMapsInstance: mapInstance, - anchor: { - x: 10, - y: 10, - }, - google, - }; - - shallow( - - This is the children. - - ); - - expect(factory).toHaveBeenCalledWith( - expect.objectContaining({ - className: 'my-marker', - anchor: { - x: 10, - y: 10, - }, - }) - ); - }); - - it('expect to register the listeners on didMount', () => { - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - shallow( - - This is the children. - - ); - - expect(utils.registerEvents).toHaveBeenCalledTimes(2); // cDM + cDU - expect(utils.registerEvents).toHaveBeenCalledWith( - expect.any(Object), - expect.any(Object), - marker - ); - }); - }); - - describe('update', () => { - it('expect to remove the listeners on didUpdate', () => { - const removeEventListeners = jest.fn(); - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - utils.registerEvents.mockImplementation(() => removeEventListeners); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - shallow( - - This is the children. - - ); - - expect(removeEventListeners).toHaveBeenCalledTimes(1); - }); - - it('expect to register the listeners on didUpdate', () => { - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - shallow( - - This is the children. - - ); - - expect(utils.registerEvents).toHaveBeenCalledTimes(2); // cDM + cDU - expect(utils.registerEvents).toHaveBeenCalledWith( - expect.any(Object), - expect.any(Object), - marker - ); - }); - }); - - describe('delete', () => { - it('expect to remove the Marker on willUnmount', () => { - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow( - - This is the children. - - ); - - wrapper.unmount(); - - expect(marker.setMap).toHaveBeenCalledTimes(1); - expect(marker.setMap).toHaveBeenCalledWith(null); - }); - }); - - describe('with portal', () => { - it('expect to render correctly', () => { - const unstableRenderSubtreeIntoContainer = jest.spyOn( - ReactDOM, - 'unstable_renderSubtreeIntoContainer' - ); - - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - utils.registerEvents.mockImplementation(() => () => {}); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - // Use `mount` instead of `shallow` to trigger the render - // of createPortal otherwise the Snapshot is empty - const wrapper = mount( - - This is the children. - - ); - - expect(wrapper).toMatchSnapshot(); - - expect(unstableRenderSubtreeIntoContainer).not.toHaveBeenCalled(); - - unstableRenderSubtreeIntoContainer.mockReset(); - unstableRenderSubtreeIntoContainer.mockRestore(); - }); - - it('expect to render correctly without a marker', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow( - - This is the children. - , - { - disableLifecycleMethods: true, - } - ); - - expect(wrapper.type()).toBe(null); - }); - - it('expect to not render on didUpdate', () => { - const unstableRenderSubtreeIntoContainer = jest.spyOn( - ReactDOM, - 'unstable_renderSubtreeIntoContainer' - ); - - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - utils.registerEvents.mockImplementation(() => () => {}); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - // Use `mount` instead of `shallow` to trigger didUpdate - const wrapper = mount( - - This is the children. - - ); - - expect(unstableRenderSubtreeIntoContainer).not.toHaveBeenCalled(); - - wrapper.instance().componentDidUpdate(); - - expect(unstableRenderSubtreeIntoContainer).not.toHaveBeenCalled(); - - unstableRenderSubtreeIntoContainer.mockReset(); - unstableRenderSubtreeIntoContainer.mockRestore(); - }); - - it('expect to not call unmountComponentAtNode on willUnmount', () => { - const unmountComponentAtNode = jest.spyOn( - ReactDOM, - 'unmountComponentAtNode' - ); - - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow( - - This is the children. - - ); - - wrapper.unmount(); - - expect(unmountComponentAtNode).not.toHaveBeenCalled(); - - unmountComponentAtNode.mockReset(); - unmountComponentAtNode.mockRestore(); - }); - }); - - describe('with unstable_renderSubtreeIntoContainer', () => { - it('expect to render correctly', () => { - const unstableRenderSubtreeIntoContainer = jest.spyOn( - ReactDOM, - 'unstable_renderSubtreeIntoContainer' - ); - - const isReact16 = jest - .spyOn(CustomMarker, 'isReact16') - .mockImplementation(() => false); - - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - utils.registerEvents.mockImplementation(() => () => {}); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = mount( - - This is the children. - - ); - - expect(wrapper).toMatchSnapshot(); - - expect(unstableRenderSubtreeIntoContainer).toHaveBeenCalledTimes(1); - expect(unstableRenderSubtreeIntoContainer).toHaveBeenCalledWith( - wrapper.instance(), - This is the children., - marker.element - ); - - unstableRenderSubtreeIntoContainer.mockReset(); - unstableRenderSubtreeIntoContainer.mockRestore(); - - isReact16.mockReset(); - isReact16.mockRestore(); - }); - - it('expect to render correctly without a marker', () => { - const isReact16 = jest - .spyOn(CustomMarker, 'isReact16') - .mockImplementation(() => false); - - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow( - - This is the children. - , - { - disableLifecycleMethods: true, - } - ); - - expect(wrapper.type()).toBe(null); - - isReact16.mockReset(); - isReact16.mockRestore(); - }); - - it('expect to render on didUpdate', () => { - const unstableRenderSubtreeIntoContainer = jest.spyOn( - ReactDOM, - 'unstable_renderSubtreeIntoContainer' - ); - - const isReact16 = jest - .spyOn(CustomMarker, 'isReact16') - .mockImplementation(() => false); - - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - utils.registerEvents.mockImplementation(() => () => {}); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - // Use `mount` instead of `shallow` to avoid issue with `unstable_renderSubtreeIntoContainer` - const wrapper = mount( - - This is the children. - - ); - - expect(unstableRenderSubtreeIntoContainer).toHaveBeenCalledTimes(1); - expect(unstableRenderSubtreeIntoContainer).toHaveBeenCalledWith( - wrapper.instance(), - This is the children., - marker.element - ); - - wrapper.instance().componentDidUpdate(); - - expect(unstableRenderSubtreeIntoContainer).toHaveBeenCalledTimes(2); - expect(unstableRenderSubtreeIntoContainer).toHaveBeenCalledWith( - wrapper.instance(), - This is the children., - marker.element - ); - - unstableRenderSubtreeIntoContainer.mockReset(); - unstableRenderSubtreeIntoContainer.mockRestore(); - - isReact16.mockReset(); - isReact16.mockRestore(); - }); - - it('expect to call unmountComponentAtNode on willUnmount', () => { - const unmountComponentAtNode = jest.spyOn( - ReactDOM, - 'unmountComponentAtNode' - ); - - const isReact16 = jest - .spyOn(CustomMarker, 'isReact16') - .mockImplementation(() => false); - - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - // Use `mount` instead of `shallow` to avoid issue with `unstable_renderSubtreeIntoContainer` - const wrapper = mount( - - This is the children. - - ); - - wrapper.unmount(); - - expect(unmountComponentAtNode).toHaveBeenCalledWith(marker.element); - - unmountComponentAtNode.mockReset(); - unmountComponentAtNode.mockRestore(); - - isReact16.mockReset(); - isReact16.mockRestore(); - }); - }); - - describe('Connected', () => { - it('expect to have access to Google Maps', () => { - const marker = createFakeHTMLMarkerInstance(); - const factory = jest.fn(() => marker); - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - createHTMLMarker.mockImplementationOnce(() => factory); - - const props = { - ...defaultProps, - }; - - mount( - - - This is the children. - - - ); - - expect(createHTMLMarker).toHaveBeenCalledWith(google); - - expect(factory).toHaveBeenCalledWith( - expect.objectContaining({ - map: mapInstance, - }) - ); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/GeoSearch.js b/packages/react-instantsearch-dom-maps/src/__tests__/GeoSearch.js deleted file mode 100644 index db081d5e0c..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/GeoSearch.js +++ /dev/null @@ -1,493 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import { createFakeGoogleReference } from '../../test/mockGoogleMaps'; -import GeoSearch from '../GeoSearch'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('GeoSearch', () => { - const ShallowWapper = ({ children }) => children; - - const defaultProps = { - google: createFakeGoogleReference(), - }; - - const defaultConnectorProps = { - hits: [], - isRefineOnMapMove: true, - hasMapMoveSinceLastRefine: false, - refine: () => {}, - toggleRefineOnMapMove: () => {}, - setMapMoveSinceLastRefine: () => {}, - }; - - const renderConnector = ({ props, connectorProps, children = () => null }) => - shallow({children}) - .find('[testID="Connector"]') - .props() - .children(connectorProps); - - describe('Connector', () => { - it('expect to render', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow({() => null}); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with enableRefineOnMapMove', () => { - const props = { - ...defaultProps, - enableRefineOnMapMove: false, - }; - - const wrapper = shallow({() => null}); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with defaultRefinement', () => { - const props = { - ...defaultProps, - defaultRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const wrapper = shallow({() => null}); - - expect(wrapper).toMatchSnapshot(); - }); - }); - - describe('Provider', () => { - it('expect to render', () => { - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const renderPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - expect(renderPropsWrapper).toMatchSnapshot(); - }); - - it('expect to render with hits', () => { - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - hits: [ - { objectID: '0001' }, - { objectID: '0002' }, - { objectID: '0003' }, - ], - }; - - const renderPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerProps = renderPropsWrapper - .find('[testID="Provider"]') - .props(); - - expect(providerProps.hits).toEqual([ - { objectID: '0001' }, - { objectID: '0002' }, - { objectID: '0003' }, - ]); - }); - - it('expect to render with position', () => { - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - position: { - lat: 10, - lng: 12, - }, - }; - - const renderConnectorWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerProps = renderConnectorWrapper - .find('[testID="Provider"]') - .props(); - - expect(providerProps.position).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to render with currentRefinement', () => { - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - currentRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const renderConnectorWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerProps = renderConnectorWrapper - .find('[testID="Provider"]') - .props(); - - expect(providerProps.currentRefinement).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to render with enableRefine', () => { - const props = { - ...defaultProps, - enableRefine: false, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const renderConnectorWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerProps = renderConnectorWrapper - .find('[testID="Provider"]') - .props(); - - expect(providerProps.isRefineEnable).toBe(false); - }); - }); - - describe('GoogleMaps', () => { - const defaultProviderProps = { - onChange: () => {}, - onIdle: () => {}, - shouldUpdate: () => true, - }; - - const renderProvider = ({ connectorPropsWrapper, providerProps }) => - connectorPropsWrapper - .find('[testID="Provider"]') - .props() - .children(providerProps); - - it('expect to render', () => { - const children = jest.fn(() =>
Hello this is the children
); - - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const providerProps = { - ...defaultProviderProps, - }; - - const connectorPropsWrapper = shallow( - - {renderConnector({ props, connectorProps, children })} - - ); - - const providerPropsWrapper = shallow( - - {renderProvider({ connectorPropsWrapper, providerProps })} - - ); - - expect(providerPropsWrapper).toMatchSnapshot(); - expect(children).toHaveBeenCalledWith({ hits: [] }); - }); - - it('expect to render with initialZoom', () => { - const props = { - ...defaultProps, - initialZoom: 8, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const providerProps = { - ...defaultProviderProps, - }; - - const connectorPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerPropsWrapper = shallow( - - {renderProvider({ connectorPropsWrapper, providerProps })} - - ); - - const googleMapProps = providerPropsWrapper - .find('[testID="GoogleMaps"]') - .props(); - - expect(googleMapProps.initialZoom).toBe(8); - }); - - it('expect to render with postiion', () => { - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - position: { - lat: 10, - lng: 12, - }, - }; - - const providerProps = { - ...defaultProviderProps, - }; - - const connectorPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerPropsWrapper = shallow( - - {renderProvider({ connectorPropsWrapper, providerProps })} - - ); - - const googleMapProps = providerPropsWrapper - .find('[testID="GoogleMaps"]') - .props(); - - expect(googleMapProps.initialPosition).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to render with initialPosition', () => { - const props = { - ...defaultProps, - initialPosition: { - lat: 10, - lng: 12, - }, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const providerProps = { - ...defaultProviderProps, - }; - - const connectorPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerPropsWrapper = shallow( - - {renderProvider({ connectorPropsWrapper, providerProps })} - - ); - - const googleMapProps = providerPropsWrapper - .find('[testID="GoogleMaps"]') - .props(); - - expect(googleMapProps.initialPosition).toEqual({ - lat: 10, - lng: 12, - }); - }); - - it('expect to render with map options', () => { - const props = { - ...defaultProps, - streetViewControl: true, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const providerProps = { - ...defaultProviderProps, - }; - - const connectorPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerPropsWrapper = shallow( - - {renderProvider({ connectorPropsWrapper, providerProps })} - - ); - - const googleMapProps = providerPropsWrapper - .find('[testID="GoogleMaps"]') - .props(); - - expect(googleMapProps.mapOptions).toEqual({ - streetViewControl: true, - }); - }); - - it('expect to render with boundingBox', () => { - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const providerProps = { - ...defaultProviderProps, - boundingBox: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - const connectorPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerPropsWrapper = shallow( - - {renderProvider({ connectorPropsWrapper, providerProps })} - - ); - - const googleMapProps = providerPropsWrapper - .find('[testID="GoogleMaps"]') - .props(); - - expect(googleMapProps.boundingBox).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to render with boundingBoxPadding', () => { - const props = { - ...defaultProps, - }; - - const connectorProps = { - ...defaultConnectorProps, - }; - - const providerProps = { - ...defaultProviderProps, - boundingBoxPadding: 10, - }; - - const connectorPropsWrapper = shallow( - - {renderConnector({ props, connectorProps })} - - ); - - const providerPropsWrapper = shallow( - - {renderProvider({ connectorPropsWrapper, providerProps })} - - ); - - const googleMapProps = providerPropsWrapper - .find('[testID="GoogleMaps"]') - .props(); - - expect(googleMapProps.boundingBoxPadding).toBe(10); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMaps.js b/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMaps.js deleted file mode 100644 index aab89d90a5..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMaps.js +++ /dev/null @@ -1,635 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow, mount } from 'enzyme'; -import React from 'react'; - -import { - createFakeGoogleReference, - createFakeMapInstance, -} from '../../test/mockGoogleMaps'; -import GoogleMaps from '../GoogleMaps'; -import GoogleMapsContext from '../GoogleMapsContext'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('GoogleMaps', () => { - const defaultProps = { - google: createFakeGoogleReference(), - initialZoom: 1, - initialPosition: { - lat: 0, - lng: 0, - }, - mapOptions: {}, - onChange: () => {}, - onIdle: () => {}, - shouldUpdate: () => true, - position: null, - boundingBox: null, - }; - - const simulateMapReadyEvent = (google) => { - google.maps.event.addListenerOnce.mock.calls[0][2](); - }; - - const simulateEvent = (fn, eventName, event) => { - fn.addListener.mock.calls.find((call) => call.includes(eventName))[1]( - event - ); - }; - - it('expect render correctly without the map rendered', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow( - -
This is the children
-
- ); - - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('[testId]').exists()).toBe(false); - }); - - it('expect render correctly with the map rendered', () => { - const google = createFakeGoogleReference(); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow( - -
This is the children
-
- ); - - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('[testId]').exists()).toBe(false); - - simulateMapReadyEvent(google); - - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('[testId]').exists()).toBe(true); - }); - - describe('creation', () => { - it('expect to create the GoogleMaps on didMount with the default options', () => { - const google = createFakeGoogleReference(); - - const props = { - ...defaultProps, - google, - }; - - mount(); - - expect(google.maps.Map).toHaveBeenCalledTimes(1); - expect(google.maps.Map).toHaveBeenCalledWith(expect.any(HTMLDivElement), { - mapTypeControl: false, - fullscreenControl: false, - streetViewControl: false, - clickableIcons: false, - zoomControlOptions: { - position: 'left:top', - }, - }); - }); - - it('expect to create the GoogleMaps on didMount with the given options', () => { - const google = createFakeGoogleReference(); - - const props = { - ...defaultProps, - mapOptions: { - streetViewControl: true, - otherOptionToPass: false, - }, - google, - }; - - mount(); - - expect(google.maps.Map).toHaveBeenCalledTimes(1); - expect(google.maps.Map).toHaveBeenCalledWith(expect.any(HTMLDivElement), { - mapTypeControl: false, - fullscreenControl: false, - streetViewControl: true, - clickableIcons: false, - otherOptionToPass: false, - zoomControlOptions: { - position: 'left:top', - }, - }); - }); - - it('expect to listen "idle" event once to setup the rest of the listeners', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow(); - - expect(google.maps.event.addListenerOnce).toHaveBeenCalledTimes(1); - expect(google.maps.event.addListenerOnce).toHaveBeenCalledWith( - mapInstance, - 'idle', - expect.any(Function) - ); - - expect(wrapper.instance().listeners).toHaveLength(1); - }); - - it('expect to setup the rest of the listener when the map is ready', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow(); - - simulateMapReadyEvent(google); - - expect(mapInstance.addListener).toHaveBeenCalledWith( - 'center_changed', - expect.any(Function) - ); - - expect(mapInstance.addListener).toHaveBeenCalledWith( - 'zoom_changed', - expect.any(Function) - ); - - expect(mapInstance.addListener).toHaveBeenCalledWith( - 'dragstart', - expect.any(Function) - ); - - expect(mapInstance.addListener).toHaveBeenCalledWith( - 'idle', - expect.any(Function) - ); - - expect(wrapper.instance().listeners).toHaveLength(4); - }); - }); - - describe('events', () => { - it('expect to trigger idle callback', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - onIdle: jest.fn(), - google, - }; - - shallow(); - - simulateMapReadyEvent(google); - - expect(props.onIdle).toHaveBeenCalledTimes(0); - - simulateEvent(mapInstance, 'idle'); - - expect(props.onIdle).toHaveBeenCalledTimes(1); - expect(props.onIdle).toHaveBeenCalledWith({ - instance: mapInstance, - }); - }); - - it('expect to not trigger idle callback on programmatic interaction', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - onIdle: jest.fn(), - google, - }; - - const wrapper = shallow(); - - simulateMapReadyEvent(google); - - // Simulate fitBounds - wrapper.instance().isUserInteraction = false; - - expect(props.onIdle).toHaveBeenCalledTimes(0); - - simulateEvent(mapInstance, 'idle'); - - expect(props.onIdle).toHaveBeenCalledTimes(0); - }); - - ['center_changed', 'zoom_changed', 'dragstart'].forEach((eventName) => { - it(`expect to call change callback on "${eventName}"`, () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - onChange: jest.fn(), - google, - }; - - shallow(); - - simulateMapReadyEvent(google); - - expect(props.onChange).toHaveBeenCalledTimes(0); - - simulateEvent(mapInstance, eventName); - - expect(props.onChange).toHaveBeenCalledTimes(1); - }); - - it(`expect to not call change callback on "${eventName}" with programmatic interaction`, () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - onChange: jest.fn(), - google, - }; - - const wrapper = shallow(); - - simulateMapReadyEvent(google); - - expect(props.onChange).toHaveBeenCalledTimes(0); - - // Simulate fitBounds - wrapper.instance().isUserInteraction = false; - - simulateEvent(mapInstance, eventName); - - expect(props.onChange).toHaveBeenCalledTimes(0); - }); - }); - }); - - describe('context', () => { - it('expect to not expose the context when the map is not ready', () => { - const google = createFakeGoogleReference({}); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow( - - {() => null} - - ); - - expect(wrapper.find(GoogleMapsContext.Consumer).exists()).toBe(false); - }); - - it('expect to expose the context only when the map is created', () => { - const google = createFakeGoogleReference({}); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow( - - {() => null} - - ); - - simulateMapReadyEvent(google); - - expect(wrapper.find(GoogleMapsContext.Consumer).exists()).toBe(true); - }); - - it('expect to expose the map instance through context only when created', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - google, - }; - - const renderFn = jest.fn(() => null); - - mount( - - {renderFn} - - ); - - simulateMapReadyEvent(google); - - expect(renderFn).toHaveBeenCalledWith({ google, instance: mapInstance }); - }); - }); - - describe('update', () => { - it('expect to call fitBounds on didUpdate when boundingBox is provided', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow( - -
This is the children
-
- ); - - simulateMapReadyEvent(google); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(0); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(1); // cDM - expect(mapInstance.setCenter).toHaveBeenCalledTimes(1); // cDM - - wrapper.setProps({ - boundingBoxPadding: 0, - boundingBox: { - northEast: { - lat: 14, - lng: 14, - }, - southWest: { - lat: 10, - lng: 10, - }, - }, - }); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(1); - expect(mapInstance.fitBounds).toHaveBeenCalledWith( - expect.objectContaining({ - northEast: { - lat: 10, - lng: 10, - }, - southWest: { - lat: 14, - lng: 14, - }, - }), - 0 - ); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(1); // cDM - expect(mapInstance.setCenter).toHaveBeenCalledTimes(1); // cDM - }); - - it('expect not to call fitBounds on didUpdate when boundingBox equal to previous', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow( - -
This is the children
-
- ); - - simulateMapReadyEvent(google); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(0); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(1); // cDM - expect(mapInstance.setCenter).toHaveBeenCalledTimes(1); // cDM - - wrapper.setProps({ - boundingBoxPadding: 0, - boundingBox: { - northEast: { - lat: 10, - lng: 10, - }, - southWest: { - lat: 14, - lng: 14, - }, - }, - }); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(1); - expect(mapInstance.fitBounds).toHaveBeenCalledWith( - expect.objectContaining({ - northEast: { - lat: 14, - lng: 14, - }, - southWest: { - lat: 10, - lng: 10, - }, - }), - 0 - ); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(1); // cDM - expect(mapInstance.setCenter).toHaveBeenCalledTimes(1); // cDM - - mapInstance.getBounds.mockImplementation( - () => - new google.maps.LatLngBounds( - { lat: 14, lng: 14 }, - { lat: 10, lng: 10 } - ) - ); - - wrapper.setProps({ - boundingBoxPadding: 0, - boundingBox: { - northEast: { - lat: 10, - lng: 10, - }, - southWest: { - lat: 14, - lng: 14, - }, - }, - }); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(1); - }); - - it('expect to call setCenter & setZoom when boundingBox is not provided', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - google.maps.LatLngBounds.mockImplementation((sw, ne) => ({ - northEast: ne, - southWest: sw, - })); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow( - -
This is the children
-
- ); - - simulateMapReadyEvent(google); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(0); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(1); // cDM - expect(mapInstance.setCenter).toHaveBeenCalledTimes(1); // cDM - - wrapper.setProps(); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(0); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(2); // cDM + cDU - expect(mapInstance.setZoom).toHaveBeenCalledWith(1); - - expect(mapInstance.setCenter).toHaveBeenCalledTimes(2); // cDM + cDU - expect(mapInstance.setCenter).toHaveBeenCalledWith({ - lat: 0, - lng: 0, - }); - }); - - it('expect to prevent the map update when shouldUpdate return false', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - shouldUpdate: () => false, - google, - }; - - const wrapper = shallow( - -
This is the children
-
- ); - - simulateMapReadyEvent(google); - simulateEvent(mapInstance, 'center_changed'); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(0); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(0); - expect(mapInstance.setCenter).toHaveBeenCalledTimes(0); - - wrapper.setProps(); - - expect(mapInstance.fitBounds).toHaveBeenCalledTimes(0); - - expect(mapInstance.setZoom).toHaveBeenCalledTimes(0); - expect(mapInstance.setCenter).toHaveBeenCalledTimes(0); - }); - - it('expect to still render the children when shouldUpdate return false', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - shouldUpdate: () => false, - google, - }; - - const wrapper = shallow( - -
This is the children
-
- ); - - simulateMapReadyEvent(google); - simulateEvent(mapInstance, 'center_changed'); - - expect(wrapper.find('.children')).toMatchSnapshot(); - - wrapper.setProps({ - children:
This is the children updated
, - }); - - expect(wrapper.find('.children')).toMatchSnapshot(); - }); - }); - - describe('delete', () => { - it('expect to remove all the listeners', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - google, - }; - - const wrapper = shallow(); - - simulateMapReadyEvent(google); - - expect(wrapper.instance().listeners).toHaveLength(4); - - const internalListeners = wrapper.instance().listeners.slice(); - - wrapper.unmount(); - - internalListeners.forEach((listener) => { - expect(listener.remove).toHaveBeenCalledTimes(1); - }); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.jsdom.js b/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.jsdom.js deleted file mode 100644 index 15acb4e906..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.jsdom.js +++ /dev/null @@ -1,147 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { wait } from '@instantsearch/testutils'; -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; -import injectScript from 'scriptjs'; - -import GoogleMapsLoader from '../GoogleMapsLoader'; - -Enzyme.configure({ adapter: new Adapter() }); - -jest.mock('scriptjs'); - -describe('GoogleMapsLoader', () => { - const defaultProps = { - apiKey: 'API_KEY', - }; - - it('expect to call Google Maps API', async () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - shallow({children}); - - await wait(0); - - expect(injectScript).toHaveBeenLastCalledWith( - 'https://maps.googleapis.com/maps/api/js?v=quarterly&key=API_KEY', - expect.any(Function) - ); - }); - - it('expect to call Google Maps API with a custom API Key', async () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - apiKey: 'CUSTOM_API_KEY', - }; - - shallow({children}); - - await wait(0); - - expect(injectScript).toHaveBeenLastCalledWith( - 'https://maps.googleapis.com/maps/api/js?v=quarterly&key=CUSTOM_API_KEY', - expect.any(Function) - ); - }); - - it('expect to call Google Maps API with a custom endpoint', async () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - endpoint: - 'https://maps.googleapis.com/maps/api/js?v=3.32&places,geometry', - }; - - shallow({children}); - - await wait(0); - - expect(injectScript).toHaveBeenLastCalledWith( - 'https://maps.googleapis.com/maps/api/js?v=3.32&places,geometry&key=API_KEY', - expect.any(Function) - ); - }); - - it("expect to render nothing when it's loading", () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - const wrapper = shallow( - {children} - ); - - expect(wrapper.type()).toBe(null); - expect(children).not.toHaveBeenCalled(); - }); - - it("expect to call children with the Google object when it's loaded", async () => { - const children = jest.fn((x) => x); - - const google = { - version: '3.1.1', - }; - - const props = { - ...defaultProps, - }; - - injectScript.mockImplementationOnce((_, callback) => { - global.google = google; - callback(); - }); - - const wrapper = shallow( - {children} - ); - - await wait(0); - - expect(wrapper.type).not.toBe(null); - expect(children).toHaveBeenCalledTimes(1); - expect(children).toHaveBeenCalledWith(google); - - delete global.google; - }); - - it('expect to not call setState when we unmount before loading is complete', async () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - let triggerLoadingComplete; - injectScript.mockImplementationOnce((endpoint, callback) => { - triggerLoadingComplete = callback; - }); - - const wrapper = shallow( - {children} - ); - - await wait(0); - - expect(wrapper.type).not.toBe(null); - expect(children).not.toHaveBeenCalled(); - - wrapper.unmount(); - - triggerLoadingComplete(); - - expect(children).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.node.js b/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.node.js deleted file mode 100644 index 998df5d5a5..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/GoogleMapsLoader.node.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @jest-environment node - */ - -describe('GoogleMapsLoader', () => { - it('expect to require the file in a Node environment', () => { - expect(() => require('../GoogleMapsLoader')).not.toThrow(); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/Marker.js b/packages/react-instantsearch-dom-maps/src/__tests__/Marker.js deleted file mode 100644 index e87034b680..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/Marker.js +++ /dev/null @@ -1,280 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow, mount } from 'enzyme'; -import React from 'react'; - -import { - createFakeGoogleReference, - createFakeMapInstance, - createFakeMarkerInstance, -} from '../../test/mockGoogleMaps'; -import GoogleMapsContext from '../GoogleMapsContext'; -import Connected, { Marker } from '../Marker'; -import * as utils from '../utils'; - -Enzyme.configure({ adapter: new Adapter() }); - -jest.mock('../utils', () => { - const module = jest.requireActual('../utils'); - - return { - registerEvents: jest.fn(), - createFilterProps: module.createFilterProps, - createListenersPropTypes: module.createListenersPropTypes, - }; -}); - -describe('Marker', () => { - const defaultProps = { - hit: { - _geoloc: { - lat: 10, - lng: 12, - }, - }, - }; - - beforeEach(() => { - utils.registerEvents.mockClear(); - utils.registerEvents.mockReset(); - }); - - it('expect render correctly', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow(); - - expect(wrapper.type()).toBe(null); - }); - - describe('creation', () => { - it('expect to create the Marker on didMount with default options', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow(, { - disableLifecycleMethods: true, - }); - - expect(google.maps.Marker).not.toHaveBeenCalled(); - - // Simulate didMount - wrapper.instance().componentDidMount(); - - expect(google.maps.Marker).toHaveBeenCalledTimes(1); - expect(google.maps.Marker).toHaveBeenCalledWith({ - map: mapInstance, - position: { - lat: 10, - lng: 12, - }, - }); - }); - - it('expect to create the Marker on didMount with given options', () => { - const mapInstance = createFakeMapInstance(); - const google = createFakeGoogleReference({ - mapInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - title: 'My Marker', - visible: false, - children: , - onClick: () => {}, - google, - }; - - const wrapper = shallow(, { - disableLifecycleMethods: true, - }); - - expect(google.maps.Marker).not.toHaveBeenCalled(); - - // Simulate didMount - wrapper.instance().componentDidMount(); - - expect(google.maps.Marker).toHaveBeenCalledTimes(1); - expect(google.maps.Marker).toHaveBeenCalledWith({ - title: 'My Marker', - visible: false, - map: mapInstance, - position: { - lat: 10, - lng: 12, - }, - }); - }); - - it('expect to register the listeners on didMount', () => { - const mapInstance = createFakeMapInstance(); - const markerInstance = createFakeMarkerInstance(); - const google = createFakeGoogleReference({ - mapInstance, - markerInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow(, { - disableLifecycleMethods: true, - }); - - expect(utils.registerEvents).toHaveBeenCalledTimes(0); - - // Simulate didMount - wrapper.instance().componentDidMount(); - - expect(utils.registerEvents).toHaveBeenCalledTimes(1); - expect(utils.registerEvents).toHaveBeenCalledWith( - expect.any(Object), - expect.any(Object), - markerInstance - ); - }); - }); - - describe('update', () => { - it('expect to remove the listener on didUpdate', () => { - const removeEventListeners = jest.fn(); - const mapInstance = createFakeMapInstance(); - const markerInstance = createFakeMarkerInstance(); - const google = createFakeGoogleReference({ - mapInstance, - markerInstance, - }); - - utils.registerEvents.mockImplementation(() => removeEventListeners); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow(); - - expect(removeEventListeners).toHaveBeenCalledTimes(0); - - // Simulate the update - wrapper.instance().componentDidUpdate(); - - expect(removeEventListeners).toHaveBeenCalledTimes(1); - }); - - it('expect to register the listeners on didUpdate', () => { - const mapInstance = createFakeMapInstance(); - const markerInstance = createFakeMarkerInstance(); - const google = createFakeGoogleReference({ - mapInstance, - markerInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - utils.registerEvents.mockImplementationOnce(() => () => {}); - - const wrapper = shallow(); - - expect(utils.registerEvents).toHaveBeenCalledTimes(1); - - // Simulate the update - wrapper.instance().componentDidUpdate(); - - expect(utils.registerEvents).toHaveBeenCalledTimes(2); - expect(utils.registerEvents).toHaveBeenLastCalledWith( - expect.any(Object), - expect.any(Object), - markerInstance - ); - }); - }); - - describe('delete', () => { - it('expect to remove the Marker on willUnmount', () => { - const mapInstance = createFakeMapInstance(); - const markerInstance = createFakeMarkerInstance(); - const google = createFakeGoogleReference({ - mapInstance, - markerInstance, - }); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - google, - }; - - const wrapper = shallow(); - - wrapper.unmount(); - - expect(markerInstance.setMap).toHaveBeenCalledTimes(1); - expect(markerInstance.setMap).toHaveBeenCalledWith(null); - }); - }); - - describe('Connected', () => { - it('expect to have access to Google Maps', () => { - const mapInstance = createFakeMapInstance(); - const markerInstance = createFakeMarkerInstance(); - const google = createFakeGoogleReference({ - mapInstance, - markerInstance, - }); - - const props = { - ...defaultProps, - }; - - mount( - - - - ); - - expect(google.maps.Marker).toHaveBeenCalledWith({ - map: mapInstance, - position: { - lat: 10, - lng: 12, - }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/Provider.js b/packages/react-instantsearch-dom-maps/src/__tests__/Provider.js deleted file mode 100644 index 2b6c01d5c9..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/Provider.js +++ /dev/null @@ -1,531 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import { - createFakeGoogleReference, - createFakeMapInstance, -} from '../../test/mockGoogleMaps'; -import Provider from '../Provider'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('Provider', () => { - const defaultProps = { - google: createFakeGoogleReference(), - hits: [], - initialPosition: { lat: 0, lng: 0 }, - isRefineOnMapMove: true, - hasMapMoveSinceLastRefine: false, - isRefineEnable: true, - refine: () => {}, - toggleRefineOnMapMove: () => {}, - setMapMoveSinceLastRefine: () => {}, - children: () => {}, - }; - - const lastRenderArgs = (fn) => fn.mock.calls[fn.mock.calls.length - 1][0]; - - it('expect to render with default props', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - shallow({children}); - - expect(children).toHaveBeenCalledTimes(1); - expect(children).toHaveBeenCalledWith({ - boundingBox: undefined, - boundingBoxPadding: undefined, - onChange: expect.any(Function), - onIdle: expect.any(Function), - shouldUpdate: expect.any(Function), - }); - }); - - describe('boundingBox', () => { - it('expect to use hits when currentRefinement is not defined and hits are not empty', () => { - const children = jest.fn((x) => x); - const google = createFakeGoogleReference(); - - google.maps.LatLngBounds.mockImplementation(() => ({ - extend: jest.fn().mockReturnThis(), - getNorthEast: () => ({ - toJSON: () => ({ - lat: 10, - lng: 10, - }), - }), - getSouthWest: () => ({ - toJSON: () => ({ - lat: 14, - lng: 14, - }), - }), - })); - - const props = { - ...defaultProps, - hits: [ - { _geoloc: { lat: 10, lng: 12 } }, - { _geoloc: { lat: 12, lng: 14 } }, - ], - google, - }; - - shallow({children}); - - expect(lastRenderArgs(children).boundingBox).toEqual({ - northEast: { - lat: 10, - lng: 10, - }, - southWest: { - lat: 14, - lng: 14, - }, - }); - }); - - it("expect to use currentRefinement when it's defined and hits are empty", () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - currentRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - shallow({children}); - - expect(lastRenderArgs(children).boundingBox).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it("expect to use currentRefinement when it's defined and hits are not empty", () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - hits: [ - { _geoloc: { lat: 10, lng: 12 } }, - { _geoloc: { lat: 12, lng: 14 } }, - ], - currentRefinement: { - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }, - }; - - shallow({children}); - - expect(lastRenderArgs(children).boundingBox).toEqual({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it("expect to use currentRefinement when it's not defined and hits are empty", () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - shallow({children}); - - expect(lastRenderArgs(children).boundingBox).toBe(undefined); - }); - }); - - describe('onChange', () => { - it('expect to call setMapMoveSinceLast refine', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - setMapMoveSinceLastRefine: jest.fn(), - }; - - shallow({children}); - - expect(props.setMapMoveSinceLastRefine).toHaveBeenCalledTimes(0); - - lastRenderArgs(children).onChange(); - - expect(props.setMapMoveSinceLastRefine).toHaveBeenCalledTimes(1); - expect(props.setMapMoveSinceLastRefine).toHaveBeenCalledWith(true); - }); - - it('expect to schedule a refine call when refine on map move is enabled', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - const wrapper = shallow({children}); - - expect(wrapper.instance().isPendingRefine).toBe(false); - - lastRenderArgs(children).onChange(); - - expect(wrapper.instance().isPendingRefine).toBe(true); - }); - - it('expect to not schedule a refine call when refine on map move is disabled', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - isRefineOnMapMove: false, - }; - - const wrapper = shallow({children}); - - expect(wrapper.instance().isPendingRefine).toBe(false); - - lastRenderArgs(children).onChange(); - - expect(wrapper.instance().isPendingRefine).toBe(false); - }); - - it('expect to do nothing when refine is disabled', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - isRefineEnable: false, - setMapMoveSinceLastRefine: jest.fn(), - }; - - const wrapper = shallow({children}); - - expect(wrapper.instance().isPendingRefine).toBe(false); - - lastRenderArgs(children).onChange(); - - expect(wrapper.instance().isPendingRefine).toBe(false); - expect(props.setMapMoveSinceLastRefine).not.toHaveBeenCalled(); - }); - }); - - describe('onIdle', () => { - it('expect to call refine when there is a pending refinement', () => { - const mapInstance = createFakeMapInstance(); - const children = jest.fn((x) => x); - - mapInstance.getBounds.mockImplementation(() => ({ - getNorthEast: () => ({ - toJSON: () => ({ - lat: 10, - lng: 12, - }), - }), - getSouthWest: () => ({ - toJSON: () => ({ - lat: 12, - lng: 14, - }), - }), - })); - - const props = { - ...defaultProps, - refine: jest.fn(), - }; - - shallow({children}); - - lastRenderArgs(children).onChange(); - lastRenderArgs(children).onIdle({ instance: mapInstance }); - - expect(props.refine).toHaveBeenCalledTimes(1); - expect(props.refine).toHaveBeenCalledWith({ - northEast: { - lat: 10, - lng: 12, - }, - southWest: { - lat: 12, - lng: 14, - }, - }); - }); - - it('expect to reset the pending refinement when there is a pending refinement', () => { - const mapInstance = createFakeMapInstance(); - const children = jest.fn((x) => x); - - mapInstance.getBounds.mockImplementation(() => ({ - getNorthEast: () => ({ - toJSON: () => {}, - }), - getSouthWest: () => ({ - toJSON: () => {}, - }), - })); - - const props = { - ...defaultProps, - refine: jest.fn(), - }; - - const wrapper = shallow({children}); - - lastRenderArgs(children).onChange(); - - expect(wrapper.instance().isPendingRefine).toBe(true); - - lastRenderArgs(children).onIdle({ instance: mapInstance }); - - expect(wrapper.instance().isPendingRefine).toBe(false); - }); - - it('expect to do nothing when there is no pending refinement', () => { - const mapInstance = createFakeMapInstance(); - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - refine: jest.fn(), - setMapMoveSinceLastRefine: jest.fn(), - }; - - const wrapper = shallow({children}); - - expect(wrapper.instance().isPendingRefine).toBe(false); - - lastRenderArgs(children).onIdle({ instance: mapInstance }); - - expect(wrapper.instance().isPendingRefine).toBe(false); - expect(props.refine).not.toHaveBeenCalled(); - expect(props.setMapMoveSinceLastRefine).not.toHaveBeenCalled(); - }); - }); - - describe('shouldUpdate', () => { - it("expect to return true when no refinement is pending and the map didn't moved", () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - shallow({children}); - - const actual = lastRenderArgs(children).shouldUpdate(); - - expect(actual).toBe(true); - }); - - it('expect to return false when there is a pending refinement', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - }; - - shallow({children}); - - lastRenderArgs(children).onChange(); - - const actual = lastRenderArgs(children).shouldUpdate(); - - expect(actual).toBe(false); - }); - - it('expect to return false when the map has moved', () => { - const children = jest.fn((x) => x); - - const props = { - ...defaultProps, - hasMapMoveSinceLastRefine: true, - }; - - shallow({children}); - - const actual = lastRenderArgs(children).shouldUpdate(); - - expect(actual).toBe(false); - }); - }); - - describe('context', () => { - it('expect to expose isRefineOnMapMove', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow({(x) => x}); - - expect(wrapper.props().value.isRefineOnMapMove).toBe(true); - }); - - it('expect to expose hasMapMoveSinceLastRefine', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow({(x) => x}); - - expect(wrapper.props().value.hasMapMoveSinceLastRefine).toBe(false); - }); - - it('expect to expose toggleRefineOnMapMove', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow({(x) => x}); - - expect(wrapper.props().value.toggleRefineOnMapMove).toBe( - props.toggleRefineOnMapMove - ); - }); - - it('expect to expose setMapMoveSinceLastRefine', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow({(x) => x}); - - expect(wrapper.props().value.setMapMoveSinceLastRefine).toBe( - props.setMapMoveSinceLastRefine - ); - }); - - it('expect to expose refineWithInstance', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow({(x) => x}); - - expect(wrapper.props().value.refineWithInstance).toBe( - wrapper.instance().refineWithInstance - ); - }); - - it('expect to pass new mapValue reference when isRefineOnMapMove changes', () => { - const props = { - ...defaultProps, - isRefineOnMapMove: true, - }; - - const wrapper = shallow(); - const mapValue = wrapper.instance().mapValue; - - expect(mapValue).toBe(wrapper.instance().mapValue); - - wrapper.setProps({ - isRefineOnMapMove: false, - }); - - expect(mapValue).not.toBe(wrapper.instance().mapValue); - }); - - it('expect to pass new mapValue reference when hasMapMoveSinceLastRefine changes', () => { - const props = { - ...defaultProps, - hasMapMoveSinceLastRefine: false, - }; - - const wrapper = shallow(); - const mapValue = wrapper.instance().mapValue; - - expect(mapValue).toBe(wrapper.instance().mapValue); - - wrapper.setProps({ - hasMapMoveSinceLastRefine: true, - }); - - expect(mapValue).not.toBe(wrapper.instance().mapValue); - }); - - it('expect to pass same mapValue reference when toggleRefineOnMapMove changes', () => { - const props = { - ...defaultProps, - toggleRefineOnMapMove: jest.fn(), - }; - - const wrapper = shallow(); - const mapValue = wrapper.instance().mapValue; - - expect(mapValue).toBe(wrapper.instance().mapValue); - - wrapper.setProps({ - toggleRefineOnMapMove: jest.fn(), - }); - - expect(mapValue).toBe(wrapper.instance().mapValue); - }); - - it('expect to pass same mapValue reference when setMapMoveSinceLastRefine changes', () => { - const props = { - ...defaultProps, - setMapMoveSinceLastRefine: jest.fn(), - }; - - const wrapper = shallow(); - const mapValue = wrapper.instance().mapValue; - - expect(mapValue).toBe(wrapper.instance().mapValue); - - wrapper.setProps({ - setMapMoveSinceLastRefine: jest.fn(), - }); - - expect(mapValue).toBe(wrapper.instance().mapValue); - }); - - it('expect to pass same mapValue reference when refineWithInstance changes', () => { - const props = { - ...defaultProps, - refineWithInstance: jest.fn(), - }; - - const wrapper = shallow(); - const mapValue = wrapper.instance().mapValue; - - expect(mapValue).toBe(wrapper.instance().mapValue); - - wrapper.setProps({ - refineWithInstance: jest.fn(), - }); - - expect(mapValue).toBe(wrapper.instance().mapValue); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/Redo.js b/packages/react-instantsearch-dom-maps/src/__tests__/Redo.js deleted file mode 100644 index c9ccbfddeb..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/Redo.js +++ /dev/null @@ -1,60 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import { createFakeMapInstance } from '../../test/mockGoogleMaps'; -import { Redo } from '../Redo'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('Redo', () => { - const defaultProps = { - googleMapsInstance: createFakeMapInstance(), - translate: (x) => x, - - hasMapMoveSinceLastRefine: false, - refineWithInstance: () => {}, - }; - - it('expect to render correctly', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow(); - - expect(wrapper.find('button').prop('disabled')).toBe(true); - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render correctly when map has moved', () => { - const props = { - ...defaultProps, - hasMapMoveSinceLastRefine: true, - }; - - const wrapper = shallow(); - - expect(wrapper.find('button').prop('disabled')).toBe(false); - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to call refineWithInstance on button click', () => { - const mapInstance = createFakeMapInstance(); - - const props = { - ...defaultProps, - googleMapsInstance: mapInstance, - refineWithInstance: jest.fn(), - }; - - const wrapper = shallow(); - - expect(props.refineWithInstance).toHaveBeenCalledTimes(0); - - wrapper.find('button').simulate('click'); - - expect(props.refineWithInstance).toHaveBeenCalledTimes(1); - expect(props.refineWithInstance).toHaveBeenCalledWith(mapInstance); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Control.js.snap b/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Control.js.snap deleted file mode 100644 index 45ece491dd..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Control.js.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Control expect to render correctly with refine on map move 1`] = ` -
- -
-`; - -exports[`Control expect to render correctly without refine on map move 1`] = ` -
- -
-`; - -exports[`Control expect to render correctly without refine on map move when the map has moved 1`] = ` -
- -
-`; diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/CustomMarker.js.snap b/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/CustomMarker.js.snap deleted file mode 100644 index cb00283cfa..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/CustomMarker.js.snap +++ /dev/null @@ -1,113 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CustomMarker with portal expect to render correctly 1`] = ` - - - - This is the children. - -
- } - > - - This is the children. - - - -`; - -exports[`CustomMarker with unstable_renderSubtreeIntoContainer expect to render correctly 1`] = ` - -`; diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GeoSearch.js.snap b/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GeoSearch.js.snap deleted file mode 100644 index cfb348aa19..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GeoSearch.js.snap +++ /dev/null @@ -1,112 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`GeoSearch Connector expect to render 1`] = ` - - - -`; - -exports[`GeoSearch Connector expect to render with defaultRefinement 1`] = ` - - - -`; - -exports[`GeoSearch Connector expect to render with enableRefineOnMapMove 1`] = ` - - - -`; - -exports[`GeoSearch GoogleMaps expect to render 1`] = ` - -
- Hello this is the children -
-
-`; - -exports[`GeoSearch Provider expect to render 1`] = ` - - - -`; diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GoogleMaps.js.snap b/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GoogleMaps.js.snap deleted file mode 100644 index a1dd5e5bf1..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/GoogleMaps.js.snap +++ /dev/null @@ -1,355 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`GoogleMaps expect render correctly with the map rendered 1`] = ` -
-
-
-`; - -exports[`GoogleMaps expect render correctly with the map rendered 2`] = ` -
-
- -
- This is the children -
-
-
-`; - -exports[`GoogleMaps expect render correctly without the map rendered 1`] = ` -
-
-
-`; - -exports[`GoogleMaps update expect to still render the children when shouldUpdate return false 1`] = ` -
- This is the children -
-`; - -exports[`GoogleMaps update expect to still render the children when shouldUpdate return false 2`] = ` -
- This is the children updated -
-`; diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Redo.js.snap b/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Redo.js.snap deleted file mode 100644 index 27ca8f25a3..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/__snapshots__/Redo.js.snap +++ /dev/null @@ -1,29 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Redo expect to render correctly 1`] = ` -
- -
-`; - -exports[`Redo expect to render correctly when map has moved 1`] = ` -
- -
-`; diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/utils.js b/packages/react-instantsearch-dom-maps/src/__tests__/utils.js deleted file mode 100644 index aa45878543..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/utils.js +++ /dev/null @@ -1,235 +0,0 @@ -import PropTypes from 'prop-types'; - -import { createFakeMarkerInstance } from '../../test/mockGoogleMaps'; -import * as utils from '../utils'; - -describe('utils', () => { - describe('registerEvents', () => { - it('expect to add listeners from events', () => { - const onClick = () => {}; - const onMouseMove = () => {}; - const instance = createFakeMarkerInstance(); - - const events = { - onClick: 'click', - onMouseMove: 'mousemove', - }; - - const props = { - onClick, - onMouseMove, - }; - - utils.registerEvents(events, props, instance); - - expect(instance.addListener).toHaveBeenCalledTimes(2); - - expect(instance.addListener).toHaveBeenCalledWith( - 'click', - expect.any(Function) - ); - - expect(instance.addListener).toHaveBeenCalledWith( - 'mousemove', - expect.any(Function) - ); - }); - - it('expect to add listeners with event & marker', () => { - const onClick = jest.fn(); - const onMouseMove = jest.fn(); - const instance = createFakeMarkerInstance(); - const listeners = []; - - instance.addListener.mockImplementation((event, listener) => - listeners.push(listener) - ); - - const events = { - onClick: 'click', - onMouseMove: 'mousemove', - }; - - const props = { - onClick, - onMouseMove, - }; - - utils.registerEvents(events, props, instance); - - listeners.forEach((listener) => listener({ type: 'event' })); - - expect(onClick).toHaveBeenCalledWith({ - event: { type: 'event' }, - marker: instance, - }); - - expect(onMouseMove).toHaveBeenCalledWith({ - event: { type: 'event' }, - marker: instance, - }); - }); - - it('expect to only add listeners listed from events', () => { - const onClick = () => {}; - const onMouseEnter = () => {}; - const instance = createFakeMarkerInstance(); - - const events = { - onClick: 'click', - onMouseMove: 'mousemove', - }; - - const props = { - onClick, - onMouseEnter, - }; - - utils.registerEvents(events, props, instance); - - expect(instance.addListener).toHaveBeenCalledTimes(1); - expect(instance.addListener).toHaveBeenCalledWith( - 'click', - expect.any(Function) - ); - }); - - it('expect to only add listeners listed from props', () => { - const onClick = () => {}; - const instance = createFakeMarkerInstance(); - - const events = { - onClick: 'click', - onMouseMove: 'mousemove', - }; - - const props = { - onClick, - }; - - utils.registerEvents(events, props, instance); - - expect(instance.addListener).toHaveBeenCalledTimes(1); - expect(instance.addListener).toHaveBeenCalledWith( - 'click', - expect.any(Function) - ); - }); - - it('expect to return a function that remove the listeners', () => { - const onClick = () => {}; - const onMouseMove = () => {}; - const remove = jest.fn(); - const instance = createFakeMarkerInstance(); - - instance.addListener.mockImplementation(() => ({ - remove, - })); - - const events = { - onClick: 'click', - onMouseMove: 'mousemove', - }; - - const props = { - onClick, - onMouseMove, - }; - - const removeEventListeners = utils.registerEvents( - events, - props, - instance - ); - - expect(remove).toHaveBeenCalledTimes(0); - - removeEventListeners(); - - expect(remove).toHaveBeenCalledTimes(2); - }); - }); - - describe('createListenersPropTypes', () => { - it('expect to return an object with listeners propType from event types', () => { - const events = { - onClick: '', - onMouseMove: '', - }; - - const expectation = { - onClick: PropTypes.func, - onMouseMove: PropTypes.func, - }; - - const actual = utils.createListenersPropTypes(events); - - expect(actual).toEqual(expectation); - }); - - it('expect to return an empty object from empty event types', () => { - const events = {}; - - const expectation = {}; - const actual = utils.createListenersPropTypes(events); - - expect(actual).toEqual(expectation); - }); - }); - - describe('createFilterProps', () => { - it('expect to return an object without excluded keys', () => { - const excludes = ['children', 'onClick']; - - const props = { - label: 'Title', - onClick: () => {}, - children: '
', - }; - - const expectation = { - label: 'Title', - }; - - const filterProps = utils.createFilterProps(excludes); - const actual = filterProps(props); - - expect(actual).toEqual(expectation); - }); - - it('expect to return the given props when excluded keys is empty', () => { - const onClick = () => {}; - const excludes = []; - - const props = { - children: '
', - onClick, - }; - - const expectation = { - children: '
', - onClick, - }; - - const filterProps = utils.createFilterProps(excludes); - const actual = filterProps(props); - - expect(actual).toEqual(expectation); - }); - - it('expect to return an empty object when all keys are excluded', () => { - const excludes = ['children', 'onClick']; - - const props = { - onClick: () => {}, - children: '
', - }; - - const expectation = {}; - const filterProps = utils.createFilterProps(excludes); - const actual = filterProps(props); - - expect(actual).toEqual(expectation); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/__tests__/withGoogleMaps.tsx b/packages/react-instantsearch-dom-maps/src/__tests__/withGoogleMaps.tsx deleted file mode 100644 index a56cb63e46..0000000000 --- a/packages/react-instantsearch-dom-maps/src/__tests__/withGoogleMaps.tsx +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; - -import GoogleMapsContext from '../GoogleMapsContext'; -import withGoogleMaps from '../withGoogleMaps'; - -import type { GoogleMapsContextState } from '../GoogleMapsContext'; -import type { WithGoogleMapsProps } from '../withGoogleMaps'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('withGoogleMaps', () => { - interface Props extends WithGoogleMapsProps { - value: number; - } - - const createFakeContext = ({ - google = {} as any, - googleMapsInstance = {} as any, - }): GoogleMapsContextState => ({ - instance: googleMapsInstance, - google, - }); - - it('expect to inject `google` prop', () => { - const fakeGoogle: any = { - maps: { - visualization: { - HeatmapLayer: jest.fn(() => ({ - getMap() { - return null; - }, - })), - }, - }, - }; - - const Fake = withGoogleMaps(({ google }: Props) => { - const heatmap = new google.maps.visualization.HeatmapLayer({ - // Google Maps expects `LatLng | WeightedLocation` but we don't have access to the whole `google` - // instance in the test. - // We use `data` just to make sure that `Fake` correctly calls `HeatmapLayer` so the type issue is fine. - data: [10, 20, 30] as any, - radius: 50, - }); - - heatmap.getMap(); - - return null; - }); - - mount( - - - - ); - - expect(fakeGoogle.maps.visualization.HeatmapLayer).toHaveBeenCalledWith({ - data: [10, 20, 30], - radius: 50, - }); - }); - - it('expect to inject `googleMapsInstance` prop', () => { - const fakeGoogleMapsInstance: any = { - fitBounds: jest.fn(), - }; - - const Fake = withGoogleMaps(({ googleMapsInstance }: Props) => { - googleMapsInstance.fitBounds({ - north: 10, - east: 12, - south: 14, - west: 16, - }); - - return null; - }); - - mount( - - - - ); - - expect(fakeGoogleMapsInstance.fitBounds).toHaveBeenCalledWith({ - north: 10, - east: 12, - south: 14, - west: 16, - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/elements/__tests__/createHTMLMarker.js b/packages/react-instantsearch-dom-maps/src/elements/__tests__/createHTMLMarker.js deleted file mode 100644 index 56ab568952..0000000000 --- a/packages/react-instantsearch-dom-maps/src/elements/__tests__/createHTMLMarker.js +++ /dev/null @@ -1,261 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { createFakeGoogleReference } from '../../../test/mockGoogleMaps'; -import createHTMLMarker from '../createHTMLMarker'; - -describe('createHTMLMarker', () => { - const createFakeParams = ({ ...rest } = {}) => ({ - position: { - lat: 10, - lng: 12, - }, - map: 'map-instance-placeholder', - className: 'ais-geo-search-marker', - ...rest, - }); - - it('expect to create a marker', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - - const marker = new HTMLMarker(params); - - expect(marker.anchor).toEqual({ x: 0, y: 0 }); - expect(marker.subscriptions).toEqual([]); - expect(marker.latLng).toEqual({ lat: 10, lng: 12 }); - - expect(marker.element).toEqual(expect.any(HTMLDivElement)); - expect(marker.element.className).toBe('ais-geo-search-marker'); - expect(marker.element.style.position).toBe('absolute'); - expect(marker.element.style.whiteSpace).toBe('nowrap'); - - expect(marker.setMap).toHaveBeenCalledWith('map-instance-placeholder'); - }); - - it('expect to create a marker with a custom anchor', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams({ - anchor: { - x: 5, - y: 10, - }, - }); - - const marker = new HTMLMarker(params); - - expect(marker.anchor).toEqual({ x: 5, y: 10 }); - }); - - it('expect to create a marker with a custom className', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams({ - className: 'my-custom-marker', - }); - - const marker = new HTMLMarker(params); - - expect(marker.element.className).toBe('my-custom-marker'); - }); - - describe('onAdd', () => { - it('expect to append the element to the overlay', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - const overlayMouseTarget = { - appendChild: jest.fn(), - }; - - const marker = new HTMLMarker(params); - - marker.getPanes.mockImplementation(() => ({ overlayMouseTarget })); - - marker.onAdd(); - - expect(overlayMouseTarget.appendChild).toHaveBeenCalledWith( - marker.element - ); - }); - - it('expect to not append the element to the overlay when panes are not available', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - const overlayMouseTarget = { - appendChild: jest.fn(), - }; - - const marker = new HTMLMarker(params); - - marker.getPanes.mockImplementation(() => null); - - marker.onAdd(); - - expect(overlayMouseTarget.appendChild).not.toHaveBeenCalled(); - }); - }); - - describe('draw', () => { - it('expect to set the correct position on the element', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - const fromLatLngToDivPixel = jest.fn(() => ({ - x: 100, - y: 50, - })); - - const marker = new HTMLMarker(params); - - marker.getProjection.mockImplementation(() => ({ - fromLatLngToDivPixel, - })); - - marker.draw(); - - expect(fromLatLngToDivPixel).toHaveBeenCalledWith({ lat: 10, lng: 12 }); - expect(marker.element.style.left).toBe('100px'); - expect(marker.element.style.top).toBe('50px'); - }); - - it('expect to set the correct zIndex on the element', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - const fromLatLngToDivPixel = jest.fn(() => ({ - x: 100, - y: 50, - })); - - const marker = new HTMLMarker(params); - - marker.getProjection.mockImplementationOnce(() => ({ - fromLatLngToDivPixel, - })); - - marker.draw(); - - expect(marker.element.style.zIndex).toBe('0'); - }); - - it('expect to not set the correct position when the projection is not available', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - - const marker = new HTMLMarker(params); - - marker.getProjection.mockImplementation(() => null); - - marker.draw(); - - expect(marker.element.style.left).toBe(''); - expect(marker.element.style.top).toBe(''); - expect(marker.element.style.zIndex).toBe(''); - }); - }); - - describe('onRemove', () => { - it('expect to remove the element', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - - const marker = new HTMLMarker(params); - - // Simulate the parentNode - const parentNode = document.createElement('div'); - parentNode.appendChild(marker.element); - - expect(parentNode.childNodes).toHaveLength(1); - - marker.onRemove(); - - expect(parentNode.childNodes).toHaveLength(0); - expect(marker.element).toBe(undefined); - }); - - it('expect to remove all the listeners', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - const remove = jest.fn(); - - const marker = new HTMLMarker(params); - - // Simulate the parentNode - const parentNode = document.createElement('div'); - parentNode.appendChild(marker.element); - - // Simulate the subscriptions - marker.subscriptions.push({ remove }); - marker.subscriptions.push({ remove }); - - marker.onRemove(); - - expect(marker.subscriptions).toEqual([]); - expect(remove).toHaveBeenCalledTimes(2); - }); - }); - - describe('addListener', () => { - it('expect to register listener', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - const onClick = () => {}; - - const marker = new HTMLMarker(params); - - const addEventListener = jest.spyOn(marker.element, 'addEventListener'); - - marker.addListener('click', onClick); - - expect(addEventListener).toHaveBeenCalledTimes(1); - expect(addEventListener).toHaveBeenCalledWith('click', onClick); - expect(marker.subscriptions).toHaveLength(1); - }); - - it('expect to return a function to remove the listener', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - const onClick = () => {}; - - const marker = new HTMLMarker(params); - - const removeEventListener = jest.spyOn( - marker.element, - 'removeEventListener' - ); - - const subscription = marker.addListener('click', onClick); - - subscription.remove(); - - expect(removeEventListener).toHaveBeenCalledTimes(1); - expect(removeEventListener).toHaveBeenCalledWith('click', onClick); - expect(marker.subscriptions).toHaveLength(0); - }); - }); - - describe('getPosition', () => { - it('expect to return the latLng', () => { - const googleReference = createFakeGoogleReference(); - const HTMLMarker = createHTMLMarker(googleReference); - const params = createFakeParams(); - - const marker = new HTMLMarker(params); - - const actual = marker.getPosition(); - const expectation = { lat: 10, lng: 12 }; - - expect(actual).toEqual(expectation); - }); - }); -}); diff --git a/packages/react-instantsearch-dom-maps/src/elements/createHTMLMarker.js b/packages/react-instantsearch-dom-maps/src/elements/createHTMLMarker.js deleted file mode 100644 index 5d091d5cbd..0000000000 --- a/packages/react-instantsearch-dom-maps/src/elements/createHTMLMarker.js +++ /dev/null @@ -1,88 +0,0 @@ -const createHTMLMarker = (google) => { - class HTMLMarker extends google.maps.OverlayView { - constructor({ - position, - map, - className, - anchor = { - x: 0, - y: 0, - }, - }) { - super(); - - this.anchor = anchor; - this.subscriptions = []; - this.latLng = new google.maps.LatLng(position); - - this.element = document.createElement('div'); - this.element.className = className; - this.element.style.position = 'absolute'; - // Force the "white-space" of the element will avoid the - // content to collapse when we move the map from center - this.element.style.whiteSpace = 'nowrap'; - - this.setMap(map); - } - - onAdd() { - if (this.getPanes()) { - this.getPanes().overlayMouseTarget.appendChild(this.element); - } - } - - draw() { - if (this.getProjection()) { - const position = this.getProjection().fromLatLngToDivPixel(this.latLng); - - const offsetX = this.anchor.x + this.element.offsetWidth / 2; - const offsetY = this.anchor.y + this.element.offsetHeight; - - this.element.style.left = `${Math.round(position.x - offsetX)}px`; - this.element.style.top = `${Math.round(position.y - offsetY)}px`; - - // Markers to the south are in front of markers to the north - // This is the default behaviour of Google Maps - this.element.style.zIndex = parseInt(this.element.style.top, 10); - } - } - - onRemove() { - if (this.element && this.element.parentNode) { - this.element.parentNode.removeChild(this.element); - - this.subscriptions.forEach((subscription) => subscription.remove()); - - delete this.element; - - this.subscriptions = []; - } - } - - addListener(eventName, listener) { - const subscription = { - remove: () => { - this.element.removeEventListener(eventName, listener); - - this.subscriptions = this.subscriptions.filter( - (_) => _ !== subscription - ); - }, - }; - - this.element.addEventListener(eventName, listener); - - this.subscriptions = this.subscriptions.concat(subscription); - - return subscription; - } - - getPosition() { - return this.latLng; - } - } - - return HTMLMarker; -}; - -export default createHTMLMarker; diff --git a/packages/react-instantsearch-dom-maps/src/index.js b/packages/react-instantsearch-dom-maps/src/index.js deleted file mode 100644 index 9e130489dc..0000000000 --- a/packages/react-instantsearch-dom-maps/src/index.js +++ /dev/null @@ -1,8 +0,0 @@ -export { default as GeoSearch } from './GeoSearch'; -export { default as Marker } from './Marker'; -export { default as CustomMarker } from './CustomMarker'; -export { default as Redo } from './Redo'; -export { default as Control } from './Control'; -export { default as GeoSearchContext } from './GeoSearchContext'; -export { default as GoogleMapsLoader } from './GoogleMapsLoader'; -export { default as withGoogleMaps } from './withGoogleMaps'; diff --git a/packages/react-instantsearch-dom-maps/src/propTypes.js b/packages/react-instantsearch-dom-maps/src/propTypes.js deleted file mode 100644 index cb0ac58e08..0000000000 --- a/packages/react-instantsearch-dom-maps/src/propTypes.js +++ /dev/null @@ -1,15 +0,0 @@ -import PropTypes from 'prop-types'; - -export const LatLngPropType = PropTypes.shape({ - lat: PropTypes.number.isRequired, - lng: PropTypes.number.isRequired, -}); - -export const BoundingBoxPropType = PropTypes.shape({ - northEast: LatLngPropType.isRequired, - southWest: LatLngPropType.isRequired, -}); - -export const GeolocHitPropType = PropTypes.shape({ - _geoloc: LatLngPropType.isRequired, -}); diff --git a/packages/react-instantsearch-dom-maps/src/utils.js b/packages/react-instantsearch-dom-maps/src/utils.js deleted file mode 100644 index bd20e19e7c..0000000000 --- a/packages/react-instantsearch-dom-maps/src/utils.js +++ /dev/null @@ -1,33 +0,0 @@ -import PropTypes from 'prop-types'; - -export const registerEvents = (events, props, instance) => { - const eventsAvailable = Object.keys(events); - const listeners = Object.keys(props) - .filter((key) => eventsAvailable.indexOf(key) !== -1) - .map((name) => - instance.addListener(events[name], (event) => { - props[name]({ event, marker: instance }); - }) - ); - - return () => { - listeners.forEach((listener) => listener.remove()); - }; -}; - -export const createListenersPropTypes = (eventTypes) => - Object.keys(eventTypes).reduce( - (acc, name) => ({ ...acc, [name]: PropTypes.func }), - {} - ); - -export const createFilterProps = (excludes) => (props) => - Object.keys(props) - .filter((name) => excludes.indexOf(name) === -1) - .reduce( - (acc, name) => ({ - ...acc, - [name]: props[name], - }), - {} - ); diff --git a/packages/react-instantsearch-dom-maps/src/withGoogleMaps.tsx b/packages/react-instantsearch-dom-maps/src/withGoogleMaps.tsx deleted file mode 100644 index 25579d4995..0000000000 --- a/packages/react-instantsearch-dom-maps/src/withGoogleMaps.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; - -import GoogleMapsContext from './GoogleMapsContext'; - -type Subtract = Omit< - TProps, - keyof TSubstractedProps ->; - -export interface WithGoogleMapsProps { - google: typeof google; - googleMapsInstance: google.maps.Map; -} - -const withGoogleMaps = ( - Wrapped: React.ComponentType -) => { - const WithGoogleMaps: React.FC> = ( - props - ) => ( - - {({ google, instance }) => ( - - )} - - ); - - return WithGoogleMaps; -}; - -export default withGoogleMaps; diff --git a/packages/react-instantsearch-dom-maps/test/mockGoogleMaps.js b/packages/react-instantsearch-dom-maps/test/mockGoogleMaps.js deleted file mode 100644 index 9e1f5fbf81..0000000000 --- a/packages/react-instantsearch-dom-maps/test/mockGoogleMaps.js +++ /dev/null @@ -1,89 +0,0 @@ -export class FakeOverlayView { - setMap = jest.fn(); - - getPanes = jest.fn(() => ({ - overlayMouseTarget: { - appendChild: jest.fn(), - }, - })); - - getProjection = jest.fn(() => ({ - fromLatLngToDivPixel: jest.fn(() => ({ - x: 0, - y: 0, - })), - })); -} - -export const MockLatLngBounds = jest.fn((ne, sw) => ({ - northEast: ne, - southWest: sw, - equals(oldBounds) { - if (!oldBounds) { - return false; - } - return ( - oldBounds.northEast.lat === this.northEast.lat && - oldBounds.northEast.lng === this.northEast.lng && - oldBounds.southWest.lat === this.southWest.lat && - oldBounds.southWest.lng === this.southWest.lng - ); - }, -})); - -export const createFakeMapInstance = () => ({ - addListener: jest.fn(() => ({ - remove: jest.fn(), - })), - getCenter: jest.fn(), - setCenter: jest.fn(), - getZoom: jest.fn(), - setZoom: jest.fn(), - getBounds: jest.fn( - () => new MockLatLngBounds({ lat: 0, lng: 0 }, { lat: 0, lng: 0 }) - ), - getProjection: jest.fn(() => ({ - fromPointToLatLng: jest.fn(() => ({ - lat: jest.fn(), - lng: jest.fn(), - })), - fromLatLngToPoint: jest.fn(() => ({ - x: 0, - y: 0, - })), - })), - fitBounds: jest.fn(), -}); - -export const createFakeMarkerInstance = () => ({ - setMap: jest.fn(), - getPosition: jest.fn(), - addListener: jest.fn(), -}); - -export const createFakeHTMLMarkerInstance = () => ({ - element: document.createElement('div'), - setMap: jest.fn(), - draw: jest.fn(), -}); - -export const createFakeGoogleReference = ({ - mapInstance = createFakeMapInstance(), - markerInstance = createFakeMarkerInstance(), -} = {}) => ({ - maps: { - LatLng: jest.fn((x) => x), - LatLngBounds: MockLatLngBounds, - Map: jest.fn(() => mapInstance), - Marker: jest.fn(() => markerInstance), - ControlPosition: { - LEFT_TOP: 'left:top', - }, - event: { - addListenerOnce: jest.fn(() => ({ - remove: jest.fn(), - })), - }, - OverlayView: FakeOverlayView, - }, -}); diff --git a/packages/react-instantsearch-dom/.storybook/addons.ts b/packages/react-instantsearch-dom/.storybook/addons.ts deleted file mode 100644 index b9991485de..0000000000 --- a/packages/react-instantsearch-dom/.storybook/addons.ts +++ /dev/null @@ -1,4 +0,0 @@ -import '@storybook/addon-knobs/register'; -import '@storybook/addon-actions/register'; -import '@storybook/addon-a11y/register'; -import '@storybook/addons'; diff --git a/packages/react-instantsearch-dom/.storybook/config.ts b/packages/react-instantsearch-dom/.storybook/config.ts deleted file mode 100644 index d214bdfdd5..0000000000 --- a/packages/react-instantsearch-dom/.storybook/config.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { configure, addDecorator, addParameters } from '@storybook/react'; -import { withKnobs } from '@storybook/addon-knobs'; -import { withA11y } from '@storybook/addon-a11y'; -import { create } from '@storybook/theming'; - -addParameters({ - options: { - panelPosition: 'right', - theme: create({ - base: 'light', - brandTitle: 'react-instantsearch', - brandUrl: 'https://github.com/algolia/instantsearch.js', - }), - }, -}); - -addDecorator(withKnobs); -addDecorator(withA11y); - -const req = require.context('../stories', true, /\.stories\.(js|ts|tsx)$/); - -function loadStories() { - req.keys().forEach((filename) => req(filename)); -} - -configure(loadStories, module); diff --git a/packages/react-instantsearch-dom/.storybook/postcss.config.js b/packages/react-instantsearch-dom/.storybook/postcss.config.js deleted file mode 100644 index a47ef4f952..0000000000 --- a/packages/react-instantsearch-dom/.storybook/postcss.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {}, - }, -}; diff --git a/packages/react-instantsearch-dom/.storybook/preview-head.html b/packages/react-instantsearch-dom/.storybook/preview-head.html deleted file mode 100644 index 5c0ee00b70..0000000000 --- a/packages/react-instantsearch-dom/.storybook/preview-head.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/react-instantsearch-dom/.storybook/public/default.css b/packages/react-instantsearch-dom/.storybook/public/default.css deleted file mode 100644 index 7713b5444d..0000000000 --- a/packages/react-instantsearch-dom/.storybook/public/default.css +++ /dev/null @@ -1,14 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - font-size: 14px; -} - -a { - color: #3e82f7; - text-decoration: none; -} - -#root img { - max-width: 100%; -} diff --git a/packages/react-instantsearch-dom/.storybook/public/react-autosuggest.css b/packages/react-instantsearch-dom/.storybook/public/react-autosuggest.css deleted file mode 100644 index 07c4db77e1..0000000000 --- a/packages/react-instantsearch-dom/.storybook/public/react-autosuggest.css +++ /dev/null @@ -1,69 +0,0 @@ -.react-autosuggest__container { - position: relative; -} - -.react-autosuggest__input { - width: 400px; - height: 30px; - padding: 10px 20px; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border: 1px solid #aaa; - border-radius: 4px; -} - -.react-autosuggest__input:focus { - outline: none; -} - -.react-autosuggest__container--open .react-autosuggest__input { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.react-autosuggest__suggestions-container { - display: none; -} - -.react-autosuggest__container--open .react-autosuggest__suggestions-container { - display: block; - position: absolute; - top: 51px; - width: 520px; - border: 1px solid #aaa; - background-color: #fff; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - z-index: 2; -} - -.react-autosuggest__suggestions-list { - margin: 0; - padding: 0; - list-style-type: none; -} - -.react-autosuggest__suggestion { - cursor: pointer; - padding: 10px 20px; -} - -.react-autosuggest__suggestion--focused { - background-color: #ddd; -} - -.react-autosuggest__section-title { - padding: 10px 0 0 10px; - font-size: 12px; - text-transform: uppercase; - color: #777; - border-top: 1px dashed #ccc; -} - -.react-autosuggest__section-container:first-child .react-autosuggest__section-title { - border-top: 0; -} \ No newline at end of file diff --git a/packages/react-instantsearch-dom/.storybook/public/rheostat.css b/packages/react-instantsearch-dom/.storybook/public/rheostat.css deleted file mode 100644 index d2756cf848..0000000000 --- a/packages/react-instantsearch-dom/.storybook/public/rheostat.css +++ /dev/null @@ -1,46 +0,0 @@ -.rheostat { - height: 24px; - position: relative; - overflow: visible -} - -.rheostat-background { - background: #dce0e0; - height: 2px; - position: relative; - top: 14px; - width: 100% -} - -.rheostat--disabled .rheostat-progress { - background-color: #edefed -} - -.rheostat--disabled .rheostat-handle { - cursor: default -} - -.rheostat-progress { - background-color: #3369e7; - height: 4px; - position: absolute; - top: 13px -} - -.rheostat-handle { - border: 1px solid #aaa; - background: #fff; - -webkit-border-radius: 100%; - -moz-border-radius: 100%; - border-radius: 100%; - -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - cursor: pointer; - height: 24px; - margin-left: -12px; - position: absolute; - z-index: 2; - width: 24px; - font-size: 0 -} \ No newline at end of file diff --git a/packages/react-instantsearch-dom/.storybook/public/util.css b/packages/react-instantsearch-dom/.storybook/public/util.css deleted file mode 100644 index 09682fb4e2..0000000000 --- a/packages/react-instantsearch-dom/.storybook/public/util.css +++ /dev/null @@ -1,272 +0,0 @@ -/** - * Container - */ - -.container { - padding: 50px 40px 40px; - position: relative; - overflow-x: hidden; - clear: left; -} - -.container:after { - font-size: 13px; - top: 0; - left: 0; - color: #999999; - padding: 4px 6px; - line-height: 1em; - position: absolute; - background-color: #f3f3f3; - border: solid 1px #e4e4e4; - border-width: 0 1px 1px 0; -} - -.footer-container { - border-radius: 0px 0px 5px 5px; - display: flex; - justify-content: space-around; - overflow-x: hidden; - clear: left; - border-bottom: solid 1px #e4e4e4; - border-left: solid 1px #e4e4e4; - border-right: solid 1px #e4e4e4; - background-color: #f3f3f3; -} - -.playground-url, -.source-code-url { - font-family: inherit; - font-size: 13px; - top: 0; - left: 0; - color: #999999; - padding: 4px 6px; - line-height: 1em; - background-color: #f3f3f3; - border: none; -} - -/** - * Widget - */ - -.widget-container { - border: solid 1px #e4e4e4; - border-radius: 5px 5px 0px 0px; -} - -.widget-container:after { - content: 'Widget display'; - border-radius: 5px 0 3px; -} - -/** - * Results - */ - -.hits-container { - border-left: solid 1px #e4e4e4; - border-right: solid 1px #e4e4e4; - border-bottom: solid 1px #e4e4e4; - display: flex; - flex-direction: column; -} - -.hits-container:after { - content: 'Results'; -} - -/** - * Search - */ - -.hit-actions { - display: flex; - align-items: center; -} - -.hit-actions > .ais-ClearRefinements { - margin-left: 15px; -} - -/** - * Hits - */ - -.hits { - margin: 25px 0; -} - -.hit { - padding: 5px 5px; - display: flex; - align-items: center; -} - -.hit--airbnb:hover, -.hit--airbnb-active { - background-color: #3369e7; - color: #ffffff; -} - -.ais-SearchBox__root { - margin-right: 10px; -} - -.hit-content { - margin-left: 20px; -} - -.hit-picture > img { - width: 80px; - height: 80px; -} - -.hit-type { - color: #888888; - font-size: 13px; -} - -/** - * Multi-index - */ - -.multi-index_content { - display: flex; - flex-direction: column; - margin-top: 10px; -} - -.multi-index_hit { - padding: 10px; -} - -.multi-index_hit { - display: flex; -} - -.multi-index_hit .multi-index_hit-content { - padding-left: 10px; -} - -/* GeoSearch */ - -.my-custom-marker { - position: relative; - background-color: white; - border: 1px solid rgba(0, 0, 0, 0.2); - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - font-weight: 500; - font-size: 1rem; - padding: 3px 5px; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.15); -} - -.my-custom-marker:hover, -.my-custom-marker--active { - background-color: #3369e7; - color: white; - cursor: pointer; -} - -.my-custom-marker::after { - content: ''; - display: block; - position: absolute; - width: 8px; - height: 8px; - bottom: -5px; - background-color: white; - border-color: rgba(0, 0, 0, 0.2); - border-width: 0 1px 1px 0; - border-style: solid; - left: 50%; - margin-left: -4px; - transform: rotate(45deg); -} - -.my-custom-marker:hover::after, -.my-custom-marker--active::after { - background-color: #3369e7; -} - -/* Voice Search */ -.custom-button-story .ais-VoiceSearch-button:hover { - background: inherit; -} - -.custom-ui .ais-VoiceSearch-button { - position: absolute; - right: 43px; - top: 53px; - z-index: 3; -} -.custom-ui .ais-VoiceSearch-status .layer { - position: absolute; - background: rgba(255, 255, 255, 0.95); - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 2; - align-items: center; - justify-content: center; - display: none; -} -.custom-ui .ais-VoiceSearch-status .layer.listening-true { - display: flex; -} -.custom-ui .ais-VoiceSearch-status .layer span { - font-size: 2rem; - color: #555; -} - -/* Related items */ - -.related-items { - display: flex; - align-items: center; -} - -.related-items .ais-Hits-list { - margin-left: 0; - margin-right: 1rem; -} - -.ais-RelatedHits-item-image { - height: 150px; - display: flex; - justify-content: center; - align-items: center; - padding: 1rem; - background-color: #fff; -} - -.ais-RelatedHits-item-image img { - max-height: 120px; -} - -.ais-RelatedHits-item-title { - text-align: center; - padding: 1rem; -} - -.ais-RelatedHits-item-title h4 { - margin: 0; -} - -.ais-RelatedHits-button { - height: 40px; - width: 40px; - border-radius: 3px; - background-color: #dfe2ee; - border: none; - color: #3a4570; - cursor: pointer; -} - -.ais-RelatedHits-button[disabled] { - cursor: not-allowed; -} diff --git a/packages/react-instantsearch-dom/.storybook/webpack.config.js b/packages/react-instantsearch-dom/.storybook/webpack.config.js deleted file mode 100644 index dc0e83b4e6..0000000000 --- a/packages/react-instantsearch-dom/.storybook/webpack.config.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = ({ config }) => ({ - ...config, - module: { - ...config.module, - rules: [ - { - test: /\.(js|ts|tsx)$/, - exclude: /node_modules/, - use: [ - { - loader: 'babel-loader', - options: { - rootMode: 'upward', - presets: [['react-app', { typescript: true }]], - exclude: /node_modules|algoliasearch-helper/, - }, - }, - ], - }, - { - test: /\.css$/, - use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], - }, - ], - }, -}); diff --git a/packages/react-instantsearch-dom/CHANGELOG.md b/packages/react-instantsearch-dom/CHANGELOG.md deleted file mode 100644 index d58649ba15..0000000000 --- a/packages/react-instantsearch-dom/CHANGELOG.md +++ /dev/null @@ -1,1891 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [6.40.4](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.40.3...react-instantsearch-dom@6.40.4) (2023-07-25) - -**Note:** Version bump only for package react-instantsearch-dom - - - - - -## [6.40.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.40.2...react-instantsearch-dom@6.40.3) (2023-07-19) - - -### Bug Fixes - -* **instantsearch:** keep algoliasearch-helper as external dependency during build ([#5765](https://github.com/algolia/instantsearch.js/issues/5765)) ([550fefa](https://github.com/algolia/instantsearch.js/commit/550fefa1401773f38dedc20322513ae662faa25d)) - - - - - -## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.40.1...react-instantsearch-dom@6.40.2) (2023-07-18) - -**Note:** Version bump only for package react-instantsearch-dom - - - - - -## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.40.0...react-instantsearch-dom@6.40.1) (2023-06-20) - - -### Bug Fixes - -* **dependencies:** update helper requirement ([#5676](https://github.com/algolia/instantsearch.js/issues/5676)) ([c289120](https://github.com/algolia/instantsearch.js/commit/c2891205c1125b1203b3b3db946d57e0fc4e4687)), closes [#5658](https://github.com/algolia/instantsearch.js/issues/5658) - - - - - -# [6.40.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.39.1...react-instantsearch-dom@6.40.0) (2023-05-16) - - -### Bug Fixes - -* **this:** ensure all functions are able to be destructured ([#5611](https://github.com/algolia/instantsearch.js/issues/5611)) ([a8b5c1e](https://github.com/algolia/instantsearch.js/commit/a8b5c1e5bbd6afac39fce523f7d7c2ec02f51153)), closes [#5589](https://github.com/algolia/instantsearch.js/issues/5589) - - -### Features - -* **instantsearch:** make root indexName optional ([#5590](https://github.com/algolia/instantsearch.js/issues/5590)) ([80f309e](https://github.com/algolia/instantsearch.js/commit/80f309ed69b61534ca118b60c9c88691e0148fca)) - - - - - -## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.39.0...react-instantsearch-dom@6.39.1) (2023-03-21) - - -### Bug Fixes - -* **searchbox:** add aria-hidden to svg icons ([#5547](https://github.com/algolia/instantsearch.js/issues/5547)) ([50344e3](https://github.com/algolia/instantsearch.js/commit/50344e3b14c22c886415c0e7d799aca778dc39ab)), closes [#5546](https://github.com/algolia/instantsearch.js/issues/5546) - - - - - -# [6.39.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.38.3...react-instantsearch-dom@6.39.0) (2023-01-26) - - -### Bug Fixes - -* **dependencies:** update typescript ([#5454](https://github.com/algolia/instantsearch.js/issues/5454)) ([0e6bb48](https://github.com/algolia/instantsearch.js/commit/0e6bb485a31cd3294436ac9902c2c2662dfcdf8b)) - - -### Features - -* **rendering:** always render with current state ([#5429](https://github.com/algolia/instantsearch.js/issues/5429)) ([920e951](https://github.com/algolia/instantsearch.js/commit/920e951f03aada0e6a1ce16bc389a82a2f00b202)) - - - - - - -## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.38.2...react-instantsearch-dom@6.38.3) (2023-01-09) - - -### Bug Fixes - -* **dependencies:** update helper requirement for minor PP vulnerability ([#5417](https://github.com/algolia/instantsearch.js/issues/5417)) ([254ef00](https://github.com/algolia/instantsearch.js/commit/254ef00439a9af48be15f22b4fd9902899610226)) - - - - - -## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-dom@6.38.1...react-instantsearch-dom@6.38.2) (2023-01-03) - -**Note:** Version bump only for package react-instantsearch-dom - - - - - -## [6.38.1](https://github.com/algolia/react-instantsearch/compare/v6.38.0...v6.38.1) (2022-11-08) - - -### Bug Fixes - -* **hooks:** avoid effect in useStableValue ([#3670](https://github.com/algolia/react-instantsearch/issues/3670)) ([d1f53ae](https://github.com/algolia/react-instantsearch/commit/d1f53ae815b75f13c18fd245e0403d57e7ae391c)) - - - -# [6.38.0](https://github.com/algolia/react-instantsearch/compare/v6.37.0...v6.38.0) (2022-10-25) - - -### Features - -* **getServerState:** allow users to inject renderToString ([#3658](https://github.com/algolia/react-instantsearch/issues/3658)) ([9c10449](https://github.com/algolia/react-instantsearch/commit/9c104497b9b32337f288d70a2582c41cafb13cd6)), closes [#3633](https://github.com/algolia/react-instantsearch/issues/3633) [#3618](https://github.com/algolia/react-instantsearch/issues/3618) [vercel/next.js#40067](https://github.com/vercel/next.js/issues/40067) - -* **PoweredBy:** update component logo ([#3661](https://github.com/algolia/react-instantsearch/issues/3661)) ([69c1b2a](https://github.com/algolia/react-instantsearch/commit/69c1b2acef64d972dfa6c6beb8967032119ad2d5)) - - - -# [6.37.0](https://github.com/algolia/react-instantsearch/compare/v6.36.0...v6.37.0) (2022-10-18) - - -### Features - -* **core:** support react 18 strict mode ([#3653](https://github.com/algolia/react-instantsearch/issues/3653)) ([9174806](https://github.com/algolia/react-instantsearch/commit/9174806a7997a45893a24d149027119f4a0709c3)) - - - -# [6.36.0](https://github.com/algolia/react-instantsearch/compare/v6.35.0...v6.36.0) (2022-10-04) - - -### Features - -* **HierarchicalMenu:** add css class for link of selected menu item ([#3646](https://github.com/algolia/react-instantsearch/issues/3646)) ([980ad70](https://github.com/algolia/react-instantsearch/commit/980ad70c75e970c35c11a10a534dbe3996d6c54c)) -* **useInstantSearch:** expose status & error ([#3645](https://github.com/algolia/react-instantsearch/issues/3645)) ([f436d31](https://github.com/algolia/react-instantsearch/commit/f436d31184f3f75b33a1fdaa19c665e77948df28)) - - - -# [6.35.0](https://github.com/algolia/react-instantsearch/compare/v6.34.0...v6.35.0) (2022-09-29) - - -### Features - -* **hooks-web:** introduce Translations API ([#3638](https://github.com/algolia/react-instantsearch/issues/3638)) ([63b506f](https://github.com/algolia/react-instantsearch/commit/63b506f9dbad284f45ac17210e17c4a2a8f099b6)) - -### Fixes -* **hooks-web:** when searchAsYouType=false, pressing the reset button searches (previously only reset the query) ([#3642](https://github.com/algolia/react-instantsearch/issues/3642)) ([f969deb](https://github.com/algolia/react-instantsearch/commit/f969deb05fd4f53aaa251ff88b52db2224ce0786)) - -# [6.34.0](https://github.com/algolia/react-instantsearch/compare/v6.33.0...v6.34.0) (2022-09-27) - - -### Features - -* **SearchBox:** expose formRef ([#3565](https://github.com/algolia/react-instantsearch/issues/3565)) ([1c2f46d](https://github.com/algolia/react-instantsearch/commit/1c2f46da2d1cf705cfd3946c52aef4ca9ec943d7)) - - - -# [6.33.0](https://github.com/algolia/react-instantsearch/compare/v6.32.1...v6.33.0) (2022-09-15) - - -### Bug Fixes - -* **react-native:** mark as compatible with react 18 ([#3614](https://github.com/algolia/react-instantsearch/issues/3614)) ([2a191a8](https://github.com/algolia/react-instantsearch/commit/2a191a84751127de5a3eb34b59b460a1d1bfe594)) -* **rish:** hide reset button when search is stalled in `SearchBox` ([#3617](https://github.com/algolia/react-instantsearch/issues/3617)) ([93ee9d0](https://github.com/algolia/react-instantsearch/commit/93ee9d0212893cef4842c86b7c78f285aa136be8)) - - -### Features - -* **dependencies:** update instantsearch and helper ([#3622](https://github.com/algolia/react-instantsearch/issues/3622)) ([6773ab1](https://github.com/algolia/react-instantsearch/commit/6773ab169cd74dfe1133e255daade4d57e99d9a4)) - - - -## [6.32.1](https://github.com/algolia/react-instantsearch/compare/v6.32.0...v6.32.1) (2022-08-26) - - -### Bug Fixes - -* **useInstantSearch:** prevent `results` from being `null` when first search is stalled ([#3597](https://github.com/algolia/react-instantsearch/issues/3597)) ([6f71d78](https://github.com/algolia/react-instantsearch/commit/6f71d78868fde55a3f9c4460edc337a1e99df4f9)) - - - -# [6.32.0](https://github.com/algolia/react-instantsearch/compare/v6.31.1...v6.32.0) (2022-08-22) - - -### Features - -* **SearchBox:** introduce `autoFocus` prop ([#3599](https://github.com/algolia/react-instantsearch/issues/3599)) ([99121b9](https://github.com/algolia/react-instantsearch/commit/99121b952fd002cb6dae52af41f08beed8f6c3e2)) - - - -## [6.31.1](https://github.com/algolia/react-instantsearch/compare/v6.31.0...v6.31.1) (2022-08-08) - - -### Bug Fixes - -* **hooks:** prevent widget cleanup on `` unmount ([#3590](https://github.com/algolia/react-instantsearch/issues/3590)) ([d94899d](https://github.com/algolia/react-instantsearch/commit/d94899d1264134f0cb1ca2d266a660f1fb2a588c)) - - - -# [6.31.0](https://github.com/algolia/react-instantsearch/compare/v6.30.3...v6.31.0) (2022-08-03) - - -### Features - -* **hooks:** add `searchAsYouType` option to `` ([#3585](https://github.com/algolia/react-instantsearch/issues/3585)) ([c610385](https://github.com/algolia/react-instantsearch/commit/c610385cb9688b23b3e041e31b9edd60392b341d)) - - - -## [6.30.3](https://github.com/algolia/react-instantsearch/compare/v6.30.2...v6.30.3) (2022-08-01) - - -### Bug Fixes - -* **hooks:** replace `labelText` CSS class with `label` in `` to align with InstantSearch's CSS specifications ([#3583](https://github.com/algolia/react-instantsearch/issues/3583)) ([3e030ae](https://github.com/algolia/react-instantsearch/commit/3e030aedb9f285ff449eb82589bc6fea60b160cb)) - - - -## [6.30.2](https://github.com/algolia/react-instantsearch/compare/v6.30.1...v6.30.2) (2022-07-18) - - -### Bug Fixes - -* **hooks:** type of DynamicWidgets props ([#3566](https://github.com/algolia/react-instantsearch/issues/3566)) ([612c98b](https://github.com/algolia/react-instantsearch/commit/612c98b5a77fb9037185c4b5efda8c07663dbd1a)), closes [#3563](https://github.com/algolia/react-instantsearch/issues/3563) -* **hooks:** use single instance in ([#3561](https://github.com/algolia/react-instantsearch/issues/3561)) ([4c358be](https://github.com/algolia/react-instantsearch/commit/4c358bebfc91451b1610f677f89c595d7a427f1f)) - - - -## [6.30.1](https://github.com/algolia/react-instantsearch/compare/v6.30.0...v6.30.1) (2022-07-12) - - -### Bug Fixes - -* **hooks:** provide state and results APIs from "render" event ([#3554](https://github.com/algolia/react-instantsearch/issues/3554)) ([67d4788](https://github.com/algolia/react-instantsearch/commit/67d4788ab09ec2a57b43d53e8093b8c11120b761)) -* **hooks**: update instantsearch.js dependency ([#3557](https://github.com/algolia/react-instantsearch/issues/3557)) - - - -# [6.30.0](https://github.com/algolia/react-instantsearch/compare/v6.29.0...v6.30.0) (2022-07-05) - - -### Features - -* **core:** update instantsearch and helper ([#3539](https://github.com/algolia/react-instantsearch/issues/3539)) ([0ac2c7a](https://github.com/algolia/react-instantsearch/commit/0ac2c7a3f2e2a827721f5b2b7c69c54560f8574f)) - - - -# [6.29.0](https://github.com/algolia/react-instantsearch/compare/v6.28.0...v6.29.0) (2022-06-21) - - -### Bug Fixes - -* **HierarchicalMenu:** show full hierarchical parent values ([#3521](https://github.com/algolia/react-instantsearch/issues/3521)) ([79c3890](https://github.com/algolia/react-instantsearch/commit/79c3890848175a4d70311e5c3929c902bb953c10)) - - -### Features - -* **core:** sort parameters for improved cache rate ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) -* **core:** support client.search for sffv ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) - - - -# [6.28.0](https://github.com/algolia/react-instantsearch/compare/v6.27.0...v6.28.0) (2022-06-15) - - -### Bug Fixes - -* **hooks-server:** import react server via an expression ([#3515](https://github.com/algolia/react-instantsearch/issues/3515)) ([91b96f7](https://github.com/algolia/react-instantsearch/commit/91b96f743b9315ed5ea781681b77fc7f5604ab6e)), closes [#3512](https://github.com/algolia/react-instantsearch/issues/3512) -* **hooks-web:** fix duplicated key in ([#3513](https://github.com/algolia/react-instantsearch/issues/3513)) ([fc94d80](https://github.com/algolia/react-instantsearch/commit/fc94d806daf139f58b234cdc0b450da2efe861ee)) -* **hooks:** mount widgets in SSR to retrieve HTML ([#3518](https://github.com/algolia/react-instantsearch/issues/3518)) ([aa5f9d8](https://github.com/algolia/react-instantsearch/commit/aa5f9d84ddb6e97d05e6ad1baf2c6caa23891281)) -* **types:** allow useInstantSearch to be generic ([#3508](https://github.com/algolia/react-instantsearch/issues/3508)) ([6807232](https://github.com/algolia/react-instantsearch/commit/68072324cf302801502a1b4c3d06703e57b55a97)), closes [algolia/instantsearch.js#5060](https://github.com/algolia/instantsearch.js/issues/5060) -* **types:** support React 18 types ([#3481](https://github.com/algolia/react-instantsearch/issues/3481)) ([74cf8cb](https://github.com/algolia/react-instantsearch/commit/74cf8cb9be8ff3d113b57a50e7083df0d1bc94f2)) - - -### Features - -* **hooks:** introduce `useInstantSearch()` ([#3494](https://github.com/algolia/react-instantsearch/issues/3494)) ([74d522c](https://github.com/algolia/react-instantsearch/commit/74d522c032326658f2a0b8f0001bd593e0085208)) -* **hooks:** support React 18 Strict Mode ([#3514](https://github.com/algolia/react-instantsearch/issues/3514)) ([eeb67c7](https://github.com/algolia/react-instantsearch/commit/eeb67c7b5dc08c696c46d9538f104eeceecef388)) - - - -# [6.27.0](https://github.com/algolia/react-instantsearch/compare/v6.26.0...v6.27.0) (2022-06-07) - - -### Bug Fixes - -* **hooks-web:** don't pass widget props to ui components ([#3501](https://github.com/algolia/react-instantsearch/issues/3501)) ([5bd53c1](https://github.com/algolia/react-instantsearch/commit/5bd53c128ddeeea87f75ae89eb8f2324d476c70e)), closes [#3499](https://github.com/algolia/react-instantsearch/issues/3499) -* **SearchBox-hooks:** correctly pass widget props ([#3499](https://github.com/algolia/react-instantsearch/issues/3499)) ([2cdf906](https://github.com/algolia/react-instantsearch/commit/2cdf90602b7c2c5895124ef64c389ce574154386)), closes [#3498](https://github.com/algolia/react-instantsearch/issues/3498) - - -### Features - -* **hooks:** migrate to `useSyncExternalStore()` ([#3489](https://github.com/algolia/react-instantsearch/issues/3489)) ([81bbdf2](https://github.com/algolia/react-instantsearch/commit/81bbdf28f2d28d8b0081cfd7d9e84c3e33038dd2)) -* **hooks:** upgrade to InstantSearch.js 4.41.0 ([#3502](https://github.com/algolia/react-instantsearch/issues/3502)) ([0b76792](https://github.com/algolia/react-instantsearch/commit/0b76792ea0c4e2ac9fe742810d70ba1aee2a3e79)) - - - -# [6.26.0](https://github.com/algolia/react-instantsearch/compare/v6.25.0...v6.26.0) (2022-05-19) - - -### Features - -* **hooks-web:** expose `sendEvent` to `hitComponent` ([#3476](https://github.com/algolia/react-instantsearch/issues/3476)) ([5cc18d1](https://github.com/algolia/react-instantsearch/commit/5cc18d19d9f22305f33d92e43fd0aca2a5cb949a)) -* **react-instantsearch-core:** allow widgets to set their `$$widgetType` ([#3472](https://github.com/algolia/react-instantsearch/issues/3472)) ([1c436e1](https://github.com/algolia/react-instantsearch/commit/1c436e1429ab4230bbfea7c6d2474d141f5c5c64)) - - - -# [6.25.0](https://github.com/algolia/react-instantsearch/compare/v6.24.3...v6.25.0) (2022-05-17) - - -### Bug Fixes - -* **hooks-highlight:** make sure highlight and snippet don't show html-escaped content ([#3471](https://github.com/algolia/react-instantsearch/issues/3471)) ([c18ddd2](https://github.com/algolia/react-instantsearch/commit/c18ddd25faca37d6bfa3d1c28f6fc22ec5fcf6d8)) -* **hooks-server:** remove faulty UMD build ([#3465](https://github.com/algolia/react-instantsearch/issues/3465)) ([c1ddfe4](https://github.com/algolia/react-instantsearch/commit/c1ddfe408b411551ac8524877a9d65ded8133c42)) - - -### Features - -* **dom-maps:** expose GeoSearchContext ([#3468](https://github.com/algolia/react-instantsearch/issues/3468)) ([a61ff96](https://github.com/algolia/react-instantsearch/commit/a61ff96222bfd4f6301cf93bf95e2fa18b263d3c)), closes [#3448](https://github.com/algolia/react-instantsearch/issues/3448) -* **hooks-server:** support import from React 18 ([#3464](https://github.com/algolia/react-instantsearch/issues/3464)) ([0a13867](https://github.com/algolia/react-instantsearch/commit/0a13867f7dd5a8a18e0957b2072bbde3b02d6490)), closes [#3453](https://github.com/algolia/react-instantsearch/issues/3453) -* **hooks:** make InstantSearch type generic ([#3466](https://github.com/algolia/react-instantsearch/issues/3466)) ([b0905b7](https://github.com/algolia/react-instantsearch/commit/b0905b73bed558c62eedb7ae701be20c2ebe25c9)) - - - -## [6.24.3](https://github.com/algolia/react-instantsearch/compare/v6.24.2...v6.24.3) (2022-05-10) - - -### Bug Fixes - -* **numericmenu:** include range values in comparison with minmax bounds ([#3461](https://github.com/algolia/react-instantsearch/issues/3461)) ([e4c2682](https://github.com/algolia/react-instantsearch/commit/e4c268261dc42a6aa43d985934b53c82f8b71089)) - - - -## [6.24.2](https://github.com/algolia/react-instantsearch/compare/v6.24.1...v6.24.2) (2022-05-05) - - -### Bug Fixes - -* **hooks:** prevent infinite loops from render state ([#3455](https://github.com/algolia/react-instantsearch/issues/3455)) ([1799fc9](https://github.com/algolia/react-instantsearch/commit/1799fc9f78a4a5aafb54df339c3e211ff9187748)) - - - -## [6.24.1](https://github.com/algolia/react-instantsearch/compare/v6.24.0...v6.24.1) (2022-05-03) - - -### Bug Fixes - -* **types:** export correct types for react-instantsearch-hooks-web ([#3454](https://github.com/algolia/react-instantsearch/issues/3454)) ([a863430](https://github.com/algolia/react-instantsearch/commit/a8634306621f7a05a2b3056a6db25ccf8d9eabf0)) - - - -# [6.24.0](https://github.com/algolia/react-instantsearch/compare/v6.23.4...v6.24.0) (2022-04-28) - - -### Features - -* **hooks:** expose DOM components ([#3450](https://github.com/algolia/react-instantsearch/issues/3450)) ([5732e3d](https://github.com/algolia/react-instantsearch/commit/5732e3de732275911f94b26ba9e2c4165bdf77e7)) -* **hooks:** remove experimental warning ([#3446](https://github.com/algolia/react-instantsearch/issues/3446)) ([84c99fe](https://github.com/algolia/react-instantsearch/commit/84c99fe91d6906a877bec620b44c61d762f0ea57)) - - - -## [6.23.4](https://github.com/algolia/react-instantsearch/compare/v6.23.3...v6.23.4) (2022-04-21) - - -### Bug Fixes - -* **hooks:** forbid importing from instantsearch.js root path ([#3437](https://github.com/algolia/react-instantsearch/issues/3437)) ([82ef9ea](https://github.com/algolia/react-instantsearch/commit/82ef9eaaec42bc54f15796b5b031a8656330a45c)) -* **packages:** correctly mark peer dependency ([#3439](https://github.com/algolia/react-instantsearch/issues/3439)) ([51e8818](https://github.com/algolia/react-instantsearch/commit/51e8818fce224819230c8bf6dea2a08d71d9be29)), closes [#3428](https://github.com/algolia/react-instantsearch/issues/3428) - - - -## [6.23.3](https://github.com/algolia/react-instantsearch/compare/v6.23.2...v6.23.3) (2022-04-05) - - -### Bug Fixes - -* **facets:** show raw value in currentRefinements ([#3420](https://github.com/algolia/react-instantsearch/issues/3420)) ([1199ce6](https://github.com/algolia/react-instantsearch/commit/1199ce6bd4152e4b54bd2ee0e1f0c9d81021c7d5)), closes [#3412](https://github.com/algolia/react-instantsearch/issues/3412) -* **multi-index:** correctly set `searching` prop in multi-index result states ([#3419](https://github.com/algolia/react-instantsearch/issues/3419)) ([7f8e97e](https://github.com/algolia/react-instantsearch/commit/7f8e97e31b3dd5e49d3febef673f41f7dfa6d45d)) - - - -## [6.23.2](https://github.com/algolia/react-instantsearch/compare/v6.23.1...v6.23.2) (2022-04-04) - - -### Bug Fixes - -* **refinements:** use escaped value for refining ([#3412](https://github.com/algolia/react-instantsearch/issues/3412)) ([f2f5f6c](https://github.com/algolia/react-instantsearch/commit/f2f5f6cbfaed48a5c494daeb8789e8deebc64293)) -* support React 18 as peer dependency ([#3411](https://github.com/algolia/react-instantsearch/issues/3411)) ([38eb5a6](https://github.com/algolia/react-instantsearch/commit/38eb5a6a8fe92cb763a25f452bea78b189a6a82a)) - - -## [6.23.1](https://algolia/compare/v6.23.0...v6.23.1) (2022-03-30) - - -### Bug Fixes - -* **hooks:** compute initial search parameters from widget ([#3399](https://algolia/issues/3399)) ([b4bd933](https://algolia/commits/b4bd93358598bdc77a1aa858252e6eee23441345)) - - - -# [6.23.0](https://algolia/compare/v6.22.0...v6.23.0) (2022-03-23) - - -### Bug Fixes - -* **ssr:** perform initial multi-index search using a single request ([#3385](https://algolia/issues/3385)) ([b96809e](https://algolia/commits/b96809e5748d298350890647956cb7adbbb55650)) - - -### Features - -* **hooks:** allow additional widget properties to be passed from hooks ([#3359](https://algolia/issues/3359)) ([a047be4](https://algolia/commits/a047be45c7fce7ee28f7d6f61d2fbfa79e3ed2d0)) -* **hooks:** allow useHits and useInfiniteHit to be generic ([#3364](https://algolia/issues/3364)) ([8e66ad3](https://algolia/commits/8e66ad3ad587197c4811c60a5cab475137ca5afb)) -* **hooks:** mark initial results as "artificial" ([#3384](https://algolia/issues/3384)) ([937efdc](https://algolia/commits/937efdc71bae1d99270f8ecb5c5c9c501b3d7769)) - - - -# [6.22.0](https://github.com/algolia/react-instantsearch/compare/v6.21.1...v6.22.0) (2022-02-01) - - -### Bug Fixes - -* **hooks:** enable pause on exceptions on warning ([#3283](https://github.com/algolia/react-instantsearch/issues/3283)) ([ce3a6c3](https://github.com/algolia/react-instantsearch/commit/ce3a6c3f2700a05ae54a91278fd23fa64a97d733)) - - -### Features - -* **hooks:** introduce `useBreadcrumb()` ([#3245](https://github.com/algolia/react-instantsearch/issues/3245)) ([5bfbaaf](https://github.com/algolia/react-instantsearch/commit/5bfbaaf746e7632968ac9b30b73357bcb0b37e91)) - - - -## [6.21.1](https://github.com/algolia/react-instantsearch/compare/v6.21.0...v6.21.1) (2022-01-25) - - -### Bug Fixes - -* **hooks:** apply initial search parameters in useConnector ([#3276](https://github.com/algolia/react-instantsearch/issues/3276)) ([f85d679](https://github.com/algolia/react-instantsearch/commit/f85d6794c31eac61521fd8fc16b75673f02ed75b)) - - - -# [6.21.0](https://github.com/algolia/react-instantsearch/compare/v6.20.0...v6.21.0) (2022-01-24) - - -### Features - -* **hooks-server:** load data twice in the case of dynamic widget usage ([#3259](https://github.com/algolia/react-instantsearch/issues/3259)) ([9b4903b](https://github.com/algolia/react-instantsearch/commit/9b4903b2ea73d9d7e33729b87d9d55990e64312c)) -* **server:** load data twice in the case of dynamic widget usage ([#3268](https://github.com/algolia/react-instantsearch/issues/3268)) ([91cd085](https://github.com/algolia/react-instantsearch/commit/91cd085f9a323ed6b872f3a098f561007a72d0d2)), closes [/github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js#L14-L16](https://github.com//github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js/issues/L14-L16) - - - -# [6.20.0](https://github.com/algolia/react-instantsearch/compare/v6.19.0...v6.20.0) (2022-01-18) - - -### Bug Fixes - -* **hooks:** allow importing via require ([#3257](https://github.com/algolia/react-instantsearch/issues/3257)) ([6aa80b3](https://github.com/algolia/react-instantsearch/commit/6aa80b3647567199c3df1b90a07d708b223ce1fd)) - - -### Features - -* **hooks:** implement `useClearRefinements()` ([#3256](https://github.com/algolia/react-instantsearch/issues/3256)) ([930b83d](https://github.com/algolia/react-instantsearch/commit/930b83df4c4bbccbc3118f6ea1001f6a30a3d464)), closes [#3252](https://github.com/algolia/react-instantsearch/issues/3252) -* **hooks:** introduce `` ([#3261](https://github.com/algolia/react-instantsearch/issues/3261)) ([3527b94](https://github.com/algolia/react-instantsearch/commit/3527b9422de48a4a6b4bd752bd643e01cd5011d8)) -* **hooks:** introduce `usePoweredBy()` ([#3251](https://github.com/algolia/react-instantsearch/issues/3251)) ([a97230b](https://github.com/algolia/react-instantsearch/commit/a97230b89e3ba1df9bf2d21a6a9f9a70b45f41b8)) -* **hooks:** introduce `useToggleRefinement()` ([#3248](https://github.com/algolia/react-instantsearch/issues/3248)) ([a561c09](https://github.com/algolia/react-instantsearch/commit/a561c090a268e1c6ca1fa2d5bf72263cc47aa273)) - - - -# [6.19.0](https://github.com/algolia/react-instantsearch/compare/v6.18.0...v6.19.0) (2022-01-05) - - -### Features - -* **hooks:** bundle as es-module ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) -* **dependencies:** update to latest algoliasearch-helper ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) - - - -# [6.18.0](https://github.com/algolia/react-instantsearch/compare/v6.17.0...v6.18.0) (2021-12-16) - - -### Features - -* **dynamicWidgets:** send facets * and maxValuesPerFacet by default ([#3242](https://github.com/algolia/react-instantsearch/issues/3242)) ([c071776](https://github.com/algolia/react-instantsearch/commit/c07177670ac30dced55250774654e8b2a77b6739)) -* **hooks:** introduce `useNumericMenu()` ([#3237](https://github.com/algolia/react-instantsearch/issues/3237)) ([e3056c9](https://github.com/algolia/react-instantsearch/commit/e3056c9e2c64b5afafb7a736599a5cbf6137575b)) -* **hooks:** introduce `useInfiniteHits()` ([#3224](https://github.com/algolia/react-instantsearch/issues/3224)) ([177ec56](https://github.com/algolia/react-instantsearch/commit/177ec56af274670c2bf8599ba104b5544979bbe8)) - - - -# [6.17.0](https://github.com/algolia/react-instantsearch/compare/v6.16.0...v6.17.0) (2021-12-08) - - -### Bug Fixes - -* **hooks:** throw invariant violations in production ([#3217](https://github.com/algolia/react-instantsearch/issues/3217)) ([6d3f99c](https://github.com/algolia/react-instantsearch/commit/6d3f99ca91f470ee742ddc55e95f57b1f1801d7b)) - - -### Features - -* **hooks:** introduce `useMenu()` ([#3197](https://github.com/algolia/react-instantsearch/issues/3197)) ([15d1cc9](https://github.com/algolia/react-instantsearch/commit/15d1cc993437b111cd5a32f43ee1d2065c639ed4)) -* **hooks:** introduce `useRange()` ([#3198](https://github.com/algolia/react-instantsearch/issues/3198)) ([df1f1c8](https://github.com/algolia/react-instantsearch/commit/df1f1c8109dc684e74d3aee1bf0359f2a0e1b9f4)) -* **hooks:** introduce `` ([#3216](https://github.com/algolia/react-instantsearch/issues/3216)) ([d99aea6](https://github.com/algolia/react-instantsearch/commit/d99aea6cfe9dea86ae6b98ee3762373f4b3843f1)) -* **hooks:** introduce `useDynamicWidgets()` ([#3210](https://github.com/algolia/react-instantsearch/issues/3210)) ([29c2ea2](https://github.com/algolia/react-instantsearch/commit/29c2ea22b91a39d9eb40a044ae9aab85f2612db8)) -* **hooks:** introduce `useQueryRules()` ([#3212](https://github.com/algolia/react-instantsearch/issues/3212)) ([3ef1e1e](https://github.com/algolia/react-instantsearch/commit/3ef1e1e4116b3e58b2c2134d0c60fbb9f40c1501)) -* **hooks:** introduce SSR support ([#3221](https://github.com/algolia/react-instantsearch/issues/3221)) ([0a6b3ec](https://github.com/algolia/react-instantsearch/commit/0a6b3ec61942ad3849c6f078e21b3328679bffff)) -* **hooks:** introduce `useCurrentRefinements()` ([#3222](https://github.com/algolia/react-instantsearch/issues/3222)) ([7ebd8c3](https://github.com/algolia/react-instantsearch/commit/7ebd8c3da8c17b0bd7e0f8deab633b98fa052e7f)) - - - -# [6.16.0](https://github.com/algolia/react-instantsearch/compare/v6.15.0...v6.16.0) (2021-11-22) - - -### Bug Fixes - -* **PoweredBy:** support environments with `window` but no `location` ([#3186](https://github.com/algolia/react-instantsearch/issues/3186)) ([22ff23b](https://github.com/algolia/react-instantsearch/commit/22ff23b67554683567393114c2f53cacec44f4a6)) - - -### Features - -* **DynamicWidgets:** release as stable ([#3090](https://github.com/algolia/react-instantsearch/issues/3090)) ([a4a1d9e](https://github.com/algolia/react-instantsearch/commit/a4a1d9e032b31c611d5d73fdda3a03ad705f5c68)) -* **hooks:** introduce `usePagination()` ([#3182](https://github.com/algolia/react-instantsearch/issues/3182)) ([d8b1b86](https://github.com/algolia/react-instantsearch/commit/d8b1b867bb598e801f1350e81b4a4220a8e528d7)) -* **hooks:** introduce `useSortBy()` ([#3190](https://github.com/algolia/react-instantsearch/issues/3190)) ([5cce33b](https://github.com/algolia/react-instantsearch/commit/5cce33b48032548fed76b592ee0201e3c42fc3c4)) -* **hooks:** introduce `useHierarchicalMenu()` ([#3199](https://github.com/algolia/react-instantsearch/issues/3199)) ([b347061](https://github.com/algolia/react-instantsearch/commit/b3470611b962ef55c55576c65a6307abc54e5efd)) - - - -# [6.15.0](https://github.com/algolia/react-instantsearch/compare/v6.14.0...v6.15.0) (2021-10-27) - - -### Bug Fixes - -* **metadata:** stricter detection of user agent ([#3184](https://github.com/algolia/react-instantsearch/issues/3184)) ([994c8ae](https://github.com/algolia/react-instantsearch/commit/994c8ae055fc23a1a067d111d2f4727b1c7bf8ca)) - - -### Features - -* **hooks:** introduce `useConfigure()` ([#3181](https://github.com/algolia/react-instantsearch/issues/3181)) ([aa2eb9b](https://github.com/algolia/react-instantsearch/commit/aa2eb9baec6335f8a3523ee8b9b761a217cfc734)) - - - -# [6.14.0](https://github.com/algolia/react-instantsearch/compare/v6.13.0...v6.14.0) (2021-10-26) - - -### Features - -* **dependencies:** update algoliasearch-helper ([#3176](https://github.com/algolia/react-instantsearch/issues/3176)) ([a8708a3](https://github.com/algolia/react-instantsearch/commit/a8708a33f31632000bc827b076539b1cca7adf6f)) -* **metadata:** expose widget information ([#3145](https://github.com/algolia/react-instantsearch/issues/3145)) ([46cddf8](https://github.com/algolia/react-instantsearch/commit/46cddf8fcc0291beaa34b04da7aaaa7f2566e52e)) - - - -# [6.13.0](https://github.com/algolia/react-instantsearch/compare/v6.12.1...v6.13.0) (2021-10-19) - -This is the initial release of the experimental **React InstantSearch Hooks** package. Check out the [**Getting Started**](https://github.com/algolia/react-instantsearch/blob/e8d72cb1c7c45300ef7c273f1f163beb6dc57622/packages/react-instantsearch-hooks/README.md#getting-started) guide. - -### Bug Fixes - -- **core:** accept objects for `hitComponent` ([#3087](https://github.com/algolia/react-instantsearch/issues/3087)) ([4ae23d4](https://github.com/algolia/react-instantsearch/commit/4ae23d432a01ccbefff1fcdc865120aeca4d3efc)) - -### Features - -- **hooks:** add InstantSearch and Index components ([#3133](https://github.com/algolia/react-instantsearch/issues/3133)) ([8e3370d](https://github.com/algolia/react-instantsearch/commit/8e3370ddb7d5e42b8b9a5ff6a1e4255490de7dbe)) -- **hooks:** bootstrap Core package ([#3132](https://github.com/algolia/react-instantsearch/issues/3132)) ([d459e62](https://github.com/algolia/react-instantsearch/commit/d459e62f5cae4c98427ab302531873f5ee23d149)) -- **hooks:** display experimental warning ([#3149](https://github.com/algolia/react-instantsearch/issues/3149)) ([623577c](https://github.com/algolia/react-instantsearch/commit/623577c50cd0c04cd87f719edafdcfa04b5527b6)) -- **hooks:** export types ([#3159](https://github.com/algolia/react-instantsearch/issues/3159)) ([182348b](https://github.com/algolia/react-instantsearch/commit/182348b4a901823a7a41aee5d2b3bdc025cce48f)) -- **hooks:** expose `displayName` on Contexts ([#3168](https://github.com/algolia/react-instantsearch/issues/3168)) ([dafd3c6](https://github.com/algolia/react-instantsearch/commit/dafd3c66d05fbec09ebf907209ac25f55804e6f5)) -- **hooks:** friendly error when using Hooks with Core ([#3150](https://github.com/algolia/react-instantsearch/issues/3150)) ([d547ccf](https://github.com/algolia/react-instantsearch/commit/d547ccf7951299e2f6b1771e02fce052696ff65a)) -- **hooks:** introduce `useConnector()` ([#3137](https://github.com/algolia/react-instantsearch/issues/3137)) ([53e8afd](https://github.com/algolia/react-instantsearch/commit/53e8afd093b9950351467a16b82d528207ac34d2)) -- **hooks:** introduce `useHits()` ([#3147](https://github.com/algolia/react-instantsearch/issues/3147)) ([cc25cff](https://github.com/algolia/react-instantsearch/commit/cc25cff06e5ec216cba40fb8261372bc327001b6)) -- **hooks:** introduce `useRefinementList()` ([#3152](https://github.com/algolia/react-instantsearch/issues/3152)) ([0385cd9](https://github.com/algolia/react-instantsearch/commit/0385cd971635a8423ad687fab451d0778358389e)) -- **hooks:** introduce `useSearchBox()` ([#3146](https://github.com/algolia/react-instantsearch/issues/3146)) ([0d2c7f9](https://github.com/algolia/react-instantsearch/commit/0d2c7f9bd25b88cf725a1babd3b228ac804644c7)) -- **hooks:** trigger single network request on load ([#3167](https://github.com/algolia/react-instantsearch/issues/3167)) ([ff1ea49](https://github.com/algolia/react-instantsearch/commit/ff1ea49079a7800fd61ba99ceeb74b9f513eb99d)) -- **hooks:** type `useConnector()` return as render state ([#3169](https://github.com/algolia/react-instantsearch/issues/3169)) ([a801468](https://github.com/algolia/react-instantsearch/commit/a80146860164a092d2c90ee0aa4fcef88d5b675f)) -- **hooks:** update GitHub bug reports link ([#3157](https://github.com/algolia/react-instantsearch/issues/3157)) ([568b5c8](https://github.com/algolia/react-instantsearch/commit/568b5c83849a3927417907706656c3835163f216)) - - - -## [6.12.1](https://github.com/algolia/react-instantsearch/compare/v6.12.0...v6.12.1) (2021-08-02) - - -### Bug Fixes - -* **server side rendering:** return a value from mock currentRefinement/metadata ([#3078](https://github.com/algolia/react-instantsearch/issues/3078)) ([09f802b](https://github.com/algolia/react-instantsearch/commit/09f802b)) - - - -# [6.12.0](https://github.com/algolia/react-instantsearch/compare/v6.11.2...v6.12.0) (2021-07-06) - - -### Bug Fixes - -* **HitsPerPage:** Adds id prop to HitsPerPage, Select components ([#3072](https://github.com/algolia/react-instantsearch/issues/3072)) ([bc75d75](https://github.com/algolia/react-instantsearch/commit/bc75d75)) -* **MenuSelect:** Adds id prop to MenuSelect ([#3073](https://github.com/algolia/react-instantsearch/issues/3073)) ([fddaaef](https://github.com/algolia/react-instantsearch/commit/fddaaef)) -* **SearchBox:** Adds inputId prop to SearchBox ([#3074](https://github.com/algolia/react-instantsearch/issues/3074)) ([a05f6a4](https://github.com/algolia/react-instantsearch/commit/a05f6a4)) -* **SortBy:** Adds `id` prop to `SortBy`, `Select` components ([#3068](https://github.com/algolia/react-instantsearch/issues/3068)) ([1f2797f](https://github.com/algolia/react-instantsearch/commit/1f2797f)) - - -### Features - -* **DynamicWidgets:** add implementation ([#3056](https://github.com/algolia/react-instantsearch/issues/3056)) ([691ef87](https://github.com/algolia/react-instantsearch/commit/691ef87)) -* **facets:** add a new option "facetOrdering" to Menu, RefinementList & HierarchicalMenu ([#3067](https://github.com/algolia/react-instantsearch/issues/3067)) ([731d9ba](https://github.com/algolia/react-instantsearch/commit/731d9ba)) - - - -## [6.11.2](https://github.com/algolia/react-instantsearch/compare/v6.11.1...v6.11.2) (2021-06-28) - - -### Bug Fixes - -* **maps:** leave zoom in place if the bounds did not change ([#3050](https://github.com/algolia/react-instantsearch/issues/3050)) ([c430598](https://github.com/algolia/react-instantsearch/commit/c430598)) - - - -## [6.11.1](https://github.com/algolia/react-instantsearch/compare/v6.11.0...v6.11.1) (2021-06-09) - - -### Bug Fixes - -* **RefinementList:** prevent searchable component to refine on empty list ([#3059](https://github.com/algolia/react-instantsearch/issues/3059)) ([04f3644](https://github.com/algolia/react-instantsearch/commit/04f3644)) - - - -# [6.11.0](https://github.com/algolia/react-instantsearch/compare/v6.10.3...v6.11.0) (2021-05-04) - - -### Features - -* **connectNumericMenu:** add support for floating point values ([#3047](https://github.com/algolia/react-instantsearch/issues/3047)) ([091bf57](https://github.com/algolia/react-instantsearch/commit/091bf57)) - - - -## [6.10.3](https://github.com/algolia/react-instantsearch/compare/v6.10.2...v6.10.3) (2021-03-03) - - -### Bug Fixes - -* **RelevantSort:** Rename `SmartSort` widget to `RelevantSort` ([#3026](https://github.com/algolia/react-instantsearch/issues/3026)) ([47d11bf](https://github.com/algolia/react-instantsearch/commit/47d11bf)) - - - -## [6.10.2](https://github.com/algolia/react-instantsearch/compare/v6.10.1...v6.10.2) (2021-03-03) - - -### Bug Fixes - -* **infiniteHits:** fix stale hits issue ([#3021](https://github.com/algolia/react-instantsearch/issues/3021)) ([a9a29c7](https://github.com/algolia/react-instantsearch/commit/a9a29c7)) - - - -## [6.10.1](https://github.com/algolia/react-instantsearch/compare/v6.10.0...v6.10.1) (2021-03-02) - - -### Bug Fixes - -* **SmartSort:** make `textComponent` and `buttonTextComponent` optional ([#3014](https://github.com/algolia/react-instantsearch/issues/3014)) ([682ee6d](https://github.com/algolia/react-instantsearch/commit/682ee6d)) - - - -# [6.10.0](https://github.com/algolia/react-instantsearch/compare/v6.9.0...v6.10.0) (2021-02-23) - - -### Bug Fixes - -* **infiniteHits:** do not cache the cached hits ([#3011](https://github.com/algolia/react-instantsearch/issues/3011)) ([b56f5f7](https://github.com/algolia/react-instantsearch/commit/b56f5f7)) - - -### Features - -* **smartSort:** add widget ([#3009](https://github.com/algolia/react-instantsearch/issues/3009)) ([4cc8412](https://github.com/algolia/react-instantsearch/commit/4cc8412)), closes [#3010](https://github.com/algolia/react-instantsearch/issues/3010) - - - -# [6.9.0](https://github.com/algolia/react-instantsearch/compare/v6.8.3...v6.9.0) (2021-02-03) - - -### Features - -* **answers:** add `EXPERIMENTAL_Answers` widget ([#2996](https://github.com/algolia/react-instantsearch/issues/2996)) ([55e4191](https://github.com/algolia/react-instantsearch/commit/55e4191)), closes [#3005](https://github.com/algolia/react-instantsearch/issues/3005) - - - -## [6.8.3](https://github.com/algolia/react-instantsearch/compare/v6.8.2...v6.8.3) (2021-01-22) - - -### Bug Fixes - -* upgrade prop-types dependency to 15.6+ ([#3003](https://github.com/algolia/react-instantsearch/issues/3003)) ([fc03496](https://github.com/algolia/react-instantsearch/commit/fc03496)) - - - -## [6.8.2](https://github.com/algolia/react-instantsearch/compare/v6.8.1...v6.8.2) (2020-10-21) - - -### Bug Fixes - -* **ssr:** provide metadata default value ([0a2f34c](https://github.com/algolia/react-instantsearch/commit/0a2f34c)) - - - -## [6.8.1](https://github.com/algolia/react-instantsearch/compare/v6.8.0...v6.8.1) (2020-10-14) - - -### Bug Fixes - -* **ssr:** hydrate metadata with a value ([9249c19](https://github.com/algolia/react-instantsearch/commit/9249c19)) - - - -# [6.8.0](https://github.com/algolia/react-instantsearch/compare/v6.7.0...v6.8.0) (2020-10-14) - - -### Bug Fixes - -* **ssr:** make sure metadata is available on initial render ([#2973](https://github.com/algolia/react-instantsearch/issues/2973)) ([be43b65](https://github.com/algolia/react-instantsearch/commit/be43b65)), closes [#2972](https://github.com/algolia/react-instantsearch/issues/2972) -* add missing dependencies ([#2975](https://github.com/algolia/react-instantsearch/issues/2975)) ([22ecb3c](https://github.com/algolia/react-instantsearch/commit/22ecb3c)) -* **ConfigureRelatedItems:** support nested attributes ([#2967](https://github.com/algolia/react-instantsearch/issues/2967)) ([86dfe86](https://github.com/algolia/react-instantsearch/commit/86dfe86)) -* **ssr:** allow "params" to be optional in custom clients ([#2961](https://github.com/algolia/react-instantsearch/issues/2961)) ([c3e3d2e](https://github.com/algolia/react-instantsearch/commit/c3e3d2e)), closes [#2958](https://github.com/algolia/react-instantsearch/issues/2958) - - - -# [6.7.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.7.0) (2020-07-20) - - -### Bug Fixes - -* **core:** appending successful index search results by returning new object reference ([#2953](https://github.com/algolia/react-instantsearch/issues/2953)) ([0a711a7](https://github.com/algolia/react-instantsearch/commit/0a711a7)) -* **ssr:** remove second instance of "query" in the response "params" for SSR ([#2945](https://github.com/algolia/react-instantsearch/issues/2945)) ([bf837c5](https://github.com/algolia/react-instantsearch/commit/bf837c5)), closes [#2941](https://github.com/algolia/react-instantsearch/issues/2941) - - -### Features - -* **infinite-hits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.6.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.6.0) (2020-06-15) - - -### Features - -* **infiniteHits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.5.0](https://github.com/algolia/react-instantsearch/compare/v6.4.0...v6.5.0) (2020-05-18) - - -### Bug Fixes - -* **connectQueryRules:** fix crash when using connectQueryRules with multiple indexes ([#2903](https://github.com/algolia/react-instantsearch/issues/2903)) ([c66d612](https://github.com/algolia/react-instantsearch/commit/c66d612)) -* **core:** fix maximum call stack size exceeded ([#2926](https://github.com/algolia/react-instantsearch/issues/2926)) ([7e883df](https://github.com/algolia/react-instantsearch/commit/7e883df)) - - -### Features - -* **SearchBox:** provide input element ref ([#2913](https://github.com/algolia/react-instantsearch/issues/2913)) ([b41bff2](https://github.com/algolia/react-instantsearch/commit/b41bff2)) - - - -# [6.4.0](https://github.com/algolia/react-instantsearch/compare/v6.3.0...v6.4.0) (2020-03-18) - - -### Bug Fixes - -* **deps:** fix "too much recursion" error with circular deps ([#2899](https://github.com/algolia/react-instantsearch/issues/2899)) ([c5f27a1](https://github.com/algolia/react-instantsearch/commit/c5f27a1)) - - - -# [6.3.0](https://github.com/algolia/react-instantsearch/compare/v6.2.0...v6.3.0) (2020-01-30) - - -### Features - -* **algoliasearch:** add support for algoliasearch v4 ([#2890](https://github.com/algolia/react-instantsearch/issues/2890)) ([c6c7382](https://github.com/algolia/react-instantsearch/commit/c6c7382)) - - - -# [6.2.0](https://github.com/algolia/react-instantsearch/compare/v6.1.0...v6.2.0) (2020-01-20) - - -### Bug Fixes - -* **deps:** update dependency algoliasearch to v3.35.1 ([#2802](https://github.com/algolia/react-instantsearch/issues/2802)) ([cfb91f0](https://github.com/algolia/react-instantsearch/commit/cfb91f0)) -* **widgets:** rename `ExperimentalConfigureRelatedItems` compon… ([#2891](https://github.com/algolia/react-instantsearch/issues/2891)) ([b910df2](https://github.com/algolia/react-instantsearch/commit/b910df2)) - - -### Features - -* **insights:** add getInsightsAnonymousUserToken helper ([#2887](https://github.com/algolia/react-instantsearch/issues/2887)) ([b5fe4f7](https://github.com/algolia/react-instantsearch/commit/b5fe4f7)) -* **widgets:** introduce `ConfigureRelatedItems` as experimental ([#2880](https://github.com/algolia/react-instantsearch/issues/2880)) ([923cd43](https://github.com/algolia/react-instantsearch/commit/923cd43)) - - - -# [6.1.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0...v6.1.0) (2019-12-17) - - -### Bug Fixes - -* **connectNumericMenu:** support numeric refinement 0 ([#2882](https://github.com/algolia/react-instantsearch/issues/2882)) ([30bd9fd](https://github.com/algolia/react-instantsearch/commit/30bd9fd)) -* **deps:** update dependency next to v9.1.1 ([9d49d33](https://github.com/algolia/react-instantsearch/commit/9d49d33)) -* **helper:** rely on stable version of algoliasearch-helper ([#2871](https://github.com/algolia/react-instantsearch/issues/2871)) ([e3531a1](https://github.com/algolia/react-instantsearch/commit/e3531a1)) - - -### Features - -* **insights:** show an error when 'clickAnalytics: true' is missing. ([#2877](https://github.com/algolia/react-instantsearch/issues/2877)) ([621656a](https://github.com/algolia/react-instantsearch/commit/621656a)) -* **voice:** add additionalQueryParameters ([#2366](https://github.com/algolia/react-instantsearch/issues/2366)) ([3a45b2c](https://github.com/algolia/react-instantsearch/commit/3a45b2c)) - - - -# [6.0.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.2...v6.0.0) (2019-10-28) - - - -# [6.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.1...v6.0.0-beta.2) (2019-10-25) - - -### Bug Fixes - -* serialize cache value on hydrate ([#2862](https://github.com/algolia/react-instantsearch/issues/2862)) ([3319665](https://github.com/algolia/react-instantsearch/commit/3319665)), closes [#2828](https://github.com/algolia/react-instantsearch/issues/2828) - - - -# [6.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.1) (2019-10-18) - - -### Bug Fixes - -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **deps:** update dependency antd to v3.19.3 ([#2530](https://github.com/algolia/react-instantsearch/issues/2530)) ([73636c5](https://github.com/algolia/react-instantsearch/commit/73636c5)) -* **deps:** update dependency antd to v3.19.4 ([#2559](https://github.com/algolia/react-instantsearch/issues/2559)) ([c3e8267](https://github.com/algolia/react-instantsearch/commit/c3e8267)) -* **deps:** update dependency antd to v3.19.5 ([#2560](https://github.com/algolia/react-instantsearch/issues/2560)) ([72efd31](https://github.com/algolia/react-instantsearch/commit/72efd31)) -* **deps:** update dependency antd to v3.19.6 ([#2564](https://github.com/algolia/react-instantsearch/issues/2564)) ([654f986](https://github.com/algolia/react-instantsearch/commit/654f986)) -* **deps:** update dependency antd to v3.19.7 ([#2573](https://github.com/algolia/react-instantsearch/issues/2573)) ([7e963ad](https://github.com/algolia/react-instantsearch/commit/7e963ad)) -* **deps:** update dependency antd to v3.19.8 ([#2584](https://github.com/algolia/react-instantsearch/issues/2584)) ([34dd9b2](https://github.com/algolia/react-instantsearch/commit/34dd9b2)) -* **deps:** update dependency antd to v3.20.0 ([#2611](https://github.com/algolia/react-instantsearch/issues/2611)) ([b976c67](https://github.com/algolia/react-instantsearch/commit/b976c67)) -* **deps:** update dependency antd to v3.20.1 ([#2635](https://github.com/algolia/react-instantsearch/issues/2635)) ([792ad9c](https://github.com/algolia/react-instantsearch/commit/792ad9c)) -* **deps:** update dependency antd to v3.20.2 ([#2655](https://github.com/algolia/react-instantsearch/issues/2655)) ([301c2d8](https://github.com/algolia/react-instantsearch/commit/301c2d8)) -* **deps:** update dependency antd to v3.20.3 ([#2658](https://github.com/algolia/react-instantsearch/issues/2658)) ([d078e70](https://github.com/algolia/react-instantsearch/commit/d078e70)) -* **deps:** update dependency antd to v3.20.5 ([#2686](https://github.com/algolia/react-instantsearch/issues/2686)) ([42ef821](https://github.com/algolia/react-instantsearch/commit/42ef821)) -* **deps:** update dependency antd to v3.20.6 ([#2711](https://github.com/algolia/react-instantsearch/issues/2711)) ([927fbfe](https://github.com/algolia/react-instantsearch/commit/927fbfe)) -* **deps:** update dependency antd to v3.20.7 ([#2712](https://github.com/algolia/react-instantsearch/issues/2712)) ([1830952](https://github.com/algolia/react-instantsearch/commit/1830952)) -* **deps:** update dependency antd to v3.21.1 ([#2736](https://github.com/algolia/react-instantsearch/issues/2736)) ([39a51a6](https://github.com/algolia/react-instantsearch/commit/39a51a6)) -* **deps:** update dependency antd to v3.21.2 ([#2738](https://github.com/algolia/react-instantsearch/issues/2738)) ([a7a998a](https://github.com/algolia/react-instantsearch/commit/a7a998a)) -* **deps:** update dependency antd to v3.21.4 ([#2747](https://github.com/algolia/react-instantsearch/issues/2747)) ([60012be](https://github.com/algolia/react-instantsearch/commit/60012be)) -* **deps:** update dependency antd to v3.22.0 ([#2758](https://github.com/algolia/react-instantsearch/issues/2758)) ([9cda468](https://github.com/algolia/react-instantsearch/commit/9cda468)) -* **deps:** update dependency antd to v3.22.2 ([#2791](https://github.com/algolia/react-instantsearch/issues/2791)) ([ff1f5d9](https://github.com/algolia/react-instantsearch/commit/ff1f5d9)) -* **deps:** update dependency antd to v3.23.2 ([#2814](https://github.com/algolia/react-instantsearch/issues/2814)) ([a190410](https://github.com/algolia/react-instantsearch/commit/a190410)) -* **deps:** update dependency lodash to v4.17.13 ([c4974cf](https://github.com/algolia/react-instantsearch/commit/c4974cf)) -* **deps:** update dependency lodash to v4.17.14 ([#2647](https://github.com/algolia/react-instantsearch/issues/2647)) ([a2d2dd5](https://github.com/algolia/react-instantsearch/commit/a2d2dd5)) -* **deps:** update dependency lodash to v4.17.15 ([#2684](https://github.com/algolia/react-instantsearch/issues/2684)) ([354143f](https://github.com/algolia/react-instantsearch/commit/354143f)) -* **deps:** update dependency next to v9 ([#2638](https://github.com/algolia/react-instantsearch/issues/2638)) ([d22f61d](https://github.com/algolia/react-instantsearch/commit/d22f61d)) -* **deps:** update dependency next to v9.0.1 ([#2652](https://github.com/algolia/react-instantsearch/issues/2652)) ([2c2dab9](https://github.com/algolia/react-instantsearch/commit/2c2dab9)) -* **deps:** update dependency next to v9.0.2 ([#2662](https://github.com/algolia/react-instantsearch/issues/2662)) ([6fa4c5e](https://github.com/algolia/react-instantsearch/commit/6fa4c5e)) -* **deps:** update dependency next to v9.0.3 ([#2724](https://github.com/algolia/react-instantsearch/issues/2724)) ([f51b04b](https://github.com/algolia/react-instantsearch/commit/f51b04b)) -* **deps:** update dependency next to v9.0.4 ([#2767](https://github.com/algolia/react-instantsearch/issues/2767)) ([9af9180](https://github.com/algolia/react-instantsearch/commit/9af9180)) -* **deps:** update dependency next to v9.0.5 ([#2789](https://github.com/algolia/react-instantsearch/issues/2789)) ([0a75f41](https://github.com/algolia/react-instantsearch/commit/0a75f41)) -* **deps:** update dependency qs to v6.8.0 ([#2757](https://github.com/algolia/react-instantsearch/issues/2757)) ([8bffb87](https://github.com/algolia/react-instantsearch/commit/8bffb87)) -* **deps:** update dependency react-compound-slider to v2.1.0 ([#2610](https://github.com/algolia/react-instantsearch/issues/2610)) ([3389ee5](https://github.com/algolia/react-instantsearch/commit/3389ee5)) -* **deps:** update dependency react-compound-slider to v2.2.0 ([#2649](https://github.com/algolia/react-instantsearch/issues/2649)) ([7b81af1](https://github.com/algolia/react-instantsearch/commit/7b81af1)) -* **deps:** update dependency react-native-vector-icons to v6.5.0 ([#2520](https://github.com/algolia/react-instantsearch/issues/2520)) ([5f7f5b6](https://github.com/algolia/react-instantsearch/commit/5f7f5b6)) -* **deps:** update dependency react-native-vector-icons to v6.6.0 ([#2599](https://github.com/algolia/react-instantsearch/issues/2599)) ([b6bb199](https://github.com/algolia/react-instantsearch/commit/b6bb199)) -* **deps:** update dependency react-router-dom to v5.0.1 ([#2506](https://github.com/algolia/react-instantsearch/issues/2506)) ([d762230](https://github.com/algolia/react-instantsearch/commit/d762230)) -* **highlight:** switch to index as key ([#2691](https://github.com/algolia/react-instantsearch/issues/2691)) ([17e75d1](https://github.com/algolia/react-instantsearch/commit/17e75d1)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### chore - -* **release:** 6.0.0-beta.1 ([#2861](https://github.com/algolia/react-instantsearch/issues/2861)) ([cb56ca0](https://github.com/algolia/react-instantsearch/commit/cb56ca0)), closes [#2023](https://github.com/algolia/react-instantsearch/issues/2023) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2203](https://github.com/algolia/react-instantsearch/issues/2203) [#2432](https://github.com/algolia/react-instantsearch/issues/2432) [#2444](https://github.com/algolia/react-instantsearch/issues/2444) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2454](https://github.com/algolia/react-instantsearch/issues/2454) [#2455](https://github.com/algolia/react-instantsearch/issues/2455) [#2459](https://github.com/algolia/react-instantsearch/issues/2459) [#2458](https://github.com/algolia/react-instantsearch/issues/2458) [#2460](https://github.com/algolia/react-instantsearch/issues/2460) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2446](https://github.com/algolia/react-instantsearch/issues/2446) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2466](https://github.com/algolia/react-instantsearch/issues/2466) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2555](https://github.com/algolia/react-instantsearch/issues/2555) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2349](https://github.com/algolia/react-instantsearch/issues/2349) [#2570](https://github.com/algolia/react-instantsearch/issues/2570) [#2462](https://github.com/algolia/react-instantsearch/issues/2462) [#2600](https://github.com/algolia/react-instantsearch/issues/2600) [#2468](https://github.com/algolia/react-instantsearch/issues/2468) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2621](https://github.com/algolia/react-instantsearch/issues/2621) [#2627](https://github.com/algolia/react-instantsearch/issues/2627) [#2644](https://github.com/algolia/react-instantsearch/issues/2644) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2645](https://github.com/algolia/react-instantsearch/issues/2645) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2643](https://github.com/algolia/react-instantsearch/issues/2643) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2722](https://github.com/algolia/react-instantsearch/issues/2722) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2701](https://github.com/algolia/react-instantsearch/issues/2701) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2530](https://github.com/algolia/react-instantsearch/issues/2530) [#2559](https://github.com/algolia/react-instantsearch/issues/2559) [#2560](https://github.com/algolia/react-instantsearch/issues/2560) [#2564](https://github.com/algolia/react-instantsearch/issues/2564) [#2573](https://github.com/algolia/react-instantsearch/issues/2573) [#2584](https://github.com/algolia/react-instantsearch/issues/2584) [#2611](https://github.com/algolia/react-instantsearch/issues/2611) [#2635](https://github.com/algolia/react-instantsearch/issues/2635) [#2655](https://github.com/algolia/react-instantsearch/issues/2655) [#2658](https://github.com/algolia/react-instantsearch/issues/2658) [#2686](https://github.com/algolia/react-instantsearch/issues/2686) [#2711](https://github.com/algolia/react-instantsearch/issues/2711) [#2712](https://github.com/algolia/react-instantsearch/issues/2712) [#2736](https://github.com/algolia/react-instantsearch/issues/2736) [#2738](https://github.com/algolia/react-instantsearch/issues/2738) [#2747](https://github.com/algolia/react-instantsearch/issues/2747) [#2758](https://github.com/algolia/react-instantsearch/issues/2758) [#2647](https://github.com/algolia/react-instantsearch/issues/2647) [#2684](https://github.com/algolia/react-instantsearch/issues/2684) [#2638](https://github.com/algolia/react-instantsearch/issues/2638) [#2652](https://github.com/algolia/react-instantsearch/issues/2652) [#2662](https://github.com/algolia/react-instantsearch/issues/2662) [#2724](https://github.com/algolia/react-instantsearch/issues/2724) [#2767](https://github.com/algolia/react-instantsearch/issues/2767) [#2757](https://github.com/algolia/react-instantsearch/issues/2757) [#2610](https://github.com/algolia/react-instantsearch/issues/2610) [#2649](https://github.com/algolia/react-instantsearch/issues/2649) [#2520](https://github.com/algolia/react-instantsearch/issues/2520) [#2599](https://github.com/algolia/react-instantsearch/issues/2599) [#2506](https://github.com/algolia/react-instantsearch/issues/2506) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2688](https://github.com/algolia/react-instantsearch/issues/2688) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2535](https://github.com/algolia/react-instantsearch/issues/2535) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2834](https://github.com/algolia/react-instantsearch/issues/2834) [#2845](https://github.com/algolia/react-instantsearch/issues/2845) [#2842](https://github.com/algolia/react-instantsearch/issues/2842) [#2852](https://github.com/algolia/react-instantsearch/issues/2852) [#2853](https://github.com/algolia/react-instantsearch/issues/2853) - - -### BREAKING CHANGES - -* **release:** translation will render default value if passed undefined as value - -* chore(lodash): remove imports - -* fix(translation): allow undefined value to be passed on purpose -* **release:** no longer do we allow paths like `attribute[5].something`, or other indexed forms, only `.` is allowed as special key. - -All existing tests still pass, and we never documented you could use `lodash.get` patterns other than `.`. - -* feat(get): accept array & bracked-separated string - -moved to utils at the same time - -* fix typo - -* feedback: test for undefined behaviour - -* chore(size): update expectation - -this will go down afterwards, but for now there's some more duplication - - - -# [6.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.0) (2019-08-21) - -[Migration guide](MIGRATION.md) - -### Bug Fixes - -* **react 17 compat:** upgrade RangeInput lifecycle ([#2289](https://github.com/algolia/react-instantsearch/issues/2289)) ([110b1af](https://github.com/algolia/react-instantsearch/commit/110b1af)) -* **react 17 compat:** upgrade RangeSlider lifecycle ([#2290](https://github.com/algolia/react-instantsearch/issues/2290)) ([69a7f53](https://github.com/algolia/react-instantsearch/commit/69a7f53)) -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **core:** searchState can be non-Object object ([#2722](https://github.com/algolia/react-instantsearch/issues/2722)) ([dea493c](https://github.com/algolia/react-instantsearch/commit/dea493c)), closes [#2568](https://github.com/algolia/react-instantsearch/issues/2568) -* **createConnector:** new React life cycles ([#2357](https://github.com/algolia/react-instantsearch/issues/2357)) ([fc10640](https://github.com/algolia/react-instantsearch/commit/fc10640)) -* **createInstantSearchManager:** do not trigger search on index update ([#2552](https://github.com/algolia/react-instantsearch/issues/2552)) ([e209362](https://github.com/algolia/react-instantsearch/commit/e209362)) -* **geo:** check for undefined in isEqual ([#2643](https://github.com/algolia/react-instantsearch/issues/2643)) ([a544231](https://github.com/algolia/react-instantsearch/commit/a544231)), closes [#2467](https://github.com/algolia/react-instantsearch/issues/2467) -* **geo:** remove lifecycle compat ([#2644](https://github.com/algolia/react-instantsearch/issues/2644)) ([2b2b898](https://github.com/algolia/react-instantsearch/commit/2b2b898)), closes [#2626](https://github.com/algolia/react-instantsearch/issues/2626) -* **highlight:** switch to index as key ([#2690](https://github.com/algolia/react-instantsearch/issues/2690)) ([51de682](https://github.com/algolia/react-instantsearch/commit/51de682)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **peerDependencies:** update React ([#2626](https://github.com/algolia/react-instantsearch/issues/2626)) ([6ccad49](https://github.com/algolia/react-instantsearch/commit/6ccad49)) -* **ssr:** avoid duplicate serializing ([#2726](https://github.com/algolia/react-instantsearch/issues/2726)) ([c768b1a](https://github.com/algolia/react-instantsearch/commit/c768b1a)) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### Code Refactoring - -* **lodash:** get ([#2461](https://github.com/algolia/react-instantsearch/issues/2461)) ([527b879](https://github.com/algolia/react-instantsearch/commit/527b879)) -* **lodash:** has ([#2434](https://github.com/algolia/react-instantsearch/issues/2434)) ([75a4a15](https://github.com/algolia/react-instantsearch/commit/75a4a15)) -* **lodash:** has been fully removed - -### Features - -* **autocomplete:** add queryID & position to provided hits ([#2687](https://github.com/algolia/react-instantsearch/issues/2687)) ([e453dab](https://github.com/algolia/react-instantsearch/commit/e453dab)) -* **client:** remove algoliaClient, appId & apiKey ([#2338](https://github.com/algolia/react-instantsearch/issues/2338)) ([b84a0b5](https://github.com/algolia/react-instantsearch/commit/b84a0b5)) (use searchClient exclusively now) -* **context:** migrate to new React context ([#2178](https://github.com/algolia/react-instantsearch/issues/2178)) ([0a1abea](https://github.com/algolia/react-instantsearch/commit/0a1abea)), closes [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) -* **ssr:** update the SSR API ([#2555](https://github.com/algolia/react-instantsearch/issues/2555)) ([925bdb8](https://github.com/algolia/react-instantsearch/commit/925bdb8)), closes [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) - - -### BREAKING CHANGES - -* **searchClient:** argument is the only option now. - -Previously there were three options to pass a search client: searchClient, appId & apiKey, algoliaClient. The latter two have been removed, and now only `searchClient` is accepted. This searchClient is an instance of the `algoliasearch` module: - -```js -import algoliasearch from 'algoliasearch/lite'; - -const searchClient = algoliasearch( - 'myAppId', - 'myApiKey', - { _useRequestCache: true } -); - -// ... - -``` - -If you were relying on duplicate requests not being fired when using appId & apiKey before, you need to enable the `_useRequestCache` option now. - -* **SSR:** imports have changed - -In the server, you now directly import `findResultsState`, which now requires a `searchClient` in the second argument. - -In the App, you now use a regular `InstantSearch` component. - -* **Index & InstantSearch:** Remove `root` DOM element - -These elements now are pure containers for their children, and don't add a `div` to the DOM anymore. If you were relying on those for styling, wrap the `InstantSearch` and `Index` element with a `div` with an appropriate class. - -* **Index & InstantSearch:** Remove support for `root` prop - -Since these two arguments now no longer wrap their children in an element, they no longer accept a `root` prop. - -* **Highlight:** some paths will no longer be accepted - -We only accept paths separated with a dot or bracket now, like before. It's possible that a different type of path worked undocumented, but no longer does. - -* **algoliasearch-helper:** updating to the next major version - -This library is mostly internal, but it has had a major refactor (including removing lodash). This has no impact, unless you are dealing with it using `createConnector`. See the [migration guide](https://github.com/algolia/algoliasearch-helper-js/blob/next/documentation-src/metalsmith/content/upgrade.md) for the v3 of algoliasearch-helper for more information. - -# [5.7.0](https://github.com/algolia/react-instantsearch/compare/v5.6.0...v5.7.0) (2019-06-04) - - -### Bug Fixes - -* **highlight:** allow array as "attribute" ([#2474](https://github.com/algolia/react-instantsearch/issues/2474)) ([9dc829a](https://github.com/algolia/react-instantsearch/commit/9dc829a)), closes [#2461](https://github.com/algolia/react-instantsearch/issues/2461) -* **indexUtils:** allow index with dots in it ([#2350](https://github.com/algolia/react-instantsearch/issues/2350)) ([f91906f](https://github.com/algolia/react-instantsearch/commit/f91906f)) - - -### Features - -* **voiceSearch:** add voice search widget ([#2316](https://github.com/algolia/react-instantsearch/issues/2316)) ([0e3b124](https://github.com/algolia/react-instantsearch/commit/0e3b124)) - - - -# [5.6.0](https://github.com/algolia/react-instantsearch/compare/v5.5.0...v5.6.0) (2019-05-15) - - -### Bug Fixes - -* **connectQueryRules:** avoid to throw an error with undefined values ([#2436](https://github.com/algolia/react-instantsearch/issues/2436)) ([1e18287](https://github.com/algolia/react-instantsearch/commit/1e18287)) - - -### Features - -* **infiniteHits:** add previous button ([#2296](https://github.com/algolia/react-instantsearch/issues/2296)) ([010a69a](https://github.com/algolia/react-instantsearch/commit/010a69a)) - - - -# [5.5.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0...v5.5.0) (2019-04-23) - - -### Bug Fixes - -* **createInstantSearch:** change the User-Agent to use the new specs ([#2209](https://github.com/algolia/react-instantsearch/issues/2209)) ([642ba0b](https://github.com/algolia/react-instantsearch/commit/642ba0b)) - - -### Features - -* **DOMMaps:** expose withGoogleMaps HOC [PART-1] ([#2000](https://github.com/algolia/react-instantsearch/issues/2000)) ([2ae1dea](https://github.com/algolia/react-instantsearch/commit/2ae1dea)) -* **queryRules:** add Query Rules features ([#2286](https://github.com/algolia/react-instantsearch/issues/2286)) ([3ae9c01](https://github.com/algolia/react-instantsearch/commit/3ae9c01)) -* **insights:** add insights features ([#2215](https://github.com/algolia/react-instantsearch/pull/2215)) ([961e7a7](https://github.com/algolia/react-instantsearch/commit/961e7a7)) - - - -# [5.4.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.1...v5.4.0) (2019-02-05) - - -### Bug Fixes - -* **DOMMaps:** set React & React DOM as peer deps ([#1922](https://github.com/algolia/react-instantsearch/issues/1922)) ([2f2cefd](https://github.com/algolia/react-instantsearch/commit/2f2cefd)) - - - -# [5.4.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.0...v5.4.0-beta.1) (2019-01-10) - - -### Bug Fixes - -* **deps:** sync algoliasearch version ([#1879](https://github.com/algolia/react-instantsearch/issues/1879)) ([40f9c26](https://github.com/algolia/react-instantsearch/commit/40f9c26)) -* **maps:** use stable version for peer deps ([#1887](https://github.com/algolia/react-instantsearch/issues/1887)) ([9055167](https://github.com/algolia/react-instantsearch/commit/9055167)) - - - -# [5.4.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.3.2...v5.4.0-beta.0) (2019-01-08) - - -### Features - -* **Index:** introduce `indexId` prop ([#1833](https://github.com/algolia/react-instantsearch/issues/1833)) ([ec9e0fb](https://github.com/algolia/react-instantsearch/commit/ec9e0fb)) - - - - -## [5.3.2](https://github.com/algolia/react-instantsearch/compare/v5.3.1...v5.3.2) (2018-10-30) - - -### Bug Fixes - -* **sffv:** clamp maxFacetHits to the allowed range ([#1696](https://github.com/algolia/react-instantsearch/issues/1696)) ([83ce245](https://github.com/algolia/react-instantsearch/commit/83ce245)) - - - - -## [5.3.1](https://github.com/algolia/react-instantsearch/compare/v5.3.0...v5.3.1) (2018-09-26) - - -### Bug Fixes - -* **connector:** ensure canRefine is computed on the transformed items ([#1568](https://github.com/algolia/react-instantsearch/pull/1568)) ([c95384f](https://github.com/algolia/react-instantsearch/commit/c95384f)) -* **toggle:** ensure facet is present ([#1613](https://github.com/algolia/react-instantsearch/issues/1613)) ([e914ff6](https://github.com/algolia/react-instantsearch/commit/e914ff6)) - - - - -# [5.3.0](https://github.com/algolia/react-instantsearch/compare/v5.2.3...v5.3.0) (2018-09-24) - - -### Bug Fixes - -* **SSR:** bind getSearchParmaters to the component instance ([f34cb3d](https://github.com/algolia/react-instantsearch/commit/f34cb3d)) -* **GoogleMapsLoader:** pick google maps version ([#1540](https://github.com/algolia/react-instantsearch/issues/1540)) ([b14efcf](https://github.com/algolia/react-instantsearch/commit/b14efcf)) - - -### Features - -* **connectToggleRefinement:** implement canRefine & count ([#1588](https://github.com/algolia/react-instantsearch/issues/1588)) ([40672dd](https://github.com/algolia/react-instantsearch/commit/40672dd)) - - - - -## [5.2.3](https://github.com/algolia/react-instantsearch/compare/v5.2.2...v5.2.3) (2018-08-16) - - -### Bug Fixes - -* Allow object as type for Root (closes [#1446](https://github.com/algolia/react-instantsearch/issues/1446)) ([#1461](https://github.com/algolia/react-instantsearch/issues/1461)) ([7c2317b](https://github.com/algolia/react-instantsearch/commit/7c2317b)) -* **List:** render children list only when required ([#1472](https://github.com/algolia/react-instantsearch/issues/1472)) ([9eb2cbb](https://github.com/algolia/react-instantsearch/commit/9eb2cbb)), closes [#1459](https://github.com/algolia/react-instantsearch/issues/1459) - - - - -## [5.2.2](https://github.com/algolia/react-instantsearch/compare/v5.2.1...v5.2.2) (2018-07-16) - - -Publish the previous version on `stable`. - - - - -## [5.2.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0...v5.2.1) (2018-07-16) - - -### Bug Fixes - -* **GoogleMapsLoader:** inline the import to scriptjs ([#1427](https://github.com/algolia/react-instantsearch/issues/1427)) ([8019416](https://github.com/algolia/react-instantsearch/commit/8019416)) - - - - -# [5.2.0](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.2...v5.2.0) (2018-07-04) - - -### Bug Fixes - -* **translatable:** avoid create a new function on every render ([#1383](https://github.com/algolia/react-instantsearch/issues/1383)) ([1285b3b](https://github.com/algolia/react-instantsearch/commit/1285b3b)) - - -### Features - -* **core:** export translatable ([#1351](https://github.com/algolia/react-instantsearch/issues/1351)) ([6d5a89d](https://github.com/algolia/react-instantsearch/commit/6d5a89d)) -* **maps:** add connector & widget ([#1171](https://github.com/algolia/react-instantsearch/issues/1171)) ([16e288a](https://github.com/algolia/react-instantsearch/commit/16e288a)) - - - - -# [5.2.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.1...v5.2.0-beta.2) (2018-06-19) - - -### Features - -* export highlight tags from DOM / native ([#1342](https://github.com/algolia/react-instantsearch/issues/1342)) ([28a699e](https://github.com/algolia/react-instantsearch/commit/28a699e)) -* **createInstantSearch:** enable _useRequestCache ([#1346](https://github.com/algolia/react-instantsearch/issues/1346)) ([f772600](https://github.com/algolia/react-instantsearch/commit/f772600)) -* **dom:** export create class name ([#1348](https://github.com/algolia/react-instantsearch/issues/1348)) ([9017468](https://github.com/algolia/react-instantsearch/commit/9017468)) - - - - -# [5.2.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.0...v5.2.0-beta.1) (2018-06-04) - - -### Bug Fixes - -* **dom:** publish server file ([#1305](https://github.com/algolia/react-instantsearch/issues/1305)) ([bd79693](https://github.com/algolia/react-instantsearch/commit/bd79693)) - - - - -# [5.2.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.1.0...v5.2.0-beta.0) (2018-06-04) - - -This new version introduce a complete revamp of the package structure, but it should be completely transparent for the users. - -If you have any troubles with this version please open a issue on [Github](https://github.com/algolia/react-instantsearch/issues/new), thanks! - - - - -# [5.1.0](https://github.com/algolia/react-instantsearch/compare/v5.0.3...v5.1.0) (2018-05-28) - - -### Bug Fixes - -* **connectInfiniteHits:** always set a value for previous page ([#1195](https://github.com/algolia/react-instantsearch/issues/1195)) ([4c218d5](https://github.com/algolia/react-instantsearch/commit/4c218d5)) -* **indexUtils:** avoid throw an error on cleanUp multi indices ([#1265](https://github.com/algolia/react-instantsearch/issues/1265)) ([12f5ace](https://github.com/algolia/react-instantsearch/commit/12f5ace)) - - -### Features - -* **searchClient:** Add support for custom Search Clients ([#1216](https://github.com/algolia/react-instantsearch/issues/1216)) ([174cc28](https://github.com/algolia/react-instantsearch/commit/174cc28)) - - - - -## [5.0.3](https://github.com/algolia/react-instantsearch/compare/v5.0.2...v5.0.3) (2018-04-03) - - -### Bug Fixes - -* revert dependencies as devDependencies ([#1135](https://github.com/algolia/react-instantsearch/issues/1135)) ([6b627bb](https://github.com/algolia/react-instantsearch/commit/6b627bb)) - - - - -## [5.0.2](https://github.com/algolia/react-instantsearch/compare/v5.0.1...v5.0.2) (2018-04-03) - - -### Bug Fixes - -* use lodash version of unsupported Array.{fill, find} ([#1118](https://github.com/algolia/react-instantsearch/issues/1118)) ([ea7bf42](https://github.com/algolia/react-instantsearch/commit/ea7bf42)) - - - - -## [5.0.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0...v5.0.1) (2018-03-12) - - -### Bug Fixes - -* **connectInfiniteHits:** always provide an array for hits ([#1064](https://github.com/algolia/react-instantsearch/issues/1064)) ([c75e38b](https://github.com/algolia/react-instantsearch/commit/c75e38b)) - - - - -# [5.0.0](https://github.com/algolia/react-instantsearch/compare/v4.5.2...v5.0.0) (2018-03-06) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.5.2](https://github.com/algolia/react-instantsearch/compare/v4.5.1...v4.5.2) (2018-03-06) - - -### Bug Fixes - -* **connectRange:** update default refinement propTypes ([#978](https://github.com/algolia/react-instantsearch/issues/978)) ([c065fb1](https://github.com/algolia/react-instantsearch/commit/c065fb1)) -* **IndexUtils:** avoid throw an error when cleanUp multi index ([#1019](https://github.com/algolia/react-instantsearch/issues/1019)) ([865a3c3](https://github.com/algolia/react-instantsearch/commit/865a3c3)) -* **SearchBox:** avoid to bind click on reset button ([#979](https://github.com/algolia/react-instantsearch/issues/979)) ([ea3063a](https://github.com/algolia/react-instantsearch/commit/ea3063a)) - - - - -# [5.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2018-02-06) - - -Apply features & bug fixes from [v4.5.0](#450-2018-02-06) & [v4.5.1](#451-2018-02-06) on the v5. - -See their CHANGELOG for more details. - - - - -## [4.5.1](https://github.com/algolia/react-instantsearch/compare/v4.5.0...v4.5.1) (2018-02-06) - - -### Bug Fixes - -* **StarRating:** move to 1 based instead of 0 ([#949](https://github.com/algolia/react-instantsearch/issues/949)) ([eb0152d](https://github.com/algolia/react-instantsearch/commit/eb0152d)) - - - - -# [4.5.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v4.5.0) (2018-02-06) - - -### Bug Fixes - -* **connectRange:** use the same behaviour for currentRefinement in getMetadata ([#923](https://github.com/algolia/react-instantsearch/issues/923)) ([08917b6](https://github.com/algolia/react-instantsearch/commit/08917b6)) -* **connectToggle:** use currentRefinement in metadata instead of the label ([#909](https://github.com/algolia/react-instantsearch/issues/909)) ([89cae2b](https://github.com/algolia/react-instantsearch/commit/89cae2b)) -* **StarRatings:** always show the stars below ([#929](https://github.com/algolia/react-instantsearch/issues/929)) ([22bf93a](https://github.com/algolia/react-instantsearch/commit/22bf93a)) - - -### Features - -* **connectStateResults:** expose isSearchStalled ([#933](https://github.com/algolia/react-instantsearch/issues/933)) ([f45ba27](https://github.com/algolia/react-instantsearch/commit/f45ba27)) - - - -# [5.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v5.0.0-beta.0) (2018-01-30) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.4.2](https://github.com/algolia/react-instantsearch/compare/v4.4.1...v4.4.2) (2018-01-24) - - -### Bug Fixes - -* **currentRefinements:** give access to id and index from transformItems for deduplication ([#830](https://github.com/algolia/react-instantsearch/issues/830)) ([316b8f5](https://github.com/algolia/react-instantsearch/commit/316b8f5)) -* pass maxFacetHits to SFFV ([#863](https://github.com/algolia/react-instantsearch/issues/863)) ([de23a46](https://github.com/algolia/react-instantsearch/commit/de23a46)) - - - - -## [4.4.1](https://github.com/algolia/react-instantsearch/compare/v4.4.0...v4.4.1) (2018-01-09) - - -### Bug Fixes - -* **SearchBox**: clear SearchBox without search as you type ([#802](https://github.com/algolia/react-instantsearch/issues/802)) ([c49b2b6](https://github.com/algolia/react-instantsearch/commit/c49b2b6)) -* **connectRange:** check if facet exist before access ([#797](https://github.com/algolia/react-instantsearch/issues/797)) ([6520760](https://github.com/algolia/react-instantsearch/commit/6520760)) -* **stories:** avoid to use linear-background it breaks Argos every time ([#804](https://github.com/algolia/react-instantsearch/issues/804)) ([0beded7](https://github.com/algolia/react-instantsearch/commit/0beded7)) -* **stories:** limit hits per page on Index ([#806](https://github.com/algolia/react-instantsearch/issues/806)) ([6eb14d3](https://github.com/algolia/react-instantsearch/commit/6eb14d3)) - - -### Features - -* **Index:** allow custom root ([#792](https://github.com/algolia/react-instantsearch/issues/792)) ([d793b0a](https://github.com/algolia/react-instantsearch/commit/d793b0a)) - - - - -# [4.4.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0...v4.4.0) (2018-01-03) - - -### Bug Fixes - -* **createInstantSearch:** remove the client from the Snapshot ([#749](https://github.com/algolia/react-instantsearch/issues/749)) ([700d8f4](https://github.com/algolia/react-instantsearch/commit/700d8f4)) -* refresh cache memory leak example ([#784](https://github.com/algolia/react-instantsearch/issues/784)) ([cf228ac](https://github.com/algolia/react-instantsearch/commit/cf228ac)) -* **stories:** rename InstantSearch to `` ([#789](https://github.com/algolia/react-instantsearch/issues/789)) ([05efda5](https://github.com/algolia/react-instantsearch/commit/05efda5)) - - -### Features - -* InstantSearch root props ([#770](https://github.com/algolia/react-instantsearch/issues/770)) ([2d458f8](https://github.com/algolia/react-instantsearch/commit/2d458f8)) - - - - -# [4.3.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0-beta.0...v4.3.0) (2017-12-20) - - -### Bug Fixes - -* reset page with multi index ([#665](https://github.com/algolia/react-instantsearch/issues/665)) ([865b7dc](https://github.com/algolia/react-instantsearch/commit/865b7dc)) -* track all index in the manager ([#660](https://github.com/algolia/react-instantsearch/issues/660)) ([793502b](https://github.com/algolia/react-instantsearch/commit/793502b)) - - -### Features - -* **SearchBox:** provide a loading indicator ([#544](https://github.com/algolia/react-instantsearch/issues/544)) ([189659e](https://github.com/algolia/react-instantsearch/commit/189659e)) -* **Highlight:** support array of strings ([#715](https://github.com/algolia/react-instantsearch/issues/715)) ([8e93c6a](https://github.com/algolia/react-instantsearch/commit/8e93c6a)) - - - - -# [4.3.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.2.0...v4.3.0-beta.0) (2017-11-27) - - -### Bug Fixes - -* **babelrc:** add a key for each env development, production, es ([#547](https://github.com/algolia/react-instantsearch/issues/547)) ([fa9528d](https://github.com/algolia/react-instantsearch/commit/fa9528d)) -* **localizecount:** allow localized string for count in MenuSelect ([#657](https://github.com/algolia/react-instantsearch/issues/657)) ([67ebd34](https://github.com/algolia/react-instantsearch/commit/67ebd34)) -* **react-router-example:** Properly update search query when using browser navigation ([#604](https://github.com/algolia/react-instantsearch/issues/604)) ([9ee6600](https://github.com/algolia/react-instantsearch/commit/9ee6600)) - - -### Features - -* **refreshcache:** add prop refresh to InstantSearch instance ([#619](https://github.com/algolia/react-instantsearch/issues/619)) ([19f6de0](https://github.com/algolia/react-instantsearch/commit/19f6de0)) - - - - -# [4.2.0](https://github.com/algolia/react-instantsearch/compare/v4.1.3...v4.2.0) (2017-11-02) - - -### Bug Fixes - -* **connectRange:** handle boundaries on first call ([9f14dc0](https://github.com/algolia/react-instantsearch/commit/9f14dc0)) -* **connectRange:** use refine instead of cleanUp in metadata ([#526](https://github.com/algolia/react-instantsearch/issues/526)) ([1861235](https://github.com/algolia/react-instantsearch/commit/1861235)) -* **hierarchicaMenu:** allow sorting and using limit ([fe178ed](https://github.com/algolia/react-instantsearch/commit/fe178ed)), closes [#92](https://github.com/algolia/react-instantsearch/issues/92) -* **InfiniteHits:** add disabled style to the LoadMore button ([#477](https://github.com/algolia/react-instantsearch/issues/477)) ([faba1ad](https://github.com/algolia/react-instantsearch/commit/faba1ad)) -* **Range:** handle float, allow reset and respect boundaries ([75969b8](https://github.com/algolia/react-instantsearch/commit/75969b8)) -* **RangeInput:** fix compatibility with React 16 & Panel ([3f218db](https://github.com/algolia/react-instantsearch/commit/3f218db)) -* **searchbox:** add maxlength 512 ([#542](https://github.com/algolia/react-instantsearch/issues/542)) ([5bd4033](https://github.com/algolia/react-instantsearch/commit/5bd4033)), closes [#510](https://github.com/algolia/react-instantsearch/issues/510) - - -### Features - -* **MenuSelect:** add component and connector ([cc6e0d7](https://github.com/algolia/react-instantsearch/commit/cc6e0d7)) - - - - -## [4.1.3](https://github.com/algolia/react-instantsearch/compare/v4.1.2...v4.1.3) (2017-10-09) - - -### Bug Fixes - -* **List:** remove React16 warning ([#442](https://github.com/algolia/react-instantsearch/issues/442)) ([8d6cf18](https://github.com/algolia/react-instantsearch/commit/8d6cf18)) - - -### Features - -* **connectStateResults:** add component props ([#434](https://github.com/algolia/react-instantsearch/issues/434)) ([c629b97](https://github.com/algolia/react-instantsearch/commit/c629b97)) - - - - -## [4.1.2](https://github.com/algolia/react-instantsearch/compare/v4.1.1...v4.1.2) (2017-09-26) - - -### Features - -* **Conditional:** add connectStateResults connector ([#357](https://github.com/algolia/react-instantsearch/issues/357)) ([462df5f](https://github.com/algolia/react-instantsearch/commit/462df5f)) - - - - -## [4.1.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0...v4.1.1) (2017-09-13) - - -### Bug Fixes - -* **MultiIndex:** reset page to 1 when share widgets refine (#312) ([c85a7bf](https://github.com/algolia/react-instantsearch/commit/c85a7bf)) -* **MultiIndex:** Trigger new search when `` props are updated (#318) ([bb11965](https://github.com/algolia/react-instantsearch/commit/bb11965)) - - - - -# [4.1.0](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.5...v4.1.0) (2017-08-28) - - -### Bug Fixes - -* **Highlighting:** revert breaking change (#245) ([045ee06](https://github.com/algolia/react-instantsearch/commit/045ee06)) -* **List:** adds support for any type of renderable element (#266) ([d848bb6](https://github.com/algolia/react-instantsearch/commit/d848bb6)) -* **Pagination:** fixed the offset ([3c0fff2](https://github.com/algolia/react-instantsearch/commit/3c0fff2)) -* **PoweredBy:** aria-* tags are not camelcased (#261) ([dc4a5bb](https://github.com/algolia/react-instantsearch/commit/dc4a5bb)) - - - - -# [4.1.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2017-08-08) - - -### Bug Fixes - -* **SSR:** clean SP before rendering agan (#238) ([e765886](https://github.com/algolia/react-instantsearch/commit/e765886)) - - -### Features - -* **Breadcrumb:** add a new widget & connector (#228) ([7f8f3ae](https://github.com/algolia/react-instantsearch/commit/7f8f3ae)) - - - - -# [4.1.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2017-08-03) - - -### Bug Fixes - -* **deps:** Update dependency lint-staged to version ^4.0.0 (#201) ([6867a1b](https://github.com/algolia/react-instantsearch/commit/6867a1b)) -* **nextjs/ssr:** parse `params.asPath` (#189) ([ae17da0](https://github.com/algolia/react-instantsearch/commit/ae17da0)) -* **PoweredBy:** add a label to the Algolia logo (#216) ([cd235bd](https://github.com/algolia/react-instantsearch/commit/cd235bd)) -* **react:** remove typo around `"" 2` (#220) ([f73eb04](https://github.com/algolia/react-instantsearch/commit/f73eb04)) -* **ScrollTo:** scroll to only if change triggered by the widget observed (#202) ([2d76022](https://github.com/algolia/react-instantsearch/commit/2d76022)) -* **theme:** format the count of items appearing in a refinement (#217) ([2225c24](https://github.com/algolia/react-instantsearch/commit/2225c24)) - - - - -# [4.1.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2017-07-25) - - -### Bug Fixes - -* **error:** reset error when receiving results of a query (not when sending it) (#179) ([bb12c29](https://github.com/algolia/react-instantsearch/commit/bb12c29)) -* **highlight:** wrong parsing between client and server (#183) ([2daae70](https://github.com/algolia/react-instantsearch/commit/2daae70)) -* **poweredBy:** SSR compatibility (#181) ([ec0fa8a](https://github.com/algolia/react-instantsearch/commit/ec0fa8a)) - - -### BREAKING CHANGES - -* **highlight:** We remove the timestamp present in our highlight preTag and postTag. If you were using regex to parse the -highlighting results then you'll need to adapt it as now it's only "ais-highlight". - - - - -# [4.1.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2017-07-20) - - -### Bug Fixes - -* **error:** reset error if next query is successful (#175) ([ff50a07](https://github.com/algolia/react-instantsearch/commit/ff50a07)) - - - - -# [4.1.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2017-07-12) - - - - -# [4.1.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.0.7...v4.1.0-beta.0) (2017-07-10) - - -### Bug Fixes - -* **argos:** address flakyness (#152) ([84ef8f1](https://github.com/algolia/react-instantsearch/commit/84ef8f1)) - - -### Features - -* **server-side rendering:** Add API features for server-side rendering ([86b14d1](https://github.com/algolia/react-instantsearch/commit/86b14d1)) - - - - -## [4.0.7](https://github.com/algolia/react-instantsearch/compare/v4.0.6...v4.0.7) (2017-07-06) - - -### Bug Fixes - -* **results:** revert commit that ensure hits are returned only if right indices (#149) ([df9aa25](https://github.com/algolia/react-instantsearch/commit/df9aa25)) - - - - -## [4.0.6](https://github.com/algolia/react-instantsearch/compare/v4.0.5...v4.0.6) (2017-06-29) - - -### Bug Fixes - -* **store:** delay call to listener to prevent infinite loops (#143) ([0945958](https://github.com/algolia/react-instantsearch/commit/0945958)) - - - - -## [4.0.5](https://github.com/algolia/react-instantsearch/compare/v4.0.4...v4.0.5) (2017-06-26) - - -### Bug Fixes - -* **MultiIndex:** ensure getResults return only hits matching index in the context (#136) ([124ffe6](https://github.com/algolia/react-instantsearch/commit/124ffe6)) -* **MultiIndex:** handle if namespace isn't in search state (#139) ([1aab324](https://github.com/algolia/react-instantsearch/commit/1aab324)) -* **storybook:** process CSS through autoprefixer (#138) ([62cf512](https://github.com/algolia/react-instantsearch/commit/62cf512)) - - - - -## [4.0.4](https://github.com/algolia/react-instantsearch/compare/v4.0.3...v4.0.4) (2017-06-19) - - -### Bug Fixes - -* **MultiIndex:** handle switch between mono and multi index (#132) ([e161921](https://github.com/algolia/react-instantsearch/commit/e161921)) - - - - -## [4.0.3](https://github.com/algolia/react-instantsearch/compare/v4.0.2...v4.0.3) (2017-06-14) - - -### Bug Fixes - -* **SFFV:** search status we're not inside search state (#125) ([5f3e670](https://github.com/algolia/react-instantsearch/commit/5f3e670)) - - - - -## [4.0.2](https://github.com/algolia/react-instantsearch/compare/v4.0.1...v4.0.2) (2017-05-30) - - - - -## [4.0.1](https://github.com/algolia/react-instantsearch/compare/v4.0.0...v4.0.1) (2017-05-17) - - -### Bug Fixes - -* **state:** nested attributes for faceting were not handled ([11bd122](https://github.com/algolia/react-instantsearch/commit/11bd122)) - - - - -# [4.0.0](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.6...v4.0.0) (2017-05-15) - -### Features and migration guide - -You can find all the details of the release and the migration guide from v3 to v4 here: https://discourse.algolia.com/t/react-instantsearch-v4/1329. - - - -# [4.0.0-beta.6](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.5...v4.0.0-beta.6) (2017-05-04) - - - - -# [4.0.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.4...v4.0.0-beta.5) (2017-05-02) - - -### Bug Fixes - -* **connectAutoComplete:** allow usage with hits from a single index (#75) ([8b3b358](https://github.com/algolia/react-instantsearch/commit/8b3b358)), closes [#74](https://github.com/algolia/react-instantsearch/issues/74) -* **InstantSearch:** update algoliaClient when it change (#70) ([9e97dbd](https://github.com/algolia/react-instantsearch/commit/9e97dbd)) - - - - -# [4.0.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.3...v4.0.0-beta.4) (2017-04-25) - - -### Bug Fixes - -* **MultIndex:** no need to nest hits, if those are from main index. (#56) ([86e0bd7](https://github.com/algolia/react-instantsearch/commit/86e0bd7)) - - -### Features - -* **MultiIndex:** remove the need for virtual hits when using connectAutoComplete (#45) ([7549019](https://github.com/algolia/react-instantsearch/commit/7549019)) - - - - -# [4.0.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.2...v4.0.0-beta.3) (2017-04-21) - - -### Bug Fixes - -* replace usage of Object.values (#47) ([4c79e3e](https://github.com/algolia/react-instantsearch/commit/4c79e3e)) - - - - -# [4.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2017-04-18) - - -### Bug Fixes - -* **InstantSearch:** dont fire request/onsearchStateChange when unmounting (#26) ([9a1487a](https://github.com/algolia/react-instantsearch/commit/9a1487a)) -* **MultiIndex:** derived helper were using main index specifics params (#36) ([991fea6](https://github.com/algolia/react-instantsearch/commit/991fea6)) -* **MultiIndex:** revert breaking change if no multiple index (#32) ([44f7de0](https://github.com/algolia/react-instantsearch/commit/44f7de0)) -* **util:** remove empty key was removing non object key (#29) ([9f795c7](https://github.com/algolia/react-instantsearch/commit/9f795c7)) - - -### Features - -* **Highlighter:** allow rendering to custom tag (#11) ([52a1212](https://github.com/algolia/react-instantsearch/commit/52a1212)) -* **SearchBox:** add default width and height to buttons. (#34) ([bcabf9b](https://github.com/algolia/react-instantsearch/commit/bcabf9b)) - - - - -# [4.0.0-beta.1](https://github.com/algolia/instantsearch.js/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2017-04-03) - - -### Bug Fixes - -* **SFFV:** fix wrong query behaviour with slow network (#2086) ([c251e8f](https://github.com/algolia/instantsearch.js/commit/c251e8f)), closes [#2086](https://github.com/algolia/instantsearch.js/issues/2086) - - - - -# [4.0.0-beta.0](https://github.com/algolia/instantsearch.js/compare/v3.3.0...v4.0.0-beta.0) (2017-03-28) - - -### Features - -* **multi-index:** ease multi index and auto complete ([09a4e1d](https://github.com/algolia/instantsearch.js/commit/09a4e1d)) - - -### BREAKING CHANGES - -* multi-index: * Reseting the pagination should be done at each connector level inside the "refine" function when returning the search state. -* The current page now appears inside the search state when a widget is used -* Query values are part of the items prop of the connectCurrentRefinements connector. Behaviour is unchanged, query will be filtered if clearsQuery prop is false. -* Add the index name to all the current refinements items. (not used by our widgets yet, but available if needed). - - - - -# [3.3.0](https://github.com/algolia/instantsearch.js/compare/v3.2.2-beta0...v3.3.0) (2017-03-22) - - -### Bug Fixes - -* **example:** Fix access to props in react-router example ([1417d6f](https://github.com/algolia/instantsearch.js/commit/1417d6f)) - - - - -## [3.2.2-beta0](https://github.com/algolia/instantsearch.js/compare/v3.2.1...v3.2.2-beta0) (2017-03-20) - - -### Bug Fixes - -* **InfiniteHits:** provide translation key for `Load More` (#2048) ([6130bf2](https://github.com/algolia/instantsearch.js/commit/6130bf2)) -* **SearchBox:** better mobile behaviour by default ([ea968b3](https://github.com/algolia/instantsearch.js/commit/ea968b3)) -* **example:** link to instantsearch/react (#2007) ([5e674cd](https://github.com/algolia/instantsearch.js/commit/5e674cd)) -* **recipes:** react router v4 ([de673bf](https://github.com/algolia/instantsearch.js/commit/de673bf)) - - -### Features - -* **SearchBox:** add role=search to the form (#2046) ([d1e90f3](https://github.com/algolia/instantsearch.js/commit/d1e90f3)) -* **SearchBox:** allow custom reset and submit components (#1991) ([cd303d7](https://github.com/algolia/instantsearch.js/commit/cd303d7)) -* **searchBox:** add event handling ([e267ab6](https://github.com/algolia/instantsearch.js/commit/e267ab6)), closes [#2017](https://github.com/algolia/instantsearch.js/issues/2017) - - - - -## [3.2.1](https://github.com/algolia/instantsearch.js/compare/v3.2.0...v3.2.1) (2017-02-22) - - -### Bug Fixes - -* **umd:** Add connectors to UMD build (#1988) ([23ac5e6](https://github.com/algolia/instantsearch.js/commit/23ac5e6)), closes [#1987](https://github.com/algolia/instantsearch.js/issues/1987) - - - - -# [3.2.0](https://github.com/algolia/instantsearch.js/compare/v3.1.0...v3.2.0) (2017-02-15) - - -### Bug Fixes - -* **Configure:** use props a unique source of truth (#1967) ([9d53d86](https://github.com/algolia/instantsearch.js/commit/9d53d86)) -* **SearchBox:** Safari can only have with xlinkHref (#1970) ([7ab00bd](https://github.com/algolia/instantsearch.js/commit/7ab00bd)), closes [#1968](https://github.com/algolia/instantsearch.js/issues/1968) - - -### Features - -* **MultiRange:** add an all range (#1959) ([a3dc950](https://github.com/algolia/instantsearch.js/commit/a3dc950)) - - -### BREAKING CHANGES - -* MultiRange: - MultiRange/connectMultiRange: will add a "All" range to allow unselection of range without the usage of CurrentRefinements. This range can be either filtered or ramove via CSS if not needed. The label can be changed by using our translations system. - - - - -# [3.1.0](https://github.com/algolia/instantsearch.js/compare/v3.0.0...v3.1.0) (2017-02-08) - - -### Bug Fixes - -* **Configure:** call onSearchStateChange when props are updated (#1953) ([7e151db](https://github.com/algolia/instantsearch.js/commit/7e151db)), closes [#1950](https://github.com/algolia/instantsearch.js/issues/1950) -* **Configure:** trigger onSearchStateChange with the right data ([11e5af8](https://github.com/algolia/instantsearch.js/commit/11e5af8)) -* **createConnector:** updates with latest props on state change (#1951) ([cd3a82c](https://github.com/algolia/instantsearch.js/commit/cd3a82c)) - - -### Features - -* **ClearAll:** add withQuery to also clear the search query (#1958) ([c0e695b](https://github.com/algolia/instantsearch.js/commit/c0e695b)) - - - - -# [3.0.0](https://github.com/algolia/instantsearch.js/compare/v2.2.5...v3.0.0) (2017-02-06) - - -### Bug Fixes - -* ***List:** disable shortcuts in *List SearchBoxes (#1921) ([51a76ae](https://github.com/algolia/instantsearch.js/commit/51a76ae)), closes [#1920](https://github.com/algolia/instantsearch.js/issues/1920) -* **Configure:** add configure parameters in search state (#1935) ([0971330](https://github.com/algolia/instantsearch.js/commit/0971330)), closes [#1863](https://github.com/algolia/instantsearch.js/issues/1863) -* **Hits:** limit the hitComponent to be only a function (#1912) ([b3c9578](https://github.com/algolia/instantsearch.js/commit/b3c9578)) -* **Pagination:** fix and indicate when pagination is disabled ([5f20199](https://github.com/algolia/instantsearch.js/commit/5f20199)), closes [#1938](https://github.com/algolia/instantsearch.js/issues/1938) -* **StarRating:** usage with filters (#1933) ([667e9d5](https://github.com/algolia/instantsearch.js/commit/667e9d5)) -* **withSearchBox:** keep displaying searchBox when no items found (#1930) ([30de4cd](https://github.com/algolia/instantsearch.js/commit/30de4cd)) - - -### Features - -* **MultiRange:** indicate if a range has no refinements (#1926) ([80b6450](https://github.com/algolia/instantsearch.js/commit/80b6450)) -* **panel:** add a panel widget (#1889) ([594e1a1](https://github.com/algolia/instantsearch.js/commit/594e1a1)) -* **starRating:** indicate when any refinement has no effect ([c547ae5](https://github.com/algolia/instantsearch.js/commit/c547ae5)) -* **widgets:** default design for disabled states (#1929) ([31f010b](https://github.com/algolia/instantsearch.js/commit/31f010b)) - -### Migration guide - -The migration to V3.0.0 should be safe and you should do it. - -There are two breaking changes that you will need to handle in your codebase: -- Anytime you are using a connector, when there are no more items in it or no more hits, we will still call your Component. Thus you will have to handle cases like dealing with empty arrays and decide if you want to unmount or hide the widget. -- Anytime you are using a widget, when there are no more items in it or no more hits, we will still display the widget. You can then decide to hide it with CSS. - - -## [2.2.5](https://github.com/algolia/instantsearch.js/compare/v2.2.4...v2.2.5) (2017-01-23) - - -### Bug Fixes - -* **currentRefinements:** make removing a toggle refinement work ([8995e64](https://github.com/algolia/instantsearch.js/commit/8995e64)) - - - - -## [2.2.4](https://github.com/algolia/instantsearch.js/compare/v2.2.3...v2.2.4) (2017-01-20) - - -### Bug Fixes - -* **publish:** publish react-instantsearch/dist instead of root (#1884) ([64414e0](https://github.com/algolia/instantsearch.js/commit/64414e0)) - - - - -## [2.2.3](https://github.com/algolia/instantsearch.js/compare/v2.2.2...v2.2.3) (2017-01-20) - - -### Bug Fixes - -* **SFFV:** translations for searchbox were not applied (#1879) ([e9b4ee1](https://github.com/algolia/instantsearch.js/commit/e9b4ee1)) - - - - -## [2.2.2](https://github.com/algolia/instantsearch.js/compare/v2.2.1...v2.2.2) (2017-01-18) - - -### Bug Fixes - -* **react-router:** search was triggered two many times (#1840) ([25e9db5](https://github.com/algolia/instantsearch.js/commit/25e9db5)) -* **SFFV:** empty query triggered a new SFFV (#1875) ([6c8259a](https://github.com/algolia/instantsearch.js/commit/6c8259a)) - - - - -## [2.2.1](https://github.com/algolia/instantsearch.js/compare/v2.2.0...v2.2.1) (2017-01-18) - - -### Bug Fixes - -* **createInstantsearch:** fix missing props (#1867) ([8d319b5](https://github.com/algolia/instantsearch.js/commit/8d319b5)), closes [#1867](https://github.com/algolia/instantsearch.js/issues/1867) - - - - -# [2.2.0](https://github.com/algolia/instantsearch.js/compare/v2.1.0...v2.2.0) (2017-01-17) - - -### Bug Fixes - -* **clear:** clearing wasn't working with too+ same type facets selected (#1820) ([a9a2364](https://github.com/algolia/instantsearch.js/commit/a9a2364)) -* **connectSearchBox:** handle `defaultRefinement` (#1829) ([7a730e2](https://github.com/algolia/instantsearch.js/commit/7a730e2)), closes [#1826](https://github.com/algolia/instantsearch.js/issues/1826) -* **Instantsearch:** Update all props on InstantSearch (#1828) ([2ed9b49](https://github.com/algolia/instantsearch.js/commit/2ed9b49)) -* **InstantSearch:** add specific `react-instantsearch ${version}` agent (#1844) ([a1113bc](https://github.com/algolia/instantsearch.js/commit/a1113bc)) -* **SFFV:** correct propTypes and add missing default values (#1845) ([a4c1b31](https://github.com/algolia/instantsearch.js/commit/a4c1b31)) -* **test:** add missing Snippet and Highliter snapshot ([4accce5](https://github.com/algolia/instantsearch.js/commit/4accce5)) -* **widgets:** replace setImmediate use with Promise use when update is needed (#1811) ([17e2497](https://github.com/algolia/instantsearch.js/commit/17e2497)) - - -### Features - -* **Menu, connectMenu:** add search for facet values (#1822) ([a6c513e](https://github.com/algolia/instantsearch.js/commit/a6c513e)) -* **snippet:** add a snippet widget to be able to highlight snippet results (#1797) ([2aecc40](https://github.com/algolia/instantsearch.js/commit/2aecc40)) -* **widgets:** add transformItems to be able to sort and filter (#1809) ([ba539f0](https://github.com/algolia/instantsearch.js/commit/ba539f0)) - - - - -# [2.1.0](https://github.com/algolia/instantsearch.js/compare/v2.0.1...v2.1.0) (2017-01-04) - - -### Bug Fixes - -* **createInstantSearchManager:** drop outdated response (#1765) ([76c5312](https://github.com/algolia/instantsearch.js/commit/76c5312)) -* **highlight:** highlight should work even if the attribute is missing (#1791) ([5b79b15](https://github.com/algolia/instantsearch.js/commit/5b79b15)), closes [#1790](https://github.com/algolia/instantsearch.js/issues/1790) -* **InfiniteHits:** better classname to loadmore btn (#1789) ([ad2ded3](https://github.com/algolia/instantsearch.js/commit/ad2ded3)) -* **starRatings:** click on selected range doesn't unselect it (#1766) ([beacc72](https://github.com/algolia/instantsearch.js/commit/beacc72)) -* **website:** broken demo links (#1802) ([0abe2f5](https://github.com/algolia/instantsearch.js/commit/0abe2f5)) -* **widgets:** add 300px width for the default SearchBox (#1803) ([bf5d791](https://github.com/algolia/instantsearch.js/commit/bf5d791)) - - -### Features - -* **InfiniteHits:** Add class to load more button (#1787) ([416febd](https://github.com/algolia/instantsearch.js/commit/416febd)) -* **RefinementList, connectRefinementList:** allow to search for facet values ([e086a81](https://github.com/algolia/instantsearch.js/commit/e086a81)) - - - - -## [2.0.1](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.1) (2016-12-15) - - -### Bug Fixes - -* **connectRange:** when unfinite numbers are passed throw ([75bec0d](https://github.com/algolia/instantsearch.js/commit/75bec0d)) -* **react-native:** use View as a container for react-native (#1729) ([5b76f75](https://github.com/algolia/instantsearch.js/commit/5b76f75)), closes [#1730](https://github.com/algolia/instantsearch.js/issues/1730) -* **SearchBox:** autocomplete was not disabled by default (#1742) ([bc76618](https://github.com/algolia/instantsearch.js/commit/bc76618)) -* **starRating:** call createURL with the right interface (min/max) (#1747) ([f9ab9b6](https://github.com/algolia/instantsearch.js/commit/f9ab9b6)) - - - - -## [2.0.0](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.0) (2016-12-08) - -First release of `react-instantsearch` diff --git a/packages/react-instantsearch-dom/README.md b/packages/react-instantsearch-dom/README.md deleted file mode 100644 index 19a1b7845d..0000000000 --- a/packages/react-instantsearch-dom/README.md +++ /dev/null @@ -1,13 +0,0 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [react-instantsearch-dom](#react-instantsearch-dom) - - - -# react-instantsearch-dom - -This is the [React](https://facebook.github.io/react) version of Algolia's `instantsearch` library. - -Go to the [React InstantSearch website](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) or the [React InstantSearch GitHub repository](https://github.com/algolia/instantsearch.js) for more information. diff --git a/packages/react-instantsearch-dom/package.json b/packages/react-instantsearch-dom/package.json deleted file mode 100644 index 9e04e9304c..0000000000 --- a/packages/react-instantsearch-dom/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "react-instantsearch-dom", - "version": "6.40.4", - "description": "⚡ Lightning-fast search for React DOM, by Algolia", - "main": "dist/cjs/index.js", - "module": "dist/es/index.js", - "sideEffects": false, - "license": "MIT", - "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/", - "repository": { - "type": "git", - "url": "https://github.com/algolia/instantsearch.js" - }, - "author": { - "name": "Algolia, Inc.", - "url": "https://www.algolia.com" - }, - "keywords": [ - "algolia", - "components", - "fast", - "instantsearch", - "react", - "react-dom", - "search" - ], - "files": [ - "README.md", - "server.js", - "dist" - ], - "scripts": { - "clean": "rm -rf dist", - "watch": "yarn build:cjs --watch", - "build": "yarn build:cjs && yarn build:es && yarn build:umd", - "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", - "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", - "build:umd": "BABEL_ENV=rollup rollup -c rollup.config.js", - "storybook": "start-storybook -p 6006 -c .storybook -s .storybook/public", - "storybook:build": "build-storybook -c .storybook -s .storybook/public -o ../../website/stories/react" - }, - "dependencies": { - "@babel/runtime": "^7.1.2", - "algoliasearch-helper": "3.14.0", - "classnames": "^2.2.5", - "prop-types": "^15.6.2", - "react-fast-compare": "^3.0.0", - "react-instantsearch-core": "6.40.4" - }, - "devDependencies": { - "@storybook/react": "5.3.9", - "babel-preset-react-app": "10.0.1", - "lodash.orderby": "4.6.0", - "nock": "11.7.2", - "react-autosuggest": "9.4.3" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 5", - "react": ">= 16.3.0 < 19", - "react-dom": ">= 16.3.0 < 19" - } -} diff --git a/packages/react-instantsearch-dom/rollup.config.js b/packages/react-instantsearch-dom/rollup.config.js deleted file mode 100644 index 5ceedb8a32..0000000000 --- a/packages/react-instantsearch-dom/rollup.config.js +++ /dev/null @@ -1,75 +0,0 @@ -import babel from 'rollup-plugin-babel'; -import commonjs from 'rollup-plugin-commonjs'; -import filesize from 'rollup-plugin-filesize'; -import globals from 'rollup-plugin-node-globals'; -import resolve from 'rollup-plugin-node-resolve'; -import replace from 'rollup-plugin-replace'; -import { uglify } from 'rollup-plugin-uglify'; - -const clear = (x) => x.filter(Boolean); - -const version = process.env.VERSION || 'UNRELEASED'; -const algolia = '© Algolia, inc.'; -const link = 'https://github.com/algolia/instantsearch.js'; -const createBanner = (name) => - `/*! React InstantSearch${name} ${version} | ${algolia} | ${link} */`; - -const plugins = [ - babel({ - exclude: /node_modules|algoliasearch-helper/, - extensions: ['.js', '.ts', '.tsx'], - rootMode: 'upward', - runtimeHelpers: true, - }), - resolve({ - browser: true, - preferBuiltins: false, - extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], - }), - commonjs(), - globals(), - replace({ - 'process.env.NODE_ENV': JSON.stringify('production'), - }), - filesize({ - showMinifiedSize: false, - showGzippedSize: true, - }), -]; - -const createConfiguration = ({ name, minify = false } = {}) => ({ - input: 'src/index.js', - external: ['react', 'react-dom'], - output: { - file: `dist/umd/ReactInstantSearch${name}${minify ? '.min' : ''}.js`, - name: `ReactInstantSearch${name}`, - format: 'umd', - globals: { - react: 'React', - 'react-dom': 'ReactDOM', - }, - banner: createBanner(name), - sourcemap: true, - }, - plugins: plugins.concat( - clear([ - minify && - uglify({ - output: { - preamble: createBanner(name), - }, - }), - ]) - ), -}); - -export default [ - createConfiguration({ - name: 'DOM', - }), - - createConfiguration({ - name: 'DOM', - minify: true, - }), -]; diff --git a/packages/react-instantsearch-dom/server.js b/packages/react-instantsearch-dom/server.js deleted file mode 100644 index 4b85d99189..0000000000 --- a/packages/react-instantsearch-dom/server.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable import/no-commonjs, no-var */ - -// This file only exist to allow the import from 'react-instantsearch-dom/server' -// We can't have this import working easily with the current project architecture. -// The main reason is that we require to have the files transpiled at development -// time to allow the import accross Workspaces (node_modules are not transpiled). -// To achieve this we scope all the packages artefacts in the `dist` folder under -// `/cjs` & `/es`. At the end we can require the package as it is with a `main` -// that point to `./dist/cjs/index.js` & `module` that point to the `es` folder. -// But the `/server` import will fallback into this file instead of the correct -// one the `dist` folder. -// We now have the solution for those use cases: private packages. You can have -// nested private packages inside your "main" package. The advantage of this -// approach is to leverage the module resolution of npm. A popular package built -// with this approach is Preact 10.x.x: https://bit.ly/2WrMjLF - -var server = require('./dist/cjs/server'); - -module.exports = server; diff --git a/packages/react-instantsearch-dom/src/components/Breadcrumb.js b/packages/react-instantsearch-dom/src/components/Breadcrumb.js deleted file mode 100644 index b511e8b85d..0000000000 --- a/packages/react-instantsearch-dom/src/components/Breadcrumb.js +++ /dev/null @@ -1,95 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -import Link from './Link'; - -const cx = createClassNames('Breadcrumb'); - -const itemsPropType = PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, - }) -); - -class Breadcrumb extends Component { - static propTypes = { - canRefine: PropTypes.bool.isRequired, - createURL: PropTypes.func.isRequired, - items: itemsPropType, - refine: PropTypes.func.isRequired, - rootURL: PropTypes.string, - separator: PropTypes.node, - translate: PropTypes.func.isRequired, - className: PropTypes.string, - }; - - static defaultProps = { - rootURL: null, - separator: ' > ', - className: '', - }; - - render() { - const { - canRefine, - createURL, - items, - refine, - rootURL, - separator, - translate, - className, - } = this.props; - const rootPath = canRefine ? ( -
  • - (!rootURL ? refine() : null)} - href={rootURL ? rootURL : createURL()} - > - {translate('rootLabel')} - -
  • - ) : null; - - const breadcrumb = items.map((item, idx) => { - const isLast = idx === items.length - 1; - return ( -
  • - {separator} - {!isLast ? ( - refine(item.value)} - href={createURL(item.value)} - > - {item.label} - - ) : ( - item.label - )} -
  • - ); - }); - - return ( -
    -
      - {rootPath} - {breadcrumb} -
    -
    - ); - } -} - -export default translatable({ - rootLabel: 'Home', -})(Breadcrumb); diff --git a/packages/react-instantsearch-dom/src/components/ClearRefinements.js b/packages/react-instantsearch-dom/src/components/ClearRefinements.js deleted file mode 100644 index 604b3d99f7..0000000000 --- a/packages/react-instantsearch-dom/src/components/ClearRefinements.js +++ /dev/null @@ -1,42 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('ClearRefinements'); - -class ClearRefinements extends Component { - static propTypes = { - items: PropTypes.arrayOf(PropTypes.object).isRequired, - canRefine: PropTypes.bool.isRequired, - refine: PropTypes.func.isRequired, - translate: PropTypes.func.isRequired, - className: PropTypes.string, - }; - - static defaultProps = { - className: '', - }; - - render() { - const { items, canRefine, refine, translate, className } = this.props; - - return ( -
    - -
    - ); - } -} - -export default translatable({ - reset: 'Clear all filters', -})(ClearRefinements); diff --git a/packages/react-instantsearch-dom/src/components/CurrentRefinements.js b/packages/react-instantsearch-dom/src/components/CurrentRefinements.js deleted file mode 100644 index 528ffad008..0000000000 --- a/packages/react-instantsearch-dom/src/components/CurrentRefinements.js +++ /dev/null @@ -1,72 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('CurrentRefinements'); - -export const CurrentRefinements = ({ - items, - canRefine, - refine, - translate, - className, -}) => ( -
    -
      - {items.map((item) => ( -
    • - {item.label} - {item.items ? ( - item.items.map((nest) => ( - - {nest.label} - - - )) - ) : ( - - - - )} -
    • - ))} -
    -
    -); - -const itemPropTypes = PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.func.isRequired, - items: (...args) => itemPropTypes(...args), - }) -); - -CurrentRefinements.propTypes = { - items: itemPropTypes.isRequired, - canRefine: PropTypes.bool.isRequired, - refine: PropTypes.func.isRequired, - translate: PropTypes.func.isRequired, - className: PropTypes.string, -}; - -CurrentRefinements.defaultProps = { - className: '', -}; - -export default translatable({ - clearFilter: '✕', -})(CurrentRefinements); diff --git a/packages/react-instantsearch-dom/src/components/HierarchicalMenu.js b/packages/react-instantsearch-dom/src/components/HierarchicalMenu.js deleted file mode 100644 index 8e9c94642a..0000000000 --- a/packages/react-instantsearch-dom/src/components/HierarchicalMenu.js +++ /dev/null @@ -1,82 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -import Link from './Link'; -import List from './List'; - -const cx = createClassNames('HierarchicalMenu'); - -const itemsPropType = PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.string, - count: PropTypes.number.isRequired, - items: (...args) => itemsPropType(...args), - }) -); - -class HierarchicalMenu extends Component { - static propTypes = { - translate: PropTypes.func.isRequired, - refine: PropTypes.func.isRequired, - createURL: PropTypes.func.isRequired, - canRefine: PropTypes.bool.isRequired, - items: itemsPropType, - showMore: PropTypes.bool, - className: PropTypes.string, - limit: PropTypes.number, - showMoreLimit: PropTypes.number, - transformItems: PropTypes.func, - }; - - static defaultProps = { - className: '', - }; - - renderItem = (item) => { - const { createURL, refine } = this.props; - - return ( - refine(item.value)} - href={createURL(item.value)} - > - {item.label}{' '} - {item.count} - - ); - }; - - render() { - const { - translate, - items, - showMore, - limit, - showMoreLimit, - canRefine, - className, - } = this.props; - return ( - - ); - } -} - -export default translatable({ - showMore: (extended) => (extended ? 'Show less' : 'Show more'), -})(HierarchicalMenu); diff --git a/packages/react-instantsearch-dom/src/components/Highlight.js b/packages/react-instantsearch-dom/src/components/Highlight.js deleted file mode 100644 index 72446675da..0000000000 --- a/packages/react-instantsearch-dom/src/components/Highlight.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import { createClassNames } from '../core/utils'; - -import Highlighter from './Highlighter'; - -const cx = createClassNames('Highlight'); - -const Highlight = (props) => ( - -); - -export default Highlight; diff --git a/packages/react-instantsearch-dom/src/components/Highlighter.js b/packages/react-instantsearch-dom/src/components/Highlighter.js deleted file mode 100644 index 4441fe6e3d..0000000000 --- a/packages/react-instantsearch-dom/src/components/Highlighter.js +++ /dev/null @@ -1,101 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -export const Highlight = ({ - cx, - value, - highlightedTagName, - isHighlighted, - nonHighlightedTagName, -}) => { - const TagName = isHighlighted ? highlightedTagName : nonHighlightedTagName; - const className = isHighlighted ? 'highlighted' : 'nonHighlighted'; - return {value}; -}; - -Highlight.propTypes = { - cx: PropTypes.func.isRequired, - value: PropTypes.string.isRequired, - isHighlighted: PropTypes.bool.isRequired, - highlightedTagName: PropTypes.string.isRequired, - nonHighlightedTagName: PropTypes.string.isRequired, -}; - -const Highlighter = ({ - cx, - hit, - attribute, - highlight, - highlightProperty, - tagName, - nonHighlightedTagName, - separator, - className, -}) => { - const parsedHighlightedValue = highlight({ - hit, - attribute, - highlightProperty, - }); - - return ( - - {parsedHighlightedValue.map((item, i) => { - if (Array.isArray(item)) { - const isLast = i === parsedHighlightedValue.length - 1; - return ( - - {item.map((element, index) => ( - - ))} - {!isLast && {separator}} - - ); - } - - return ( - - ); - })} - - ); -}; - -Highlighter.propTypes = { - cx: PropTypes.func.isRequired, - hit: PropTypes.object.isRequired, - attribute: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.string), - PropTypes.string, - ]).isRequired, - highlight: PropTypes.func.isRequired, - highlightProperty: PropTypes.string.isRequired, - tagName: PropTypes.string, - nonHighlightedTagName: PropTypes.string, - className: PropTypes.string, - separator: PropTypes.node, -}; - -Highlighter.defaultProps = { - tagName: 'em', - nonHighlightedTagName: 'span', - className: '', - separator: ', ', -}; - -export default Highlighter; diff --git a/packages/react-instantsearch-dom/src/components/Hits.tsx b/packages/react-instantsearch-dom/src/components/Hits.tsx deleted file mode 100644 index 5f5798538c..0000000000 --- a/packages/react-instantsearch-dom/src/components/Hits.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import { createClassNames } from '../core/utils'; - -type Hit = { - objectID: string | number; -}; - -type HitProps = { - hit: Hit; -}; - -type Props = { - hits: Hit[]; - className?: string; - hitComponent?: - | string - | React.ElementType - | React.ExoticComponent; -}; - -const cx = createClassNames('Hits'); - -const DefaultHitComponent: React.FC = (props) => ( -
    - {JSON.stringify(props).slice(0, 100)} - ... -
    -); - -const Hits: React.FC = ({ - hits, - className = '', - hitComponent: HitComponent = DefaultHitComponent, -}) => ( -
    -
      - {hits.map((hit) => ( -
    • - -
    • - ))} -
    -
    -); - -const HitPropTypes = PropTypes.shape({ - objectID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - .isRequired, -}); - -Hits.propTypes = { - hits: PropTypes.arrayOf(HitPropTypes.isRequired).isRequired, - className: PropTypes.string, - - // this is actually PropTypes.elementType, but our prop-types version is outdated - hitComponent: PropTypes.any, -}; - -export default Hits; diff --git a/packages/react-instantsearch-dom/src/components/HitsPerPage.js b/packages/react-instantsearch-dom/src/components/HitsPerPage.js deleted file mode 100644 index 0fc1e95f56..0000000000 --- a/packages/react-instantsearch-dom/src/components/HitsPerPage.js +++ /dev/null @@ -1,46 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import { createClassNames } from '../core/utils'; - -import Select from './Select'; - -const cx = createClassNames('HitsPerPage'); - -class HitsPerPage extends Component { - static propTypes = { - id: PropTypes.string, - items: PropTypes.arrayOf( - PropTypes.shape({ - value: PropTypes.number.isRequired, - label: PropTypes.string, - }) - ).isRequired, - currentRefinement: PropTypes.number.isRequired, - refine: PropTypes.func.isRequired, - className: PropTypes.string, - }; - - static defaultProps = { - className: '', - }; - - render() { - const { id, items, currentRefinement, refine, className } = this.props; - - return ( -
    - - - - {items.map((item) => ( - - ))} - -
    - ); - } -} - -export default translatable({ - seeAllOption: 'See all', -})(MenuSelect); diff --git a/packages/react-instantsearch-dom/src/components/NumericMenu.js b/packages/react-instantsearch-dom/src/components/NumericMenu.js deleted file mode 100644 index d6a39a052a..0000000000 --- a/packages/react-instantsearch-dom/src/components/NumericMenu.js +++ /dev/null @@ -1,68 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -import List from './List'; - -const cx = createClassNames('NumericMenu'); - -class NumericMenu extends Component { - static propTypes = { - items: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node.isRequired, - value: PropTypes.string.isRequired, - isRefined: PropTypes.bool.isRequired, - noRefinement: PropTypes.bool.isRequired, - }) - ).isRequired, - canRefine: PropTypes.bool.isRequired, - refine: PropTypes.func.isRequired, - translate: PropTypes.func.isRequired, - className: PropTypes.string, - }; - - static defaultProps = { - className: '', - }; - - renderItem = (item) => { - const { refine, translate } = this.props; - - return ( - - ); - }; - - render() { - const { items, canRefine, className } = this.props; - - return ( - ({ ...item, key: item.value }))} - className={className} - /> - ); - } -} - -export default translatable({ - all: 'All', -})(NumericMenu); diff --git a/packages/react-instantsearch-dom/src/components/Pagination.js b/packages/react-instantsearch-dom/src/components/Pagination.js deleted file mode 100644 index 3a08a29f9f..0000000000 --- a/packages/react-instantsearch-dom/src/components/Pagination.js +++ /dev/null @@ -1,194 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames, capitalize, range } from '../core/utils'; - -import LinkList from './LinkList'; - -const cx = createClassNames('Pagination'); - -// Determines the size of the widget (the number of pages displayed - that the user can directly click on) -function calculateSize(padding, maxPages) { - return Math.min(2 * padding + 1, maxPages); -} - -function calculatePaddingLeft(currentPage, padding, maxPages, size) { - if (currentPage <= padding) { - return currentPage; - } - - if (currentPage >= maxPages - padding) { - return size - (maxPages - currentPage); - } - - return padding + 1; -} - -// Retrieve the correct page range to populate the widget -function getPages(currentPage, maxPages, padding) { - const size = calculateSize(padding, maxPages); - // If the widget size is equal to the max number of pages, return the entire page range - if (size === maxPages) return range({ start: 1, end: maxPages + 1 }); - - const paddingLeft = calculatePaddingLeft( - currentPage, - padding, - maxPages, - size - ); - const paddingRight = size - paddingLeft; - - const first = currentPage - paddingLeft; - const last = currentPage + paddingRight; - return range({ start: first + 1, end: last + 1 }); -} - -class Pagination extends Component { - static propTypes = { - nbPages: PropTypes.number.isRequired, - currentRefinement: PropTypes.number.isRequired, - refine: PropTypes.func.isRequired, - createURL: PropTypes.func.isRequired, - canRefine: PropTypes.bool.isRequired, - - translate: PropTypes.func.isRequired, - listComponent: PropTypes.func, - - showFirst: PropTypes.bool, - showPrevious: PropTypes.bool, - showNext: PropTypes.bool, - showLast: PropTypes.bool, - padding: PropTypes.number, - totalPages: PropTypes.number, - className: PropTypes.string, - }; - - static defaultProps = { - listComponent: LinkList, - showFirst: true, - showPrevious: true, - showNext: true, - showLast: false, - padding: 3, - totalPages: Infinity, - className: '', - }; - - getItem(modifier, translationKey, value) { - const { nbPages, totalPages, translate } = this.props; - return { - key: `${modifier}.${value}`, - modifier, - disabled: value < 1 || value >= Math.min(totalPages, nbPages), - label: translate(translationKey, value), - value, - ariaLabel: translate(`aria${capitalize(translationKey)}`, value), - }; - } - - render() { - const { - listComponent: ListComponent, - nbPages, - totalPages, - currentRefinement, - padding, - showFirst, - showPrevious, - showNext, - showLast, - refine, - createURL, - canRefine, - translate, - className, - ...otherProps - } = this.props; - - const maxPages = Math.min(nbPages, totalPages); - const lastPage = maxPages; - - let items = []; - if (showFirst) { - items.push({ - key: 'first', - modifier: 'item--firstPage', - disabled: currentRefinement === 1, - label: translate('first'), - value: 1, - ariaLabel: translate('ariaFirst'), - }); - } - if (showPrevious) { - items.push({ - key: 'previous', - modifier: 'item--previousPage', - disabled: currentRefinement === 1, - label: translate('previous'), - value: currentRefinement - 1, - ariaLabel: translate('ariaPrevious'), - }); - } - - items = items.concat( - getPages(currentRefinement, maxPages, padding).map((value) => ({ - key: value, - modifier: 'item--page', - label: translate('page', value), - value, - selected: value === currentRefinement, - ariaLabel: translate('ariaPage', value), - })) - ); - if (showNext) { - items.push({ - key: 'next', - modifier: 'item--nextPage', - disabled: currentRefinement === lastPage || lastPage <= 1, - label: translate('next'), - value: currentRefinement + 1, - ariaLabel: translate('ariaNext'), - }); - } - if (showLast) { - items.push({ - key: 'last', - modifier: 'item--lastPage', - disabled: currentRefinement === lastPage || lastPage <= 1, - label: translate('last'), - value: lastPage, - ariaLabel: translate('ariaLast'), - }); - } - - return ( -
    - -
    - ); - } -} - -export default translatable({ - previous: '‹', - next: '›', - first: '«', - last: '»', - page: (currentRefinement) => currentRefinement.toString(), - ariaPrevious: 'Previous page', - ariaNext: 'Next page', - ariaFirst: 'First page', - ariaLast: 'Last page', - ariaPage: (currentRefinement) => `Page ${currentRefinement.toString()}`, -})(Pagination); diff --git a/packages/react-instantsearch-dom/src/components/Panel.js b/packages/react-instantsearch-dom/src/components/Panel.js deleted file mode 100644 index 9b2c4906b1..0000000000 --- a/packages/react-instantsearch-dom/src/components/Panel.js +++ /dev/null @@ -1,54 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component, createContext } from 'react'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('Panel'); - -export const { Consumer: PanelConsumer, Provider: PanelProvider } = - createContext(function setCanRefine() {}); - -class Panel extends Component { - static propTypes = { - children: PropTypes.node.isRequired, - className: PropTypes.string, - header: PropTypes.node, - footer: PropTypes.node, - }; - - static defaultProps = { - className: '', - header: null, - footer: null, - }; - - state = { - canRefine: true, - }; - - setCanRefine = (nextCanRefine) => { - this.setState({ canRefine: nextCanRefine }); - }; - - render() { - const { children, className, header, footer } = this.props; - const { canRefine } = this.state; - - return ( -
    - {header &&
    {header}
    } - -
    - {children} -
    - - {footer &&
    {footer}
    } -
    - ); - } -} - -export default Panel; diff --git a/packages/react-instantsearch-dom/src/components/PanelCallbackHandler.d.ts b/packages/react-instantsearch-dom/src/components/PanelCallbackHandler.d.ts deleted file mode 100644 index 04cca097e8..0000000000 --- a/packages/react-instantsearch-dom/src/components/PanelCallbackHandler.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type React from 'react'; - -export default function PanelCallbackHandler(props: { - canRefine?: boolean; - children: React.reactNode; -}): JSX.Element; diff --git a/packages/react-instantsearch-dom/src/components/PanelCallbackHandler.js b/packages/react-instantsearch-dom/src/components/PanelCallbackHandler.js deleted file mode 100644 index 1201223e6b..0000000000 --- a/packages/react-instantsearch-dom/src/components/PanelCallbackHandler.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import { PanelConsumer } from './Panel'; - -class PanelCallbackHandler extends Component { - static propTypes = { - children: PropTypes.node.isRequired, - canRefine: PropTypes.bool.isRequired, - setCanRefine: PropTypes.func.isRequired, - }; - - componentDidMount() { - this.props.setCanRefine(this.props.canRefine); - } - - componentDidUpdate(prevProps) { - if (prevProps.canRefine !== this.props.canRefine) { - this.props.setCanRefine(this.props.canRefine); - } - } - - render() { - return this.props.children; - } -} - -const PanelWrapper = ({ canRefine, children }) => ( - - {(setCanRefine) => ( - - {children} - - )} - -); - -PanelWrapper.propTypes = { - canRefine: PropTypes.bool.isRequired, - children: PropTypes.node.isRequired, -}; - -export default PanelWrapper; diff --git a/packages/react-instantsearch-dom/src/components/PoweredBy.js b/packages/react-instantsearch-dom/src/components/PoweredBy.js deleted file mode 100644 index d24009679b..0000000000 --- a/packages/react-instantsearch-dom/src/components/PoweredBy.js +++ /dev/null @@ -1,48 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('PoweredBy'); - -const AlgoliaLogo = () => ( - - - -); - -class PoweredBy extends Component { - static propTypes = { - url: PropTypes.string.isRequired, - translate: PropTypes.func.isRequired, - className: PropTypes.string, - }; - - render() { - const { url, translate, className } = this.props; - - return ( -
    - {translate('searchBy')}{' '} - - - -
    - ); - } -} - -export default translatable({ - searchBy: 'Search by', -})(PoweredBy); diff --git a/packages/react-instantsearch-dom/src/components/QueryRuleCustomData.tsx b/packages/react-instantsearch-dom/src/components/QueryRuleCustomData.tsx deleted file mode 100644 index e1699fac3a..0000000000 --- a/packages/react-instantsearch-dom/src/components/QueryRuleCustomData.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import { createClassNames } from '../core/utils'; - -type CustomUserData = { - [key: string]: any; -}; - -const cx = createClassNames('QueryRuleCustomData'); - -type QueryRuleCustomDataRenderProps = { - items: TItem[]; -}; - -export type QueryRuleCustomDataProps = { - items: TItem[]; - className?: string; - children: (options: QueryRuleCustomDataRenderProps) => React.ReactNode; -}; - -const QueryRuleCustomData: React.FC> = - ({ items, className, children }) => ( -
    {children({ items })}
    - ); - -QueryRuleCustomData.propTypes = { - items: PropTypes.arrayOf(PropTypes.object).isRequired as any, - className: PropTypes.string, - children: PropTypes.func.isRequired, -}; - -export default QueryRuleCustomData; diff --git a/packages/react-instantsearch-dom/src/components/RangeInput.js b/packages/react-instantsearch-dom/src/components/RangeInput.js deleted file mode 100644 index 7fdfc5a50d..0000000000 --- a/packages/react-instantsearch-dom/src/components/RangeInput.js +++ /dev/null @@ -1,127 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('RangeInput'); - -export class RawRangeInput extends Component { - static propTypes = { - canRefine: PropTypes.bool.isRequired, - precision: PropTypes.number.isRequired, - translate: PropTypes.func.isRequired, - refine: PropTypes.func.isRequired, - min: PropTypes.number, - max: PropTypes.number, - currentRefinement: PropTypes.shape({ - min: PropTypes.number, - max: PropTypes.number, - }), - className: PropTypes.string, - }; - - static defaultProps = { - currentRefinement: {}, - className: '', - }; - - constructor(props) { - super(props); - - this.state = this.normalizeStateForRendering(props); - } - - componentDidUpdate(prevProps) { - if ( - this.props.canRefine && - (prevProps.currentRefinement.min !== this.props.currentRefinement.min || - prevProps.currentRefinement.max !== this.props.currentRefinement.max) - ) { - this.setState(this.normalizeStateForRendering(this.props)); - } - } - - onSubmit = (e) => { - e.preventDefault(); - - this.props.refine({ - min: this.state.from, - max: this.state.to, - }); - }; - - normalizeStateForRendering(props) { - const { canRefine, min: rangeMin, max: rangeMax } = props; - const { min: valueMin, max: valueMax } = props.currentRefinement; - - return { - from: - canRefine && valueMin !== undefined && valueMin !== rangeMin - ? valueMin - : '', - to: - canRefine && valueMax !== undefined && valueMax !== rangeMax - ? valueMax - : '', - }; - } - - normalizeRangeForRendering({ canRefine, min, max }) { - const hasMin = min !== undefined; - const hasMax = max !== undefined; - - return { - min: canRefine && hasMin && hasMax ? min : '', - max: canRefine && hasMin && hasMax ? max : '', - }; - } - - render() { - const { from, to } = this.state; - const { precision, translate, canRefine, className } = this.props; - const { min, max } = this.normalizeRangeForRendering(this.props); - const step = 1 / Math.pow(10, precision); - - return ( -
    -
    - this.setState({ from: e.currentTarget.value })} - /> - {translate('separator')} - this.setState({ to: e.currentTarget.value })} - /> - -
    -
    - ); - } -} - -export default translatable({ - submit: 'ok', - separator: 'to', -})(RawRangeInput); diff --git a/packages/react-instantsearch-dom/src/components/RatingMenu.js b/packages/react-instantsearch-dom/src/components/RatingMenu.js deleted file mode 100644 index c8011c3dc0..0000000000 --- a/packages/react-instantsearch-dom/src/components/RatingMenu.js +++ /dev/null @@ -1,188 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames, find, range } from '../core/utils'; - -const cx = createClassNames('RatingMenu'); - -class RatingMenu extends Component { - static propTypes = { - translate: PropTypes.func.isRequired, - refine: PropTypes.func.isRequired, - createURL: PropTypes.func.isRequired, - min: PropTypes.number, - max: PropTypes.number, - currentRefinement: PropTypes.shape({ - min: PropTypes.number, - max: PropTypes.number, - }), - count: PropTypes.arrayOf( - PropTypes.shape({ - value: PropTypes.string, - count: PropTypes.number, - }) - ), - canRefine: PropTypes.bool.isRequired, - className: PropTypes.string, - }; - - static defaultProps = { - className: '', - }; - - onClick(min, max, e) { - e.preventDefault(); - e.stopPropagation(); - if ( - min === this.props.currentRefinement.min && - max === this.props.currentRefinement.max - ) { - this.props.refine({ min: this.props.min, max: this.props.max }); - } else { - this.props.refine({ min, max }); - } - } - - buildItem({ - max, - lowerBound, - count, - translate, - createURL, - isLastSelectableItem, - }) { - const disabled = !count; - const isCurrentMinLower = this.props.currentRefinement.min < lowerBound; - const selected = - (isLastSelectableItem && isCurrentMinLower) || - (!disabled && - lowerBound === this.props.currentRefinement.min && - max === this.props.currentRefinement.max); - - const icons = []; - let rating = 0; - for (let icon = 0; icon < max; icon++) { - if (icon < lowerBound) { - rating++; - } - icons.push([ - = lowerBound ? 'starIcon--empty' : 'starIcon--full' - )} - aria-hidden="true" - width="24" - height="24" - > - = lowerBound ? 'starEmptySymbol' : 'starSymbol' - )}`} - /> - , - ' ', - ]); - } - - // The last item of the list (the default item), should not - // be clickable if it is selected. - const isLastAndSelect = isLastSelectableItem && selected; - const onClickHandler = - disabled || isLastAndSelect - ? {} - : { - href: createURL({ min: lowerBound, max }), - onClick: this.onClick.bind(this, lowerBound, max), - }; - - return ( -
  • - - {icons} - {' '} - {count} - -
  • - ); - } - - render() { - const { min, max, translate, count, createURL, canRefine, className } = - this.props; - - // min & max are always set when there is a results, otherwise it means - // that we don't want to render anything since we don't have any values. - const limitMin = min !== undefined && min >= 0 ? min : 1; - const limitMax = max !== undefined && max >= 0 ? max : 0; - const inclusiveLength = limitMax - limitMin + 1; - - const values = count - .map((item) => ({ ...item, value: parseFloat(item.value) })) - .filter((item) => item.value >= limitMin && item.value <= limitMax); - - const items = range({ start: 0, end: Math.max(inclusiveLength, 0) }) - .map((index) => { - const element = find(values, (item) => item.value === limitMax - index); - const placeholder = { value: limitMax - index, count: 0, total: 0 }; - - return element || placeholder; - }) - .reduce( - (acc, item, index) => - acc.concat({ - ...item, - total: index === 0 ? item.count : acc[index - 1].total + item.count, - }), - [] - ) - .map((item, index, arr) => - this.buildItem({ - lowerBound: item.value, - count: item.total, - isLastSelectableItem: arr.length - 1 === index, - max: limitMax, - translate, - createURL, - }) - ); - - return ( -
    - - - - - - - - -
      - {items} -
    -
    - ); - } -} - -export default translatable({ - ratingLabel: ' & Up', -})(RatingMenu); diff --git a/packages/react-instantsearch-dom/src/components/RefinementList.js b/packages/react-instantsearch-dom/src/components/RefinementList.js deleted file mode 100644 index 3557517073..0000000000 --- a/packages/react-instantsearch-dom/src/components/RefinementList.js +++ /dev/null @@ -1,112 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; -import Highlight from '../widgets/Highlight'; - -import List from './List'; - -const cx = createClassNames('RefinementList'); - -class RefinementList extends Component { - static propTypes = { - translate: PropTypes.func.isRequired, - refine: PropTypes.func.isRequired, - searchForItems: PropTypes.func.isRequired, - searchable: PropTypes.bool, - createURL: PropTypes.func.isRequired, - items: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.arrayOf(PropTypes.string).isRequired, - count: PropTypes.number.isRequired, - isRefined: PropTypes.bool.isRequired, - }) - ), - isFromSearch: PropTypes.bool.isRequired, - canRefine: PropTypes.bool.isRequired, - showMore: PropTypes.bool, - limit: PropTypes.number, - showMoreLimit: PropTypes.number, - transformItems: PropTypes.func, - className: PropTypes.string, - }; - - static defaultProps = { - className: '', - }; - - state = { - query: '', - }; - - selectItem = (item, resetQuery) => { - resetQuery(); - this.props.refine(item.value); - }; - - renderItem = (item, resetQuery) => { - const label = this.props.isFromSearch ? ( - - ) : ( - item.label - ); - - return ( - - ); - }; - - render() { - const { - translate, - items, - showMore, - limit, - showMoreLimit, - isFromSearch, - searchForItems, - searchable, - canRefine, - className, - } = this.props; - return ( - - ); - } -} - -export default translatable({ - showMore: (extended) => (extended ? 'Show less' : 'Show more'), - noResults: 'No results', - submit: null, - reset: null, - resetTitle: 'Clear the search query.', - submitTitle: 'Submit your search query.', - placeholder: 'Search here…', -})(RefinementList); diff --git a/packages/react-instantsearch-dom/src/components/RelevantSort.tsx b/packages/react-instantsearch-dom/src/components/RelevantSort.tsx deleted file mode 100644 index ff691f5fc6..0000000000 --- a/packages/react-instantsearch-dom/src/components/RelevantSort.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('RelevantSort'); - -export type RelevantSortComponentProps = { - isRelevantSorted: boolean; -}; - -export type RelevantSortProps = { - className?: string; - isVirtualReplica: boolean; - isRelevantSorted: boolean; - buttonTextComponent?: React.FunctionComponent; - textComponent?: React.FunctionComponent; - refine: (relevancyStrictness: number | undefined) => void; -}; - -const DefaultButtonTextComponent = ({ - isRelevantSorted, -}: RelevantSortComponentProps) => ( - {isRelevantSorted ? 'See all results' : 'See relevant results'} -); - -const RelevantSort: React.FC = ({ - className = '', - isVirtualReplica, - isRelevantSorted, - buttonTextComponent: ButtonTextComponent = DefaultButtonTextComponent, - textComponent: TextComponent, - refine, -}) => - !isVirtualReplica ? null : ( -
    -
    - {TextComponent && } -
    - -
    - ); - -RelevantSort.propTypes = { - buttonTextComponent: PropTypes.func, - className: PropTypes.string, - isVirtualReplica: PropTypes.bool.isRequired, - isRelevantSorted: PropTypes.bool.isRequired, - refine: PropTypes.func.isRequired, - textComponent: PropTypes.func, -}; - -export default RelevantSort; diff --git a/packages/react-instantsearch-dom/src/components/ScrollTo.js b/packages/react-instantsearch-dom/src/components/ScrollTo.js deleted file mode 100644 index f86189f3a6..0000000000 --- a/packages/react-instantsearch-dom/src/components/ScrollTo.js +++ /dev/null @@ -1,31 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('ScrollTo'); - -class ScrollTo extends Component { - static propTypes = { - value: PropTypes.any, - children: PropTypes.node, - hasNotChanged: PropTypes.bool, - }; - - componentDidUpdate(prevProps) { - const { value, hasNotChanged } = this.props; - if (value !== prevProps.value && hasNotChanged) { - this.el.scrollIntoView(); - } - } - - render() { - return ( -
    (this.el = ref)} className={cx('')}> - {this.props.children} -
    - ); - } -} - -export default ScrollTo; diff --git a/packages/react-instantsearch-dom/src/components/SearchBox.js b/packages/react-instantsearch-dom/src/components/SearchBox.js deleted file mode 100644 index 270bb34f62..0000000000 --- a/packages/react-instantsearch-dom/src/components/SearchBox.js +++ /dev/null @@ -1,316 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('SearchBox'); - -const defaultLoadingIndicator = ( - -); - -const defaultReset = ( - -); - -const defaultSubmit = ( - -); - -class SearchBox extends Component { - static propTypes = { - currentRefinement: PropTypes.string, - className: PropTypes.string, - refine: PropTypes.func.isRequired, - translate: PropTypes.func.isRequired, - - loadingIndicator: PropTypes.node, - reset: PropTypes.node, - submit: PropTypes.node, - - focusShortcuts: PropTypes.arrayOf( - PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - ), - - autoFocus: PropTypes.bool, - - searchAsYouType: PropTypes.bool, - onSubmit: PropTypes.func, - onReset: PropTypes.func, - onChange: PropTypes.func, - - isSearchStalled: PropTypes.bool, - showLoadingIndicator: PropTypes.bool, - - formRef: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.exact({ current: PropTypes.object }), - ]), - - inputRef: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.exact({ current: PropTypes.object }), - ]), - inputId: PropTypes.string, - }; - - static defaultProps = { - currentRefinement: '', - className: '', - focusShortcuts: ['s', '/'], - autoFocus: false, - searchAsYouType: true, - showLoadingIndicator: false, - isSearchStalled: false, - loadingIndicator: defaultLoadingIndicator, - reset: defaultReset, - submit: defaultSubmit, - }; - - constructor(props) { - super(); - - this.state = { - query: props.searchAsYouType ? null : props.currentRefinement, - }; - } - - componentDidMount() { - document.addEventListener('keydown', this.onKeyDown); - } - - componentWillUnmount() { - document.removeEventListener('keydown', this.onKeyDown); - } - - componentDidUpdate(prevProps) { - if ( - !this.props.searchAsYouType && - prevProps.currentRefinement !== this.props.currentRefinement - ) { - this.setState({ - query: this.props.currentRefinement, - }); - } - } - - getQuery = () => - this.props.searchAsYouType - ? this.props.currentRefinement - : this.state.query; - - onInputMount = (input) => { - this.input = input; - if (!this.props.inputRef) return; - if (typeof this.props.inputRef === 'function') { - this.props.inputRef(input); - } else { - this.props.inputRef.current = input; - } - }; - - // From https://github.com/algolia/autocomplete.js/pull/86 - onKeyDown = (e) => { - if (!this.props.focusShortcuts) { - return; - } - - const shortcuts = this.props.focusShortcuts.map((key) => - typeof key === 'string' ? key.toUpperCase().charCodeAt(0) : key - ); - - const elt = e.target || e.srcElement; - const tagName = elt.tagName; - if ( - elt.isContentEditable || - tagName === 'INPUT' || - tagName === 'SELECT' || - tagName === 'TEXTAREA' - ) { - // already in an input - return; - } - - const which = e.which || e.keyCode; - if (shortcuts.indexOf(which) === -1) { - // not the right shortcut - return; - } - - this.input.focus(); - e.stopPropagation(); - e.preventDefault(); - }; - - onSubmit = (e) => { - e.preventDefault(); - e.stopPropagation(); - this.input.blur(); - - const { refine, searchAsYouType } = this.props; - if (!searchAsYouType) { - refine(this.getQuery()); - } - return false; - }; - - onChange = (event) => { - const { searchAsYouType, refine, onChange } = this.props; - const value = event.target.value; - - if (searchAsYouType) { - refine(value); - } else { - this.setState({ query: value }); - } - - if (onChange) { - onChange(event); - } - }; - - onReset = (event) => { - const { searchAsYouType, refine, onReset } = this.props; - - refine(''); - this.input.focus(); - - if (!searchAsYouType) { - this.setState({ query: '' }); - } - - if (onReset) { - onReset(event); - } - }; - - render() { - const { - className, - inputId, - translate, - autoFocus, - loadingIndicator, - submit, - reset, - } = this.props; - const query = this.getQuery(); - - const searchInputEvents = Object.keys(this.props).reduce((props, prop) => { - if ( - ['onsubmit', 'onreset', 'onchange'].indexOf(prop.toLowerCase()) === - -1 && - prop.indexOf('on') === 0 - ) { - return { ...props, [prop]: this.props[prop] }; - } - - return props; - }, {}); - - const isSearchStalled = - this.props.showLoadingIndicator && this.props.isSearchStalled; - - return ( -
    -
    - - - - {this.props.showLoadingIndicator && ( - - )} -
    -
    - ); - } -} - -export default translatable({ - resetTitle: 'Clear the search query.', - submitTitle: 'Submit your search query.', - placeholder: 'Search here…', -})(SearchBox); diff --git a/packages/react-instantsearch-dom/src/components/Select.js b/packages/react-instantsearch-dom/src/components/Select.js deleted file mode 100644 index 81b831eadf..0000000000 --- a/packages/react-instantsearch-dom/src/components/Select.js +++ /dev/null @@ -1,50 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -export default class Select extends Component { - static propTypes = { - cx: PropTypes.func.isRequired, - id: PropTypes.string, - onSelect: PropTypes.func.isRequired, - items: PropTypes.arrayOf( - PropTypes.shape({ - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - .isRequired, - - key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - label: PropTypes.string, - disabled: PropTypes.bool, - }) - ).isRequired, - selectedItem: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - .isRequired, - }; - - onChange = (e) => { - this.props.onSelect(e.target.value); - }; - - render() { - const { cx, id, items, selectedItem } = this.props; - - return ( - - ); - } -} diff --git a/packages/react-instantsearch-dom/src/components/Snippet.js b/packages/react-instantsearch-dom/src/components/Snippet.js deleted file mode 100644 index f5287d99e0..0000000000 --- a/packages/react-instantsearch-dom/src/components/Snippet.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import { createClassNames } from '../core/utils'; - -import Highlighter from './Highlighter'; - -const cx = createClassNames('Snippet'); - -const Snippet = (props) => ( - -); - -export default Snippet; diff --git a/packages/react-instantsearch-dom/src/components/SortBy.js b/packages/react-instantsearch-dom/src/components/SortBy.js deleted file mode 100644 index 7d05023617..0000000000 --- a/packages/react-instantsearch-dom/src/components/SortBy.js +++ /dev/null @@ -1,46 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import { createClassNames } from '../core/utils'; - -import Select from './Select'; - -const cx = createClassNames('SortBy'); - -class SortBy extends Component { - static propTypes = { - id: PropTypes.string, - items: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string, - value: PropTypes.string.isRequired, - }) - ).isRequired, - currentRefinement: PropTypes.string.isRequired, - refine: PropTypes.func.isRequired, - className: PropTypes.string, - }; - - static defaultProps = { - className: '', - }; - - render() { - const { id, items, currentRefinement, refine, className } = this.props; - - return ( -
    - refine(event.target.checked)} - /> - {label} - -
    -); - -ToggleRefinement.propTypes = { - currentRefinement: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, - canRefine: PropTypes.bool.isRequired, - refine: PropTypes.func.isRequired, - className: PropTypes.string, -}; - -ToggleRefinement.defaultProps = { - className: '', -}; - -export default ToggleRefinement; diff --git a/packages/react-instantsearch-dom/src/components/VoiceSearch.tsx b/packages/react-instantsearch-dom/src/components/VoiceSearch.tsx deleted file mode 100644 index b9fba7b732..0000000000 --- a/packages/react-instantsearch-dom/src/components/VoiceSearch.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import React, { Component } from 'react'; -import { translatable } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; -import createVoiceSearchHelper from '../lib/voiceSearchHelper'; - -import type { - VoiceSearchHelper, - VoiceListeningState, - Status, - SpeechRecognitionErrorCode, -} from '../lib/voiceSearchHelper'; -const cx = createClassNames('VoiceSearch'); - -type ButtonSvgProps = { - children: React.ReactNode; -}; - -export type InnerComponentProps = { - status: Status; - errorCode?: SpeechRecognitionErrorCode; - isListening: boolean; - transcript: string; - isSpeechFinal: boolean; - isBrowserSupported: boolean; -}; - -type Translate = (key: string, ...params: any[]) => string; - -type VoiceSearchProps = { - searchAsYouSpeak: boolean; - language?: string; - additionalQueryParameters?: (params: { query: string }) => {} | void; - - refine: (query: string) => void; - translate: Translate; - buttonTextComponent: React.FC; - statusComponent: React.FC; -}; - -const ButtonSvg = ({ children }: ButtonSvgProps) => ( - - {children} - -); - -const DefaultButtonText = ({ - status, - errorCode, - isListening, -}: InnerComponentProps) => { - return status === 'error' && errorCode === 'not-allowed' ? ( - - - - - - - - ) : ( - - - - - - - ); -}; - -const DefaultStatus = ({ transcript }: InnerComponentProps) => ( -

    {transcript}

    -); - -class VoiceSearch extends Component { - protected static defaultProps = { - searchAsYouSpeak: false, - buttonTextComponent: DefaultButtonText, - statusComponent: DefaultStatus, - }; - private voiceSearchHelper?: VoiceSearchHelper; - - public componentDidMount() { - const { searchAsYouSpeak = false, language, refine } = this.props; - this.voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak, - language, - onQueryChange: (query) => refine(query), - onStateChange: () => { - this.setState(this.voiceSearchHelper!.getState()); - }, - }); - this.setState(this.voiceSearchHelper.getState()); - } - - public render() { - if (!this.voiceSearchHelper) { - return null; - } - - const { status, transcript, isSpeechFinal, errorCode } = this.state; - const { isListening, isBrowserSupported } = this.voiceSearchHelper; - const { - translate, - buttonTextComponent: ButtonTextComponent, - statusComponent: StatusComponent, - } = this.props; - const innerProps: InnerComponentProps = { - status, - errorCode, - isListening: isListening(), - transcript, - isSpeechFinal, - isBrowserSupported: isBrowserSupported(), - }; - - return ( -
    - -
    - -
    -
    - ); - } - - public componentWillUnmount() { - if (this.voiceSearchHelper) { - this.voiceSearchHelper.dispose(); - } - } - - private onClick = (event: React.MouseEvent) => { - if (!this.voiceSearchHelper) { - return; - } - event.currentTarget.blur(); - const { toggleListening } = this.voiceSearchHelper; - toggleListening(); - }; -} - -export default translatable({ - buttonTitle: 'Search by voice', - disabledButtonTitle: 'Search by voice (not supported on this browser)', -})(VoiceSearch); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Breadcrumb.js b/packages/react-instantsearch-dom/src/components/__tests__/Breadcrumb.js deleted file mode 100644 index df144fcf54..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Breadcrumb.js +++ /dev/null @@ -1,239 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Breadcrumb from '../Breadcrumb'; -import Link from '../Link'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('Breadcrumb', () => { - it('outputs the default breadcrumb', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - items={[ - { - value: 'white', - label: 'white', - }, - { - value: 'white > white1', - label: 'white1', - }, - { - value: 'white > white1 > white1.1', - label: 'white1.1', - }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('outputs the default breadcrumb with no refinement', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - items={[ - { - value: 'white', - label: 'white', - }, - { - value: 'white > white1', - label: 'white1', - }, - { - value: 'white > white1 > white1.1', - label: 'white1.1', - }, - ]} - canRefine={false} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('outputs the default breadcrumb with a custom className', () => { - const tree = renderer.create( - null} - createURL={() => '#'} - items={[ - { - value: 'white', - label: 'white', - }, - { - value: 'white > white1', - label: 'white1', - }, - { - value: 'white > white1 > white1.1', - label: 'white1.1', - }, - ]} - canRefine={true} - /> - ); - - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { - value: 'white', - label: 'white', - }, - { - value: 'white > white1', - label: 'white1', - }, - { - value: 'white > white1 > white1.1', - label: 'white1.1', - }, - ]} - canRefine={true} - /> - ); - - const breadcrumb = wrapper.find('ul'); - - expect(breadcrumb.children()).toHaveLength(4); - - breadcrumb.children().first().find(Link).simulate('click'); - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual(); - - breadcrumb.children().at(1).find(Link).simulate('click'); - expect(refine.mock.calls).toHaveLength(2); - expect(refine.mock.calls[1][0]).toEqual('white'); - - breadcrumb.children().at(2).find(Link).simulate('click'); - expect(refine.mock.calls).toHaveLength(3); - expect(refine.mock.calls[2][0]).toEqual('white > white1'); - - const lastItem = breadcrumb.children().at(3).find(Link); - - expect(lastItem).toHaveLength(0); - - wrapper.unmount(); - }); - - it('has a rootURL prop', () => { - const refine = jest.fn(); - const rootLink = 'www.algolia.com'; - - const wrapper = mount( - '#'} - rootURL={rootLink} - items={[ - { - value: 'white', - label: 'white', - }, - { - value: 'white > white1', - label: 'white1', - }, - { - value: 'white > white1 > white1.1', - label: 'white1.1', - }, - ]} - canRefine={true} - /> - ); - - const breadcrumb = wrapper.find('ul'); - - expect(breadcrumb.children()).toHaveLength(4); - - breadcrumb.children().first().find(Link).simulate('click'); - expect(refine.mock.calls).toHaveLength(0); - expect(wrapper.find('a').first().prop('href')).toEqual('www.algolia.com'); - - wrapper.unmount(); - }); - - it('has a separator prop that can be a custom component', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - separator={🔍} - items={[ - { - value: 'white', - label: 'white', - }, - { - value: 'white > white1', - label: 'white1', - }, - { - value: 'white > white1 > white1.1', - label: 'white1.1', - }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('has customizable translations', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - translations={{ - rootLabel: 'ROOT_LABEL', - }} - items={[ - { - value: 'white', - label: 'white', - }, - { - value: 'white > white1', - label: 'white1', - }, - { - value: 'white > white1 > white1.1', - label: 'white1.1', - }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/ClearRefinements.js b/packages/react-instantsearch-dom/src/components/__tests__/ClearRefinements.js deleted file mode 100644 index 7e27ab8c8d..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/ClearRefinements.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import ClearRefinements from '../ClearRefinements'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('ClearRefinements', () => { - it('renders a clickable button', () => - expect( - renderer - .create( - null} - /> - ) - .toJSON() - ).toMatchSnapshot()); - - it('renders a clickable button with a custom className', () => - expect( - renderer - .create( - null} - /> - ) - .toJSON() - ).toMatchSnapshot()); - - it('has a disabled mode', () => - expect( - renderer - .create( - null} /> - ) - .toJSON() - ).toMatchSnapshot()); - - it('is disabled when there is no filters', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - - expect(refine.mock.calls).toHaveLength(0); - wrapper.find('button').simulate('click'); - expect(refine.mock.calls).toHaveLength(0); - }); - - it('is not disabled when there are filters', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - - expect(refine.mock.calls).toHaveLength(0); - wrapper.find('button').simulate('click'); - expect(refine.mock.calls).toHaveLength(1); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/CurrentRefinements.js b/packages/react-instantsearch-dom/src/components/__tests__/CurrentRefinements.js deleted file mode 100644 index 0c99707f03..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/CurrentRefinements.js +++ /dev/null @@ -1,154 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import Connected, { CurrentRefinements } from '../CurrentRefinements'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('CurrentRefinements', () => { - const defaultProps = { - items: [], - canRefine: true, - refine: () => {}, - translate: (x) => x, - }; - - it('expect to render a list of current refinements', () => { - const props = { - ...defaultProps, - items: [ - { label: 'color: Red', value: () => {} }, - { - label: 'category:', - value: () => {}, - items: [ - { label: 'iPhone', value: () => {} }, - { label: 'iPad', value: () => {} }, - ], - }, - ], - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render a list without refinements', () => { - const props = { - ...defaultProps, - canRefine: false, - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render a list with a custom className', () => { - const props = { - ...defaultProps, - className: 'MyCustomCurrentRefinements', - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to refine the "color" onClick', () => { - const value = () => {}; - const props = { - ...defaultProps, - items: [ - { label: 'color: Red', value }, - { - label: 'category:', - value: () => {}, - items: [ - { label: 'iPhone', value: () => {} }, - { label: 'iPad', value: () => {} }, - ], - }, - ], - refine: jest.fn(), - }; - - const wrapper = shallow(); - - expect(props.refine).not.toHaveBeenCalled(); - - wrapper.find('li').first().find('button').simulate('click'); - - expect(props.refine).toHaveBeenCalledWith(value); - }); - - it('expect to refine the "category: iPad" onClick', () => { - const value = () => {}; - const props = { - ...defaultProps, - items: [ - { label: 'color: Red', value: () => {} }, - { - label: 'category:', - value: () => {}, - items: [ - { label: 'iPhone', value: () => {} }, - { label: 'iPad', value }, - ], - }, - ], - refine: jest.fn(), - }; - - const wrapper = shallow(); - - expect(props.refine).not.toHaveBeenCalled(); - - wrapper.find('li').last().find('button').last().simulate('click'); - - expect(props.refine).toHaveBeenCalledWith(value); - }); -}); - -describe('CurrentRefinements - Connected', () => { - const defaultProps = { - items: [ - { label: 'color: Red', value: () => {} }, - { - label: 'category:', - value: () => {}, - items: [ - { label: 'iPhone', value: () => {} }, - { label: 'iPad', value: () => {} }, - ], - }, - ], - canRefine: true, - refine: () => {}, - }; - - it('expect to render a list of current refinements', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow().dive(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render a list of current refinements with custom translations', () => { - const props = { - ...defaultProps, - translations: { - clearFilter: 'DELETE', - }, - }; - - const wrapper = shallow().dive(); - - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/HierarchicalMenu.js b/packages/react-instantsearch-dom/src/components/__tests__/HierarchicalMenu.js deleted file mode 100644 index 01cfe3071a..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/HierarchicalMenu.js +++ /dev/null @@ -1,194 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import HierarchicalMenu from '../HierarchicalMenu'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('HierarchicalMenu', () => { - it('default hierarchical menu', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - items={[ - { - value: 'white', - count: 10, - label: 'white', - items: [ - { value: 'white1', label: 'white1', count: 3 }, - { value: 'white2', label: 'white2', count: 4 }, - ], - }, - { value: 'black', isRefined: true, count: 20, label: 'black' }, - { value: 'blue', count: 30, label: 'blue' }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('default hierarchical menu with a custom className', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - items={[ - { - value: 'white', - count: 10, - label: 'white', - items: [ - { value: 'white1', label: 'white1', count: 3 }, - { value: 'white2', label: 'white2', count: 4 }, - ], - }, - { value: 'black', count: 20, label: 'black' }, - { value: 'blue', count: 30, label: 'blue' }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - canRefine={true} - /> - ) - .toJSON(); - - expect(tree).toMatchSnapshot(); - }); - - it('applies translations', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - items={[ - { - value: 'white', - count: 10, - label: 'white', - items: [ - { value: 'white1', label: 'white1', count: 3 }, - { value: 'white2', label: 'white2', count: 4 }, - ], - }, - { value: 'black', count: 20, label: 'black' }, - { value: 'blue', count: 30, label: 'blue' }, - ]} - translations={{ - showMore: ' display more', - }} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { - value: 'white', - count: 10, - label: 'white', - items: [ - { value: 'white1', label: 'white1', count: 3 }, - { value: 'white2', label: 'white2', count: 4 }, - ], - }, - { value: 'black', count: 20, label: 'black' }, - { value: 'blue', count: 30, label: 'blue' }, - ]} - canRefine={true} - /> - ); - - const itemParent = wrapper.find('.ais-HierarchicalMenu-item--parent'); - - expect(itemParent).toHaveLength(1); - - itemParent.find('Link').first().simulate('click'); - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual('white'); - - wrapper.unmount(); - }); - - it('should respect defined limits', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { value: 'white', count: 10, label: 'white' }, - { value: 'black', count: 20, label: 'black' }, - { value: 'blue', count: 30, label: 'blue' }, - { value: 'green', count: 30, label: 'green' }, - { value: 'cyan', count: 30, label: 'cyan' }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(2); - - wrapper.find('button').simulate('click'); - - expect(wrapper.find('li')).toHaveLength(4); - - wrapper.unmount(); - }); - - it('should disabled show more when no more item to display', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { value: 'white', count: 10, label: 'white' }, - { value: 'black', count: 20, label: 'black' }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(2); - - expect(wrapper.find('button')).toBeDefined(); - - wrapper.unmount(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Highlight.js b/packages/react-instantsearch-dom/src/components/__tests__/Highlight.js deleted file mode 100644 index 36451674ee..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Highlight.js +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Highlight from '../Highlight'; - -describe('Highlight', () => { - it('parses an highlighted attribute of hit object', () => { - const hitFromAPI = { - objectID: 0, - deep: { attribute: { value: 'awesome highlighted hit!' } }, - _highlightResult: { - deep: { - attribute: { - value: { - value: - 'awesome highlighted hit!', - fullyHighlighted: true, - matchLevel: 'full', - matchedWords: [''], - }, - }, - }, - }, - }; - - const highlight = () => [ - { value: 'awesome ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 'ghlighted ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 't!', isHighlighted: false }, - ]; - - const tree = renderer.create( - x.join(' ')} - attribute="deep.attribute.value" - hit={hitFromAPI} - highlight={highlight} - /> - ); - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('renders a hit with a custom tag correctly', () => { - const hitFromAPI = { - objectID: 0, - deep: { attribute: { value: 'awesome highlighted hit!' } }, - _highlightResult: { - deep: { - attribute: { - value: { - value: - 'awesome highlighted hit!', - fullyHighlighted: true, - matchLevel: 'full', - matchedWords: [''], - }, - }, - }, - }, - }; - - const highlight = () => [ - { value: 'awesome ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 'ghlighted ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 't!', isHighlighted: false }, - ]; - - const tree = renderer.create( - x.join(' ')} - attribute="deep.attribute.value" - hit={hitFromAPI} - highlight={highlight} - tagName="marquee" - /> - ); - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('renders a hit with a custom className', () => { - const hitFromAPI = { - objectID: 0, - deep: { attribute: { value: 'awesome highlighted hit!' } }, - _highlightResult: { - deep: { - attribute: { - value: { - value: - 'awesome highlighted hit!', - fullyHighlighted: true, - matchLevel: 'full', - matchedWords: [''], - }, - }, - }, - }, - }; - - const highlight = () => [ - { value: 'awesome ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 'ghlighted ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 't!', isHighlighted: false }, - ]; - - const tree = renderer.create( - x.join(' ')} - className="MyCustomHighlight" - attribute="deep.attribute.value" - hit={hitFromAPI} - highlight={highlight} - /> - ); - - expect(tree.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Highlighter.js b/packages/react-instantsearch-dom/src/components/__tests__/Highlighter.js deleted file mode 100644 index 4ab4a9606d..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Highlighter.js +++ /dev/null @@ -1,312 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import Highlighter, { Highlight } from '../Highlighter'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('Highlighter - Highlight', () => { - const defaultProps = { - value: 'test', - highlightedTagName: 'em', - isHighlighted: false, - nonHighlightedTagName: 'div', - }; - - it('renders a highlight', () => { - const props = { - ...defaultProps, - isHighlighted: true, - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a nonhighlight', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); -}); - -describe('Highlighter - simple', () => { - const hitFromAPI = { - objectID: 3, - title: 'Apple', - _highlight: { - title: { - value: 'Apple', - }, - }, - }; - - const defaultProps = { - hit: hitFromAPI, - attribute: 'title', - highlightProperty: '_highlight', - }; - - it('renders a highlighted value', () => { - const props = { - ...defaultProps, - highlight: () => [ - { value: 'Ap', isHighlighted: true }, - { value: 'ple', isHighlighted: false }, - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a non highlighted value', () => { - const props = { - ...defaultProps, - hit: { - objectID: 3, - title: 'Apple', - _highlight: { - title: { - value: 'Apple', - }, - }, - }, - highlight: () => [{ value: 'Apple', isHighlighted: false }], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a highlighted value with a custom tagName', () => { - const props = { - ...defaultProps, - tagName: 'strong', - highlight: () => [ - { value: 'Ap', isHighlighted: true }, - { value: 'ple', isHighlighted: false }, - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a highlighted value with a custom nonHighlightedTagName', () => { - const props = { - ...defaultProps, - nonHighlightedTagName: 'p', - highlight: () => [ - { value: 'Ap', isHighlighted: true }, - { value: 'ple', isHighlighted: false }, - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders with a custom className', () => { - const props = { - ...defaultProps, - className: 'MyCustomHighlighter', - highlight: () => [ - { value: 'Ap', isHighlighted: true }, - { value: 'ple', isHighlighted: false }, - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); -}); - -describe('Highlighter - multi', () => { - const hitFromAPI = { - objectID: 3, - titles: ['Apple', 'Samsung', 'Philips'], - _highlight: { - titles: [ - { - value: 'Apple', - }, - { - value: 'Samsung', - }, - { - value: 'Philips', - }, - ], - }, - }; - - const defaultProps = { - hit: hitFromAPI, - attribute: 'titles', - highlightProperty: '_highlight', - }; - - it('renders a highlighted value', () => { - const props = { - ...defaultProps, - highlight: () => [ - [{ value: 'Apple', isHighlighted: false }], - [ - { value: 'Sam', isHighlighted: true }, - { value: 'sung', isHighlighted: false }, - ], - [{ value: 'Philips', isHighlighted: false }], - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a non highlighted value', () => { - const props = { - ...defaultProps, - hit: { - objectID: 3, - titles: ['Apple', 'Samsung', 'Philips'], - _highlight: { - titles: [ - { - value: 'Apple', - }, - { - value: 'Samsung', - }, - { - value: 'Philips', - }, - ], - }, - }, - highlight: () => [ - [{ value: 'Apple', isHighlighted: false }], - [{ value: 'Samsung', isHighlighted: false }], - [{ value: 'Philips', isHighlighted: false }], - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a highlighted value with a custom tagName', () => { - const props = { - ...defaultProps, - tagName: 'strong', - highlight: () => [ - [{ value: 'Apple', isHighlighted: false }], - [ - { value: 'Sam', isHighlighted: true }, - { value: 'sung', isHighlighted: false }, - ], - [{ value: 'Philips', isHighlighted: false }], - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a highlighted value with a custom nonHighlightedTagName', () => { - const props = { - ...defaultProps, - nonHighlightedTagName: 'p', - highlight: () => [ - [{ value: 'Apple', isHighlighted: false }], - [ - { value: 'Sam', isHighlighted: true }, - { value: 'sung', isHighlighted: false }, - ], - [{ value: 'Philips', isHighlighted: false }], - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a highlighted value with a custom separator', () => { - const props = { - ...defaultProps, - separator: '-', - highlight: () => [ - [{ value: 'Apple', isHighlighted: false }], - [ - { value: 'Sam', isHighlighted: true }, - { value: 'sung', isHighlighted: false }, - ], - [{ value: 'Philips', isHighlighted: false }], - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('renders a custom className', () => { - const props = { - ...defaultProps, - className: 'MyCustomHighlighter', - highlight: () => [ - [{ value: 'Apple', isHighlighted: false }], - [ - { value: 'Sam', isHighlighted: true }, - { value: 'sung', isHighlighted: false }, - ], - [{ value: 'Philips', isHighlighted: false }], - ], - }; - - const wrapper = shallow( - x.join(' ')} {...props} /> - ); - - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Hits.tsx b/packages/react-instantsearch-dom/src/components/__tests__/Hits.tsx deleted file mode 100644 index 9f4cbb17f8..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Hits.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Hits from '../Hits'; - -describe('Hits', () => { - type Props = { hit: any }; - - const Hit = ({ hit }: Props) =>
    ; - - it('accepts a hitComponent prop', () => { - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - - const tree = renderer.create(); - - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('accepts a custom className', () => { - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - - const tree = renderer.create( - - ); - - expect(tree.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/HitsPerPage.js b/packages/react-instantsearch-dom/src/components/__tests__/HitsPerPage.js deleted file mode 100644 index 19799c8d5c..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/HitsPerPage.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import HitsPerPage from '../HitsPerPage'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('HitsPerPage', () => { - it('renders', () => - expect( - renderer - .create( - null} - currentRefinement={5} - items={[ - { - value: 5, - label: 'show 5 hits', - }, - { - value: 10, - label: 'show 10 hits', - }, - ]} - /> - ) - .toJSON() - ).toMatchSnapshot()); - - it('renders with a custom className', () => - expect( - renderer - .create( - null} - currentRefinement={5} - items={[ - { - value: 5, - label: 'show 5 hits', - }, - { - value: 10, - label: 'show 10 hits', - }, - ]} - /> - ) - .toJSON() - ).toMatchSnapshot()); - - it('should forward the id to Select', () => { - const id = 'ais-select'; - const wrapper = mount( - '#'} - items={[ - { value: 2, label: '2 hits per page' }, - { value: 4, label: '4 hits per page' }, - { value: 6, label: '6 hits per page' }, - { value: 8, label: '8 hits per page' }, - ]} - refine={() => null} - currentRefinement={2} - /> - ); - - const selectedValue = wrapper.find('select').getDOMNode(); - expect(selectedValue.getAttribute('id')).toEqual(id); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { value: 2, label: '2 hits per page' }, - { value: 4, label: '4 hits per page' }, - { value: 6, label: '6 hits per page' }, - { value: 8, label: '8 hits per page' }, - ]} - refine={refine} - currentRefinement={2} - /> - ); - - const selectedValue = wrapper.find('select'); - expect(selectedValue.find('option')).toHaveLength(4); - expect(selectedValue.find('option').first().text()).toBe('2 hits per page'); - - selectedValue.find('select').simulate('change', { target: { value: '6' } }); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual('6'); - }); - - it('should use value if no label provided', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[{ value: 2 }, { value: 4 }, { value: 6 }, { value: 8 }]} - refine={refine} - currentRefinement={2} - /> - ); - - const selectedValue = wrapper.find('select'); - expect(selectedValue.find('option')).toHaveLength(4); - expect(selectedValue.find('option').first().text()).toBe('2'); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/InfiniteHits.js b/packages/react-instantsearch-dom/src/components/__tests__/InfiniteHits.js deleted file mode 100644 index 52256d35bd..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/InfiniteHits.js +++ /dev/null @@ -1,144 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import PropTypes from 'prop-types'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import InfiniteHits from '../InfiniteHits'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('InfiniteHits', () => { - const Hit = ({ hit }) =>
    {JSON.stringify(hit)}
    ; - - Hit.propTypes = { - hit: PropTypes.object, - }; - - it('accepts a hitComponent prop', () => { - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - const tree = renderer.create( - undefined} - refineNext={() => undefined} - /> - ); - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('accepts a custom className', () => { - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - const tree = renderer.create( - undefined} - refineNext={() => undefined} - /> - ); - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('calls refineNext when the load more button is clicked', () => { - const mockedRefine = jest.fn(); - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - const wrapped = mount( - undefined} - refineNext={mockedRefine} - hitComponent={Hit} - hits={hits} - hasMore={true} - hasPrevious={false} - /> - ); - expect(mockedRefine.mock.calls).toHaveLength(0); - wrapped.find('button').simulate('click'); - expect(mockedRefine.mock.calls).toHaveLength(1); - }); - - it('calls refinePrevious when the load previous button is clicked', () => { - const mockedRefinePrevious = jest.fn(); - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - const wrapped = mount( - undefined} - hitComponent={Hit} - hits={hits} - hasMore={true} - hasPrevious={true} - /> - ); - expect(mockedRefinePrevious).toHaveBeenCalledTimes(0); - wrapped.find('.ais-InfiniteHits-loadPrevious').simulate('click'); - expect(mockedRefinePrevious).toHaveBeenCalledTimes(1); - }); - - it('render "Show previous" button depending of `showPrevious` prop', () => { - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - const wrapped = mount( - undefined} - refineNext={() => undefined} - hitComponent={Hit} - hits={hits} - hasMore={false} - hasPrevious={false} - /> - ); - expect(wrapped.find('.ais-InfiniteHits-loadPrevious').length).toEqual(0); - - wrapped.setProps({ showPrevious: true }); - - expect(wrapped.find('.ais-InfiniteHits-loadPrevious').length).toEqual(1); - }); - - it('"Show more" button is disabled when it is the last page', () => { - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - const wrapped = mount( - undefined} - refineNext={() => undefined} - hitComponent={Hit} - hits={hits} - hasMore={false} - hasPrevious={false} - /> - ); - expect(wrapped.find('.ais-InfiniteHits-loadMore').props().disabled).toBe( - true - ); - }); - - it('"Show previous" button is disabled when it is the first page', () => { - const hits = [{ objectID: 0 }, { objectID: 1 }, { objectID: 2 }]; - const wrapped = mount( - undefined} - refineNext={() => undefined} - hitComponent={Hit} - hits={hits} - hasMore={false} - hasPrevious={false} - /> - ); - expect( - wrapped.find('.ais-InfiniteHits-loadPrevious').props().disabled - ).toBe(true); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/List.js b/packages/react-instantsearch-dom/src/components/__tests__/List.js deleted file mode 100644 index 3d0d3da308..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/List.js +++ /dev/null @@ -1,148 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import List from '../List'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('List', () => { - const defaultProps = { - items: [], - canRefine: true, - renderItem: (item) => {item.value}, - cx: (...args) => args.filter(Boolean).join(' '), - }; - - const apple = { - label: 'Apple', - value: 'Apple', - count: 100, - isRefined: false, - }; - - const appleSubElements = [ - { - label: 'iPhone', - value: 'iPhone', - count: 50, - isRefined: false, - }, - { - label: 'iPad', - value: 'iPad', - count: 50, - isRefined: false, - }, - ]; - - const samsung = { - label: 'Samsung', - value: 'Samsung', - count: 50, - isRefined: false, - }; - - const samsungSubElements = [ - { - label: 'S8', - value: 'S8', - count: 25, - isRefined: false, - }, - { - label: 'Note 5', - value: 'Note 5', - count: 25, - isRefined: false, - }, - ]; - - const microsoft = { - label: 'Microsoft', - value: 'Microsoft', - count: 25, - isRefined: false, - }; - - const microsoftSubElements = [ - { - label: 'Surface', - value: 'Surface', - count: 13, - isRefined: false, - }, - { - label: 'Surface Pro', - value: 'Surface Pro', - count: 12, - isRefined: false, - }, - ]; - - it('expect to render a list of items', () => { - const props = { - ...defaultProps, - items: [apple, samsung, microsoft], - }; - - const wrapper = shallow(); - - expect(wrapper.find('.item')).toHaveLength(3); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render a list of nested items', () => { - const props = { - ...defaultProps, - items: [ - { - ...apple, - items: appleSubElements, - }, - { - ...samsung, - items: samsungSubElements, - }, - { - ...microsoft, - items: microsoftSubElements, - }, - ], - }; - - const wrapper = shallow(); - - expect(wrapper.find('.item')).toHaveLength(9); // 3 parents + (3 * 2 children) - expect(wrapper.find('.item--parent')).toHaveLength(3); - expect(wrapper.find('.list--child')).toHaveLength(3); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render a list of nested items with empty children', () => { - const props = { - ...defaultProps, - items: [ - { - ...apple, - items: appleSubElements, - }, - { - ...samsung, - items: samsungSubElements, - }, - microsoft, - ], - }; - - const wrapper = shallow(); - - expect(wrapper.find('.item')).toHaveLength(7); // 3 parents + (2 * 2 children) - expect(wrapper.find('.item--parent')).toHaveLength(2); - expect(wrapper.find('.list--child')).toHaveLength(2); - - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Menu.js b/packages/react-instantsearch-dom/src/components/__tests__/Menu.js deleted file mode 100644 index ed796d3835..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Menu.js +++ /dev/null @@ -1,320 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Link from '../Link'; -import Menu from '../Menu'; - -Enzyme.configure({ adapter: new Adapter() }); - -jest.mock('../../widgets/Highlight', () => () => null); - -describe('Menu', () => { - it('default menu', () => { - const tree = renderer - .create( - null} - searchForItems={() => null} - createURL={() => '#'} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - { label: 'green', value: 'green', count: 30, isRefined: false }, - { label: 'red', value: 'red', count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('default menu with custom className', () => { - const tree = renderer - .create( - null} - searchForItems={() => null} - createURL={() => '#'} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - { label: 'green', value: 'green', count: 30, isRefined: false }, - { label: 'red', value: 'red', count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('Menu with search inside items but no search results', () => { - const tree = renderer - .create( - null} - searchForItems={() => null} - searchable - createURL={() => '#'} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: true }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - ]} - isFromSearch={false} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('Menu with search inside items with search results', () => { - const tree = renderer - .create( - null} - searchForItems={() => null} - searchable - createURL={() => '#'} - items={[ - { - label: 'white', - value: 'white', - count: 10, - isRefined: true, - _highlightResult: { label: 'white' }, - }, - ]} - isFromSearch={true} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('applies translations', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - searchForItems={() => null} - searchable - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - { label: 'green', value: 'green', count: 30, isRefined: false }, - { label: 'red', value: 'red', count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - translations={{ - showMore: ' display more', - placeholder: 'placeholder', - }} - isFromSearch={false} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - null} - createURL={() => '#'} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - ]} - isFromSearch={false} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(3); - - const firstItem = items.first().find(Link); - - firstItem.simulate('click'); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual('white'); - - wrapper.unmount(); - }); - - it('should respect defined limits', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - { label: 'green', value: 'green', count: 30, isRefined: false }, - { label: 'red', value: 'red', count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - searchForItems={() => null} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(2); - - wrapper.find('button').simulate('click'); - - expect(wrapper.find('li')).toHaveLength(4); - - wrapper.unmount(); - }); - - it('should disabled show more when no more item to display', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - searchForItems={() => null} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(2); - - expect(wrapper.find('button[disabled]')).toBeDefined(); - - wrapper.unmount(); - }); - - describe('search for facets value', () => { - const refine = jest.fn(); - const searchForItems = jest.fn(); - const menu = ( - '#'} - items={[ - { - label: 'white', - value: 'white', - count: 10, - isRefined: false, - _highlightResult: { label: { value: 'white' } }, - }, - { - label: 'black', - value: 'black', - count: 20, - isRefined: false, - _highlightResult: { label: { value: 'black' } }, - }, - ]} - isFromSearch={true} - canRefine={true} - /> - ); - - it('a searchbox should be displayed if the feature is activated', () => { - const wrapper = mount(menu); - - const searchBox = wrapper.find('.searchBox'); - - expect(searchBox).toBeDefined(); - - wrapper.unmount(); - }); - - it('searching for a value should call searchForItems', () => { - const wrapper = mount(menu); - - wrapper.find('input').simulate('change', { target: { value: 'query' } }); - - expect(searchForItems.mock.calls).toHaveLength(1); - expect(searchForItems.mock.calls[0][0]).toBe('query'); - - wrapper.unmount(); - }); - - it('should refine the selected value and display selected refinement back', () => { - const wrapper = mount(menu); - - const firstItem = wrapper.find('li').first().find(Link); - firstItem.simulate('click'); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual('white'); - expect(wrapper.find('input').props().value).toBe(''); - - const selectedRefinements = wrapper.find('li'); - expect(selectedRefinements).toHaveLength(2); - - wrapper.unmount(); - }); - - it('hit enter on the search values results list should refine the first facet value', () => { - refine.mockClear(); - const wrapper = mount(menu); - - wrapper.find('form').simulate('submit'); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual('white'); - expect(wrapper.find('input').props().value).toBe(''); - - const selectedRefinements = wrapper.find('li'); - expect(selectedRefinements).toHaveLength(2); - - wrapper.unmount(); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/MenuSelect.js b/packages/react-instantsearch-dom/src/components/__tests__/MenuSelect.js deleted file mode 100644 index 7b1487b50d..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/MenuSelect.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import MenuSelect from '../MenuSelect'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('MenuSelect', () => { - it('default menu select', () => { - const tree = renderer - .create( - {}} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - { label: 'green', value: 'green', count: 30, isRefined: false }, - { label: 'red', value: 'red', count: 30, isRefined: false }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('default menu select with custom className', () => { - const tree = renderer - .create( - {}} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - { label: 'green', value: 'green', count: 30, isRefined: false }, - { label: 'red', value: 'red', count: 30, isRefined: false }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('default menu select with custom id', () => { - const id = 'menu-select'; - const wrapper = mount( - {}} items={[]} canRefine={false} /> - ); - - const select = wrapper.find('select').getDOMNode(); - expect(select.getAttribute('id')).toEqual(id); - }); - - it('default menu select with no refinement', () => { - const tree = renderer - .create( {}} items={[]} canRefine={false} />) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('applies translations', () => { - const tree = renderer - .create( - {}} - items={[ - { label: 'white', value: 'white', count: 10, isRefined: false }, - { label: 'black', value: 'black', count: 20, isRefined: false }, - { label: 'blue', value: 'blue', count: 30, isRefined: false }, - { label: 'green', value: 'green', count: 30, isRefined: false }, - { label: 'red', value: 'red', count: 30, isRefined: false }, - ]} - translations={{ - seeAllOption: 'Everything', - }} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - - const items = wrapper.find('option'); - expect(items).toHaveLength(4); // +1 from "see all option" - - wrapper.find('select').simulate('change', { target: { value: 'blue' } }); - - expect(refine).toHaveBeenCalledTimes(1); - expect(refine).toHaveBeenCalledWith('blue'); - - wrapper.unmount(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/NumericMenu.js b/packages/react-instantsearch-dom/src/components/__tests__/NumericMenu.js deleted file mode 100644 index 28c7385f32..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/NumericMenu.js +++ /dev/null @@ -1,268 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import NumericMenu from '../NumericMenu'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('NumericMenu', () => { - it('supports passing items values', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - items={[ - { - label: 'label1', - value: '10:', - isRefined: false, - noRefinement: false, - }, - { - label: 'label2', - value: '10:20', - isRefined: false, - noRefinement: false, - }, - { - label: 'label3', - value: '20:30', - isRefined: false, - noRefinement: false, - }, - { - label: 'label4', - value: '30:', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('supports passing custom className', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - items={[ - { - label: 'label1', - value: '10:', - isRefined: false, - noRefinement: false, - }, - { - label: 'label2', - value: '10:20', - isRefined: false, - noRefinement: false, - }, - { - label: 'label3', - value: '20:30', - isRefined: false, - noRefinement: false, - }, - { - label: 'label4', - value: '30:', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('supports having a selected item', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - items={[ - { - label: 'label1', - value: '10:', - isRefined: false, - noRefinement: false, - }, - { - label: 'label2', - value: '10:20', - isRefined: true, - noRefinement: false, - }, - { - label: 'label3', - value: '20:30', - isRefined: false, - noRefinement: false, - }, - { - label: 'label4', - value: '30:', - isRefined: false, - noRefinement: false, - }, - { label: 'All', value: '', isRefined: false, noRefinement: false }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('no refinements', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - items={[ - { - label: 'label1', - value: '10:', - isRefined: false, - noRefinement: true, - }, - { - label: 'label2', - value: '10:20', - isRefined: false, - noRefinement: true, - }, - { - label: 'label3', - value: '20:30', - isRefined: false, - noRefinement: true, - }, - { - label: 'label4', - value: '30:', - isRefined: false, - noRefinement: true, - }, - { label: 'All', value: '', isRefined: true, noRefinement: false }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(4); - - const firstItem = items.first().find('input'); - - firstItem.simulate('change', { target: { checked: true } }); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual('10:'); - - wrapper.unmount(); - }); - - it('indicate when there is no refinement', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - - const itemWrapper = wrapper.find('.ais-NumericMenu-list--noRefinement'); - expect(itemWrapper).toHaveLength(1); - - const items = wrapper.find('.ais-NumericMenu-item--noRefinement'); - expect(items).toHaveLength(4); - - wrapper.unmount(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Pagination.js b/packages/react-instantsearch-dom/src/components/__tests__/Pagination.js deleted file mode 100644 index a1405ae8eb..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Pagination.js +++ /dev/null @@ -1,395 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Link from '../Link'; -import Pagination from '../Pagination'; - -Enzyme.configure({ adapter: new Adapter() }); - -const REQ_PROPS = { - createURL: () => '#', - refine: () => null, - canRefine: true, -}; - -const DEFAULT_PROPS = { - ...REQ_PROPS, - nbPages: 20, - currentRefinement: 9, -}; - -describe('Pagination', () => { - it('applies its default props', () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('applies its default props without refinement', () => { - const tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('applies its default props with custom className', () => { - const tree = renderer - .create() - .toJSON(); - - expect(tree).toMatchSnapshot(); - }); - - it('displays the correct padding of links', () => { - let tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('allows toggling display of the first page button on and off', () => { - let tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('indicates when first button is relevant', () => { - let tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('allows toggling display of the last page button on and off', () => { - let tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('allows toggling display of the previous page button on and off', () => { - let tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('allows toggling display of the next page button on and off', () => { - let tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create() - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('lets you force a maximum of pages', () => { - let tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - - tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('lets you customize its theme', () => { - const tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('lets you customize its translations', () => { - const tree = renderer - .create( - `PAGE_${(page + 1).toString()}`, - ariaPrevious: 'ARIA_PREVIOUS', - ariaNext: 'ARIA_NEXT', - ariaFirst: 'ARIA_FIRST', - ariaLast: 'ARIA_LAST', - ariaPage: (page) => `ARIA_PAGE_${(page + 1).toString()}`, - }} - showLast - padding={4} - nbPages={10} - currentRefinement={8} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('disabled all button if no results', () => { - const tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refines its value when clicking on a page link', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - - wrapper - .find(Link) - .filterWhere((e) => e.text() === '8') - .simulate('click'); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual(8); - wrapper - .find(Link) - .filterWhere((e) => e.text() === '9') - .simulate('click'); - expect(refine.mock.calls).toHaveLength(2); - const parameters = refine.mock.calls[1][0]; - expect(parameters.valueOf()).toBe(9); - wrapper - .find('.ais-Pagination-item--previousPage') - .find(Link) - .simulate('click'); - expect(refine.mock.calls).toHaveLength(3); - expect(refine.mock.calls[2][0]).toEqual(8); - wrapper.find('.ais-Pagination-item--nextPage').find(Link).simulate('click'); - expect(refine.mock.calls).toHaveLength(4); - expect(refine.mock.calls[3][0]).toEqual(10); - wrapper - .find('.ais-Pagination-item--firstPage') - .find(Link) - .simulate('click'); - expect(refine.mock.calls).toHaveLength(5); - expect(refine.mock.calls[4][0]).toEqual(1); - wrapper.find('.ais-Pagination-item--lastPage').find(Link).simulate('click'); - expect(refine.mock.calls).toHaveLength(6); - expect(refine.mock.calls[5][0]).toEqual(20); - }); - - it('ignores special clicks', () => { - const refine = jest.fn(); - const wrapper = mount(); - const el = wrapper.find(Link).filterWhere((e) => e.text() === '8'); - el.simulate('click', { button: 1 }); - el.simulate('click', { altKey: true }); - el.simulate('click', { ctrlKey: true }); - el.simulate('click', { metaKey: true }); - el.simulate('click', { shiftKey: true }); - expect(refine.mock.calls).toHaveLength(0); - }); - - describe('padding behaviour', () => { - it('should be adjusted when currentPage < padding (at the very beginning)', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - const pages = wrapper.find('.ais-Pagination-item--page'); - const pageSelected = wrapper.find('.ais-Pagination-item--selected'); - // Since padding = 2, the Pagination widget's size should be 5 - expect(pages).toHaveLength(5); - - expect(pages.first().text()).toEqual('1'); - - expect(pageSelected.first().text()).toEqual('2'); - expect(pages.at(1).text()).toEqual('2'); - - expect(pages.at(2).text()).toEqual('3'); - expect(pages.at(3).text()).toEqual('4'); - expect(pages.at(4).text()).toEqual('5'); - }); - it('should be adjusted when currentPage < totalPages - padding (at the end)', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - const pages = wrapper.find('.ais-Pagination-item--page'); - const pageSelected = wrapper.find('.ais-Pagination-item--selected'); - // Since padding = 2, the Pagination widget's size should be 5 - expect(pages).toHaveLength(5); - - expect(pages.first().text()).toEqual('14'); - expect(pages.at(1).text()).toEqual('15'); - expect(pages.at(2).text()).toEqual('16'); - expect(pages.at(3).text()).toEqual('17'); - - expect(pageSelected.first().text()).toEqual('18'); - expect(pages.at(4).text()).toEqual('18'); - }); - it('should render the correct padding in every other case', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - const pages = wrapper.find('.ais-Pagination-item--page'); - const pageSelected = wrapper.find('.ais-Pagination-item--selected'); - // Since padding = 2, the Pagination widget's size should be 5 - expect(pages).toHaveLength(5); - - expect(pages.first().text()).toEqual('6'); - expect(pages.at(1).text()).toEqual('7'); - - expect(pageSelected.first().text()).toEqual('8'); - expect(pages.at(2).text()).toEqual('8'); - - expect(pages.at(3).text()).toEqual('9'); - expect(pages.at(4).text()).toEqual('10'); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Panel.js b/packages/react-instantsearch-dom/src/components/__tests__/Panel.js deleted file mode 100644 index 28d9bcb920..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Panel.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow, mount } from 'enzyme'; -import { createSerializer } from 'enzyme-to-json'; -import React from 'react'; - -import Panel, { PanelConsumer } from '../Panel'; - -expect.addSnapshotSerializer(createSerializer()); -Enzyme.configure({ adapter: new Adapter() }); - -describe('Panel', () => { - it('expect to render', () => { - const wrapper = shallow( - -
    Hello content
    -
    - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with custom className', () => { - const props = { - className: 'ais-Panel-Breadcrumb', - }; - - const wrapper = shallow( - -
    Hello content
    -
    - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render without refinement', () => { - const wrapper = shallow( - -
    Hello content
    -
    - ); - - wrapper.setState({ canRefine: false }); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with header', () => { - const props = { - header:

    Header

    , - }; - - const wrapper = shallow( - -
    Hello content
    -
    - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with footer', () => { - const props = { - footer:

    Footer

    , - }; - - const wrapper = shallow( - -
    Hello content
    -
    - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to expose context to its children', () => { - let provided; - const wrapper = mount( - - - {(setCanRefine) => { - provided = setCanRefine; - return null; - }} - - - ); - - expect(provided).toEqual(wrapper.instance().setCanRefine); - }); - - it('expect to render when setCanRefine is called', () => { - const wrapper = mount( - - - {(setCanRefine) => ( - - )} - - - ); - - expect(wrapper.state('canRefine')).toBe(true); - expect(wrapper).toMatchSnapshot(); - - // Simulate context call - wrapper.find('button').simulate('click'); - - expect(wrapper.state('canRefine')).toBe(false); - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/PanelCallbackHandler.js b/packages/react-instantsearch-dom/src/components/__tests__/PanelCallbackHandler.js deleted file mode 100644 index 9170744d4b..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/PanelCallbackHandler.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow, mount } from 'enzyme'; -import { createSerializer } from 'enzyme-to-json'; -import React from 'react'; - -import { PanelProvider } from '../Panel'; -import PanelCallbackHandler from '../PanelCallbackHandler'; - -expect.addSnapshotSerializer(createSerializer()); -Enzyme.configure({ adapter: new Adapter() }); - -describe('PanelCallbackHandler', () => { - it('expect to render', () => { - const wrapper = mount( - -
    Hello content
    -
    - ); - - expect(wrapper).toMatchInlineSnapshot(` - - -
    - Hello content -
    -
    -
    - `); - }); - - describe('didMount', () => { - it('expect to call setCanRefine when the context is given', () => { - const setCanRefine = jest.fn(); - - mount( - - -
    Hello content
    -
    -
    - ); - - expect(setCanRefine).toHaveBeenCalledTimes(1); - expect(setCanRefine).toHaveBeenCalledWith(true); - }); - - it('expect to not throw when the context is not given', () => { - expect(() => - shallow( - -
    Hello content
    -
    - ) - ).not.toThrow(); - }); - }); - - describe('didUpdate', () => { - it('expect to call setCanRefine when the context is given', () => { - const setCanRefine = jest.fn(); - - const wrapper = mount( - - -
    Hello content
    -
    -
    - ); - - wrapper.setProps({ - children: ( - -
    Hello content
    -
    - ), - }); - - expect(setCanRefine).toHaveBeenCalledTimes(2); - expect(setCanRefine).toHaveBeenLastCalledWith(false); - }); - - it('expect to not call setCanRefine when the nextProps is the same', () => { - const setCanRefine = jest.fn(); - - const wrapper = mount( - - -
    Hello content
    -
    -
    - ); - - wrapper.setProps({ - children: ( - -
    Hello content
    -
    - ), - }); - - expect(setCanRefine).toHaveBeenCalledTimes(1); - }); - - it('expect to not throw when the context is not given', () => { - expect(() => { - const wrapper = shallow( - -
    Hello content
    -
    - ); - - wrapper.setProps({ canRefine: false }); - }).not.toThrow(); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/PoweredBy.js b/packages/react-instantsearch-dom/src/components/__tests__/PoweredBy.js deleted file mode 100644 index 2c26f2b98e..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/PoweredBy.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; - -import PoweredBy from '../PoweredBy'; - -describe('PoweredBy', () => { - it('default', () => { - const tree = renderer - .create( '#'} url="url" />) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('default with className', () => { - const tree = renderer - .create( - '#'} - url="url" - /> - ) - .toJSON(); - - expect(tree).toMatchSnapshot(); - }); - - it('applies translations', () => { - const tree = renderer - .create( - '#'} - url="url" - translations={{ - searchBy: ' Search By', - }} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/QueryRuleCustomData.tsx b/packages/react-instantsearch-dom/src/components/__tests__/QueryRuleCustomData.tsx deleted file mode 100644 index a73f05aed0..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/QueryRuleCustomData.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import QueryRuleCustomData from '../QueryRuleCustomData'; - -import type { QueryRuleCustomDataProps } from '../QueryRuleCustomData'; - -Enzyme.configure({ adapter: new Adapter() }); - -type CustomDataItem = { - [key: string]: any; -}; - -type CustomDataProps = QueryRuleCustomDataProps; - -describe('QueryRuleCustomData', () => { - it('expects to render the empty container with empty items', () => { - const props: CustomDataProps = { - items: [], - children: jest.fn(({ items }) => - items.map((item) => ( -
    - {item.title} -
    - )) - ), - }; - - const wrapper = shallow(); - - expect(props.children).toHaveBeenCalledTimes(1); - expect(props.children).toHaveBeenCalledWith({ items: props.items }); - expect(wrapper).toMatchSnapshot(); - }); - - it('expects to render multiple items', () => { - const props: CustomDataProps = { - items: [ - { title: 'Image 1', banner: 'image-1.png' }, - { title: 'Image 2', banner: 'image-2.png' }, - ], - children: jest.fn(({ items }) => - items.map((item) => ( -
    - {item.title} -
    - )) - ), - }; - - const wrapper = shallow(); - - expect(props.children).toHaveBeenCalledTimes(1); - expect(props.children).toHaveBeenCalledWith({ items: props.items }); - expect(wrapper).toMatchSnapshot(); - }); - - it('expects to render with custom className', () => { - const props: CustomDataProps = { - items: [], - className: 'CustomClassName', - children: jest.fn(() => null), - }; - - const wrapper = shallow(); - - expect(wrapper.props().className).toContain('CustomClassName'); - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/RangeInput.js b/packages/react-instantsearch-dom/src/components/__tests__/RangeInput.js deleted file mode 100644 index 47506f898f..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/RangeInput.js +++ /dev/null @@ -1,320 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import RangeInput, { RawRangeInput } from '../RangeInput'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('RangeInput', () => { - const shallowRender = (props = {}) => { - const defaultProps = { - currentRefinement: { - min: undefined, - max: undefined, - }, - canRefine: true, - precision: 0, - refine: () => {}, - min: undefined, - max: undefined, - }; - - return shallow(); - }; - - it('render with translations', () => { - const props = { - translations: { - submit: 'SUBMIT', - separator: 'SEPARATOR', - }, - }; - - const component = shallowRender(props).shallow(); - - expect(component).toMatchSnapshot(); - }); -}); - -describe('RawRangeInput', () => { - const shallowRender = (props = {}) => { - const defaultProps = { - currentRefinement: { - min: undefined, - max: undefined, - }, - canRefine: true, - precision: 0, - refine: () => {}, - translate: (x) => x, - min: undefined, - max: undefined, - }; - - return shallow(); - }; - - it('render with empty values', () => { - const props = {}; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with empty values when refinement is equal to min / max', () => { - const props = { - currentRefinement: { - min: 0, - max: 500, - }, - min: 0, - max: 500, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with refinement', () => { - const props = { - currentRefinement: { - min: 10, - max: 490, - }, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with min / max', () => { - const props = { - min: 0, - max: 500, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with min only', () => { - const props = { - min: 0, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with max only', () => { - const props = { - max: 500, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with precision of 1', () => { - const props = { - precision: 1, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with precision of 2', () => { - const props = { - precision: 2, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it("render when can't refine", () => { - const props = { - canRefine: false, - min: 0, - max: 100, - currentRefinement: { - min: 10, - max: 90, - }, - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - it('render with custom className', () => { - const props = { - className: 'MyCustomRangeInput', - }; - - const component = shallowRender(props); - - expect(component).toMatchSnapshot(); - }); - - describe('didUpdate', () => { - it('expect to update state when props have changed', () => { - const props = { - canRefine: false, - currentRefinement: { - min: 0, - max: 100, - }, - }; - - const wrapper = shallowRender(props); - - wrapper.setProps({ - canRefine: true, - currentRefinement: { - min: 10, - max: 90, - }, - }); - - wrapper.update(); - - expect(wrapper.state()).toEqual({ - from: 10, - to: 90, - }); - }); - - it("expect to not update state when props don't have changed", () => { - const props = { - canRefine: true, - currentRefinement: { - min: 0, - max: 100, - }, - }; - - const wrapper = shallowRender(props); - - wrapper.setState({ - from: 10, - to: 90, - }); - - wrapper.setProps({ - canRefine: true, - currentRefinement: { - min: 0, - max: 100, - }, - }); - - wrapper.update(); - - expect(wrapper.state()).toEqual({ - from: 10, - to: 90, - }); - }); - }); - - describe('onChange', () => { - it('expect to update min onChange', () => { - const props = {}; - const component = shallowRender(props); - - component - .find('input') - .first() - .simulate('change', { - currentTarget: { - value: 10, - }, - }); - - expect(component).toMatchSnapshot(); - expect(component.state()).toEqual({ - from: 10, - to: '', - }); - }); - - it('expect to update max onChange', () => { - const props = {}; - const component = shallowRender(props); - - component - .find('input') - .last() - .simulate('change', { - currentTarget: { - value: 490, - }, - }); - - expect(component).toMatchSnapshot(); - expect(component.state()).toEqual({ - from: '', - to: 490, - }); - }); - }); - - describe('onSubmit', () => { - it('expect to call refine onSubmit with values', () => { - const props = { - refine: jest.fn(), - }; - - const event = { - preventDefault: jest.fn(), - }; - - const component = shallowRender(props); - - component.setState({ - from: 10, - to: 490, - }); - - component.find('form').simulate('submit', event); - - expect(event.preventDefault).toHaveBeenCalled(); - expect(props.refine).toHaveBeenCalledWith({ - min: 10, - max: 490, - }); - }); - - it('expect to not call refine with empty string', () => { - const props = { - refine: jest.fn(), - }; - - const event = { - preventDefault: jest.fn(), - }; - - const component = shallowRender(props); - - component.find('form').simulate('submit', event); - - expect(event.preventDefault).toHaveBeenCalled(); - expect(props.refine).toHaveBeenCalledWith({ - min: '', - max: '', - }); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/RatingMenu.js b/packages/react-instantsearch-dom/src/components/__tests__/RatingMenu.js deleted file mode 100644 index b31efe8b2e..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/RatingMenu.js +++ /dev/null @@ -1,319 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import RatingMenu from '../RatingMenu'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('RatingMenu', () => { - it('supports passing max/min values', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - min={1} - max={5} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('supports passing max/min values smaller than count max & min', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - min={2} - max={4} - currentRefinement={{}} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('supports passing custom className', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - min={1} - max={5} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('expect to render without refinement', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - min={1} - max={5} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={false} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('applies translations', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - translations={{ - ratingLabel: ' & Up', - }} - min={1} - max={5} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('expect to not throw when only min is defined', () => { - expect(() => { - renderer.create( - '#'} - refine={() => null} - translations={{ - ratingLabel: ' & Up', - }} - min={3} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ); - }).not.toThrow(); - }); - - it('expect to render when only max is defined', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - translations={{ - ratingLabel: ' & Up', - }} - max={3} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('expect to render from from 1 when min is negative', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - translations={{ - ratingLabel: ' & Up', - }} - min={-5} - max={5} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('expect to render nothing when min is higher than max', () => { - const tree = renderer - .create( - '#'} - refine={() => null} - translations={{ - ratingLabel: ' & Up', - }} - min={5} - max={3} - currentRefinement={{ min: 1, max: 5 }} - count={[ - { value: '1', count: 1 }, - { value: '2', count: 2 }, - { value: '3', count: 3 }, - { value: '4', count: 4 }, - { value: '5', count: 5 }, - ]} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - const refine = jest.fn(); - const createURL = jest.fn(); - const ratingMenu = ( - - ); - - beforeEach(() => { - refine.mockClear(); - createURL.mockClear(); - }); - - it('should create an URL for each row except for the largest: the default selected one', () => { - const wrapper = mount(ratingMenu); - - expect(createURL.mock.calls).toHaveLength(3); - expect(createURL.mock.calls[0][0]).toEqual({ min: 4, max: 5 }); - expect(createURL.mock.calls[1][0]).toEqual({ min: 3, max: 5 }); - expect(createURL.mock.calls[2][0]).toEqual({ min: 2, max: 5 }); - - wrapper.unmount(); - }); - - it('refines its value on change', () => { - const wrapper = mount(ratingMenu); - const links = wrapper.find('.ais-RatingMenu-link'); - expect(links).toHaveLength(5); - - let selectedItem = wrapper.find('.ais-RatingMenu-item--selected'); - expect(selectedItem).toHaveLength(1); - - links.first().simulate('click'); - - expect(refine.mock.calls).toHaveLength(0); - - selectedItem = wrapper.find('.ais-RatingMenu-item--selected'); - expect(selectedItem).toBeDefined(); - - const disabledIcon = wrapper - .find('.ais-RatingMenu-item--disabled') - .find('.ais-RatingMenu-starIcon'); - - expect(disabledIcon).toHaveLength(5); - wrapper.unmount(); - }); - - it('should display the right number of stars', () => { - const wrapper = mount(ratingMenu); - wrapper.find('.ais-RatingMenu-link').last().simulate('click'); - - const selectedItem = wrapper.find('.ais-RatingMenu-item--selected'); - - const fullIcon = selectedItem.find('.ais-RatingMenu-starIcon--full'); - const emptyIcon = selectedItem.find('.ais-RatingMenu-starIcon--empty'); - - expect(fullIcon).toHaveLength(1); - expect(emptyIcon).toHaveLength(4); - wrapper.unmount(); - }); - - it('clicking on the selected refinement should select the largest range', () => { - const wrapper = mount(ratingMenu); - wrapper.setProps({ currentRefinement: { min: 4, max: 5 } }); - - const links = wrapper.find('.ais-RatingMenu-link'); - links.at(1).simulate('click'); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual({ min: 1, max: 5 }); - wrapper.unmount(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/RefinementList.js b/packages/react-instantsearch-dom/src/components/__tests__/RefinementList.js deleted file mode 100644 index 449dca96c2..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/RefinementList.js +++ /dev/null @@ -1,368 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import RefinementList from '../RefinementList'; - -Enzyme.configure({ adapter: new Adapter() }); - -jest.mock('../../widgets/Highlight', () => () => null); - -describe('RefinementList', () => { - it('default refinement list', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - searchForItems={() => null} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: true }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - { label: 'blue', value: ['blue'], count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('default refinement list with custom className', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - searchForItems={() => null} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: true }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - { label: 'blue', value: ['blue'], count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('default refinement list with no refinement', () => { - const tree = renderer - .create( - null} - createURL={() => '#'} - searchForItems={() => null} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: true }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - { label: 'blue', value: ['blue'], count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - canRefine={false} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refinement list with search inside items but no search results', () => { - const tree = renderer - .create( - null} - searchForItems={() => null} - searchable - createURL={() => '#'} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: true }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - { label: 'blue', value: ['blue'], count: 30, isRefined: false }, - ]} - isFromSearch={false} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refinement list with search inside items with search results', () => { - const tree = renderer - .create( - null} - searchable - searchForItems={() => null} - createURL={() => '#'} - items={[ - { - label: 'white', - value: ['white'], - count: 10, - isRefined: true, - _highlightResult: { label: 'white' }, - }, - ]} - isFromSearch={true} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('applies translations', () => { - const tree = renderer - .create( - null} - searchForItems={() => null} - searchable - createURL={() => '#'} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: false }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - { label: 'blue', value: ['blue'], count: 30, isRefined: false }, - ]} - isFromSearch={false} - translations={{ - showMore: ' display more', - noResults: ' no results', - placeholder: 'placeholder', - }} - canRefine={true} - /> - ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - null} - createURL={() => '#'} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: false }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - { label: 'blue', value: ['blue'], count: 30, isRefined: false }, - ]} - isFromSearch={false} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(3); - - const firstItem = items.first().find('input[type="checkbox"]'); - - firstItem.simulate('change', { target: { checked: true } }); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual(['white']); - - wrapper.unmount(); - }); - - it('should respect defined limits', () => { - const refine = jest.fn(); - const wrapper = mount( - null} - createURL={() => '#'} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: false }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - { label: 'blue', value: ['blue'], count: 30, isRefined: false }, - { label: 'red', value: ['red'], count: 30, isRefined: false }, - { label: 'green', value: ['green'], count: 30, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(2); - - wrapper.find('button').simulate('click'); - - expect(wrapper.find('li')).toHaveLength(4); - - wrapper.unmount(); - }); - - it('should disabled show more when no more item to display', () => { - const refine = jest.fn(); - const wrapper = mount( - null} - createURL={() => '#'} - items={[ - { label: 'white', value: ['white'], count: 10, isRefined: false }, - { label: 'black', value: ['black'], count: 20, isRefined: false }, - ]} - limit={2} - showMoreLimit={4} - showMore={true} - isFromSearch={false} - canRefine={true} - /> - ); - - const items = wrapper.find('li'); - - expect(items).toHaveLength(2); - - expect(wrapper.find('button[disabled]')).toBeDefined(); - - wrapper.unmount(); - }); - - describe('search inside items', () => { - const refine = jest.fn(); - const searchForItems = jest.fn(); - const refinementList = ( - '#'} - items={[ - { - label: 'white', - value: ['white'], - count: 10, - isRefined: false, - _highlightResult: { label: { value: 'white' } }, - }, - { - label: 'black', - value: ['black'], - count: 20, - isRefined: false, - _highlightResult: { label: { value: 'black' } }, - }, - ]} - isFromSearch={true} - canRefine={true} - /> - ); - - it('a searchbox should be displayed if the feature is activated', () => { - const wrapper = mount(refinementList); - - const searchBox = wrapper.find('input[type="search"]'); - - expect(searchBox).toBeDefined(); - - wrapper.unmount(); - }); - - it('searching for a value should call searchForItems', () => { - const wrapper = mount(refinementList); - - wrapper - .find('input[type="search"]') - .simulate('change', { target: { value: 'query' } }); - - expect(searchForItems.mock.calls).toHaveLength(1); - expect(searchForItems.mock.calls[0][0]).toBe('query'); - - wrapper.unmount(); - }); - - it('should refine the selected value and display selected refinement back', () => { - const wrapper = mount(refinementList); - - const firstItem = wrapper - .find('li') - .first() - .find('input[type="checkbox"]'); - firstItem.simulate('change', { target: { checked: true } }); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual(['white']); - expect(wrapper.find('input[type="search"]').props().value).toBe(''); - - const selectedRefinements = wrapper.find('li'); - expect(selectedRefinements).toHaveLength(2); - - wrapper.unmount(); - }); - - it('hit enter on the search values results list should refine the first facet value', () => { - refine.mockClear(); - const wrapper = mount(refinementList); - - wrapper.find('form').simulate('submit'); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual(['white']); - expect(wrapper.find('input[type="search"]').props().value).toBe(''); - - const selectedRefinements = wrapper.find('li'); - expect(selectedRefinements).toHaveLength(2); - - wrapper.unmount(); - }); - - it('hit enter on an empty search values results list should do nothing', () => { - const emptyRefinementList = ( - '#'} - items={[]} - isFromSearch={true} - canRefine={true} - /> - ); - - refine.mockClear(); - const wrapper = mount(emptyRefinementList); - const input = wrapper.find('input[type="search"]'); - input.props().value = 'white'; - - wrapper.find('form').simulate('submit'); - - expect(refine.mock.calls).toHaveLength(0); - expect(input.props().value).toBe('white'); - - const selectedRefinements = wrapper.find('li'); - expect(selectedRefinements).toHaveLength(0); - - wrapper.unmount(); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/RelevantSort.tsx b/packages/react-instantsearch-dom/src/components/__tests__/RelevantSort.tsx deleted file mode 100644 index 68d107e0cf..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/RelevantSort.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; - -import RelevantSort from '../RelevantSort'; - -import type { RelevantSortComponentProps } from '../RelevantSort'; - -describe('RelevantSort', () => { - it("returns null if it's not a virtual replica", () => { - const tree = renderer.create( - {}} - /> - ); - - expect(tree.toJSON()).toBeNull(); - }); - - it('accepts a custom className', () => { - const tree = renderer.create( - {}} - /> - ); - - expect(tree.root.props.className.includes('MyCustomRelevantSort')).toBe( - true - ); - }); - - it('forward isRelevantSorted to props components', () => { - const mockTextComponent = jest.fn(() => null); - - const mockButtonTextComponent = jest.fn(() => null); - - renderer.create( - {}} - textComponent={mockButtonTextComponent} - /> - ); - - expect(mockTextComponent).toHaveBeenCalledWith( - { - isRelevantSorted: true, - }, - {} - ); - expect(mockButtonTextComponent).toHaveBeenCalledWith( - { - isRelevantSorted: true, - }, - {} - ); - }); - - it('renders with the default ButtonTextComponent', () => { - const tree = renderer.create( - {}} - /> - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    -
    - -
    - `); - }); - - it('renders with a custom ButtonTextComponent', () => { - const tree = renderer.create( - ( - - {isRelevantSorted ? 'See all results' : 'See relevant results'} - - )} - isVirtualReplica={true} - isRelevantSorted={true} - refine={() => {}} - /> - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    -
    - -
    - `); - }); - - it('renders without a textComponent', () => { - const tree = renderer.create( - {}} - /> - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    -
    - -
    - `); - }); - - it('renders with a custom textComponent', () => { - const tree = renderer.create( - {}} - textComponent={({ isRelevantSorted }: RelevantSortComponentProps) => ( -

    - {isRelevantSorted - ? 'We removed some search results to show you the most relevant ones' - : 'Currently showing all results'} -

    - )} - /> - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    -
    -

    - Currently showing all results -

    -
    - -
    - `); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/ScrollTo.js b/packages/react-instantsearch-dom/src/components/__tests__/ScrollTo.js deleted file mode 100644 index efcb617f76..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/ScrollTo.js +++ /dev/null @@ -1,70 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import ScrollTo from '../ScrollTo'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('ScrollTo', () => { - it('expect to render', () => { - const wrapper = shallow( - - content - - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to call scrollIntoView', () => { - const wrapper = shallow( - - content - - ); - - // Simulate ref - wrapper.instance().el = { - scrollIntoView: jest.fn(), - }; - - wrapper.setProps({ value: 5 }); - - expect(wrapper.instance().el.scrollIntoView).toHaveBeenCalled(); - }); - - it('expect to not call scrollIntoView when previous value do not have changed', () => { - const wrapper = shallow( - - content - - ); - - // Simulate ref - wrapper.instance().el = { - scrollIntoView: jest.fn(), - }; - - wrapper.setProps({ value: 4 }); - - expect(wrapper.instance().el.scrollIntoView).not.toHaveBeenCalled(); - }); - - it('expect to not call scrollIntoView when hasNotChanged is false', () => { - const wrapper = shallow( - - content - - ); - - // Simulate ref - wrapper.instance().el = { - scrollIntoView: jest.fn(), - }; - - wrapper.setProps({ value: 5 }); - - expect(wrapper.instance().el.scrollIntoView).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/SearchBox.js b/packages/react-instantsearch-dom/src/components/__tests__/SearchBox.js deleted file mode 100644 index 4619f01611..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/SearchBox.js +++ /dev/null @@ -1,367 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow, mount } from 'enzyme'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import SearchBox from '../SearchBox'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('SearchBox', () => { - it('applies its default props', () => { - const instance = renderer.create( null} />); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.unmount(); - }); - - it('applies its default props with custom className', () => { - const instance = renderer.create( - null} /> - ); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.unmount(); - }); - - it('applies its default props with custom inputId', () => { - const inputId = 'search-box'; - const wrapper = mount( null} />); - - const input = wrapper.find('input').getDOMNode(); - expect(input.getAttribute('id')).toEqual(inputId); - }); - - it('transfers the autoFocus prop to the underlying input element', () => { - const instance = renderer.create( - null} autoFocus /> - ); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.unmount(); - }); - - it('treats its query prop as its input value', () => { - const instance = renderer.create( - null} currentRefinement="QUERY1" /> - ); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.update( - null} currentRefinement="QUERY2" /> - ); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.unmount(); - }); - - it('lets you customize its theme', () => { - const instance = renderer.create( - null} - theme={{ - root: 'ROOT', - wrapper: 'WRAPPER', - input: 'INPUT', - submit: 'SUBMIT', - reset: 'RESET', - }} - /> - ); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.unmount(); - }); - - it('lets you give custom components for reset and submit', () => { - const instance = renderer.create( - null} - submit={🔍} - reset={ - - - - } - /> - ); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.unmount(); - }); - - it('lets you customize its translations', () => { - const instance = renderer.create( - null} - translations={{ - resetTitle: 'RESET_TITLE', - placeholder: 'PLACEHOLDER', - }} - /> - ); - - expect(instance.toJSON()).toMatchSnapshot(); - - instance.unmount(); - }); - - it('treats query as a default value when searchAsYouType=false', () => { - const wrapper = mount( - null} - currentRefinement="QUERY1" - searchAsYouType={false} - /> - ); - - expect(wrapper.find('input').props().value).toBe('QUERY1'); - - wrapper.find('input').simulate('change', { target: { value: 'QUERY2' } }); - - expect(wrapper.find('input').props().value).toBe('QUERY2'); - - wrapper.unmount(); - }); - - it('refines its value on change when searchAsYouType=true', () => { - const refine = jest.fn(); - const wrapper = mount(); - - wrapper.find('input').simulate('change', { target: { value: 'hello' } }); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toBe('hello'); - - wrapper.unmount(); - }); - - it('only refines its query on submit when searchAsYouType=false', () => { - const refine = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('input').simulate('change', { target: { value: 'hello' } }); - - expect(refine.mock.calls).toHaveLength(0); - - wrapper.find('form').simulate('submit'); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toBe('hello'); - - wrapper.unmount(); - }); - - it('onSubmit behavior should be override if provided as props', () => { - const onSubmit = jest.fn(); - const refine = jest.fn(); - const wrapper = mount( - - ); - - wrapper.find('form').simulate('submit'); - - expect(onSubmit.mock.calls).toHaveLength(1); - expect(refine.mock.calls).toHaveLength(0); - - wrapper.unmount(); - }); - - it('focuses the input when one of the keys in focusShortcuts is pressed', () => { - let input; - - const wrapper = mount( - null} - focusShortcuts={['s', 84]} - inputRef={(node) => { - input = node; - }} - /> - ); - - input.focus = jest.fn(); - - const event1 = new KeyboardEvent('keydown', { keyCode: 82 }); - document.dispatchEvent(event1); - expect(input.focus.mock.calls).toHaveLength(0); - - const event2 = new KeyboardEvent('keydown', { keyCode: 83 }); - document.dispatchEvent(event2); - expect(input.focus.mock.calls).toHaveLength(1); - - const event3 = new KeyboardEvent('keydown', { keyCode: 84 }); - document.dispatchEvent(event3); - expect(input.focus.mock.calls).toHaveLength(2); - - wrapper.unmount(); - }); - - it('should accept `onXXX` events', () => { - const onSubmit = jest.fn(); - const onReset = jest.fn(); - - const inputEventsList = [ - 'onChange', - 'onFocus', - 'onBlur', - 'onSelect', - 'onKeyDown', - 'onKeyPress', - ]; - - const inputProps = inputEventsList.reduce( - (props, prop) => ({ ...props, [prop]: jest.fn() }), - {} - ); - - const wrapper = mount( - null} - onSubmit={onSubmit} - onReset={onReset} - {...inputProps} - /> - ); - - // simulate form events `onReset` && `onSubmit` - wrapper.find('form').simulate('submit'); - expect(onSubmit).toHaveBeenCalled(); - - wrapper.find('form').simulate('reset'); - expect(onReset).toHaveBeenCalled(); - - // simulate input search events - inputEventsList.forEach((eventName) => { - wrapper - .find('input') - .simulate(eventName.replace(/^on/, '').toLowerCase()); - - expect(inputProps[eventName]).toHaveBeenCalled(); - }); - - wrapper.unmount(); - }); - - it('should render the loader if showLoadingIndicator is true', () => { - const instanceWithoutLoadingIndicator = renderer.create( - null} showLoadingIndicator /> - ); - - expect(instanceWithoutLoadingIndicator.toJSON()).toMatchSnapshot(); - - const instanceWithLoadingIndicator = renderer.create( - null} showLoadingIndicator isSearchStalled /> - ); - - expect(instanceWithLoadingIndicator.toJSON()).toMatchSnapshot(); - - instanceWithoutLoadingIndicator.unmount(); - instanceWithLoadingIndicator.unmount(); - }); - - it('expect to clear the query when the form is reset with searchAsYouType=true', () => { - const refine = jest.fn(); - - const wrapper = shallow( - - ).dive(); - - // Simulate the ref - wrapper.instance().input = { focus: jest.fn() }; - - wrapper.find('form').simulate('reset'); - - expect(refine).toHaveBeenCalledWith(''); - expect(wrapper.instance().input.focus).toHaveBeenCalled(); - }); - - it('expect to clear the query when the form is reset with searchAsYouType=false', () => { - const refine = jest.fn(); - - const wrapper = shallow( - - ).dive(); - - // Simulate the ref - wrapper.instance().input = { focus: jest.fn() }; - - // Simulate change event - wrapper.setState({ query: 'Hello' }); - - wrapper.find('form').simulate('reset'); - - expect(refine).toHaveBeenCalledWith(''); - expect(wrapper.instance().input.focus).toHaveBeenCalled(); - expect(wrapper.state()).toEqual({ query: '' }); - }); - - it('should point created refs to its input element', () => { - const inputRef = React.createRef(); - const wrapper = mount( - null} inputRef={inputRef} /> - ); - - const refSpy = jest.spyOn(inputRef.current, 'focus'); - - // Trigger input.focus() - wrapper.find('form').simulate('reset'); - - expect(refSpy).toHaveBeenCalled(); - expect(inputRef.current.tagName).toEqual('INPUT'); - }); - - it('should return a ref when given a callback as input ref', () => { - let inputRef; - const wrapper = mount( - null} - inputRef={(ref) => { - inputRef = ref; - }} - /> - ); - - const refSpy = jest.spyOn(inputRef, 'focus'); - - // Trigger input.focus() - wrapper.find('form').simulate('reset'); - - expect(refSpy).toHaveBeenCalled(); - expect(inputRef.tagName).toEqual('INPUT'); - }); - - it('should point created refs to its form element', () => { - const formRef = React.createRef(); - mount( null} formRef={formRef} />); - - expect(formRef.current.tagName).toEqual('FORM'); - }); - - it('should return a ref when given a callback as form ref', () => { - let formRef; - mount( - null} - formRef={(ref) => { - formRef = ref; - }} - /> - ); - - expect(formRef.tagName).toEqual('FORM'); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Snippet.js b/packages/react-instantsearch-dom/src/components/__tests__/Snippet.js deleted file mode 100644 index ea6f121cd6..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Snippet.js +++ /dev/null @@ -1,122 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Snippet from '../Snippet'; - -describe('Snippet', () => { - it('parses an highlighted snippet attribute of hit object', () => { - const hitFromAPI = { - objectID: 0, - deep: { attribute: { value: 'awesome highlighted hit!' } }, - _snippetResult: { - deep: { - attribute: { - value: { - value: - 'awesome highlighted hit!', - fullyHighlighted: true, - matchLevel: 'full', - matchedWords: [''], - }, - }, - }, - }, - }; - - const highlight = () => [ - { value: 'awesome ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 'ghlighted ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 't!', isHighlighted: false }, - ]; - - const tree = renderer.create( - x.join(' ')} - attribute="deep.attribute.value" - hit={hitFromAPI} - highlight={highlight} - /> - ); - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('renders a hit with a custom tag correctly', () => { - const hitFromAPI = { - objectID: 0, - deep: { attribute: { value: 'awesome highlighted hit!' } }, - _snippetResult: { - deep: { - attribute: { - value: { - value: - 'awesome highlighted hit!', - fullyHighlighted: true, - matchLevel: 'full', - matchedWords: [''], - }, - }, - }, - }, - }; - - const highlight = () => [ - { value: 'awesome ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 'ghlighted ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 't!', isHighlighted: false }, - ]; - - const tree = renderer.create( - x.join(' ')} - attribute="deep.attribute.value" - hit={hitFromAPI} - highlight={highlight} - tagName="strong" - /> - ); - expect(tree.toJSON()).toMatchSnapshot(); - }); - - it('renders a hit with a custom className', () => { - const hitFromAPI = { - objectID: 0, - deep: { attribute: { value: 'awesome highlighted hit!' } }, - _snippetResult: { - deep: { - attribute: { - value: { - value: - 'awesome highlighted hit!', - fullyHighlighted: true, - matchLevel: 'full', - matchedWords: [''], - }, - }, - }, - }, - }; - - const highlight = () => [ - { value: 'awesome ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 'ghlighted ', isHighlighted: false }, - { value: 'hi', isHighlighted: true }, - { value: 't!', isHighlighted: false }, - ]; - - const tree = renderer.create( - x.join(' ')} - className="MyCustomSnippet" - attribute="deep.attribute.value" - hit={hitFromAPI} - highlight={highlight} - /> - ); - expect(tree.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/SortBy.js b/packages/react-instantsearch-dom/src/components/__tests__/SortBy.js deleted file mode 100644 index a02aceef98..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/SortBy.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow, mount } from 'enzyme'; -import React from 'react'; - -import SortBy from '../SortBy'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('SortBy', () => { - it('expect to render with custom className', () => { - const wrapper = shallow( - '#'} - items={[ - { value: 'index1', label: 'index name 1' }, - { value: 'index2', label: 'index name 2' }, - { value: 'index3', label: 'index name 3' }, - { value: 'index4', label: 'index name 4' }, - ]} - currentRefinement={'index1'} - refine={() => null} - /> - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('should forward the id to Select', () => { - const id = 'ais-select'; - const wrapper = mount( - null} - /> - ); - - const select = wrapper.find('select').getDOMNode(); - expect(select.getAttribute('id')).toEqual(id); - }); - - it('refines its value on change', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { value: 'index1', label: 'index name 1' }, - { value: 'index2', label: 'index name 2' }, - { value: 'index3', label: 'index name 3' }, - { value: 'index4', label: 'index name 4' }, - ]} - currentRefinement={'index1'} - refine={refine} - /> - ); - - const selectedValue = wrapper.find('select'); - expect(selectedValue.find('option')).toHaveLength(4); - expect(selectedValue.find('option').first().text()).toBe('index name 1'); - - selectedValue - .find('select') - .simulate('change', { target: { value: 'index3' } }); - - expect(refine.mock.calls).toHaveLength(1); - expect(refine.mock.calls[0][0]).toEqual('index3'); - }); - - it('should use value if no label provided', () => { - const refine = jest.fn(); - const wrapper = mount( - '#'} - items={[ - { value: 'index1' }, - { value: 'index2' }, - { value: 'index3' }, - { value: 'index4' }, - ]} - refine={refine} - currentRefinement={'index1'} - /> - ); - - const selectedValue = wrapper.find('select'); - expect(selectedValue.find('option')).toHaveLength(4); - expect(selectedValue.find('option').first().text()).toBe('index1'); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/Stats.tsx b/packages/react-instantsearch-dom/src/components/__tests__/Stats.tsx deleted file mode 100644 index f1ff1faa42..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/Stats.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; - -import Stats from '../Stats'; - -describe('Stats', () => { - it('renders with default props', () => { - const tree = renderer.create( - - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    - - 42 results found in 0ms - -
    - `); - }); - - it('accepts a custom className', () => { - const tree = renderer.create( - - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    - - 42 results found in 0ms - -
    - `); - }); - - it('renders rely on areHitsSorted', () => { - const tree = renderer.create( - - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    - - 21 relevant results sorted out of 42 found in 0ms - -
    - `); - }); - - it('renders default implementation if nbHits is equal to nbSortedHits', () => { - const tree = renderer.create( - - ); - - expect(tree.toJSON()).toMatchInlineSnapshot(` -
    - - 42 results found in 0ms - -
    - `); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/ToggleRefinement.js b/packages/react-instantsearch-dom/src/components/__tests__/ToggleRefinement.js deleted file mode 100644 index 904074367a..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/ToggleRefinement.js +++ /dev/null @@ -1,78 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { shallow } from 'enzyme'; -import React from 'react'; - -import ToggleRefinement from '../ToggleRefinement'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('ToggleRefinement', () => { - const defaultProps = { - currentRefinement: true, - label: 'toggle the refinement', - canRefine: true, - refine: () => {}, - }; - - it('expect to render with a positive currentRefinement', () => { - const props = { - ...defaultProps, - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with a negative currentRefinement', () => { - const props = { - ...defaultProps, - currentRefinement: false, - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with a negative canRefine', () => { - const props = { - ...defaultProps, - canRefine: false, - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to render with custom className', () => { - const props = { - ...defaultProps, - className: 'MyCustomToggleRefinement', - }; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('expect to call refine onChange', () => { - const props = { - ...defaultProps, - refine: jest.fn(), - }; - - const wrapper = shallow(); - - expect(props.refine).not.toHaveBeenCalled(); - - wrapper.find('input[type="checkbox"]').simulate('change', { - target: { - checked: false, - }, - }); - - expect(props.refine).toHaveBeenCalledWith(false); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/VoiceSearch.tsx b/packages/react-instantsearch-dom/src/components/__tests__/VoiceSearch.tsx deleted file mode 100644 index c73abfb8e4..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/VoiceSearch.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import Enzyme, { mount } from 'enzyme'; -import { createSerializer } from 'enzyme-to-json'; -import React from 'react'; - -import VoiceSearch from '../VoiceSearch'; - -import type { InnerComponentProps } from '../VoiceSearch'; - -expect.addSnapshotSerializer(createSerializer() as any); -Enzyme.configure({ adapter: new Adapter() }); - -const mockGetState = jest.fn().mockImplementation(() => ({})); -const mockIsBrowserSupported = jest.fn().mockImplementation(() => true); -const mockIsListening = jest.fn(); -const mockToggleListening = jest.fn(); -const mockDispose = jest.fn(); - -jest.mock('../../lib/voiceSearchHelper', () => { - return () => { - return { - getState: mockGetState, - isBrowserSupported: mockIsBrowserSupported, - isListening: mockIsListening, - toggleListening: mockToggleListening, - dispose: mockDispose, - }; - }; -}); - -describe('VoiceSearch', () => { - afterEach(() => { - mockGetState.mockImplementation(() => ({})); - mockIsBrowserSupported.mockImplementation(() => true); - mockIsListening.mockClear(); - mockToggleListening.mockClear(); - }); - - describe('button', () => { - it('calls toggleListening when button is clicked', () => { - const wrapper = mount(); - wrapper.find('button').simulate('click'); - expect(mockToggleListening).toHaveBeenCalledTimes(1); - }); - }); - - describe('Rendering', () => { - it('with default props', () => { - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); - }); - - it('with custom component for button with isListening: false', () => { - const customButtonText = ({ isListening }: { isListening: boolean }) => - isListening ? 'Stop' : 'Start'; - - const wrapper = mount( - - ); - expect(wrapper.find('button').text()).toBe('Start'); - }); - - it('with custom component for button with isListening: true', () => { - const customButtonText = ({ isListening }: { isListening: boolean }) => - isListening ? 'Stop' : 'Start'; - mockIsListening.mockImplementation(() => true); - - const wrapper = mount( - - ); - expect(wrapper.find('button').text()).toBe('Stop'); - }); - - it('renders a disabled button when the browser is not supported', () => { - mockIsBrowserSupported.mockImplementation(() => false); - const wrapper = mount(); - expect(wrapper.find('button').prop('title')).toBe( - 'Search by voice (not supported on this browser)' - ); - expect(wrapper.find('button').prop('disabled')).toBe(true); - }); - - it('with custom template for status', () => { - const customStatus = ({ - status, - errorCode, - isListening, - transcript, - isSpeechFinal, - isBrowserSupported, - }: InnerComponentProps) => ( -
    -

    status: {status}

    -

    errorCode: {errorCode}

    -

    isListening: {isListening ? 'true' : 'false'}

    -

    transcript: {transcript}

    -

    isSpeechFinal: {isSpeechFinal ? 'true' : 'false'}

    -

    isBrowserSupported: {isBrowserSupported ? 'true' : 'false'}

    -
    - ); - - mockIsListening.mockImplementation(() => true); - mockGetState.mockImplementation(() => ({ - status: 'recognizing', - transcript: 'Hello', - isSpeechFinal: false, - errorCode: undefined, - })); - - const wrapper = mount(); - expect(wrapper.find('.ais-VoiceSearch-status')).toMatchSnapshot(); - }); - - it('calls voiceSearchHelper.dispose() on unmount', () => { - const wrapper = mount(); - wrapper.find('button').simulate('click'); - wrapper.unmount(); - expect(mockDispose).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Breadcrumb.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Breadcrumb.js.snap deleted file mode 100644 index f0aebbacda..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Breadcrumb.js.snap +++ /dev/null @@ -1,316 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Breadcrumb has a separator prop that can be a custom component 1`] = ` -
    - -
    -`; - -exports[`Breadcrumb has customizable translations 1`] = ` -
    - -
    -`; - -exports[`Breadcrumb outputs the default breadcrumb 1`] = ` -
    - -
    -`; - -exports[`Breadcrumb outputs the default breadcrumb with a custom className 1`] = ` -
    - -
    -`; - -exports[`Breadcrumb outputs the default breadcrumb with no refinement 1`] = ` -
    - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ClearRefinements.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ClearRefinements.js.snap deleted file mode 100644 index ddc5e21cec..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ClearRefinements.js.snap +++ /dev/null @@ -1,43 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ClearRefinements has a disabled mode 1`] = ` -
    - -
    -`; - -exports[`ClearRefinements renders a clickable button 1`] = ` -
    - -
    -`; - -exports[`ClearRefinements renders a clickable button with a custom className 1`] = ` -
    - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/CurrentRefinements.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/CurrentRefinements.js.snap deleted file mode 100644 index 5ac83b0a83..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/CurrentRefinements.js.snap +++ /dev/null @@ -1,240 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CurrentRefinements - Connected expect to render a list of current refinements 1`] = ` -
    -
      -
    • - - color: Red - - - - -
    • -
    • - - category: - - - - iPhone - - - - - - iPad - - - -
    • -
    -
    -`; - -exports[`CurrentRefinements - Connected expect to render a list of current refinements with custom translations 1`] = ` -
    -
      -
    • - - color: Red - - - - -
    • -
    • - - category: - - - - iPhone - - - - - - iPad - - - -
    • -
    -
    -`; - -exports[`CurrentRefinements expect to render a list of current refinements 1`] = ` -
    -
      -
    • - - color: Red - - - - -
    • -
    • - - category: - - - - iPhone - - - - - - iPad - - - -
    • -
    -
    -`; - -exports[`CurrentRefinements expect to render a list with a custom className 1`] = ` -
    -
      -
    -`; - -exports[`CurrentRefinements expect to render a list without refinements 1`] = ` -
    -
      -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HierarchicalMenu.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HierarchicalMenu.js.snap deleted file mode 100644 index 6bd0ec285b..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HierarchicalMenu.js.snap +++ /dev/null @@ -1,333 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`HierarchicalMenu applies translations 1`] = ` - -`; - -exports[`HierarchicalMenu default hierarchical menu 1`] = ` - -`; - -exports[`HierarchicalMenu default hierarchical menu with a custom className 1`] = ` - -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlight.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlight.js.snap deleted file mode 100644 index 5a8d743438..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlight.js.snap +++ /dev/null @@ -1,97 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Highlight parses an highlighted attribute of hit object 1`] = ` - - - awesome - - - hi - - - ghlighted - - - hi - - - t! - - -`; - -exports[`Highlight renders a hit with a custom className 1`] = ` - - - awesome - - - hi - - - ghlighted - - - hi - - - t! - - -`; - -exports[`Highlight renders a hit with a custom tag correctly 1`] = ` - - - awesome - - - hi - - - ghlighted - - - hi - - - t! - - -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlighter.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlighter.js.snap deleted file mode 100644 index 9d7921dafc..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Highlighter.js.snap +++ /dev/null @@ -1,482 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Highlighter - Highlight renders a highlight 1`] = ` - - test - -`; - -exports[`Highlighter - Highlight renders a nonhighlight 1`] = ` -
    - test -
    -`; - -exports[`Highlighter - multi renders a custom className 1`] = ` - - - - - , - - - - - - - , - - - - - - -`; - -exports[`Highlighter - multi renders a highlighted value 1`] = ` - - - - - , - - - - - - - , - - - - - - -`; - -exports[`Highlighter - multi renders a highlighted value with a custom nonHighlightedTagName 1`] = ` - - - - - , - - - - - - - , - - - - - - -`; - -exports[`Highlighter - multi renders a highlighted value with a custom separator 1`] = ` - - - - - - - - - - - - - - - - - - - - -`; - -exports[`Highlighter - multi renders a highlighted value with a custom tagName 1`] = ` - - - - - , - - - - - - - , - - - - - - -`; - -exports[`Highlighter - multi renders a non highlighted value 1`] = ` - - - - - , - - - - - - , - - - - - - -`; - -exports[`Highlighter - simple renders a highlighted value 1`] = ` - - - - -`; - -exports[`Highlighter - simple renders a highlighted value with a custom nonHighlightedTagName 1`] = ` - - - - -`; - -exports[`Highlighter - simple renders a highlighted value with a custom tagName 1`] = ` - - - - -`; - -exports[`Highlighter - simple renders a non highlighted value 1`] = ` - - - -`; - -exports[`Highlighter - simple renders with a custom className 1`] = ` - - - - -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Hits.tsx.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Hits.tsx.snap deleted file mode 100644 index 7626f41400..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Hits.tsx.snap +++ /dev/null @@ -1,65 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Hits accepts a custom className 1`] = ` -
    -
      -
    • -
      -
    • -
    • -
      -
    • -
    • -
      -
    • -
    -
    -`; - -exports[`Hits accepts a hitComponent prop 1`] = ` -
    -
      -
    • -
      -
    • -
    • -
      -
    • -
    • -
      -
    • -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HitsPerPage.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HitsPerPage.js.snap deleted file mode 100644 index 74ec1ccaea..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/HitsPerPage.js.snap +++ /dev/null @@ -1,51 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`HitsPerPage renders 1`] = ` -
    - -
    -`; - -exports[`HitsPerPage renders with a custom className 1`] = ` -
    - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/InfiniteHits.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/InfiniteHits.js.snap deleted file mode 100644 index bb3f6c8984..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/InfiniteHits.js.snap +++ /dev/null @@ -1,79 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`InfiniteHits accepts a custom className 1`] = ` -
    -
      -
    • -
      - {"objectID":0} -
      -
    • -
    • -
      - {"objectID":1} -
      -
    • -
    • -
      - {"objectID":2} -
      -
    • -
    - -
    -`; - -exports[`InfiniteHits accepts a hitComponent prop 1`] = ` -
    -
      -
    • -
      - {"objectID":0} -
      -
    • -
    • -
      - {"objectID":1} -
      -
    • -
    • -
      - {"objectID":2} -
      -
    • -
    - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/List.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/List.js.snap deleted file mode 100644 index 657a88ee98..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/List.js.snap +++ /dev/null @@ -1,206 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`List expect to render a list of items 1`] = ` -
    -
      -
    • - - Apple - -
    • -
    • - - Samsung - -
    • -
    • - - Microsoft - -
    • -
    -
    -`; - -exports[`List expect to render a list of nested items 1`] = ` -
    -
      -
    • - - Apple - -
        -
      • - - iPhone - -
      • -
      • - - iPad - -
      • -
      -
    • -
    • - - Samsung - -
        -
      • - - S8 - -
      • -
      • - - Note 5 - -
      • -
      -
    • -
    • - - Microsoft - -
        -
      • - - Surface - -
      • -
      • - - Surface Pro - -
      • -
      -
    • -
    -
    -`; - -exports[`List expect to render a list of nested items with empty children 1`] = ` -
    -
      -
    • - - Apple - -
        -
      • - - iPhone - -
      • -
      • - - iPad - -
      • -
      -
    • -
    • - - Samsung - -
        -
      • - - S8 - -
      • -
      • - - Note 5 - -
      • -
      -
    • -
    • - - Microsoft - -
    • -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Menu.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Menu.js.snap deleted file mode 100644 index 6e8ca2cf36..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Menu.js.snap +++ /dev/null @@ -1,489 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Menu Menu with search inside items but no search results 1`] = ` -
    -
    -
    -
    - - - -
    -
    -
    - -
    -`; - -exports[`Menu Menu with search inside items with search results 1`] = ` -
    -
    -
    -
    - - - -
    -
    -
    - -
    -`; - -exports[`Menu applies translations 1`] = ` -
    -
    -
    -
    - - - -
    -
    -
    - - -
    -`; - -exports[`Menu default menu 1`] = ` - -`; - -exports[`Menu default menu with custom className 1`] = ` - -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/MenuSelect.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/MenuSelect.js.snap deleted file mode 100644 index 79213421d1..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/MenuSelect.js.snap +++ /dev/null @@ -1,212 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`MenuSelect applies translations 1`] = ` -
    - -
    -`; - -exports[`MenuSelect default menu select 1`] = ` -
    - -
    -`; - -exports[`MenuSelect default menu select with custom className 1`] = ` -
    - -
    -`; - -exports[`MenuSelect default menu select with no refinement 1`] = ` -
    - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/NumericMenu.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/NumericMenu.js.snap deleted file mode 100644 index e2f377760d..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/NumericMenu.js.snap +++ /dev/null @@ -1,445 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NumericMenu no refinements 1`] = ` -
    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -`; - -exports[`NumericMenu supports having a selected item 1`] = ` -
    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -`; - -exports[`NumericMenu supports passing custom className 1`] = ` -
    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -`; - -exports[`NumericMenu supports passing items values 1`] = ` -
    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Pagination.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Pagination.js.snap deleted file mode 100644 index 57dc5dc343..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Pagination.js.snap +++ /dev/null @@ -1,2919 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Pagination allows toggling display of the first page button on and off 1`] = ` -
    - -
    -`; - -exports[`Pagination allows toggling display of the first page button on and off 2`] = ` -
    - -
    -`; - -exports[`Pagination allows toggling display of the last page button on and off 1`] = ` -
    - -
    -`; - -exports[`Pagination allows toggling display of the last page button on and off 2`] = ` -
    - -
    -`; - -exports[`Pagination allows toggling display of the next page button on and off 1`] = ` -
    - -
    -`; - -exports[`Pagination allows toggling display of the next page button on and off 2`] = ` -
    - -
    -`; - -exports[`Pagination allows toggling display of the previous page button on and off 1`] = ` -
    - -
    -`; - -exports[`Pagination allows toggling display of the previous page button on and off 2`] = ` -
    - -
    -`; - -exports[`Pagination applies its default props 1`] = ` -
    - -
    -`; - -exports[`Pagination applies its default props with custom className 1`] = ` -
    - -
    -`; - -exports[`Pagination applies its default props without refinement 1`] = ` -
    - -
    -`; - -exports[`Pagination disabled all button if no results 1`] = ` -
    -
      -
    • - - « - -
    • -
    • - - ‹ - -
    • -
    • - - › - -
    • -
    • - - » - -
    • -
    -
    -`; - -exports[`Pagination displays the correct padding of links 1`] = ` -
    - -
    -`; - -exports[`Pagination displays the correct padding of links 2`] = ` -
    - -
    -`; - -exports[`Pagination displays the correct padding of links 3`] = ` -
    - -
    -`; - -exports[`Pagination displays the correct padding of links 4`] = ` -
    - -
    -`; - -exports[`Pagination indicates when first button is relevant 1`] = ` -
    - -
    -`; - -exports[`Pagination indicates when first button is relevant 2`] = ` -
    - -
    -`; - -exports[`Pagination lets you customize its theme 1`] = ` -
    - -
    -`; - -exports[`Pagination lets you customize its translations 1`] = ` - -`; - -exports[`Pagination lets you force a maximum of pages 1`] = ` -
    - -
    -`; - -exports[`Pagination lets you force a maximum of pages 2`] = ` -
    - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Panel.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Panel.js.snap deleted file mode 100644 index 044f0bb356..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Panel.js.snap +++ /dev/null @@ -1,149 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Panel expect to render 1`] = ` -
    -
    - -
    - Hello content -
    -
    -
    -
    -`; - -exports[`Panel expect to render when setCanRefine is called 1`] = ` - -
    -
    - -
    -
    -
    -`; - -exports[`Panel expect to render when setCanRefine is called 2`] = ` - -
    -
    - -
    -
    -
    -`; - -exports[`Panel expect to render with custom className 1`] = ` -
    -
    - -
    - Hello content -
    -
    -
    -
    -`; - -exports[`Panel expect to render with footer 1`] = ` -
    -
    - -
    - Hello content -
    -
    -
    -
    -

    - Footer -

    -
    -
    -`; - -exports[`Panel expect to render with header 1`] = ` -
    -
    -

    - Header -

    -
    -
    - -
    - Hello content -
    -
    -
    -
    -`; - -exports[`Panel expect to render without refinement 1`] = ` -
    -
    - -
    - Hello content -
    -
    -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/PoweredBy.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/PoweredBy.js.snap deleted file mode 100644 index 239022d11a..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/PoweredBy.js.snap +++ /dev/null @@ -1,97 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PoweredBy applies translations 1`] = ` -
    - - Search By - - - - - - - -
    -`; - -exports[`PoweredBy default 1`] = ` -
    - - Search by - - - - - - - -
    -`; - -exports[`PoweredBy default with className 1`] = ` -
    - - Search by - - - - - - - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/QueryRuleCustomData.tsx.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/QueryRuleCustomData.tsx.snap deleted file mode 100644 index e203e7a623..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/QueryRuleCustomData.tsx.snap +++ /dev/null @@ -1,36 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`QueryRuleCustomData expects to render multiple items 1`] = ` -
    -
    - Image 1 -
    -
    - Image 2 -
    -
    -`; - -exports[`QueryRuleCustomData expects to render the empty container with empty items 1`] = ` -
    -`; - -exports[`QueryRuleCustomData expects to render with custom className 1`] = ` -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RangeInput.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RangeInput.js.snap deleted file mode 100644 index e18e3e8e54..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RangeInput.js.snap +++ /dev/null @@ -1,586 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`RangeInput render with translations 1`] = ` -
    -
    - - - SEPARATOR - - - -
    -
    -`; - -exports[`RawRangeInput onChange expect to update max onChange 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput onChange expect to update min onChange 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render when can't refine 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with custom className 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with empty values 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with empty values when refinement is equal to min / max 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with max only 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with min / max 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with min only 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with precision of 1 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with precision of 2 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; - -exports[`RawRangeInput render with refinement 1`] = ` -
    -
    - - - separator - - - -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RatingMenu.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RatingMenu.js.snap deleted file mode 100644 index 787e1c4bbd..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RatingMenu.js.snap +++ /dev/null @@ -1,2597 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`RatingMenu applies translations 1`] = ` - -`; - -exports[`RatingMenu expect to render from from 1 when min is negative 1`] = ` - -`; - -exports[`RatingMenu expect to render nothing when min is higher than max 1`] = ` -
    - - - - - - - - -
      -
    -`; - -exports[`RatingMenu expect to render when only max is defined 1`] = ` - -`; - -exports[`RatingMenu expect to render without refinement 1`] = ` - -`; - -exports[`RatingMenu supports passing custom className 1`] = ` - -`; - -exports[`RatingMenu supports passing max/min values 1`] = ` - -`; - -exports[`RatingMenu supports passing max/min values smaller than count max & min 1`] = ` - -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RefinementList.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RefinementList.js.snap deleted file mode 100644 index b3fa1eaef4..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/RefinementList.js.snap +++ /dev/null @@ -1,615 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`RefinementList applies translations 1`] = ` -
    -
    -
    -
    - - - -
    -
    -
    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -`; - -exports[`RefinementList default refinement list 1`] = ` -
    -
      -
    • - -
    • -
    • - -
    • -
    - -
    -`; - -exports[`RefinementList default refinement list with custom className 1`] = ` -
    -
      -
    • - -
    • -
    • - -
    • -
    - -
    -`; - -exports[`RefinementList default refinement list with no refinement 1`] = ` -
    -
      -
    • - -
    • -
    • - -
    • -
    - -
    -`; - -exports[`RefinementList refinement list with search inside items but no search results 1`] = ` -
    -
    -
    -
    - - - -
    -
    -
    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -`; - -exports[`RefinementList refinement list with search inside items with search results 1`] = ` -
    -
    -
    -
    - - - -
    -
    -
    -
      -
    • - -
    • -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ScrollTo.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ScrollTo.js.snap deleted file mode 100644 index 4b8d2bb38e..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/ScrollTo.js.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ScrollTo expect to render 1`] = ` -
    - content -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SearchBox.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SearchBox.js.snap deleted file mode 100644 index 2b7935ce80..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SearchBox.js.snap +++ /dev/null @@ -1,743 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SearchBox applies its default props 1`] = ` -
    -
    - - - -
    -
    -`; - -exports[`SearchBox applies its default props with custom className 1`] = ` -
    -
    - - - -
    -
    -`; - -exports[`SearchBox lets you customize its theme 1`] = ` -
    -
    - - - -
    -
    -`; - -exports[`SearchBox lets you customize its translations 1`] = ` -
    -
    - - - -
    -
    -`; - -exports[`SearchBox lets you give custom components for reset and submit 1`] = ` -
    -
    - - - -
    -
    -`; - -exports[`SearchBox should render the loader if showLoadingIndicator is true 1`] = ` -
    -
    - - - - -
    -
    -`; - -exports[`SearchBox should render the loader if showLoadingIndicator is true 2`] = ` -
    -
    - - - - -
    -
    -`; - -exports[`SearchBox transfers the autoFocus prop to the underlying input element 1`] = ` -
    -
    - - - -
    -
    -`; - -exports[`SearchBox treats its query prop as its input value 1`] = ` -
    -
    - - - -
    -
    -`; - -exports[`SearchBox treats its query prop as its input value 2`] = ` -
    -
    - - - -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Snippet.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Snippet.js.snap deleted file mode 100644 index 9f713bcb09..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/Snippet.js.snap +++ /dev/null @@ -1,97 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Snippet parses an highlighted snippet attribute of hit object 1`] = ` - - - awesome - - - hi - - - ghlighted - - - hi - - - t! - - -`; - -exports[`Snippet renders a hit with a custom className 1`] = ` - - - awesome - - - hi - - - ghlighted - - - hi - - - t! - - -`; - -exports[`Snippet renders a hit with a custom tag correctly 1`] = ` - - - awesome - - - hi - - - ghlighted - - - hi - - - t! - - -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SortBy.js.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SortBy.js.snap deleted file mode 100644 index 951d935259..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/SortBy.js.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SortBy expect to render with custom className 1`] = ` -
    - - - toggle the refinement - - -
    -`; - -exports[`ToggleRefinement expect to render with a negative currentRefinement 1`] = ` -
    - -
    -`; - -exports[`ToggleRefinement expect to render with a positive currentRefinement 1`] = ` -
    - -
    -`; - -exports[`ToggleRefinement expect to render with custom className 1`] = ` -
    - -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/VoiceSearch.tsx.snap b/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/VoiceSearch.tsx.snap deleted file mode 100644 index 6d9e612fa0..0000000000 --- a/packages/react-instantsearch-dom/src/components/__tests__/__snapshots__/VoiceSearch.tsx.snap +++ /dev/null @@ -1,111 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`VoiceSearch Rendering with custom template for status 1`] = ` -
    - -
    -

    - status: - recognizing -

    -

    - errorCode: -

    -

    - isListening: - true -

    -

    - transcript: - Hello -

    -

    - isSpeechFinal: - false -

    -

    - isBrowserSupported: - true -

    -
    -
    -
    -`; - -exports[`VoiceSearch Rendering with default props 1`] = ` - - -
    - -
    - -

    - -

    -
    -
    -
    -`; diff --git a/packages/react-instantsearch-dom/src/components/index.js b/packages/react-instantsearch-dom/src/components/index.js deleted file mode 100644 index 2ca115c7ec..0000000000 --- a/packages/react-instantsearch-dom/src/components/index.js +++ /dev/null @@ -1,18 +0,0 @@ -export { default as CurrentRefinements } from './CurrentRefinements'; -export { default as HierarchicalMenu } from './HierarchicalMenu'; -export { default as Hits } from './Hits'; -export { default as HitsPerPage } from './HitsPerPage'; -export { default as Menu } from './Menu'; -export { default as NumericMenu } from './NumericMenu'; -export { default as Pagination } from './Pagination'; -export { default as PoweredBy } from './PoweredBy'; -export { default as RangeInput } from './RangeInput'; -export { default as RatingMenu } from './RatingMenu'; -export { default as RefinementList } from './RefinementList'; -export { default as ClearRefinements } from './ClearRefinements'; -export { default as ScrollTo } from './ScrollTo'; -export { default as SearchBox } from './SearchBox'; -export { default as RelevantSort } from './RelevantSort'; -export { default as SortBy } from './SortBy'; -export { default as Stats } from './Stats'; -export { default as ToggleRefinement } from './ToggleRefinement'; diff --git a/packages/react-instantsearch-dom/src/core/__tests__/createInstantSearchServer.js b/packages/react-instantsearch-dom/src/core/__tests__/createInstantSearchServer.js deleted file mode 100644 index 1051daf589..0000000000 --- a/packages/react-instantsearch-dom/src/core/__tests__/createInstantSearchServer.js +++ /dev/null @@ -1,1255 +0,0 @@ -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; -import { SearchParameters } from 'algoliasearch-helper'; -import Enzyme from 'enzyme'; -import React from 'react'; -import { - Index, - InstantSearch, - DynamicWidgets, - createConnector, - version, - connectRefinementList, -} from 'react-instantsearch-core'; -import { SearchBox } from 'react-instantsearch-dom'; - -import { findResultsState } from '../createInstantSearchServer'; - -Enzyme.configure({ adapter: new Adapter() }); - -describe('findResultsState', () => { - const createSearchClient = ({ transformResponseParams } = {}) => ({ - search: jest.fn((requests) => - Promise.resolve({ - results: requests.map(({ indexName, params: { query } }) => ({ - query, - hits: [], - renderingContent: { - facetOrdering: { - facets: { - order: ['brand', 'hierarchicalCategories.lvl0', 'categories'], - }, - values: { - brand: { - sortRemainingBy: 'count', - }, - categories: { - sortRemainingBy: 'count', - }, - 'hierarchicalCategories.lvl0': { - sortRemainingBy: 'count', - }, - }, - }, - }, - params: transformResponseParams - ? transformResponseParams(query) - : `query=${encodeURIComponent(query)}`, - index: indexName, - })), - }) - ), - }); - - const createWidget = ({ getSearchParameters = () => {} } = {}) => - createConnector({ - displayName: 'CoolConnector', - getProvidedProps: () => null, - getSearchParameters(searchParameters, props, searchState) { - getSearchParameters(); - - const fallback = props.defaultRefinement || 'Apple'; - - if (this.props.indexContextValue) { - const index = this.props.indexContextValue.targetedIndex; - const indexSearchState = - searchState.indices && searchState.indices[index] - ? searchState.indices[index] - : {}; - - return searchParameters.setQuery(indexSearchState.query || fallback); - } - - return searchParameters.setQuery(searchState.query || fallback); - }, - getMetadata(props, searchState) { - return { id: 'cool', props, searchState }; - }, - getId: () => 'id', - })(() => null); - - const requiredProps = { - indexName: 'indexName', - searchClient: createSearchClient({}), - }; - - it('throws an error if props are not provided', () => { - const App = () =>
    ; - - const trigger = () => findResultsState(App); - - expect(() => trigger()).toThrowErrorMatchingInlineSnapshot( - `"The function \`findResultsState\` must be called with props: \`findResultsState(App, props)\`"` - ); - }); - - it('throws an error if props does not have a `searchClient`', () => { - const App = () =>
    ; - - const props = {}; - - const trigger = () => findResultsState(App, props); - - expect(() => trigger()).toThrowErrorMatchingInlineSnapshot( - `"The props provided to \`findResultsState\` must have a \`searchClient\`"` - ); - }); - - it('throws an error if props does not have an `indexName`', () => { - const App = () =>
    ; - - const props = { - searchClient: createSearchClient({}), - }; - - const trigger = () => findResultsState(App, props); - - expect(() => trigger()).toThrowErrorMatchingInlineSnapshot( - `"The props provided to \`findResultsState\` must have an \`indexName\`"` - ); - }); - - it('adds expected Algolia agents', () => { - const App = (props) => ( - - - - ); - - const searchClient = { - ...createSearchClient({}), - addAlgoliaAgent: jest.fn(), - }; - - const props = { - ...requiredProps, - searchClient, - }; - - findResultsState(App, props); - - // The `addAlgoliaAgent` method is called 7 times: - // - 1 times with react-instantsearch-dom/server - // - 2 times with react-instantsearch-core/InstantSearch - // - 4 times with the AlgoliasearchHelper - - expect(searchClient.addAlgoliaAgent).toHaveBeenCalledTimes(7); - expect(searchClient.addAlgoliaAgent).toHaveBeenCalledWith( - `react (${React.version})` - ); - expect(searchClient.addAlgoliaAgent).toHaveBeenCalledWith( - `react-instantsearch (${version})` - ); - expect(searchClient.addAlgoliaAgent).toHaveBeenCalledWith( - `react-instantsearch-server (${version})` - ); - }); - - it('does not throw if `searchClient` does not have a `addAlgoliaAgent()` method', () => { - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({}), - }; - - const trigger = () => findResultsState(App, props); - - expect(() => trigger()).not.toThrow(); - }); - - it('throws if no widgets are added', () => { - const App = (props) => ; - - const props = { - ...requiredProps, - searchClient: createSearchClient({}), - }; - - expect(() => - findResultsState(App, props) - ).toThrowErrorMatchingInlineSnapshot( - `"[ssr]: no widgets were added, you likely did not pass the \`widgetsCollector\` down to the InstantSearch component."` - ); - }); - - describe('single index', () => { - it('results should be state & results', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - }; - - const results = await findResultsState(App, props); - - expect(results).toEqual({ - metadata: [expect.objectContaining({ id: 'cool' })], - state: expect.any(SearchParameters), - rawResults: expect.arrayContaining([ - expect.objectContaining({ query: expect.any(String) }), - ]), - }); - }); - - it('searchParameters should be cleaned each time', async () => { - const getSearchParameters = jest.fn(); - const Connected = createWidget({ - getSearchParameters, - }); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - }; - - await findResultsState(App, props); - - expect(getSearchParameters).toHaveBeenCalledTimes(1); - - getSearchParameters.mockClear(); - - await findResultsState(App, props); - - expect(getSearchParameters).toHaveBeenCalledTimes(1); - }); - - it('without search state', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - }; - - const data = await findResultsState(App, props); - - expect(data).toEqual({ - metadata: [expect.objectContaining({ id: 'cool' })], - rawResults: [ - expect.objectContaining({ index: 'indexName', query: 'Apple' }), - ], - state: expect.objectContaining({ index: 'indexName', query: 'Apple' }), - }); - }); - - it('with search state', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchState: { - query: 'iPhone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data).toEqual({ - metadata: [expect.objectContaining({ id: 'cool' })], - rawResults: [ - expect.objectContaining({ index: 'indexName', query: 'iPhone' }), - ], - state: expect.objectContaining({ index: 'indexName', query: 'iPhone' }), - }); - }); - - it('forwards metadata', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchState: { - query: 'godlike', - }, - }; - - const results = await findResultsState(App, props); - - expect(results).toEqual({ - metadata: [ - { - id: 'cool', - props: expect.objectContaining({ prop: 'something' }), - searchState: { query: 'godlike' }, - }, - ], - state: expect.any(SearchParameters), - rawResults: expect.arrayContaining([ - expect.objectContaining({ query: expect.any(String) }), - ]), - }); - }); - - describe('cleaning "params"', () => { - it('with one query', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({}), - searchState: { - query: 'iPhone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPhone"` - ); - }); - - it('with custom client, without "params"', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams() { - return undefined; - }, - }), - searchState: { - query: 'iPhone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toBeUndefined(); - }); - - it('with shadowing query', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({}), - searchState: { - query: 'iPhone&query=iphone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPhone%26query%3Diphone"` - ); - }); - - it('with modified query', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams: (query) => - `query=${encodeURIComponent(query)}&query=modified`, - }), - searchState: { - query: 'iPhone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPhone"` - ); - }); - - it('with shadowing query and modified query', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams: (query) => - `query=${encodeURIComponent(query)}&query=modified`, - }), - searchState: { - query: 'iPhone&query=iphone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPhone%26query%3Diphone"` - ); - }); - - it('with padded, modified query', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams: (query) => - `test=1&query=${encodeURIComponent(query)}&boo=ba&query=modified`, - }), - searchState: { - query: 'iPhone&query=iphone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toMatchInlineSnapshot( - `"test=1&query=iPhone%26query%3Diphone&boo=ba"` - ); - }); - - it('with nothing returned', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams: () => '', - }), - searchState: { - query: 'iPhone&query=iphone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toMatchInlineSnapshot(`""`); - }); - - it('with no query returned', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams: () => 'lol=lol', - }), - searchState: { - query: 'iPhone&query=iphone', - }, - }; - - const data = await findResultsState(App, props); - - expect(data.rawResults[0].params).toMatchInlineSnapshot(`"lol=lol"`); - }); - }); - - it('searches twice (cached) with dynamic widgets', async () => { - const RefinementList = connectRefinementList(() => null); - const App = (props) => ( - - - - ); - - const props = { - searchClient: createSearchClient({}), - indexName: 'abc', - searchState: { - query: 'iPhone', - }, - }; - - await findResultsState(App, props); - - expect(props.searchClient.search).toHaveBeenCalledTimes(2); - // both calls are the same, so they're cached - expect(props.searchClient.search.mock.calls[0][0]).toEqual( - props.searchClient.search.mock.calls[1][0] - ); - }); - - it('searches twice with dynamic widgets and a refinement', async () => { - const RefinementList = connectRefinementList(() => null); - const App = (props) => ( - - - - ); - - const props = { - searchClient: createSearchClient({}), - indexName: 'instant_search', - searchState: { - query: 'iPhone', - refinementList: { categories: ['refined!'] }, - }, - }; - - await findResultsState(App, props); - - expect(props.searchClient.search).toHaveBeenCalledTimes(2); - - // first query doesn't have the fallback widget mounted yet - expect(props.searchClient.search.mock.calls[0][0][0]).toEqual({ - indexName: 'instant_search', - params: { - facets: ['*'], - highlightPostTag: '', - highlightPreTag: '', - maxValuesPerFacet: 20, - tagFilters: '', - }, - }); - - // second query does have the fallback widget mounted, and thus also the refinement - expect(props.searchClient.search.mock.calls[1][0][0]).toEqual({ - indexName: 'instant_search', - params: { - facetFilters: [['categories:refined!']], - facets: ['*'], - highlightPostTag: '', - highlightPreTag: '', - maxValuesPerFacet: 20, - tagFilters: '', - }, - }); - }); - }); - - describe('multi index', () => { - it('results should be instance of SearchResults and SearchParameters', async () => { - const Connected = createWidget(); - - const App = (props) => ( - - - - - - ); - - const props = { - ...requiredProps, - }; - - const { results } = await findResultsState(App, props); - - results.forEach((result) => { - expect(result.state).toBeInstanceOf(SearchParameters); - expect(result.rawResults).toBeInstanceOf(Array); - }); - }); - - it('searchParameters should be cleaned each time', async () => { - const getSearchParameters = jest.fn(); - const Connected = createWidget({ - getSearchParameters, - }); - - const App = (props) => ( - - - - - - ); - - const props = { - ...requiredProps, - }; - - await findResultsState(App, props); - - expect(getSearchParameters).toHaveBeenCalledTimes(1); - - getSearchParameters.mockClear(); - - await findResultsState(App, props); - - expect(getSearchParameters).toHaveBeenCalledTimes(1); - }); - - it('without search state - first API', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - - - ); - - const props = { - ...requiredProps, - indexName: 'index1', - }; - - const data = await findResultsState(App, props); - - expect(data.results).toHaveLength(2); - - const { metadata, results } = data; - const [first, second] = results; - - expect(metadata).toEqual([ - expect.objectContaining({ id: 'cool' }), - expect.objectContaining({ id: 'cool' }), - ]); - - expect(first).toEqual({ - _internalIndexId: 'index1', - state: expect.objectContaining({ index: 'index1', query: 'Apple' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'Apple' }), - ], - }); - - expect(second).toEqual({ - _internalIndexId: 'index2', - state: expect.objectContaining({ index: 'index2', query: 'Apple' }), - rawResults: [ - expect.objectContaining({ index: 'index2', query: 'Apple' }), - ], - }); - }); - - it('without search state - second API', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - ); - - const props = { - ...requiredProps, - indexName: 'index1', - }; - - const { results, metadata } = await findResultsState(App, props); - - expect(metadata).toEqual([ - expect.objectContaining({ id: 'cool' }), - expect.objectContaining({ id: 'cool' }), - ]); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first).toEqual({ - _internalIndexId: 'index1', - state: expect.objectContaining({ index: 'index1', query: 'Apple' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'Apple' }), - ], - }); - - expect(second).toEqual({ - _internalIndexId: 'index2', - state: expect.objectContaining({ index: 'index2', query: 'Apple' }), - rawResults: [ - expect.objectContaining({ index: 'index2', query: 'Apple' }), - ], - }); - }); - - it('without search state - same index', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - ); - - const props = { - ...requiredProps, - indexName: 'index1', - }; - - const { results, metadata } = await findResultsState(App, props); - - expect(metadata).toEqual([ - expect.objectContaining({ id: 'cool' }), - expect.objectContaining({ id: 'cool' }), - ]); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first).toEqual({ - _internalIndexId: 'index1', - state: expect.objectContaining({ index: 'index1', query: 'Apple' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'Apple' }), - ], - }); - - expect(second).toEqual({ - _internalIndexId: 'index1_with_refinement', - state: expect.objectContaining({ index: 'index1', query: 'iWatch' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'iWatch' }), - ], - }); - }); - - it('with search state - first API', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - - - ); - - const props = { - ...requiredProps, - indexName: 'index1', - searchState: { - indices: { - index1: { - query: 'iPhone', - }, - index2: { - query: 'iPad', - }, - }, - }, - }; - - const { results, metadata } = await findResultsState(App, props); - - expect(metadata).toEqual([ - expect.objectContaining({ id: 'cool' }), - expect.objectContaining({ id: 'cool' }), - ]); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first).toEqual({ - _internalIndexId: 'index1', - state: expect.objectContaining({ index: 'index1', query: 'iPhone' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'iPhone' }), - ], - }); - - expect(second).toEqual({ - _internalIndexId: 'index2', - state: expect.objectContaining({ index: 'index2', query: 'iPad' }), - rawResults: [ - expect.objectContaining({ index: 'index2', query: 'iPad' }), - ], - }); - }); - - it('with search state - second API', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - ); - - const props = { - ...requiredProps, - indexName: 'index1', - searchState: { - query: 'iPhone', - indices: { - index2: { - query: 'iPad', - }, - }, - }, - }; - - const { results, metadata } = await findResultsState(App, props); - - expect(metadata).toEqual([ - expect.objectContaining({ id: 'cool' }), - expect.objectContaining({ id: 'cool' }), - ]); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first).toEqual({ - _internalIndexId: 'index1', - state: expect.objectContaining({ index: 'index1', query: 'iPhone' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'iPhone' }), - ], - }); - - expect(second).toEqual({ - _internalIndexId: 'index2', - state: expect.objectContaining({ index: 'index2', query: 'iPad' }), - rawResults: [ - expect.objectContaining({ index: 'index2', query: 'iPad' }), - ], - }); - }); - - it('with search state - same index', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - ); - - const props = { - ...requiredProps, - indexName: 'index1', - searchState: { - query: 'iPhone', - indices: { - index1WithRefinement: { - query: 'iPad', - }, - }, - }, - }; - - const { results, metadata } = await findResultsState(App, props); - - expect(metadata).toEqual([ - expect.objectContaining({ id: 'cool' }), - expect.objectContaining({ id: 'cool' }), - ]); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first).toEqual({ - _internalIndexId: 'index1', - state: expect.objectContaining({ index: 'index1', query: 'iPhone' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'iPhone' }), - ], - }); - expect(second).toEqual({ - _internalIndexId: 'index1WithRefinement', - state: expect.objectContaining({ index: 'index1', query: 'iPad' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'iPad' }), - ], - }); - }); - - it('forwards metadata', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - ); - - const searchState = { - query: 'iPhone', - indices: { - index1WithRefinement: { - query: 'iPad', - }, - }, - }; - - const props = { - ...requiredProps, - indexName: 'index1', - searchState, - }; - - const { results, metadata } = await findResultsState(App, props); - - expect(metadata).toEqual([ - { - id: 'cool', - searchState, - props: expect.objectContaining({ prop: 'value1' }), - }, - { - id: 'cool', - searchState, - props: expect.objectContaining({ prop: 'value2' }), - }, - ]); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first).toEqual({ - _internalIndexId: 'index1', - state: expect.objectContaining({ index: 'index1', query: 'iPhone' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'iPhone' }), - ], - }); - expect(second).toEqual({ - _internalIndexId: 'index1WithRefinement', - state: expect.objectContaining({ index: 'index1', query: 'iPad' }), - rawResults: [ - expect.objectContaining({ index: 'index1', query: 'iPad' }), - ], - }); - }); - - describe('cleaning "params"', () => { - it('multiple queries', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({}), - indexName: 'index1', - searchState: { - query: 'iPhone', - indices: { - index1WithRefinement: { - query: 'iPad&query=test', - }, - }, - }, - }; - - const { results } = await findResultsState(App, props); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPhone"` - ); - expect(second.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPad%26query%3Dtest"` - ); - }); - - it('custom client without "params"', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams() { - return undefined; - }, - }), - indexName: 'index1', - searchState: { - query: 'iPhone', - indices: { - index1WithRefinement: { - query: 'iPad&query=test', - }, - }, - }, - }; - - const { results } = await findResultsState(App, props); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first.rawResults[0].params).toBeUndefined(); - expect(second.rawResults[0].params).toBeUndefined(); - }); - - it('server-side params', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - ); - - const props = { - ...requiredProps, - searchClient: createSearchClient({ - transformResponseParams: (query) => - `query=${encodeURIComponent(query)}&query=modified`, - }), - indexName: 'index1', - searchState: { - query: 'iPhone', - indices: { - index1WithRefinement: { - query: 'iPad&query=test', - }, - }, - }, - }; - - const { results } = await findResultsState(App, props); - - expect(results).toHaveLength(2); - - const [first, second] = results; - - expect(first.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPhone"` - ); - expect(second.rawResults[0].params).toMatchInlineSnapshot( - `"query=iPad%26query%3Dtest"` - ); - }); - }); - - it('searches once with multiple indices', async () => { - const Connected = createWidget(); - const App = (props) => ( - - - - - - - - - - ); - - const props = { - searchClient: createSearchClient({}), - indexName: 'abc', - searchState: { - query: 'iPhone', - }, - }; - - const { results } = await findResultsState(App, props); - - expect(props.searchClient.search).toHaveBeenCalledTimes(1); - expect(results.map(({ _internalIndexId }) => _internalIndexId)).toEqual([ - 'abc', - 'index1WithRefinement', - 'index2WithRefinement', - ]); - }); - - it('searches twice (cached) with dynamic widgets', async () => { - const RefinementList = connectRefinementList(() => null); - const App = (props) => ( - - - - - - ); - - const props = { - searchClient: createSearchClient({}), - indexName: 'abc', - searchState: { - query: 'iPhone', - }, - }; - - await findResultsState(App, props); - - // [search, dynamic] - expect(props.searchClient.search).toHaveBeenCalledTimes(2); - - // both calls are the same, so they're cached - expect(props.searchClient.search.mock.calls[0][0]).toEqual( - props.searchClient.search.mock.calls[1][0] - ); - }); - - it('searches twice with dynamic widgets and a refinement', async () => { - const RefinementList = connectRefinementList(() => null); - const App = (props) => ( - - - - - - ); - - const props = { - searchClient: createSearchClient({}), - indexName: 'instant_search', - searchState: { - query: 'iPhone', - indices: { - index1WithRefinement: { - refinementList: { categories: ['refined!'] }, - }, - }, - }, - }; - - await findResultsState(App, props); - - // [search, dynamic] - expect(props.searchClient.search).toHaveBeenCalledTimes(2); - - // first query doesn't have the fallback widget mounted yet - expect(props.searchClient.search.mock.calls[0][0][1]).toEqual({ - indexName: 'index1WithRefinement', - params: { - facets: ['*'], - highlightPostTag: '', - highlightPreTag: '', - maxValuesPerFacet: 20, - tagFilters: '', - }, - }); - - // second query does have the fallback widget mounted, and thus also the refinement - expect(props.searchClient.search.mock.calls[1][0][1]).toEqual({ - indexName: 'index1WithRefinement', - params: { - facetFilters: [['categories:refined!']], - facets: ['*'], - highlightPostTag: '', - highlightPreTag: '', - maxValuesPerFacet: 20, - tagFilters: '', - }, - }); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/core/__tests__/getInsightsAnonymousUserToken.ts b/packages/react-instantsearch-dom/src/core/__tests__/getInsightsAnonymousUserToken.ts deleted file mode 100644 index 1a914f6622..0000000000 --- a/packages/react-instantsearch-dom/src/core/__tests__/getInsightsAnonymousUserToken.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import getInsightsAnonymousUserToken, { - ANONYMOUS_TOKEN_COOKIE_KEY, -} from '../getInsightsAnonymousUserToken'; - -const DAY = 86400000; /* 1 day in ms*/ -const DATE_TOMORROW = new Date(Date.now() + DAY).toUTCString(); -const DATE_YESTERDAY = new Date(Date.now() - DAY).toUTCString(); - -const resetCookie = (cookieKey: string) => { - document.cookie = `${cookieKey}=;expires=Thu, 01-Jan-1970 00:00:01 GMT;`; -}; - -describe('getInsightsAnonymousUserToken', () => { - beforeEach(() => { - resetCookie(ANONYMOUS_TOKEN_COOKIE_KEY); - }); - - it('should return undefined when no cookies', () => { - expect(getInsightsAnonymousUserToken()).toBe(undefined); - }); - - it('should return undefined when cookie present but expired', () => { - document.cookie = `${ANONYMOUS_TOKEN_COOKIE_KEY}=anonymous-uuid;expires=${DATE_YESTERDAY};`; - expect(getInsightsAnonymousUserToken()).toBe(undefined); - }); - - it('should return the anonymous uuid when cookie present and valid', () => { - document.cookie = `${ANONYMOUS_TOKEN_COOKIE_KEY}=anonymous-uuid;expires=${DATE_TOMORROW};`; - expect(getInsightsAnonymousUserToken()).toBe('anonymous-uuid'); - }); - - it('should return the anonymous uuid when other cookies are invalid', () => { - document.cookie = `${ANONYMOUS_TOKEN_COOKIE_KEY}=anonymous-uuid;expires=${DATE_TOMORROW};`; - document.cookie = `BAD_COOKIE=val%ue;expires=${DATE_TOMORROW};path=/`; - expect(getInsightsAnonymousUserToken()).toBe('anonymous-uuid'); - }); -}); diff --git a/packages/react-instantsearch-dom/src/core/__tests__/utils.js b/packages/react-instantsearch-dom/src/core/__tests__/utils.js deleted file mode 100644 index d850eec7e1..0000000000 --- a/packages/react-instantsearch-dom/src/core/__tests__/utils.js +++ /dev/null @@ -1,110 +0,0 @@ -import * as utils from '../utils'; - -describe('utils', () => { - describe('createClassNames', () => { - it('expect to return classNames', () => { - const cx = utils.createClassNames('Widget'); - - const actual = cx('', null, undefined, false, 'one', 'two').split(' '); - const expectation = ['ais-Widget', 'ais-Widget-one', 'ais-Widget-two']; - - expect(actual).toEqual(expectation); - }); - - it('expect to return classNames with custom prefix', () => { - const cx = utils.createClassNames('Widget', 'ris'); - - const actual = cx('', null, undefined, false, 'one', 'two').split(' '); - const expectation = ['ris-Widget', 'ris-Widget-one', 'ris-Widget-two']; - - expect(actual).toEqual(expectation); - }); - }); - - describe('isSpecialClick', () => { - it('returns true if a modifier key is pressed', () => { - expect(utils.isSpecialClick({ altKey: true })).toBe(true); - expect(utils.isSpecialClick({ ctrlKey: true })).toBe(true); - expect(utils.isSpecialClick({ metaKey: true })).toBe(true); - expect(utils.isSpecialClick({ shiftKey: true })).toBe(true); - }); - - it("returns true if it's a middle click", () => { - expect(utils.isSpecialClick({ button: 1 })).toBe(true); - }); - - it('returns false otherwise', () => { - expect(utils.isSpecialClick({})).toBe(false); - }); - }); - - describe('capitalize', () => { - it('capitalizes a string', () => { - expect(utils.capitalize('oooh spooky')).toBe('Oooh spooky'); - }); - - it('works with empty strings', () => { - expect(utils.capitalize('')).toBe(''); - }); - }); - - describe('range', () => { - test('with end', () => { - expect(utils.range({ end: 4 })).toEqual([0, 1, 2, 3]); - }); - - test('with start and end', () => { - expect(utils.range({ start: 1, end: 5 })).toEqual([1, 2, 3, 4]); - }); - - test('with end and step', () => { - expect(utils.range({ end: 20, step: 5 })).toEqual([0, 5, 10, 15]); - }); - - test('rounds decimal array lengths', () => { - // (5000 - 1) / 500 = 9.998 so we want the array length to be rounded to 10. - expect(utils.range({ start: 1, end: 5000, step: 500 })).toHaveLength(10); - expect(utils.range({ start: 1, end: 5000, step: 500 })).toEqual([ - 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, - ]); - }); - }); - - describe('find', () => { - test('returns the first match based on the comparator', () => { - expect( - utils.find([1], () => { - return true; - }) - ).toBe(1); - expect( - utils.find([1, 2], () => { - return true; - }) - ).toBe(1); - - expect( - utils.find([{ nice: false }, { nice: true }], function (el) { - return el.nice; - }) - ).toEqual({ nice: true }); - }); - - test('returns undefined in non-found cases', () => { - expect( - utils.find([], () => { - return false; - }) - ).toBeUndefined(); - expect( - utils.find(undefined, () => { - return false; - }) - ).toBeUndefined(); - - expect(() => { - utils.find([1, 2, 3], undefined); - }).toThrow(); - }); - }); -}); diff --git a/packages/react-instantsearch-dom/src/core/createInstantSearchServer.js b/packages/react-instantsearch-dom/src/core/createInstantSearchServer.js deleted file mode 100644 index 2ef6a19cf6..0000000000 --- a/packages/react-instantsearch-dom/src/core/createInstantSearchServer.js +++ /dev/null @@ -1,213 +0,0 @@ -import algoliasearchHelper, { SearchParameters } from 'algoliasearch-helper'; -import React from 'react'; -import { renderToString } from 'react-dom/server'; -import { version, HIGHLIGHT_TAGS } from 'react-instantsearch-core'; - -const hasMultipleIndices = (context) => context && context.multiIndexContext; - -const getIndexId = (context) => - hasMultipleIndices(context) - ? context.multiIndexContext.targetedIndex - : context.ais.mainTargetedIndex; - -function createWidgetsCollector(accumulator) { - return (props) => { - accumulator.push({ - ...props, - index: getIndexId(props.context), - }); - }; -} - -function getMetadata(widgets) { - return widgets - .filter((widget) => widget.getMetadata) - .map((widget) => { - return widget.getMetadata(widget.props, widget.searchState); - }); -} - -const getSearchParameters = (indexName, widgets) => { - const sharedParameters = widgets - .filter((widget) => !hasMultipleIndices(widget.context)) - .reduce( - (acc, widget) => - widget.getSearchParameters(acc, widget.props, widget.searchState), - new algoliasearchHelper.SearchParameters({ - ...HIGHLIGHT_TAGS, - index: indexName, - }) - ); - - const derivedParameters = widgets - .filter((widget) => hasMultipleIndices(widget.context)) - .reduce((acc, widget) => { - const indexId = getIndexId(widget.context); - - return { - ...acc, - [indexId]: widget.getSearchParameters( - acc[indexId] || sharedParameters, - widget.props, - widget.searchState - ), - }; - }, {}); - - return { - sharedParameters, - derivedParameters, - }; -}; - -/** - * The engine can return params: "query=xxx&query=yyy" if e.g. a query rule modifies it. - * This however will cause us to miss the cache hydration, so we make sure to keep - * only the first query (always the one from the parameters). - * @param {string} params the parameters to clean - * @returns {string} the parameters without duplicate query - */ -function removeDuplicateQuery(params) { - if (!params) { - return params; - } - let hasFoundQuery = false; - const queryParamRegex = /&?query=[^&]*/g; - return params.replace(queryParamRegex, function replacer(match) { - if (hasFoundQuery) { - return ''; - } - hasFoundQuery = true; - return match; - }); -} - -function cleanRawResults(rawResults) { - return rawResults.map((res) => { - return { - ...res, - params: removeDuplicateQuery(res.params), - }; - }); -} - -const singleIndexSearch = (helper, parameters) => - helper.searchOnce(parameters).then((res) => ({ - rawResults: cleanRawResults(res.content._rawResults), - state: res.content._state, - })); - -const multiIndexSearch = ( - indexName, - client, - sharedParameters, - { [indexName]: mainParameters, ...derivedParameters } -) => { - const helper = algoliasearchHelper(client, indexName); - const indexIds = Object.keys(derivedParameters); - - const searches = [ - new SearchParameters({ ...sharedParameters, ...mainParameters }), - ...indexIds.map((indexId) => derivedParameters[indexId]), - ].map( - (params) => - new Promise((resolve) => - helper.derive(() => params).once('result', resolve) - ) - ); - - helper.searchOnlyWithDerivedHelpers(); - - // We attach `indexId` on the results to be able to reconstruct the object - // on the client side. We cannot rely on `state.index` anymore because we - // may have multiple times the same index. - return Promise.all(searches).then((results) => - [indexName, ...indexIds].map((indexId, i) => ({ - rawResults: cleanRawResults(results[i].results._rawResults), - state: results[i].results._state, - _internalIndexId: indexId, - })) - ); -}; - -export const findResultsState = function (App, props) { - if (!props) { - throw new Error( - 'The function `findResultsState` must be called with props: `findResultsState(App, props)`' - ); - } - - if (!props.searchClient) { - throw new Error( - 'The props provided to `findResultsState` must have a `searchClient`' - ); - } - - if (!props.indexName) { - throw new Error( - 'The props provided to `findResultsState` must have an `indexName`' - ); - } - - let widgets = []; - // eslint-disable-next-line no-shadow - function execute(props) { - widgets = []; - renderToString( - - ); - - if (widgets.length === 0) { - throw new Error( - '[ssr]: no widgets were added, you likely did not pass the `widgetsCollector` down to the InstantSearch component.' - ); - } - - const { sharedParameters, derivedParameters } = getSearchParameters( - props.indexName, - widgets - ); - - const metadata = getMetadata(widgets); - - const helper = algoliasearchHelper( - props.searchClient, - sharedParameters.index - ); - - if (typeof props.searchClient.addAlgoliaAgent === 'function') { - props.searchClient.addAlgoliaAgent( - `react-instantsearch-server (${version})` - ); - } - - if (Object.keys(derivedParameters).length === 0) { - return singleIndexSearch(helper, sharedParameters).then((res) => { - return { - metadata, - ...res, - }; - }); - } - - return multiIndexSearch( - props.indexName, - props.searchClient, - sharedParameters, - derivedParameters - ).then((results) => { - return { metadata, results }; - }); - } - - return execute(props).then((resultsState) => { - // requires another query to retrieve the dynamic widgets - // to render. - if ( - widgets.some((widget) => widget.displayName === 'AlgoliaDynamicWidgets') - ) { - return execute({ ...props, resultsState }); - } - return resultsState; - }); -}; diff --git a/packages/react-instantsearch-dom/src/core/getInsightsAnonymousUserToken.ts b/packages/react-instantsearch-dom/src/core/getInsightsAnonymousUserToken.ts deleted file mode 100644 index 65d48e4335..0000000000 --- a/packages/react-instantsearch-dom/src/core/getInsightsAnonymousUserToken.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const ANONYMOUS_TOKEN_COOKIE_KEY = '_ALGOLIA'; - -function getCookie(name: string): string | undefined { - const prefix = `${name}=`; - const cookies = document.cookie.split(';'); - for (let i = 0; i < cookies.length; i++) { - let cookie = cookies[i]; - while (cookie.charAt(0) === ' ') { - cookie = cookie.substring(1); - } - if (cookie.indexOf(prefix) === 0) { - return cookie.substring(prefix.length, cookie.length); - } - } - return undefined; -} - -export default function getInsightsAnonymousUserToken(): string | undefined { - return getCookie(ANONYMOUS_TOKEN_COOKIE_KEY); -} diff --git a/packages/react-instantsearch-dom/src/core/utils.ts b/packages/react-instantsearch-dom/src/core/utils.ts deleted file mode 100644 index e74f43a0c5..0000000000 --- a/packages/react-instantsearch-dom/src/core/utils.ts +++ /dev/null @@ -1,68 +0,0 @@ -import cx from 'classnames'; - -export const createClassNames = - (block: string, prefix = 'ais') => - (...elements: string[]) => { - const suitElements = elements - .filter((element) => element || element === '') - .map((element) => { - const baseClassName = `${prefix}-${block}`; - - return element ? `${baseClassName}-${element}` : baseClassName; - }); - - return cx(suitElements); - }; - -export const isSpecialClick = (event: MouseEvent) => { - const isMiddleClick = event.button === 1; - return Boolean( - isMiddleClick || - event.altKey || - event.ctrlKey || - event.metaKey || - event.shiftKey - ); -}; - -export const capitalize = (key: string) => - key.length === 0 ? '' : `${key[0].toUpperCase()}${key.slice(1)}`; - -type RangeOptions = { - start?: number; - end: number; - step?: number; -}; - -// taken from InstantSearch.js/utils -export function range({ start = 0, end, step = 1 }: RangeOptions): number[] { - // We can't divide by 0 so we re-assign the step to 1 if it happens. - const limitStep = step === 0 ? 1 : step; - - // In some cases the array to create has a decimal length. - // We therefore need to round the value. - // Example: - // { start: 1, end: 5000, step: 500 } - // => Array length = (5000 - 1) / 500 = 9.998 - const arrayLength = Math.round((end - start) / limitStep); - - return [...Array(arrayLength)].map( - (_, current) => (start + current) * limitStep - ); -} - -export function find( - array: TItem[], - comparator: (item: TItem) => boolean -): TItem | undefined { - if (!Array.isArray(array)) { - return undefined; - } - - for (let i = 0; i < array.length; i++) { - if (comparator(array[i])) { - return array[i]; - } - } - return undefined; -} diff --git a/packages/react-instantsearch-dom/src/hooks/useAnswers.js b/packages/react-instantsearch-dom/src/hooks/useAnswers.js deleted file mode 100644 index ee502577f8..0000000000 --- a/packages/react-instantsearch-dom/src/hooks/useAnswers.js +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useState, useEffect, useMemo, useContext } from 'react'; -import { instantSearchContext } from 'react-instantsearch-core'; - -import { createConcurrentSafePromise } from '../lib/createConcurrentSafePromise'; -import { debounce } from '../lib/debounce'; - -function hasReactHooks() { - // >= 16.8.0 - const [major, minor] = React.version.split('.').map(Number); - return major >= 17 || (major === 16 && minor >= 8); -} - -export default function useAnswers({ - searchClient, - queryLanguages, - attributesForPrediction, - nbHits, - renderDebounceTime = 100, - searchDebounceTime = 100, - ...extraParameters -}) { - if (!hasReactHooks()) { - throw new Error( - `\`Answers\` component and \`useAnswers\` hook require all React packages to be 16.8.0 or higher.` - ); - } - const context = useContext(instantSearchContext); - const [query, setQuery] = useState(context.store.getState().widgets.query); - const [index, setIndex] = useState(context.mainTargetedIndex); - const [isLoading, setIsLoading] = useState(false); - const [hits, setHits] = useState([]); - const runConcurrentSafePromise = useMemo( - () => createConcurrentSafePromise(), - [] - ); - const searchIndex = useMemo( - () => searchClient.initIndex(index), - [searchClient, index] - ); - if (!searchIndex.findAnswers) { - throw new Error( - '`Answers` component and `useAnswers` hook require `algoliasearch` to be 4.8.0 or higher.' - ); - } - - const debouncedSearch = useMemo(() => { - return debounce(searchIndex.findAnswers, searchDebounceTime); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchIndex]); - - useEffect(() => { - setIndex(context.mainTargetedIndex); - - return context.store.subscribe(() => { - const { widgets } = context.store.getState(); - setQuery(widgets.query); - }); - }, [context]); - - const setDebouncedResult = useMemo( - () => - debounce((result) => { - setIsLoading(false); - setHits(result.hits); - }, renderDebounceTime), - // eslint-disable-next-line react-hooks/exhaustive-deps - [setIsLoading, setHits] - ); - - const fetchAnswers = () => { - if (!query) { - setIsLoading(false); - setHits([]); - return; - } - setIsLoading(true); - runConcurrentSafePromise( - debouncedSearch(query, queryLanguages, { - ...extraParameters, - nbHits, - attributesForPrediction, - }) - ).then((result) => { - if (!result) { - // It's undefined when it's debounced. - return; - } - setDebouncedResult(result); - }); - }; - - useEffect(() => { - fetchAnswers(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [query]); - - return { hits, isLoading }; -} diff --git a/packages/react-instantsearch-dom/src/index.js b/packages/react-instantsearch-dom/src/index.js deleted file mode 100644 index 8063c74a1b..0000000000 --- a/packages/react-instantsearch-dom/src/index.js +++ /dev/null @@ -1,93 +0,0 @@ -// Core -export { createConnector } from 'react-instantsearch-core'; -export { HIGHLIGHT_TAGS } from 'react-instantsearch-core'; -export { translatable } from 'react-instantsearch-core'; - -// Widget -export { Configure } from 'react-instantsearch-core'; -export { ExperimentalConfigureRelatedItems } from 'react-instantsearch-core'; -export { QueryRuleContext } from 'react-instantsearch-core'; -export { Index } from 'react-instantsearch-core'; -export { InstantSearch } from 'react-instantsearch-core'; - -// Connectors -export { connectAutoComplete } from 'react-instantsearch-core'; -export { connectBreadcrumb } from 'react-instantsearch-core'; -export { connectConfigure } from 'react-instantsearch-core'; -export { EXPERIMENTAL_connectConfigureRelatedItems } from 'react-instantsearch-core'; -export { connectCurrentRefinements } from 'react-instantsearch-core'; -export { - connectDynamicWidgets, - EXPERIMENTAL_connectDynamicWidgets, -} from 'react-instantsearch-core'; -export { connectGeoSearch } from 'react-instantsearch-core'; -export { connectHierarchicalMenu } from 'react-instantsearch-core'; -export { connectHighlight } from 'react-instantsearch-core'; -export { connectHitInsights } from 'react-instantsearch-core'; -export { connectHits } from 'react-instantsearch-core'; -export { connectHitsPerPage } from 'react-instantsearch-core'; -export { connectInfiniteHits } from 'react-instantsearch-core'; -export { connectMenu } from 'react-instantsearch-core'; -export { connectNumericMenu } from 'react-instantsearch-core'; -export { connectPagination } from 'react-instantsearch-core'; -export { connectPoweredBy } from 'react-instantsearch-core'; -export { connectQueryRules } from 'react-instantsearch-core'; -export { connectRange } from 'react-instantsearch-core'; -export { connectRefinementList } from 'react-instantsearch-core'; -export { connectScrollTo } from 'react-instantsearch-core'; -export { connectSearchBox } from 'react-instantsearch-core'; -export { connectRelevantSort } from 'react-instantsearch-core'; -export { connectSortBy } from 'react-instantsearch-core'; -export { connectStateResults } from 'react-instantsearch-core'; -export { connectStats } from 'react-instantsearch-core'; -export { connectToggleRefinement } from 'react-instantsearch-core'; - -// DOM -export { default as Breadcrumb } from './widgets/Breadcrumb'; -export { default as ClearRefinements } from './widgets/ClearRefinements'; -export { default as CurrentRefinements } from './widgets/CurrentRefinements'; -export { default as HierarchicalMenu } from './widgets/HierarchicalMenu'; -export { default as Highlight } from './widgets/Highlight'; -export { default as Hits } from './widgets/Hits'; -export { default as HitsPerPage } from './widgets/HitsPerPage'; -export { default as InfiniteHits } from './widgets/InfiniteHits'; -export { default as Menu } from './widgets/Menu'; -export { default as MenuSelect } from './widgets/MenuSelect'; -export { default as NumericMenu } from './widgets/NumericMenu'; -export { default as Pagination } from './widgets/Pagination'; -export { default as Panel } from './widgets/Panel'; -export { default as PoweredBy } from './widgets/PoweredBy'; -export { default as RangeInput } from './widgets/RangeInput'; -export { default as RangeSlider } from './widgets/RangeSlider'; -export { default as RatingMenu } from './widgets/RatingMenu'; -export { default as RefinementList } from './widgets/RefinementList'; -export { default as ScrollTo } from './widgets/ScrollTo'; -export { default as SearchBox } from './widgets/SearchBox'; -export { default as Snippet } from './widgets/Snippet'; -export { default as RelevantSort } from './widgets/RelevantSort'; -export { default as SortBy } from './widgets/SortBy'; -export { default as Stats } from './widgets/Stats'; -export { default as ToggleRefinement } from './widgets/ToggleRefinement'; -export { default as VoiceSearch } from './widgets/VoiceSearch'; -export { default as QueryRuleCustomData } from './widgets/QueryRuleCustomData'; -export { default as EXPERIMENTAL_Answers } from './widgets/Answers'; - -import DynamicWidgets from './widgets/DynamicWidgets'; -/** @deprecated use DynamicWidgets */ -const ExperimentalDynamicWidgets = DynamicWidgets; -export { DynamicWidgets, ExperimentalDynamicWidgets }; - -// hooks -export { default as EXPERIMENTAL_useAnswers } from './hooks/useAnswers'; - -// Utils -export { createClassNames } from './core/utils'; - -// voiceSearchHelper -export { default as createVoiceSearchHelper } from './lib/voiceSearchHelper'; - -// insights -export { default as getInsightsAnonymousUserToken } from './core/getInsightsAnonymousUserToken'; - -// InfiniteHits Cache -export { createInfiniteHitsSessionStorageCache } from './lib/infiniteHitsCache'; diff --git a/packages/react-instantsearch-dom/src/lib/createConcurrentSafePromise.js b/packages/react-instantsearch-dom/src/lib/createConcurrentSafePromise.js deleted file mode 100644 index 6ed07d1d49..0000000000 --- a/packages/react-instantsearch-dom/src/lib/createConcurrentSafePromise.js +++ /dev/null @@ -1,43 +0,0 @@ -// copied from -// https://github.com/algolia/autocomplete.js/blob/307a7acc4283e10a19cb7d067f04f1bea79dc56f/packages/autocomplete-core/src/utils/createConcurrentSafePromise.ts#L1:L1 -// eslint-disable-next-line valid-jsdoc -/** - * Creates a runner that executes promises in a concurrent-safe way. - * - * This is useful to prevent older promises to resolve after a newer promise, - * otherwise resulting in stale resolved values. - */ -export function createConcurrentSafePromise() { - let basePromiseId = -1; - let latestResolvedId = -1; - let latestResolvedValue = undefined; - - return function runConcurrentSafePromise(promise) { - basePromiseId++; - const currentPromiseId = basePromiseId; - - return Promise.resolve(promise).then((x) => { - // The promise might take too long to resolve and get outdated. This would - // result in resolving stale values. - // When this happens, we ignore the promise value and return the one - // coming from the latest resolved value. - // - // +----------------------------------+ - // | 100ms | - // | run(1) +---> R1 | - // | 300ms | - // | run(2) +-------------> R2 (SKIP) | - // | 200ms | - // | run(3) +--------> R3 | - // +----------------------------------+ - if (latestResolvedValue && currentPromiseId < latestResolvedId) { - return latestResolvedValue; - } - - latestResolvedId = currentPromiseId; - latestResolvedValue = x; - - return x; - }); - }; -} diff --git a/packages/react-instantsearch-dom/src/lib/debounce.ts b/packages/react-instantsearch-dom/src/lib/debounce.ts deleted file mode 100644 index 3cd2020a9b..0000000000 --- a/packages/react-instantsearch-dom/src/lib/debounce.ts +++ /dev/null @@ -1,28 +0,0 @@ -type Func = (...args: any[]) => any; - -type DebouncedFunction = ( - this: ThisParameterType, - ...args: Parameters -) => Promise>; - -// Debounce a function call to the trailing edge. -// The debounced function returns a promise. -export function debounce( - func: TFunction, - wait: number -): DebouncedFunction { - let lastTimeout: ReturnType | null = null; - return function (...args) { - // @ts-ignore-next-line - const that = this; - return new Promise((resolve, reject) => { - if (lastTimeout) { - clearTimeout(lastTimeout); - } - lastTimeout = setTimeout(() => { - lastTimeout = null; - Promise.resolve(func.apply(that, args)).then(resolve).catch(reject); - }, wait); - }); - }; -} diff --git a/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/__tests__/sessionStorage.js b/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/__tests__/sessionStorage.js deleted file mode 100644 index bd67e0cd60..0000000000 --- a/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/__tests__/sessionStorage.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import createInfiniteHitsSessionStorageCache from '../sessionStorage'; - -const KEY = 'ais.infiniteHits'; - -describe('createInfiniteHitsSessionStorageCache', () => { - const originalSessionStorage = window.sessionStorage; - delete window.sessionStorage; - - let store = {}; - const getItem = jest.fn((key) => store[key]); - const setItem = jest.fn((key, value) => { - store[key] = value; - }); - const removeItem = jest.fn((key) => delete store[key]); - const defaultHits = [ - { objectID: 'a', __position: 0 }, - { objectID: 'b', __position: 1 }, - { objectID: 'c', __position: 2 }, - ]; - - beforeAll(() => { - Object.defineProperty(window, 'sessionStorage', { - writable: true, - value: { - getItem, - setItem, - removeItem, - }, - }); - }); - - beforeEach(() => { - store = {}; - }); - - afterEach(() => { - getItem.mockClear(); - setItem.mockClear(); - removeItem.mockClear(); - }); - - afterAll(() => { - Object.defineProperty(window, 'sessionStorage', { - value: originalSessionStorage, - }); - }); - - it('returns null initially', () => { - const cache = createInfiniteHitsSessionStorageCache(); - expect(cache.read({ state: {} })).toBeNull(); - }); - - it('returns what it was assigned before', () => { - const cache = createInfiniteHitsSessionStorageCache(); - const state = { query: 'hello' }; - const hits = { - 1: defaultHits, - }; - cache.write({ state, hits }); - expect(cache.read({ state })).toEqual(hits); - }); - - it('returns null if the state is different', () => { - const cache = createInfiniteHitsSessionStorageCache(); - const state = { query: 'hello' }; - const newState = { query: 'world' }; - const hits = { 1: defaultHits }; - cache.write({ state, hits }); - expect(cache.read({ state: newState })).toBeNull(); - }); - - it('clears cache if fails to read', () => { - getItem.mockImplementation(() => '{invalid_json}'); - const cache = createInfiniteHitsSessionStorageCache(); - cache.read({ state: {} }); - expect(removeItem).toHaveBeenCalledTimes(1); - expect(removeItem).toHaveBeenCalledWith(KEY); - }); - - it('returns null if sessionStorage.getItem() throws', () => { - getItem.mockImplementation(() => { - throw new Error(); - }); - const cache = createInfiniteHitsSessionStorageCache(); - expect(cache.read({ state: {} })).toBeNull(); - }); - - it('does nothing if sessionStorage.setItem() throws', () => { - setItem.mockImplementation(() => { - throw new Error(); - }); - const cache = createInfiniteHitsSessionStorageCache(); - expect(() => { - cache.write({ state: {}, hits: [] }); - }).not.toThrow(); - }); -}); diff --git a/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/index.js b/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/index.js deleted file mode 100644 index 6fd47d4f82..0000000000 --- a/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as createInfiniteHitsSessionStorageCache } from './sessionStorage'; diff --git a/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/sessionStorage.js b/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/sessionStorage.js deleted file mode 100644 index d07cc63e53..0000000000 --- a/packages/react-instantsearch-dom/src/lib/infiniteHitsCache/sessionStorage.js +++ /dev/null @@ -1,56 +0,0 @@ -import isEqual from 'react-fast-compare'; - -function getStateWithoutPage(state) { - const { page, ...rest } = state || {}; - return rest; -} - -const KEY = 'ais.infiniteHits'; - -function hasSessionStorage() { - return ( - typeof window !== 'undefined' && - typeof window.sessionStorage !== 'undefined' - ); -} - -export default function createInfiniteHitsSessionStorageCache() { - return { - read({ state }) { - if (!hasSessionStorage()) { - return null; - } - try { - const cache = JSON.parse(window.sessionStorage.getItem(KEY)); - return cache && isEqual(cache.state, getStateWithoutPage(state)) - ? cache.hits - : null; - } catch (error) { - if (error instanceof SyntaxError) { - try { - window.sessionStorage.removeItem(KEY); - } catch (err) { - // do nothing - } - } - return null; - } - }, - write({ state, hits }) { - if (!hasSessionStorage()) { - return; - } - try { - window.sessionStorage.setItem( - KEY, - JSON.stringify({ - state: getStateWithoutPage(state), - hits, - }) - ); - } catch (error) { - // do nothing - } - }, - }; -} diff --git a/packages/react-instantsearch-dom/src/lib/voiceSearchHelper/__tests__/index.ts b/packages/react-instantsearch-dom/src/lib/voiceSearchHelper/__tests__/index.ts deleted file mode 100644 index bda056ac92..0000000000 --- a/packages/react-instantsearch-dom/src/lib/voiceSearchHelper/__tests__/index.ts +++ /dev/null @@ -1,218 +0,0 @@ -/** - * @jest-environment jsdom - */ - -// copied from https://github.com/algolia/instantsearch.js/blob/688e36a67bb4c63d008d8abc02257a7b7c04e513/src/lib/voiceSearchHelper/__tests__/index-test.ts - -import createVoiceSearchHelper from '..'; - -type DummySpeechRecognition = () => void; -declare global { - interface Window { - webkitSpeechRecognition?: SpeechRecognition | DummySpeechRecognition; - SpeechRecognition?: SpeechRecognition | DummySpeechRecognition; - } -} - -const start = jest.fn(); -const stop = jest.fn(); - -const createFakeSpeechRecognition = (): jest.Mock => { - const simulateListener: any = {}; - const mock = jest.fn().mockImplementation(() => ({ - start, - stop, - addEventListener(eventName: string, callback: () => void) { - simulateListener[eventName] = callback; - }, - removeEventListener() {}, - })); - (mock as any).simulateListener = simulateListener; - return mock; -}; - -describe('VoiceSearchHelper', () => { - afterEach(() => { - // @ts-expect-error - delete window.webkitSpeechRecognition; - // @ts-expect-error - delete window.SpeechRecognition; - }); - - it('has initial state correctly', () => { - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: false, - onQueryChange: () => {}, - onStateChange: () => {}, - }); - expect(voiceSearchHelper.getState()).toEqual({ - errorCode: undefined, - isSpeechFinal: false, - status: 'initial', - transcript: '', - }); - }); - - it('is not supported', () => { - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: false, - onQueryChange: () => {}, - onStateChange: () => {}, - }); - expect(voiceSearchHelper.isBrowserSupported()).toBe(false); - }); - - it('is not listening', () => { - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: false, - onQueryChange: () => {}, - onStateChange: () => {}, - }); - expect(voiceSearchHelper.isListening()).toBe(false); - }); - - it('is supported with webkitSpeechRecognition', () => { - // @ts-expect-error - window.webkitSpeechRecognition = () => {}; - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: false, - onQueryChange: () => {}, - onStateChange: () => {}, - }); - expect(voiceSearchHelper.isBrowserSupported()).toBe(true); - }); - - it('is supported with SpeechRecognition', () => { - window.SpeechRecognition = createFakeSpeechRecognition(); - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: false, - onQueryChange: () => {}, - onStateChange: () => {}, - }); - expect(voiceSearchHelper.isBrowserSupported()).toBe(true); - }); - - it('works with mock SpeechRecognition (searchAsYouSpeak:false)', () => { - window.SpeechRecognition = createFakeSpeechRecognition(); - const { simulateListener } = window.SpeechRecognition as any; - const onQueryChange = jest.fn(); - const onStateChange = jest.fn(); - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: false, - onQueryChange, - onStateChange, - }); - - voiceSearchHelper.toggleListening(); - expect(onStateChange).toHaveBeenCalledTimes(1); - expect(voiceSearchHelper.getState().status).toEqual('askingPermission'); - simulateListener.start(); - expect(voiceSearchHelper.getState().status).toEqual('waiting'); - simulateListener.result({ - results: [ - (() => { - const obj = [ - { - transcript: 'Hello World', - }, - ]; - (obj as any).isFinal = true; - return obj; - })(), - ], - }); - expect(voiceSearchHelper.getState().status).toEqual('recognizing'); - expect(voiceSearchHelper.getState().transcript).toEqual('Hello World'); - expect(voiceSearchHelper.getState().isSpeechFinal).toBe(true); - expect(onQueryChange).toHaveBeenCalledTimes(0); - simulateListener.end(); - expect(onQueryChange).toHaveBeenCalledWith('Hello World'); - expect(voiceSearchHelper.getState().status).toEqual('finished'); - }); - - it('works with mock SpeechRecognition (searchAsYouSpeak:true)', () => { - window.SpeechRecognition = createFakeSpeechRecognition(); - const { simulateListener } = window.SpeechRecognition as any; - const onQueryChange = jest.fn(); - const onStateChange = jest.fn(); - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: true, - onQueryChange, - onStateChange, - }); - - voiceSearchHelper.toggleListening(); - expect(onStateChange).toHaveBeenCalledTimes(1); - expect(voiceSearchHelper.getState().status).toEqual('askingPermission'); - simulateListener.start(); - expect(voiceSearchHelper.getState().status).toEqual('waiting'); - simulateListener.result({ - results: [ - (() => { - const obj = [ - { - transcript: 'Hello World', - }, - ]; - (obj as any).isFinal = true; - return obj; - })(), - ], - }); - expect(voiceSearchHelper.getState().status).toEqual('recognizing'); - expect(voiceSearchHelper.getState().transcript).toEqual('Hello World'); - expect(voiceSearchHelper.getState().isSpeechFinal).toBe(true); - expect(onQueryChange).toHaveBeenCalledWith('Hello World'); - simulateListener.end(); - expect(onQueryChange).toHaveBeenCalledTimes(1); - expect(voiceSearchHelper.getState().status).toEqual('finished'); - }); - - it('works with onerror', () => { - window.SpeechRecognition = createFakeSpeechRecognition(); - const { simulateListener } = window.SpeechRecognition as any; - const onQueryChange = jest.fn(); - const onStateChange = jest.fn(); - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: true, - onQueryChange, - onStateChange, - }); - voiceSearchHelper.toggleListening(); - expect(voiceSearchHelper.getState().status).toEqual('askingPermission'); - simulateListener.error({ - error: 'not-allowed', - }); - expect(voiceSearchHelper.getState().status).toEqual('error'); - expect(voiceSearchHelper.getState().errorCode).toEqual('not-allowed'); - simulateListener.end(); - expect(onQueryChange).toHaveBeenCalledTimes(0); - }); - - it('stops listening on `dispose`', () => { - window.SpeechRecognition = createFakeSpeechRecognition(); - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: false, - onQueryChange: () => {}, - onStateChange: () => {}, - }); - voiceSearchHelper.toggleListening(); - voiceSearchHelper.dispose(); - expect(stop).toHaveBeenCalledTimes(1); - }); - - it('stops and the status becomes `finished`', () => { - window.SpeechRecognition = createFakeSpeechRecognition(); - const onQueryChange = (): void => {}; - const onStateChange = (): void => {}; - const voiceSearchHelper = createVoiceSearchHelper({ - searchAsYouSpeak: true, - onQueryChange, - onStateChange, - }); - - voiceSearchHelper.toggleListening(); - voiceSearchHelper.toggleListening(); - expect(voiceSearchHelper.getState().status).toBe('finished'); - }); -}); diff --git a/packages/react-instantsearch-dom/src/lib/voiceSearchHelper/index.ts b/packages/react-instantsearch-dom/src/lib/voiceSearchHelper/index.ts deleted file mode 100644 index a682921ccd..0000000000 --- a/packages/react-instantsearch-dom/src/lib/voiceSearchHelper/index.ts +++ /dev/null @@ -1,181 +0,0 @@ -// copied from https://github.com/algolia/instantsearch.js/blob/688e36a67bb4c63d008d8abc02257a7b7c04e513/src/lib/voiceSearchHelper/index.ts - -// #region wrong SpeechRecognition-related types -// This is not released in typescript yet, so we're copy&pasting the type definition from -// https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/924 -export type SpeechRecognitionErrorCode = - | 'aborted' - | 'audio-capture' - | 'bad-grammar' - | 'language-not-supported' - | 'network' - | 'no-speech' - | 'not-allowed' - | 'service-not-allowed'; - -interface SpeechRecognitionErrorEvent extends Event { - readonly error: SpeechRecognitionErrorCode; - readonly message: string; -} -// #endregion wrong SpeechRecognition-related types - -export type VoiceSearchHelperParams = { - searchAsYouSpeak: boolean; - language?: string; - onQueryChange: (query: string) => void; - onStateChange: () => void; -}; - -export type Status = - | 'initial' - | 'askingPermission' - | 'waiting' - | 'recognizing' - | 'finished' - | 'error'; - -export type VoiceListeningState = { - status: Status; - transcript: string; - isSpeechFinal: boolean; - errorCode?: SpeechRecognitionErrorCode; -}; - -export type VoiceSearchHelper = { - getState: () => VoiceListeningState; - isBrowserSupported: () => boolean; - isListening: () => boolean; - toggleListening: () => void; - dispose: () => void; -}; - -export type ToggleListening = () => void; - -export default function createVoiceSearchHelper({ - searchAsYouSpeak, - language, - onQueryChange, - onStateChange, -}: VoiceSearchHelperParams): VoiceSearchHelper { - const SpeechRecognitionAPI: new () => SpeechRecognition = - (window as any).webkitSpeechRecognition || - (window as any).SpeechRecognition; - const getDefaultState = (status: Status): VoiceListeningState => ({ - status, - transcript: '', - isSpeechFinal: false, - errorCode: undefined, - }); - let state: VoiceListeningState = getDefaultState('initial'); - let recognition: SpeechRecognition | undefined; - - const isBrowserSupported = (): boolean => Boolean(SpeechRecognitionAPI); - - const isListening = (): boolean => - state.status === 'askingPermission' || - state.status === 'waiting' || - state.status === 'recognizing'; - - const setState = (newState: Partial = {}): void => { - state = { ...state, ...newState }; - onStateChange(); - }; - - const getState = (): VoiceListeningState => state; - - const resetState = (status: Status = 'initial'): void => { - setState(getDefaultState(status)); - }; - - const onStart = (): void => { - setState({ - status: 'waiting', - }); - }; - - const onError = (event: SpeechRecognitionErrorEvent): void => { - setState({ status: 'error', errorCode: event.error }); - }; - - const onResult = (event: SpeechRecognitionEvent): void => { - setState({ - status: 'recognizing', - transcript: - (event.results[0] && - event.results[0][0] && - event.results[0][0].transcript) || - '', - isSpeechFinal: event.results[0] && event.results[0].isFinal, - }); - if (searchAsYouSpeak && state.transcript) { - onQueryChange(state.transcript); - } - }; - - const onEnd = (): void => { - if (!state.errorCode && state.transcript && !searchAsYouSpeak) { - onQueryChange(state.transcript); - } - if (state.status !== 'error') { - setState({ status: 'finished' }); - } - }; - - const start = (): void => { - recognition = new SpeechRecognitionAPI(); - if (!recognition) { - return; - } - resetState('askingPermission'); - recognition.interimResults = true; - if (language) { - recognition.lang = language; - } - recognition.addEventListener('start', onStart); - // @ts-ignore: refer to the top `wrong SpeechRecognition-related types` comments - recognition.addEventListener('error', onError); - recognition.addEventListener('result', onResult); - recognition.addEventListener('end', onEnd); - recognition.start(); - }; - - const dispose = (): void => { - if (!recognition) { - return; - } - recognition.stop(); - recognition.removeEventListener('start', onStart); - // @ts-ignore: refer to the top `wrong SpeechRecognition-related types` comments - recognition.removeEventListener('error', onError); - recognition.removeEventListener('result', onResult); - recognition.removeEventListener('end', onEnd); - recognition = undefined; - }; - - const stop = (): void => { - dispose(); - // Because `dispose` removes event listeners, `end` listener is not called. - // So we're setting the `status` as `finished` here. - // If we don't do it, it will be still `waiting` or `recognizing`. - resetState('finished'); - }; - - const toggleListening = (): void => { - if (!isBrowserSupported()) { - return; - } - if (isListening()) { - stop(); - } else { - start(); - } - }; - - return { - getState, - isBrowserSupported, - isListening, - toggleListening, - dispose, - }; -} diff --git a/packages/react-instantsearch-dom/src/server.js b/packages/react-instantsearch-dom/src/server.js deleted file mode 100644 index 3b03381188..0000000000 --- a/packages/react-instantsearch-dom/src/server.js +++ /dev/null @@ -1 +0,0 @@ -export { findResultsState } from './core/createInstantSearchServer'; diff --git a/packages/react-instantsearch-dom/src/widgets/Answers.js b/packages/react-instantsearch-dom/src/widgets/Answers.js deleted file mode 100644 index 03e9884acf..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/Answers.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; - -import { createClassNames } from '../core/utils'; -import useAnswers from '../hooks/useAnswers'; - -const cx = createClassNames('Answers'); - -/* eslint-disable react/prop-types */ -function DefaultAnswersComponent({ isLoading, hits }) { - return ( -
    -
    - {isLoading ? ( -
    - ) : ( -
      - {hits.map((hit, index) => ( -
    • - {JSON.stringify(hit)} -
    • - ))} -
    - )} -
    - ); -} - -export default function Answers({ - searchClient, - queryLanguages, - attributesForPrediction, - nbHits = 1, - renderDebounceTime, - searchDebounceTime, - answersComponent: AnswersComponent = DefaultAnswersComponent, - ...extraParameters -}) { - const { hits, isLoading } = useAnswers({ - searchClient, - queryLanguages, - attributesForPrediction, - nbHits, - renderDebounceTime, - searchDebounceTime, - ...extraParameters, - }); - - return ; -} -/* eslint-enable react/prop-types */ diff --git a/packages/react-instantsearch-dom/src/widgets/Breadcrumb.js b/packages/react-instantsearch-dom/src/widgets/Breadcrumb.js deleted file mode 100644 index 5c736ede7f..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/Breadcrumb.js +++ /dev/null @@ -1,103 +0,0 @@ -import React from 'react'; -import { connectBreadcrumb } from 'react-instantsearch-core'; - -import Breadcrumb from '../components/Breadcrumb'; -import PanelCallbackHandler from '../components/PanelCallbackHandler'; - -/** - * A breadcrumb is a secondary navigation scheme that allows the user to see where the current page - * is in relation to the website or web application’s hierarchy. - * In terms of usability, using a breadcrumb reduces the number of actions a visitor needs to take in - * order to get to a higher-level page. - * - * If you want to select a specific refinement for your Breadcrumb component, you will need to - * use a [Virtual Hierarchical Menu](https://community.algolia.com/react-instantsearch/guide/Virtual_widgets.html) - * and set its defaultRefinement that will be then used by the Breadcrumb. - * - * @name Breadcrumb - * @kind widget - * @requirements Breadcrumbs are used for websites with a large amount of content organised in a hierarchical manner. - * The typical example is an e-commerce website which has a large variety of products grouped into logical categories - * (with categories, subcategories which also have subcategories).To use this widget, your attributes must be formatted in a specific way. - * - * Keep in mind that breadcrumbs shouldn’t replace effective primary navigation menus: - * it is only an alternative way to navigate around the website. - * - * If, for instance, you would like to have a breadcrumb of categories, objects in your index - * should be formatted this way: - * - * ```json - * { - * "categories.lvl0": "products", - * "categories.lvl1": "products > fruits", - * "categories.lvl2": "products > fruits > citrus" - * } - * ``` - * - * It's also possible to provide more than one path for each level: - * - * ```json - * { - * "categories.lvl0": ["products", "goods"], - * "categories.lvl1": ["products > fruits", "goods > to eat"] - * } - * ``` - * - * All attributes passed to the `attributes` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * - * @propType {array.} attributes - List of attributes to use to generate the hierarchy of the menu. See the example for the convention to follow - * @propType {node} [separator='>'] - Symbol used for separating hyperlinks - * @propType {string} [rootURL=null] - The originating page (homepage) - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return - * @themeKey ais-Breadcrumb - the root div of the widget - * @themeKey ais-Breadcrumb--noRefinement - the root div of the widget when there is no refinement - * @themeKey ais-Breadcrumb-list - the list of all breadcrumb items - * @themeKey ais-Breadcrumb-item - the breadcrumb navigation item - * @themeKey ais-Breadcrumb-item--selected - the selected breadcrumb item - * @themeKey ais-Breadcrumb-separator - the separator of each breadcrumb item - * @themeKey ais-Breadcrumb-link - the clickable breadcrumb element - * @translationKey rootLabel - The root's label. Accepts a string - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { Breadcrumb, InstantSearch, HierarchicalMenu } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * - * ); - */ - -const BreadcrumbWidget = (props) => ( - - - -); - -export default connectBreadcrumb(BreadcrumbWidget, { - $$widgetType: 'ais.breadcrumb', -}); diff --git a/packages/react-instantsearch-dom/src/widgets/ClearRefinements.js b/packages/react-instantsearch-dom/src/widgets/ClearRefinements.js deleted file mode 100644 index ba427c1567..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/ClearRefinements.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { connectCurrentRefinements } from 'react-instantsearch-core'; - -import ClearRefinements from '../components/ClearRefinements'; -import PanelCallbackHandler from '../components/PanelCallbackHandler'; - -/** - * The ClearRefinements widget displays a button that lets the user clean every refinement applied - * to the search. - * @name ClearRefinements - * @kind widget - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @propType {boolean} [clearsQuery=false] - Pass true to also clear the search query - * @themeKey ais-ClearRefinements - the root div of the widget - * @themeKey ais-ClearRefinements-button - the clickable button - * @themeKey ais-ClearRefinements-button--disabled - the disabled clickable button - * @translationKey reset - the clear all button value - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, ClearRefinements, RefinementList } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * const App = () => ( - * - * - * - * - * ); - */ - -const ClearRefinementsWidget = (props) => ( - - - -); - -export default connectCurrentRefinements(ClearRefinementsWidget, { - $$widgetType: 'ais.clearRefinements', -}); diff --git a/packages/react-instantsearch-dom/src/widgets/CurrentRefinements.js b/packages/react-instantsearch-dom/src/widgets/CurrentRefinements.js deleted file mode 100644 index 09411f3829..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/CurrentRefinements.js +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { connectCurrentRefinements } from 'react-instantsearch-core'; - -import CurrentRefinements from '../components/CurrentRefinements'; -import PanelCallbackHandler from '../components/PanelCallbackHandler'; - -/** - * The CurrentRefinements widget displays the list of currently applied filters. - * - * It allows the user to selectively remove them. - * @name CurrentRefinements - * @kind widget - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @themeKey ais-CurrentRefinements - the root div of the widget - * @themeKey ais-CurrentRefinements--noRefinement - the root div of the widget when there is no refinement - * @themeKey ais-CurrentRefinements-list - the list of all refined items - * @themeKey ais-CurrentRefinements-list--noRefinement - the list of all refined items when there is no refinement - * @themeKey ais-CurrentRefinements-item - the refined list item - * @themeKey ais-CurrentRefinements-button - the button of each refined list item - * @themeKey ais-CurrentRefinements-label - the refined list label - * @themeKey ais-CurrentRefinements-category - the category of each item - * @themeKey ais-CurrentRefinements-categoryLabel - the label of each catgory - * @themeKey ais-CurrentRefinements-delete - the delete button of each label - * @translationKey clearFilter - the remove filter button label - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, CurrentRefinements, RefinementList } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * - * ); - */ - -const CurrentRefinementsWidget = (props) => ( - - - -); - -export default connectCurrentRefinements(CurrentRefinementsWidget, { - $$widgetType: 'ais.currentRefinements', -}); diff --git a/packages/react-instantsearch-dom/src/widgets/DynamicWidgets.js b/packages/react-instantsearch-dom/src/widgets/DynamicWidgets.js deleted file mode 100644 index 89eac2eadd..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/DynamicWidgets.js +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable react/prop-types */ -import classNames from 'classnames'; -import React from 'react'; -import { DynamicWidgets as CoreDynamicWidgets } from 'react-instantsearch-core'; - -import { createClassNames } from '../core/utils'; - -const cx = createClassNames('DynamicWidgets'); - -export default function DynamicWidgets({ children, className, ...props }) { - return ( -
    - {children} -
    - ); -} diff --git a/packages/react-instantsearch-dom/src/widgets/HierarchicalMenu.js b/packages/react-instantsearch-dom/src/widgets/HierarchicalMenu.js deleted file mode 100644 index 81d067095e..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/HierarchicalMenu.js +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react'; -import { connectHierarchicalMenu } from 'react-instantsearch-core'; - -import HierarchicalMenu from '../components/HierarchicalMenu'; -import PanelCallbackHandler from '../components/PanelCallbackHandler'; - -/** - * The hierarchical menu lets the user browse attributes using a tree-like structure. - * - * This is commonly used for multi-level categorization of products on e-commerce - * websites. From a UX point of view, we suggest not displaying more than two levels deep. - * - * @name HierarchicalMenu - * @kind widget - * @requirements To use this widget, your attributes must be formatted in a specific way. - * If you want for example to have a hierarchical menu of categories, objects in your index - * should be formatted this way: - * - * ```json - * [{ - * "objectID": "321432", - * "name": "lemon", - * "categories.lvl0": "products", - * "categories.lvl1": "products > fruits", - * }, - * { - * "objectID": "8976987", - * "name": "orange", - * "categories.lvl0": "products", - * "categories.lvl1": "products > fruits", - * }] - * ``` - * - * It's also possible to provide more than one path for each level: - * - * ```json - * { - * "objectID": "321432", - * "name": "lemon", - * "categories.lvl0": ["products", "goods"], - * "categories.lvl1": ["products > fruits", "goods > to eat"] - * } - * ``` - * - * All attributes passed to the `attributes` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * - * @propType {array.} attributes - List of attributes to use to generate the hierarchy of the menu. See the example for the convention to follow. - * @propType {boolean} [showMore=false] - Flag to activate the show more button, for toggling the number of items between limit and showMoreLimit. - * @propType {number} [limit=10] - The maximum number of items displayed. - * @propType {number} [showMoreLimit=20] - The maximum number of items displayed when the user triggers the show more. Not considered if `showMore` is false. - * @propType {string} [separator='>'] - Specifies the level separator used in the data. - * @propType {string} [rootPath=null] - The path to use if the first level is not the root level. - * @propType {boolean} [showParentLevel=true] - Flag to set if the parent level should be displayed. - * @propType {string} [defaultRefinement] - the item value selected by default - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @themeKey ais-HierarchicalMenu - the root div of the widget - * @themeKey ais-HierarchicalMenu-noRefinement - the root div of the widget when there is no refinement - * @themeKey ais-HierarchicalMenu-searchBox - the search box of the widget. See [the SearchBox documentation](widgets/SearchBox.html#classnames) for the classnames and translation keys of the SearchBox. - * @themeKey ais-HierarchicalMenu-list - the list of menu items - * @themeKey ais-HierarchicalMenu-list--child - the child list of menu items - * @themeKey ais-HierarchicalMenu-item - the menu list item - * @themeKey ais-HierarchicalMenu-item--selected - the selected menu list item - * @themeKey ais-HierarchicalMenu-item--parent - the menu list item containing children - * @themeKey ais-HierarchicalMenu-link - the clickable menu element - * @themeKey ais-HierarchicalMenu-link--selected - the clickable element of a selected menu list item - * @themeKey ais-HierarchicalMenu-label - the label of each item - * @themeKey ais-HierarchicalMenu-count - the count of values for each item - * @themeKey ais-HierarchicalMenu-showMore - the button used to display more categories - * @themeKey ais-HierarchicalMenu-showMore--disabled - the disabled button used to display more categories - * @translationKey showMore - The label of the show more button. Accepts one parameter, a boolean that is true if the values are expanded - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, HierarchicalMenu } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * ); - */ - -const HierarchicalMenuWidget = (props) => ( - - - -); - -export default connectHierarchicalMenu(HierarchicalMenuWidget, { - $$widgetType: 'ais.hierarchicalMenu', -}); diff --git a/packages/react-instantsearch-dom/src/widgets/Highlight.js b/packages/react-instantsearch-dom/src/widgets/Highlight.js deleted file mode 100644 index 066e9f9c9d..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/Highlight.js +++ /dev/null @@ -1,46 +0,0 @@ -import { connectHighlight } from 'react-instantsearch-core'; - -import Highlight from '../components/Highlight'; - -/** - * Renders any attribute from a hit into its highlighted form when relevant. - * - * Read more about it in the [Highlighting results](guide/Highlighting_results.html) guide. - * @name Highlight - * @kind widget - * @propType {string} attribute - location of the highlighted attribute in the hit (the corresponding element can be either a string or an array of strings) - * @propType {object} hit - hit object containing the highlighted attribute - * @propType {string} [tagName='em'] - tag to be used for highlighted parts of the hit - * @propType {string} [nonHighlightedTagName='span'] - tag to be used for the parts of the hit that are not highlighted - * @propType {node} [separator=','] - symbol used to separate the elements of the array in case the attribute points to an array of strings. - * @themeKey ais-Highlight - root of the component - * @themeKey ais-Highlight-highlighted - part of the text which is highlighted - * @themeKey ais-Highlight-nonHighlighted - part of the text that is not highlighted - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, SearchBox, Hits, Highlight } from 'react-instantsearch-dom'; - * - * const Hit = ({ hit }) => ( - *
    - * - *
    - * ); - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * - * ); - */ - -export default connectHighlight(Highlight, { $$widgetType: 'ais.highlight' }); diff --git a/packages/react-instantsearch-dom/src/widgets/Hits.js b/packages/react-instantsearch-dom/src/widgets/Hits.js deleted file mode 100644 index e5b202ed76..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/Hits.js +++ /dev/null @@ -1,38 +0,0 @@ -import { connectHits } from 'react-instantsearch-core'; - -import Hits from '../components/Hits'; - -/** - * Displays a list of hits. - * - * To configure the number of hits being shown, use the [HitsPerPage widget](widgets/HitsPerPage.html), - * [connectHitsPerPage connector](connectors/connectHitsPerPage.html) or the [Configure widget](widgets/Configure.html). - * - * @name Hits - * @kind widget - * @propType {Component} [hitComponent] - Component used for rendering each hit from - * the results. If it is not provided the rendering defaults to displaying the - * hit in its JSON form. The component will be called with a `hit` prop. - * @themeKey ais-Hits - the root div of the widget - * @themeKey ais-Hits-list - the list of results - * @themeKey ais-Hits-item - the hit list item - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, Hits } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * const App = () => ( - * - * - * - * ); - */ - -export default connectHits(Hits, { $$widgetType: 'ais.hits' }); diff --git a/packages/react-instantsearch-dom/src/widgets/HitsPerPage.js b/packages/react-instantsearch-dom/src/widgets/HitsPerPage.js deleted file mode 100644 index 464cc1e35b..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/HitsPerPage.js +++ /dev/null @@ -1,50 +0,0 @@ -import { connectHitsPerPage } from 'react-instantsearch-core'; - -import HitsPerPage from '../components/HitsPerPage'; - -/** - * The HitsPerPage widget displays a dropdown menu to let the user change the number - * of displayed hits. - * - * If you only want to configure the number of hits per page without - * displaying a widget, you should use the `` widget. See [`` documentation](widgets/Configure.html) - * - * @name HitsPerPage - * @kind widget - * @propType {string} id - The id of the select input - * @propType {{value: number, label: string}[]} items - List of available options. - * @propType {number} defaultRefinement - The number of items selected by default - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @themeKey ais-HitsPerPage - the root div of the widget - * @themeKey ais-HitsPerPage-select - the select - * @themeKey ais-HitsPerPage-option - the select option - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, HitsPerPage, Hits } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * - * ); - */ - -export default connectHitsPerPage(HitsPerPage, { - $$widgetType: 'ais.hitsPerPage', -}); diff --git a/packages/react-instantsearch-dom/src/widgets/InfiniteHits.js b/packages/react-instantsearch-dom/src/widgets/InfiniteHits.js deleted file mode 100644 index 7cc9d03816..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/InfiniteHits.js +++ /dev/null @@ -1,44 +0,0 @@ -import { connectInfiniteHits } from 'react-instantsearch-core'; - -import InfiniteHits from '../components/InfiniteHits'; - -/** - * Displays an infinite list of hits along with a **load more** button. - * - * To configure the number of hits being shown, use the [HitsPerPage widget](widgets/HitsPerPage.html), - * [connectHitsPerPage connector](connectors/connectHitsPerPage.html) or the [Configure widget](widgets/Configure.html). - * - * @name InfiniteHits - * @kind widget - * @propType {Component} [hitComponent] - Component used for rendering each hit from - * the results. If it is not provided the rendering defaults to displaying the - * hit in its JSON form. The component will be called with a `hit` prop. - * @themeKey ais-InfiniteHits - the root div of the widget - * @themeKey ais-InfiniteHits-list - the list of hits - * @themeKey ais-InfiniteHits-item - the hit list item - * @themeKey ais-InfiniteHits-loadMore - the button used to display more results - * @themeKey ais-InfiniteHits-loadMore--disabled - the disabled button used to display more results - * @translationKey loadMore - the label of load more button - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, InfiniteHits } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * ); - */ - -export default connectInfiniteHits(InfiniteHits, { - $$widgetType: 'ais.infiniteHits', -}); diff --git a/packages/react-instantsearch-dom/src/widgets/Menu.js b/packages/react-instantsearch-dom/src/widgets/Menu.js deleted file mode 100644 index c6780c5797..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/Menu.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import { connectMenu } from 'react-instantsearch-core'; - -import Menu from '../components/Menu'; -import PanelCallbackHandler from '../components/PanelCallbackHandler'; - -/** - * The Menu component displays a menu that lets the user choose a single value for a specific attribute. - * @name Menu - * @kind widget - * @requirements The attribute passed to the `attribute` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * - * If you are using the `searchable` prop, you'll also need to make the attribute searchable using - * the [dashboard](https://www.algolia.com/explorer/display/) or using the [API](https://www.algolia.com/doc/guides/searching/faceting/#search-for-facet-values). - * @propType {string} attribute - the name of the attribute in the record - * @propType {boolean} [showMore=false] - true if the component should display a button that will expand the number of items - * @propType {number} [limit=10] - the minimum number of diplayed items - * @propType {number} [showMoreLimit=20] - the maximun number of displayed items. Only used when showMore is set to `true` - * @propType {string} [defaultRefinement] - the value of the item selected by default - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @propType {boolean} [searchable=false] - true if the component should display an input to search for facet values.
    In order to make this feature work, you need to make the attribute searchable [using the API](https://www.algolia.com/doc/guides/searching/faceting/?language=js#declaring-a-searchable-attribute-for-faceting) or [the dashboard](https://www.algolia.com/explorer/display/). - * @themeKey ais-Menu - the root div of the widget - * @themeKey ais-Menu-searchBox - the search box of the widget. See [the SearchBox documentation](widgets/SearchBox.html#classnames) for the classnames and translation keys of the SearchBox. - * @themeKey ais-Menu-list - the list of all menu items - * @themeKey ais-Menu-item - the menu list item - * @themeKey ais-Menu-item--selected - the selected menu list item - * @themeKey ais-Menu-link - the clickable menu element - * @themeKey ais-Menu-label - the label of each item - * @themeKey ais-Menu-count - the count of values for each item - * @themeKey ais-Menu-noResults - the div displayed when there are no results - * @themeKey ais-Menu-showMore - the button used to display more categories - * @themeKey ais-Menu-showMore--disabled - the disabled button used to display more categories - * @translationkey showMore - The label of the show more button. Accepts one parameters, a boolean that is true if the values are expanded - * @translationkey noResults - The label of the no results text when no search for facet values results are found. - * @example - * import React from 'react'; - * import algoliasearch from 'algoliasearch/lite'; - * import { InstantSearch, Menu } from 'react-instantsearch-dom'; - * - * const searchClient = algoliasearch( - * 'latency', - * '6be0576ff61c053d5f9a3225e2a90f76' - * ); - * - * const App = () => ( - * - * - * - * ); - */ - -const MenuWidget = (props) => ( - - - -); - -export default connectMenu(MenuWidget, { $$widgetType: 'ais.menu' }); diff --git a/packages/react-instantsearch-dom/src/widgets/MenuSelect.js b/packages/react-instantsearch-dom/src/widgets/MenuSelect.js deleted file mode 100644 index 7089f12f75..0000000000 --- a/packages/react-instantsearch-dom/src/widgets/MenuSelect.js +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { connectMenu } from 'react-instantsearch-core'; - -import MenuSelect from '../components/MenuSelect'; -import PanelCallbackHandler from '../components/PanelCallbackHandler'; - -/** - * The MenuSelect component displays a select that lets the user choose a single value for a specific attribute. - * @name MenuSelect - * @kind widget - * @requirements The attribute passed to the `attribute` prop must be present in "attributes for faceting" - * on the Algolia dashboard or configured as `attributesForFaceting` via a set settings call to the Algolia API. - * @propType {string} id - the id of the select input - * @propType {string} attribute - the name of the attribute in the record - * @propType {string} [defaultRefinement] - the value of the item selected by default - * @propType {number} [limit=10] - the minimum number of diplayed items - * @propType {function} [transformItems] - Function to modify the items being displayed, e.g. for filtering or sorting them. Takes an items as parameter and expects it back in return. - * @themeKey ais-MenuSelect - the root div of the widget - * @themeKey ais-MenuSelect-noRefinement - the root div of the widget when there is no refinement - * @themeKey ais-MenuSelect-select - the ` - {eventName} - - ))} - -
    - - (Click on the action logger tab of the right sidebar to see event - logs) - -
    -
    - - - - ); - } -} - -stories.add('with event listeners', () => ); diff --git a/packages/react-instantsearch-dom/stories/Snippet.stories.js b/packages/react-instantsearch-dom/stories/Snippet.stories.js deleted file mode 100644 index 0a5720d20f..0000000000 --- a/packages/react-instantsearch-dom/stories/Snippet.stories.js +++ /dev/null @@ -1,61 +0,0 @@ -import { text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { Snippet, Hits, Configure } from 'react-instantsearch-dom'; - -import { WrapWithHits } from './util'; - -const stories = storiesOf('Snippet', module); - -const Default = ({ hit }) => ( -
    -

    - -

    -

    - -

    -
    -); - -Default.propTypes = { - hit: PropTypes.object.isRequired, -}; - -const StrongHits = ({ hit }) => ( -
    -

    - -

    -

    - -

    -
    -); - -StrongHits.propTypes = { - hit: PropTypes.object.isRequired, -}; - -stories - .add('default', () => ( - - - - - )) - .add('playground', () => ( - - - - - )); diff --git a/packages/react-instantsearch-dom/stories/SortBy.stories.js b/packages/react-instantsearch-dom/stories/SortBy.stories.js deleted file mode 100644 index 5a429b4916..0000000000 --- a/packages/react-instantsearch-dom/stories/SortBy.stories.js +++ /dev/null @@ -1,47 +0,0 @@ -import { storiesOf } from '@storybook/react'; -import React from 'react'; -import { Panel, SortBy } from 'react-instantsearch-dom'; - -import { WrapWithHits } from './util'; - -const stories = storiesOf('SortBy', module); - -stories - .add('default', () => ( - - - - )) - .add('without label', () => ( - - - - )) - .add('with Panel', () => ( - - - - - - )); diff --git a/packages/react-instantsearch-dom/stories/Stats.stories.js b/packages/react-instantsearch-dom/stories/Stats.stories.js deleted file mode 100644 index 2aad5e665d..0000000000 --- a/packages/react-instantsearch-dom/stories/Stats.stories.js +++ /dev/null @@ -1,49 +0,0 @@ -import { storiesOf } from '@storybook/react'; -import React from 'react'; -import { Panel, Stats, RelevantSort } from 'react-instantsearch-dom'; - -import { WrapWithHits } from './util'; - -const stories = storiesOf('Stats', module); - -stories - .add('default', () => ( - -
    - -
    -
    - )) - .add('with Panel', () => ( - - - - - - )) - .add('with sorted hits', () => ( - -
    - - ( -
    - {isRelevantSorted - ? 'We removed some search results to show you the most relevant ones' - : 'Currently showing all results'} -
    - )} - buttonTextComponent={({ isRelevantSorted }) => ( -
    - {isRelevantSorted ? 'See all results' : 'See relevant results'} -
    - )} - /> -
    -
    - )); diff --git a/packages/react-instantsearch-dom/stories/ToggleRefinement.stories.js b/packages/react-instantsearch-dom/stories/ToggleRefinement.stories.js deleted file mode 100644 index c08d3441be..0000000000 --- a/packages/react-instantsearch-dom/stories/ToggleRefinement.stories.js +++ /dev/null @@ -1,39 +0,0 @@ -import { storiesOf } from '@storybook/react'; -import React from 'react'; -import { Panel, ToggleRefinement } from 'react-instantsearch-dom'; - -import { WrapWithHits } from './util'; - -const stories = storiesOf('ToggleRefinement', module); - -stories - .add('default', () => ( - - - - )) - .add('checked by default', () => ( - - - - )) - .add('with Panel', () => ( - - - - - - )); diff --git a/packages/react-instantsearch-dom/stories/VoiceSearch.stories.tsx b/packages/react-instantsearch-dom/stories/VoiceSearch.stories.tsx deleted file mode 100644 index b453474011..0000000000 --- a/packages/react-instantsearch-dom/stories/VoiceSearch.stories.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { storiesOf } from '@storybook/react'; -import React from 'react'; -import { VoiceSearch, SearchBox } from 'react-instantsearch-dom'; - -import { WrapWithHits } from './util'; - -import type { InnerComponentProps } from '../src/components/VoiceSearch'; - -const stories = storiesOf('VoiceSearch', module); - -stories - .add('default', () => ( - -

    - To see this button disabled, test it on unsupported browsers like - Safari, Firefox, etc. -

    - -
    - )) - .add('without status', () => ( - - null} /> - - )) - .add('with a SearchBox', () => ( - - - - - )) - .add('with a custom button text', () => ( - -
    - - isListening ? '⏹' : '🎙' - } - /> -
    -
    - )) - .add('with full status', () => { - const Status = ({ - status, - errorCode, - isListening, - transcript, - isSpeechFinal, - isBrowserSupported, - }: InnerComponentProps) => { - return ( -
    -

    status: {status}

    -

    errorCode: {errorCode}

    -

    isListening: {isListening ? 'true' : 'false'}

    -

    transcript: {transcript}

    -

    isSpeechFinal: {isSpeechFinal ? 'true' : 'false'}

    -

    isBrowserSupported: {isBrowserSupported ? 'true' : 'false'}

    -
    - ); - }; - - return ( - - - - ); - }) - .add('search as you speak', () => { - const Status = ({ - status, - errorCode, - isListening, - transcript, - isSpeechFinal, - isBrowserSupported, - }: InnerComponentProps) => { - return ( -
    -

    status: {status}

    -

    errorCode: {errorCode}

    -

    isListening: {isListening ? 'true' : 'false'}

    -

    transcript: {transcript}

    -

    isSpeechFinal: {isSpeechFinal ? 'true' : 'false'}

    -

    isBrowserSupported: {isBrowserSupported ? 'true' : 'false'}

    -
    - ); - }; - return ( - - - - ); - }) - .add('example of dynamic UI working with SearchBox', () => { - const Status = ({ isListening, transcript }: InnerComponentProps) => { - return ( -
    - {transcript} -
    - ); - }; - - return ( - -
    - - -
    -
    - ); - }) - .add('with additional paramaters', () => ( - - {}} /> - - - )) - .add('with additional paramaters & language', () => ( - - {}} /> - - )) - .add('with additional paramaters & user set & language', () => ( - - ({ analyticsTags: ['voice'] })} - /> - - )); diff --git a/packages/react-instantsearch-dom/stories/places/connector.js b/packages/react-instantsearch-dom/stories/places/connector.js deleted file mode 100644 index f0998d6929..0000000000 --- a/packages/react-instantsearch-dom/stories/places/connector.js +++ /dev/null @@ -1,29 +0,0 @@ -import { createConnector } from 'react-instantsearch-dom'; - -export default createConnector({ - displayName: 'AlgoliaGeoSearch', - - getProvidedProps() { - return {}; - }, - - refine(props, searchState, nextValue) { - return { - ...searchState, - aroundLatLng: nextValue, - boundingBox: {}, - }; - }, - - getSearchParameters(searchParameters, props, searchState) { - const currentRefinement = - searchState.aroundLatLng || props.defaultRefinement; - - return searchParameters - .setQueryParameter('insideBoundingBox') - .setQueryParameter( - 'aroundLatLng', - `${currentRefinement.lat}, ${currentRefinement.lng}` - ); - }, -}); diff --git a/packages/react-instantsearch-dom/stories/places/index.js b/packages/react-instantsearch-dom/stories/places/index.js deleted file mode 100644 index cbfa455beb..0000000000 --- a/packages/react-instantsearch-dom/stories/places/index.js +++ /dev/null @@ -1,45 +0,0 @@ -import places from 'places.js'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import connect from './connector'; - -class Places extends Component { - static propTypes = { - refine: PropTypes.func.isRequired, - defaultRefinement: PropTypes.object.isRequired, - }; - - createRef = (c) => (this.element = c); - - componentDidMount() { - const { refine, defaultRefinement } = this.props; - - const autocomplete = places({ - container: this.element, - }); - - autocomplete.on('change', (event) => { - refine(event.suggestion.latlng); - }); - - autocomplete.on('clear', () => { - refine(defaultRefinement); - }); - } - - render() { - return ( -
    - -
    - ); - } -} - -export default connect(Places); diff --git a/packages/react-instantsearch-dom/stories/util.d.ts b/packages/react-instantsearch-dom/stories/util.d.ts deleted file mode 100644 index aff0cc4cf3..0000000000 --- a/packages/react-instantsearch-dom/stories/util.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { PlainSearchParameters } from 'algoliasearch-helper'; -import type React from 'react'; - -export const Content: React.FC<{ - linkedStoryGroup?: string; - hasPlayground?: boolean; - children?: React.ReactNode; - resultsView?: React.ReactNode; -}>; - -export const WrapWithHits: React.FC<{ - appId?: string; - apiKey?: string; - indexName?: string; - children?: React.ReactNode; - searchBox?: boolean; - linkedStoryGroup?: string; - hasPlayground?: boolean; - pagination?: boolean; - searchParameters?: PlainSearchParameters; - hitsElement?: element; - initialSearchState?: any; - onSearchStateChange?: (searchState: any) => any; -}>; diff --git a/packages/react-instantsearch-dom/stories/util.js b/packages/react-instantsearch-dom/stories/util.js deleted file mode 100644 index bc78bcee6e..0000000000 --- a/packages/react-instantsearch-dom/stories/util.js +++ /dev/null @@ -1,196 +0,0 @@ -import { linkTo } from '@storybook/addon-links'; -import algoliasearch from 'algoliasearch/lite'; -import PropTypes from 'prop-types'; -import React, { useState, useMemo } from 'react'; -import { - InstantSearch, - ClearRefinements, - SearchBox, - Pagination, - Highlight, - Configure, - connectHits, -} from 'react-instantsearch-dom'; -import 'instantsearch.css/themes/algolia.css'; - -const Hits = ({ hits }) => ( -
    - {hits.map((hit) => ( -
    - {hit.image && ( -
    - -
    - )} -
    -
    - - - ${hit.price} - - {hit.rating} stars -
    -
    - -
    -
    - -
    -
    -
    - ))} -
    -); - -Hits.propTypes = { - hits: PropTypes.array.isRequired, -}; - -export const CustomHits = connectHits(Hits); - -export function Content({ - hasPlayground, - linkedStoryGroup, - children, - resultsView, -}) { - const sourceCodeUrl = `https://github.com/algolia/instantsearch.js/tree/master/packages/react-instantsearch-dom/stories/${linkedStoryGroup}`; - const playgroundLink = hasPlayground ? ( - - ) : null; - - const footer = linkedStoryGroup ? ( -
    - {playgroundLink} - -
    View source code
    -
    -
    - ) : null; - - const results = resultsView ? ( -
    - {resultsView} -
    - ) : null; - - return ( -
    -
    {children}
    - {results} - {footer} -
    - ); -} - -Content.propTypes = { - linkedStoryGroup: PropTypes.string, - hasPlayground: PropTypes.bool, - children: PropTypes.node, - resultsView: PropTypes.node, -}; - -export function WrapWithHits({ - searchParameters: askedSearchParameters = {}, - children, - searchBox = true, - hasPlayground = false, - linkedStoryGroup, - pagination = true, - hitsElement, - appId, - apiKey, - indexName, - initialSearchState, - onSearchStateChange, -}) { - const searchClient = useMemo(() => { - return algoliasearch(appId, apiKey); - }, [appId, apiKey]); - - const hits = hitsElement || ; - - const searchParameters = { - hitsPerPage: 3, - ...askedSearchParameters, - }; - - const [searchState, setSearchState] = useState(initialSearchState); - - const setNextSearchState = (nextSearchState) => { - setSearchState(nextSearchState); - onSearchStateChange(nextSearchState); - }; - - return ( - - - -
    - {searchBox ? ( - - ) : null} - -
    - {hits} -
    - {pagination ? : null} -
    -
    - } - > - {children} - - - ); -} - -WrapWithHits.propTypes = { - appId: PropTypes.string, - apiKey: PropTypes.string, - indexName: PropTypes.string, - children: PropTypes.node, - searchBox: PropTypes.bool, - linkedStoryGroup: PropTypes.string, - hasPlayground: PropTypes.bool, - pagination: PropTypes.bool, - searchParameters: PropTypes.object, - hitsElement: PropTypes.element, - initialSearchState: PropTypes.object, - onSearchStateChange: PropTypes.func, -}; - -// defaultProps added so that they're displayed in the JSX addon -WrapWithHits.defaultProps = { - appId: 'latency', - apiKey: '6be0576ff61c053d5f9a3225e2a90f76', - indexName: 'instant_search', - initialSearchState: {}, - onSearchStateChange: (searchState) => searchState, -}; diff --git a/packages/react-instantsearch-hooks-server/CHANGELOG.md b/packages/react-instantsearch-hooks-server/CHANGELOG.md deleted file mode 100644 index 19b5636d1d..0000000000 --- a/packages/react-instantsearch-hooks-server/CHANGELOG.md +++ /dev/null @@ -1,2004 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [6.47.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.47.2...react-instantsearch-hooks-server@6.47.3) (2023-07-27) - - -### Bug Fixes - -* add a future warning when the package name changes ([#5778](https://github.com/algolia/instantsearch.js/issues/5778)) ([3d22ee4](https://github.com/algolia/instantsearch.js/commit/3d22ee45e1f03a443323a371621262f1fe45e664)) - - - - - -## [6.47.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.47.1...react-instantsearch-hooks-server@6.47.2) (2023-07-25) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.47.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.47.0...react-instantsearch-hooks-server@6.47.1) (2023-07-19) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.47.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.46.0...react-instantsearch-hooks-server@6.47.0) (2023-07-18) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.46.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.45.1...react-instantsearch-hooks-server@6.46.0) (2023-07-10) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.45.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.45.0...react-instantsearch-hooks-server@6.45.1) (2023-07-04) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -# [6.45.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.44.3...react-instantsearch-hooks-server@6.45.0) (2023-06-20) - - -### Features - -* **algoliaAgent:** track Next.js version as Algolia agent ([#5677](https://github.com/algolia/instantsearch.js/issues/5677)) ([b205ac0](https://github.com/algolia/instantsearch.js/commit/b205ac019f79699cd608d63792b8ff5a0832aa7c)) - - - - - -## [6.44.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.44.2...react-instantsearch-hooks-server@6.44.3) (2023-06-13) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.44.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.44.1...react-instantsearch-hooks-server@6.44.2) (2023-06-05) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.44.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.44.0...react-instantsearch-hooks-server@6.44.1) (2023-05-30) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.43.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.43.0...react-instantsearch-hooks-server@6.43.1) (2023-05-16) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -# [6.43.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.42.2...react-instantsearch-hooks-server@6.43.0) (2023-04-24) - - -### Bug Fixes - -* **server:** error when multiple InstantSearch instances in getServerState ([#5588](https://github.com/algolia/instantsearch.js/issues/5588)) ([0778460](https://github.com/algolia/instantsearch.js/commit/0778460dee935b929e420b362948b4962978d3a5)) - - - - - -## [6.42.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.42.1...react-instantsearch-hooks-server@6.42.2) (2023-04-11) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.42.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.42.0...react-instantsearch-hooks-server@6.42.1) (2023-03-28) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.42.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.41.0...react-instantsearch-hooks-server@6.42.0) (2023-03-21) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.41.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.40.1...react-instantsearch-hooks-server@6.41.0) (2023-03-07) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.40.1...react-instantsearch-hooks-server@6.40.2) (2023-02-28) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.40.0...react-instantsearch-hooks-server@6.40.1) (2023-02-21) - - -### Bug Fixes - -* **readme:** remove experimental warning ([#5485](https://github.com/algolia/instantsearch.js/issues/5485)) ([4409993](https://github.com/algolia/instantsearch.js/commit/44099939b5c51c94ef294b31dc91548f1530b65b)) - - - - - -## [6.40.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.39.3...react-instantsearch-hooks-server@6.40.0) (2023-02-14) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - -## [6.39.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.39.2...react-instantsearch-hooks-server@6.39.3) (2023-02-07) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.39.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.39.1...react-instantsearch-hooks-server@6.39.2) (2023-01-30) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.39.0...react-instantsearch-hooks-server@6.39.1) (2023-01-26) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -# [6.39.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.38.3...react-instantsearch-hooks-server@6.39.0) (2023-01-25) - - -### Features - -* **rendering:** always render with current state ([#5429](https://github.com/algolia/instantsearch.js/issues/5429)) ([920e951](https://github.com/algolia/instantsearch.js/commit/920e951f03aada0e6a1ce16bc389a82a2f00b202)) - - - - - -## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.38.2...react-instantsearch-hooks-server@6.38.3) (2023-01-09) - -**Note:** Version bump only for package react-instantsearch-hooks-server - - - - - -## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-server@6.38.1...react-instantsearch-hooks-server@6.38.2) (2023-01-03) - -### Bug Fixes - -* **getServerState:** correctly forward asynchronous errors ([#5405](https://github.com/algolia/instantsearch/pull/5405)) ([0f0e84d6](https://github.com/algolia/instantsearch/commit/0f0e84d62c58fdbe81f6e5c823ab6d19b45ee8f3)) - - - - - -## [6.38.1](https://github.com/algolia/react-instantsearch/compare/v6.38.0...v6.38.1) (2022-11-08) - - -### Bug Fixes - -* **hooks:** avoid effect in useStableValue ([#3670](https://github.com/algolia/react-instantsearch/issues/3670)) ([d1f53ae](https://github.com/algolia/react-instantsearch/commit/d1f53ae815b75f13c18fd245e0403d57e7ae391c)) - - - -# [6.38.0](https://github.com/algolia/react-instantsearch/compare/v6.37.0...v6.38.0) (2022-10-25) - - -### Features - -* **getServerState:** allow users to inject renderToString ([#3658](https://github.com/algolia/react-instantsearch/issues/3658)) ([9c10449](https://github.com/algolia/react-instantsearch/commit/9c104497b9b32337f288d70a2582c41cafb13cd6)), closes [#3633](https://github.com/algolia/react-instantsearch/issues/3633) [#3618](https://github.com/algolia/react-instantsearch/issues/3618) [vercel/next.js#40067](https://github.com/vercel/next.js/issues/40067) - -* **PoweredBy:** update component logo ([#3661](https://github.com/algolia/react-instantsearch/issues/3661)) ([69c1b2a](https://github.com/algolia/react-instantsearch/commit/69c1b2acef64d972dfa6c6beb8967032119ad2d5)) - - - -# [6.37.0](https://github.com/algolia/react-instantsearch/compare/v6.36.0...v6.37.0) (2022-10-18) - - -### Features - -* **core:** support react 18 strict mode ([#3653](https://github.com/algolia/react-instantsearch/issues/3653)) ([9174806](https://github.com/algolia/react-instantsearch/commit/9174806a7997a45893a24d149027119f4a0709c3)) - - - -# [6.36.0](https://github.com/algolia/react-instantsearch/compare/v6.35.0...v6.36.0) (2022-10-04) - - -### Features - -* **HierarchicalMenu:** add css class for link of selected menu item ([#3646](https://github.com/algolia/react-instantsearch/issues/3646)) ([980ad70](https://github.com/algolia/react-instantsearch/commit/980ad70c75e970c35c11a10a534dbe3996d6c54c)) -* **useInstantSearch:** expose status & error ([#3645](https://github.com/algolia/react-instantsearch/issues/3645)) ([f436d31](https://github.com/algolia/react-instantsearch/commit/f436d31184f3f75b33a1fdaa19c665e77948df28)) - - - -# [6.35.0](https://github.com/algolia/react-instantsearch/compare/v6.34.0...v6.35.0) (2022-09-29) - - -### Features - -* **hooks-web:** introduce Translations API ([#3638](https://github.com/algolia/react-instantsearch/issues/3638)) ([63b506f](https://github.com/algolia/react-instantsearch/commit/63b506f9dbad284f45ac17210e17c4a2a8f099b6)) - -### Fixes -* **hooks-web:** when searchAsYouType=false, pressing the reset button searches (previously only reset the query) ([#3642](https://github.com/algolia/react-instantsearch/issues/3642)) ([f969deb](https://github.com/algolia/react-instantsearch/commit/f969deb05fd4f53aaa251ff88b52db2224ce0786)) - -# [6.34.0](https://github.com/algolia/react-instantsearch/compare/v6.33.0...v6.34.0) (2022-09-27) - - -### Features - -* **SearchBox:** expose formRef ([#3565](https://github.com/algolia/react-instantsearch/issues/3565)) ([1c2f46d](https://github.com/algolia/react-instantsearch/commit/1c2f46da2d1cf705cfd3946c52aef4ca9ec943d7)) - - - -# [6.33.0](https://github.com/algolia/react-instantsearch/compare/v6.32.1...v6.33.0) (2022-09-15) - - -### Bug Fixes - -* **react-native:** mark as compatible with react 18 ([#3614](https://github.com/algolia/react-instantsearch/issues/3614)) ([2a191a8](https://github.com/algolia/react-instantsearch/commit/2a191a84751127de5a3eb34b59b460a1d1bfe594)) -* **rish:** hide reset button when search is stalled in `SearchBox` ([#3617](https://github.com/algolia/react-instantsearch/issues/3617)) ([93ee9d0](https://github.com/algolia/react-instantsearch/commit/93ee9d0212893cef4842c86b7c78f285aa136be8)) - - -### Features - -* **dependencies:** update instantsearch and helper ([#3622](https://github.com/algolia/react-instantsearch/issues/3622)) ([6773ab1](https://github.com/algolia/react-instantsearch/commit/6773ab169cd74dfe1133e255daade4d57e99d9a4)) - - - -## [6.32.1](https://github.com/algolia/react-instantsearch/compare/v6.32.0...v6.32.1) (2022-08-26) - - -### Bug Fixes - -* **useInstantSearch:** prevent `results` from being `null` when first search is stalled ([#3597](https://github.com/algolia/react-instantsearch/issues/3597)) ([6f71d78](https://github.com/algolia/react-instantsearch/commit/6f71d78868fde55a3f9c4460edc337a1e99df4f9)) - - - -# [6.32.0](https://github.com/algolia/react-instantsearch/compare/v6.31.1...v6.32.0) (2022-08-22) - - -### Features - -* **SearchBox:** introduce `autoFocus` prop ([#3599](https://github.com/algolia/react-instantsearch/issues/3599)) ([99121b9](https://github.com/algolia/react-instantsearch/commit/99121b952fd002cb6dae52af41f08beed8f6c3e2)) - - - -## [6.31.1](https://github.com/algolia/react-instantsearch/compare/v6.31.0...v6.31.1) (2022-08-08) - - -### Bug Fixes - -* **hooks:** prevent widget cleanup on `` unmount ([#3590](https://github.com/algolia/react-instantsearch/issues/3590)) ([d94899d](https://github.com/algolia/react-instantsearch/commit/d94899d1264134f0cb1ca2d266a660f1fb2a588c)) - - - -# [6.31.0](https://github.com/algolia/react-instantsearch/compare/v6.30.3...v6.31.0) (2022-08-03) - - -### Features - -* **hooks:** add `searchAsYouType` option to `` ([#3585](https://github.com/algolia/react-instantsearch/issues/3585)) ([c610385](https://github.com/algolia/react-instantsearch/commit/c610385cb9688b23b3e041e31b9edd60392b341d)) - - - -## [6.30.3](https://github.com/algolia/react-instantsearch/compare/v6.30.2...v6.30.3) (2022-08-01) - - -### Bug Fixes - -* **hooks:** replace `labelText` CSS class with `label` in `` to align with InstantSearch's CSS specifications ([#3583](https://github.com/algolia/react-instantsearch/issues/3583)) ([3e030ae](https://github.com/algolia/react-instantsearch/commit/3e030aedb9f285ff449eb82589bc6fea60b160cb)) - - - -## [6.30.2](https://github.com/algolia/react-instantsearch/compare/v6.30.1...v6.30.2) (2022-07-18) - - -### Bug Fixes - -* **hooks:** type of DynamicWidgets props ([#3566](https://github.com/algolia/react-instantsearch/issues/3566)) ([612c98b](https://github.com/algolia/react-instantsearch/commit/612c98b5a77fb9037185c4b5efda8c07663dbd1a)), closes [#3563](https://github.com/algolia/react-instantsearch/issues/3563) -* **hooks:** use single instance in ([#3561](https://github.com/algolia/react-instantsearch/issues/3561)) ([4c358be](https://github.com/algolia/react-instantsearch/commit/4c358bebfc91451b1610f677f89c595d7a427f1f)) - - - -## [6.30.1](https://github.com/algolia/react-instantsearch/compare/v6.30.0...v6.30.1) (2022-07-12) - - -### Bug Fixes - -* **hooks:** provide state and results APIs from "render" event ([#3554](https://github.com/algolia/react-instantsearch/issues/3554)) ([67d4788](https://github.com/algolia/react-instantsearch/commit/67d4788ab09ec2a57b43d53e8093b8c11120b761)) -* **hooks**: update instantsearch.js dependency ([#3557](https://github.com/algolia/react-instantsearch/issues/3557)) - - - -# [6.30.0](https://github.com/algolia/react-instantsearch/compare/v6.29.0...v6.30.0) (2022-07-05) - - -### Features - -* **core:** update instantsearch and helper ([#3539](https://github.com/algolia/react-instantsearch/issues/3539)) ([0ac2c7a](https://github.com/algolia/react-instantsearch/commit/0ac2c7a3f2e2a827721f5b2b7c69c54560f8574f)) - - - -# [6.29.0](https://github.com/algolia/react-instantsearch/compare/v6.28.0...v6.29.0) (2022-06-21) - - -### Bug Fixes - -* **HierarchicalMenu:** show full hierarchical parent values ([#3521](https://github.com/algolia/react-instantsearch/issues/3521)) ([79c3890](https://github.com/algolia/react-instantsearch/commit/79c3890848175a4d70311e5c3929c902bb953c10)) - - -### Features - -* **core:** sort parameters for improved cache rate ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) -* **core:** support client.search for sffv ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) - - - -# [6.28.0](https://github.com/algolia/react-instantsearch/compare/v6.27.0...v6.28.0) (2022-06-15) - - -### Bug Fixes - -* **hooks-server:** import react server via an expression ([#3515](https://github.com/algolia/react-instantsearch/issues/3515)) ([91b96f7](https://github.com/algolia/react-instantsearch/commit/91b96f743b9315ed5ea781681b77fc7f5604ab6e)), closes [#3512](https://github.com/algolia/react-instantsearch/issues/3512) -* **hooks-web:** fix duplicated key in ([#3513](https://github.com/algolia/react-instantsearch/issues/3513)) ([fc94d80](https://github.com/algolia/react-instantsearch/commit/fc94d806daf139f58b234cdc0b450da2efe861ee)) -* **hooks:** mount widgets in SSR to retrieve HTML ([#3518](https://github.com/algolia/react-instantsearch/issues/3518)) ([aa5f9d8](https://github.com/algolia/react-instantsearch/commit/aa5f9d84ddb6e97d05e6ad1baf2c6caa23891281)) -* **types:** allow useInstantSearch to be generic ([#3508](https://github.com/algolia/react-instantsearch/issues/3508)) ([6807232](https://github.com/algolia/react-instantsearch/commit/68072324cf302801502a1b4c3d06703e57b55a97)), closes [algolia/instantsearch.js#5060](https://github.com/algolia/instantsearch.js/issues/5060) -* **types:** support React 18 types ([#3481](https://github.com/algolia/react-instantsearch/issues/3481)) ([74cf8cb](https://github.com/algolia/react-instantsearch/commit/74cf8cb9be8ff3d113b57a50e7083df0d1bc94f2)) - - -### Features - -* **hooks:** introduce `useInstantSearch()` ([#3494](https://github.com/algolia/react-instantsearch/issues/3494)) ([74d522c](https://github.com/algolia/react-instantsearch/commit/74d522c032326658f2a0b8f0001bd593e0085208)) -* **hooks:** support React 18 Strict Mode ([#3514](https://github.com/algolia/react-instantsearch/issues/3514)) ([eeb67c7](https://github.com/algolia/react-instantsearch/commit/eeb67c7b5dc08c696c46d9538f104eeceecef388)) - - - -# [6.27.0](https://github.com/algolia/react-instantsearch/compare/v6.26.0...v6.27.0) (2022-06-07) - - -### Bug Fixes - -* **hooks-web:** don't pass widget props to ui components ([#3501](https://github.com/algolia/react-instantsearch/issues/3501)) ([5bd53c1](https://github.com/algolia/react-instantsearch/commit/5bd53c128ddeeea87f75ae89eb8f2324d476c70e)), closes [#3499](https://github.com/algolia/react-instantsearch/issues/3499) -* **SearchBox-hooks:** correctly pass widget props ([#3499](https://github.com/algolia/react-instantsearch/issues/3499)) ([2cdf906](https://github.com/algolia/react-instantsearch/commit/2cdf90602b7c2c5895124ef64c389ce574154386)), closes [#3498](https://github.com/algolia/react-instantsearch/issues/3498) - - -### Features - -* **hooks:** migrate to `useSyncExternalStore()` ([#3489](https://github.com/algolia/react-instantsearch/issues/3489)) ([81bbdf2](https://github.com/algolia/react-instantsearch/commit/81bbdf28f2d28d8b0081cfd7d9e84c3e33038dd2)) -* **hooks:** upgrade to InstantSearch.js 4.41.0 ([#3502](https://github.com/algolia/react-instantsearch/issues/3502)) ([0b76792](https://github.com/algolia/react-instantsearch/commit/0b76792ea0c4e2ac9fe742810d70ba1aee2a3e79)) - - - -# [6.26.0](https://github.com/algolia/react-instantsearch/compare/v6.25.0...v6.26.0) (2022-05-19) - - -### Features - -* **hooks-web:** expose `sendEvent` to `hitComponent` ([#3476](https://github.com/algolia/react-instantsearch/issues/3476)) ([5cc18d1](https://github.com/algolia/react-instantsearch/commit/5cc18d19d9f22305f33d92e43fd0aca2a5cb949a)) -* **react-instantsearch-core:** allow widgets to set their `$$widgetType` ([#3472](https://github.com/algolia/react-instantsearch/issues/3472)) ([1c436e1](https://github.com/algolia/react-instantsearch/commit/1c436e1429ab4230bbfea7c6d2474d141f5c5c64)) - - - -# [6.25.0](https://github.com/algolia/react-instantsearch/compare/v6.24.3...v6.25.0) (2022-05-17) - - -### Bug Fixes - -* **hooks-highlight:** make sure highlight and snippet don't show html-escaped content ([#3471](https://github.com/algolia/react-instantsearch/issues/3471)) ([c18ddd2](https://github.com/algolia/react-instantsearch/commit/c18ddd25faca37d6bfa3d1c28f6fc22ec5fcf6d8)) -* **hooks-server:** remove faulty UMD build ([#3465](https://github.com/algolia/react-instantsearch/issues/3465)) ([c1ddfe4](https://github.com/algolia/react-instantsearch/commit/c1ddfe408b411551ac8524877a9d65ded8133c42)) - - -### Features - -* **dom-maps:** expose GeoSearchContext ([#3468](https://github.com/algolia/react-instantsearch/issues/3468)) ([a61ff96](https://github.com/algolia/react-instantsearch/commit/a61ff96222bfd4f6301cf93bf95e2fa18b263d3c)), closes [#3448](https://github.com/algolia/react-instantsearch/issues/3448) -* **hooks-server:** support import from React 18 ([#3464](https://github.com/algolia/react-instantsearch/issues/3464)) ([0a13867](https://github.com/algolia/react-instantsearch/commit/0a13867f7dd5a8a18e0957b2072bbde3b02d6490)), closes [#3453](https://github.com/algolia/react-instantsearch/issues/3453) -* **hooks:** make InstantSearch type generic ([#3466](https://github.com/algolia/react-instantsearch/issues/3466)) ([b0905b7](https://github.com/algolia/react-instantsearch/commit/b0905b73bed558c62eedb7ae701be20c2ebe25c9)) - - - -## [6.24.3](https://github.com/algolia/react-instantsearch/compare/v6.24.2...v6.24.3) (2022-05-10) - - -### Bug Fixes - -* **numericmenu:** include range values in comparison with minmax bounds ([#3461](https://github.com/algolia/react-instantsearch/issues/3461)) ([e4c2682](https://github.com/algolia/react-instantsearch/commit/e4c268261dc42a6aa43d985934b53c82f8b71089)) - - - -## [6.24.2](https://github.com/algolia/react-instantsearch/compare/v6.24.1...v6.24.2) (2022-05-05) - - -### Bug Fixes - -* **hooks:** prevent infinite loops from render state ([#3455](https://github.com/algolia/react-instantsearch/issues/3455)) ([1799fc9](https://github.com/algolia/react-instantsearch/commit/1799fc9f78a4a5aafb54df339c3e211ff9187748)) - - - -## [6.24.1](https://github.com/algolia/react-instantsearch/compare/v6.24.0...v6.24.1) (2022-05-03) - - -### Bug Fixes - -* **types:** export correct types for react-instantsearch-hooks-web ([#3454](https://github.com/algolia/react-instantsearch/issues/3454)) ([a863430](https://github.com/algolia/react-instantsearch/commit/a8634306621f7a05a2b3056a6db25ccf8d9eabf0)) - - - -# [6.24.0](https://github.com/algolia/react-instantsearch/compare/v6.23.4...v6.24.0) (2022-04-28) - - -### Features - -* **hooks:** expose DOM components ([#3450](https://github.com/algolia/react-instantsearch/issues/3450)) ([5732e3d](https://github.com/algolia/react-instantsearch/commit/5732e3de732275911f94b26ba9e2c4165bdf77e7)) -* **hooks:** remove experimental warning ([#3446](https://github.com/algolia/react-instantsearch/issues/3446)) ([84c99fe](https://github.com/algolia/react-instantsearch/commit/84c99fe91d6906a877bec620b44c61d762f0ea57)) - - - -## [6.23.4](https://github.com/algolia/react-instantsearch/compare/v6.23.3...v6.23.4) (2022-04-21) - - -### Bug Fixes - -* **hooks:** forbid importing from instantsearch.js root path ([#3437](https://github.com/algolia/react-instantsearch/issues/3437)) ([82ef9ea](https://github.com/algolia/react-instantsearch/commit/82ef9eaaec42bc54f15796b5b031a8656330a45c)) -* **packages:** correctly mark peer dependency ([#3439](https://github.com/algolia/react-instantsearch/issues/3439)) ([51e8818](https://github.com/algolia/react-instantsearch/commit/51e8818fce224819230c8bf6dea2a08d71d9be29)), closes [#3428](https://github.com/algolia/react-instantsearch/issues/3428) - - - -## [6.23.3](https://github.com/algolia/react-instantsearch/compare/v6.23.2...v6.23.3) (2022-04-05) - - -### Bug Fixes - -* **facets:** show raw value in currentRefinements ([#3420](https://github.com/algolia/react-instantsearch/issues/3420)) ([1199ce6](https://github.com/algolia/react-instantsearch/commit/1199ce6bd4152e4b54bd2ee0e1f0c9d81021c7d5)), closes [#3412](https://github.com/algolia/react-instantsearch/issues/3412) -* **multi-index:** correctly set `searching` prop in multi-index result states ([#3419](https://github.com/algolia/react-instantsearch/issues/3419)) ([7f8e97e](https://github.com/algolia/react-instantsearch/commit/7f8e97e31b3dd5e49d3febef673f41f7dfa6d45d)) - - - -## [6.23.2](https://github.com/algolia/react-instantsearch/compare/v6.23.1...v6.23.2) (2022-04-04) - - -### Bug Fixes - -* **refinements:** use escaped value for refining ([#3412](https://github.com/algolia/react-instantsearch/issues/3412)) ([f2f5f6c](https://github.com/algolia/react-instantsearch/commit/f2f5f6cbfaed48a5c494daeb8789e8deebc64293)) -* support React 18 as peer dependency ([#3411](https://github.com/algolia/react-instantsearch/issues/3411)) ([38eb5a6](https://github.com/algolia/react-instantsearch/commit/38eb5a6a8fe92cb763a25f452bea78b189a6a82a)) - - -## [6.23.1](https://algolia/compare/v6.23.0...v6.23.1) (2022-03-30) - - -### Bug Fixes - -* **hooks:** compute initial search parameters from widget ([#3399](https://algolia/issues/3399)) ([b4bd933](https://algolia/commits/b4bd93358598bdc77a1aa858252e6eee23441345)) - - - -# [6.23.0](https://algolia/compare/v6.22.0...v6.23.0) (2022-03-23) - - -### Bug Fixes - -* **ssr:** perform initial multi-index search using a single request ([#3385](https://algolia/issues/3385)) ([b96809e](https://algolia/commits/b96809e5748d298350890647956cb7adbbb55650)) - - -### Features - -* **hooks:** allow additional widget properties to be passed from hooks ([#3359](https://algolia/issues/3359)) ([a047be4](https://algolia/commits/a047be45c7fce7ee28f7d6f61d2fbfa79e3ed2d0)) -* **hooks:** allow useHits and useInfiniteHit to be generic ([#3364](https://algolia/issues/3364)) ([8e66ad3](https://algolia/commits/8e66ad3ad587197c4811c60a5cab475137ca5afb)) -* **hooks:** mark initial results as "artificial" ([#3384](https://algolia/issues/3384)) ([937efdc](https://algolia/commits/937efdc71bae1d99270f8ecb5c5c9c501b3d7769)) - - - -# [6.22.0](https://github.com/algolia/react-instantsearch/compare/v6.21.1...v6.22.0) (2022-02-01) - - -### Bug Fixes - -* **hooks:** enable pause on exceptions on warning ([#3283](https://github.com/algolia/react-instantsearch/issues/3283)) ([ce3a6c3](https://github.com/algolia/react-instantsearch/commit/ce3a6c3f2700a05ae54a91278fd23fa64a97d733)) - - -### Features - -* **hooks:** introduce `useBreadcrumb()` ([#3245](https://github.com/algolia/react-instantsearch/issues/3245)) ([5bfbaaf](https://github.com/algolia/react-instantsearch/commit/5bfbaaf746e7632968ac9b30b73357bcb0b37e91)) - - - -## [6.21.1](https://github.com/algolia/react-instantsearch/compare/v6.21.0...v6.21.1) (2022-01-25) - - -### Bug Fixes - -* **hooks:** apply initial search parameters in useConnector ([#3276](https://github.com/algolia/react-instantsearch/issues/3276)) ([f85d679](https://github.com/algolia/react-instantsearch/commit/f85d6794c31eac61521fd8fc16b75673f02ed75b)) - - - -# [6.21.0](https://github.com/algolia/react-instantsearch/compare/v6.20.0...v6.21.0) (2022-01-24) - - -### Features - -* **hooks-server:** load data twice in the case of dynamic widget usage ([#3259](https://github.com/algolia/react-instantsearch/issues/3259)) ([9b4903b](https://github.com/algolia/react-instantsearch/commit/9b4903b2ea73d9d7e33729b87d9d55990e64312c)) -* **server:** load data twice in the case of dynamic widget usage ([#3268](https://github.com/algolia/react-instantsearch/issues/3268)) ([91cd085](https://github.com/algolia/react-instantsearch/commit/91cd085f9a323ed6b872f3a098f561007a72d0d2)), closes [/github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js#L14-L16](https://github.com//github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js/issues/L14-L16) - - - -# [6.20.0](https://github.com/algolia/react-instantsearch/compare/v6.19.0...v6.20.0) (2022-01-18) - - -### Bug Fixes - -* **hooks:** allow importing via require ([#3257](https://github.com/algolia/react-instantsearch/issues/3257)) ([6aa80b3](https://github.com/algolia/react-instantsearch/commit/6aa80b3647567199c3df1b90a07d708b223ce1fd)) - - -### Features - -* **hooks:** implement `useClearRefinements()` ([#3256](https://github.com/algolia/react-instantsearch/issues/3256)) ([930b83d](https://github.com/algolia/react-instantsearch/commit/930b83df4c4bbccbc3118f6ea1001f6a30a3d464)), closes [#3252](https://github.com/algolia/react-instantsearch/issues/3252) -* **hooks:** introduce `` ([#3261](https://github.com/algolia/react-instantsearch/issues/3261)) ([3527b94](https://github.com/algolia/react-instantsearch/commit/3527b9422de48a4a6b4bd752bd643e01cd5011d8)) -* **hooks:** introduce `usePoweredBy()` ([#3251](https://github.com/algolia/react-instantsearch/issues/3251)) ([a97230b](https://github.com/algolia/react-instantsearch/commit/a97230b89e3ba1df9bf2d21a6a9f9a70b45f41b8)) -* **hooks:** introduce `useToggleRefinement()` ([#3248](https://github.com/algolia/react-instantsearch/issues/3248)) ([a561c09](https://github.com/algolia/react-instantsearch/commit/a561c090a268e1c6ca1fa2d5bf72263cc47aa273)) - - - -# [6.19.0](https://github.com/algolia/react-instantsearch/compare/v6.18.0...v6.19.0) (2022-01-05) - - -### Features - -* **hooks:** bundle as es-module ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) -* **dependencies:** update to latest algoliasearch-helper ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) - - - -# [6.18.0](https://github.com/algolia/react-instantsearch/compare/v6.17.0...v6.18.0) (2021-12-16) - - -### Features - -* **dynamicWidgets:** send facets * and maxValuesPerFacet by default ([#3242](https://github.com/algolia/react-instantsearch/issues/3242)) ([c071776](https://github.com/algolia/react-instantsearch/commit/c07177670ac30dced55250774654e8b2a77b6739)) -* **hooks:** introduce `useNumericMenu()` ([#3237](https://github.com/algolia/react-instantsearch/issues/3237)) ([e3056c9](https://github.com/algolia/react-instantsearch/commit/e3056c9e2c64b5afafb7a736599a5cbf6137575b)) -* **hooks:** introduce `useInfiniteHits()` ([#3224](https://github.com/algolia/react-instantsearch/issues/3224)) ([177ec56](https://github.com/algolia/react-instantsearch/commit/177ec56af274670c2bf8599ba104b5544979bbe8)) - - - -# [6.17.0](https://github.com/algolia/react-instantsearch/compare/v6.16.0...v6.17.0) (2021-12-08) - - -### Bug Fixes - -* **hooks:** throw invariant violations in production ([#3217](https://github.com/algolia/react-instantsearch/issues/3217)) ([6d3f99c](https://github.com/algolia/react-instantsearch/commit/6d3f99ca91f470ee742ddc55e95f57b1f1801d7b)) - - -### Features - -* **hooks:** introduce `useMenu()` ([#3197](https://github.com/algolia/react-instantsearch/issues/3197)) ([15d1cc9](https://github.com/algolia/react-instantsearch/commit/15d1cc993437b111cd5a32f43ee1d2065c639ed4)) -* **hooks:** introduce `useRange()` ([#3198](https://github.com/algolia/react-instantsearch/issues/3198)) ([df1f1c8](https://github.com/algolia/react-instantsearch/commit/df1f1c8109dc684e74d3aee1bf0359f2a0e1b9f4)) -* **hooks:** introduce `` ([#3216](https://github.com/algolia/react-instantsearch/issues/3216)) ([d99aea6](https://github.com/algolia/react-instantsearch/commit/d99aea6cfe9dea86ae6b98ee3762373f4b3843f1)) -* **hooks:** introduce `useDynamicWidgets()` ([#3210](https://github.com/algolia/react-instantsearch/issues/3210)) ([29c2ea2](https://github.com/algolia/react-instantsearch/commit/29c2ea22b91a39d9eb40a044ae9aab85f2612db8)) -* **hooks:** introduce `useQueryRules()` ([#3212](https://github.com/algolia/react-instantsearch/issues/3212)) ([3ef1e1e](https://github.com/algolia/react-instantsearch/commit/3ef1e1e4116b3e58b2c2134d0c60fbb9f40c1501)) -* **hooks:** introduce SSR support ([#3221](https://github.com/algolia/react-instantsearch/issues/3221)) ([0a6b3ec](https://github.com/algolia/react-instantsearch/commit/0a6b3ec61942ad3849c6f078e21b3328679bffff)) -* **hooks:** introduce `useCurrentRefinements()` ([#3222](https://github.com/algolia/react-instantsearch/issues/3222)) ([7ebd8c3](https://github.com/algolia/react-instantsearch/commit/7ebd8c3da8c17b0bd7e0f8deab633b98fa052e7f)) - - - -# [6.16.0](https://github.com/algolia/react-instantsearch/compare/v6.15.0...v6.16.0) (2021-11-22) - - -### Bug Fixes - -* **PoweredBy:** support environments with `window` but no `location` ([#3186](https://github.com/algolia/react-instantsearch/issues/3186)) ([22ff23b](https://github.com/algolia/react-instantsearch/commit/22ff23b67554683567393114c2f53cacec44f4a6)) - - -### Features - -* **DynamicWidgets:** release as stable ([#3090](https://github.com/algolia/react-instantsearch/issues/3090)) ([a4a1d9e](https://github.com/algolia/react-instantsearch/commit/a4a1d9e032b31c611d5d73fdda3a03ad705f5c68)) -* **hooks:** introduce `usePagination()` ([#3182](https://github.com/algolia/react-instantsearch/issues/3182)) ([d8b1b86](https://github.com/algolia/react-instantsearch/commit/d8b1b867bb598e801f1350e81b4a4220a8e528d7)) -* **hooks:** introduce `useSortBy()` ([#3190](https://github.com/algolia/react-instantsearch/issues/3190)) ([5cce33b](https://github.com/algolia/react-instantsearch/commit/5cce33b48032548fed76b592ee0201e3c42fc3c4)) -* **hooks:** introduce `useHierarchicalMenu()` ([#3199](https://github.com/algolia/react-instantsearch/issues/3199)) ([b347061](https://github.com/algolia/react-instantsearch/commit/b3470611b962ef55c55576c65a6307abc54e5efd)) - - - -# [6.15.0](https://github.com/algolia/react-instantsearch/compare/v6.14.0...v6.15.0) (2021-10-27) - - -### Bug Fixes - -* **metadata:** stricter detection of user agent ([#3184](https://github.com/algolia/react-instantsearch/issues/3184)) ([994c8ae](https://github.com/algolia/react-instantsearch/commit/994c8ae055fc23a1a067d111d2f4727b1c7bf8ca)) - - -### Features - -* **hooks:** introduce `useConfigure()` ([#3181](https://github.com/algolia/react-instantsearch/issues/3181)) ([aa2eb9b](https://github.com/algolia/react-instantsearch/commit/aa2eb9baec6335f8a3523ee8b9b761a217cfc734)) - - - -# [6.14.0](https://github.com/algolia/react-instantsearch/compare/v6.13.0...v6.14.0) (2021-10-26) - - -### Features - -* **dependencies:** update algoliasearch-helper ([#3176](https://github.com/algolia/react-instantsearch/issues/3176)) ([a8708a3](https://github.com/algolia/react-instantsearch/commit/a8708a33f31632000bc827b076539b1cca7adf6f)) -* **metadata:** expose widget information ([#3145](https://github.com/algolia/react-instantsearch/issues/3145)) ([46cddf8](https://github.com/algolia/react-instantsearch/commit/46cddf8fcc0291beaa34b04da7aaaa7f2566e52e)) - - - -# [6.13.0](https://github.com/algolia/react-instantsearch/compare/v6.12.1...v6.13.0) (2021-10-19) - -This is the initial release of the experimental **React InstantSearch Hooks** package. Check out the [**Getting Started**](https://github.com/algolia/react-instantsearch/blob/e8d72cb1c7c45300ef7c273f1f163beb6dc57622/packages/react-instantsearch-hooks/README.md#getting-started) guide. - -### Bug Fixes - -- **core:** accept objects for `hitComponent` ([#3087](https://github.com/algolia/react-instantsearch/issues/3087)) ([4ae23d4](https://github.com/algolia/react-instantsearch/commit/4ae23d432a01ccbefff1fcdc865120aeca4d3efc)) - -### Features - -- **hooks:** add InstantSearch and Index components ([#3133](https://github.com/algolia/react-instantsearch/issues/3133)) ([8e3370d](https://github.com/algolia/react-instantsearch/commit/8e3370ddb7d5e42b8b9a5ff6a1e4255490de7dbe)) -- **hooks:** bootstrap Core package ([#3132](https://github.com/algolia/react-instantsearch/issues/3132)) ([d459e62](https://github.com/algolia/react-instantsearch/commit/d459e62f5cae4c98427ab302531873f5ee23d149)) -- **hooks:** display experimental warning ([#3149](https://github.com/algolia/react-instantsearch/issues/3149)) ([623577c](https://github.com/algolia/react-instantsearch/commit/623577c50cd0c04cd87f719edafdcfa04b5527b6)) -- **hooks:** export types ([#3159](https://github.com/algolia/react-instantsearch/issues/3159)) ([182348b](https://github.com/algolia/react-instantsearch/commit/182348b4a901823a7a41aee5d2b3bdc025cce48f)) -- **hooks:** expose `displayName` on Contexts ([#3168](https://github.com/algolia/react-instantsearch/issues/3168)) ([dafd3c6](https://github.com/algolia/react-instantsearch/commit/dafd3c66d05fbec09ebf907209ac25f55804e6f5)) -- **hooks:** friendly error when using Hooks with Core ([#3150](https://github.com/algolia/react-instantsearch/issues/3150)) ([d547ccf](https://github.com/algolia/react-instantsearch/commit/d547ccf7951299e2f6b1771e02fce052696ff65a)) -- **hooks:** introduce `useConnector()` ([#3137](https://github.com/algolia/react-instantsearch/issues/3137)) ([53e8afd](https://github.com/algolia/react-instantsearch/commit/53e8afd093b9950351467a16b82d528207ac34d2)) -- **hooks:** introduce `useHits()` ([#3147](https://github.com/algolia/react-instantsearch/issues/3147)) ([cc25cff](https://github.com/algolia/react-instantsearch/commit/cc25cff06e5ec216cba40fb8261372bc327001b6)) -- **hooks:** introduce `useRefinementList()` ([#3152](https://github.com/algolia/react-instantsearch/issues/3152)) ([0385cd9](https://github.com/algolia/react-instantsearch/commit/0385cd971635a8423ad687fab451d0778358389e)) -- **hooks:** introduce `useSearchBox()` ([#3146](https://github.com/algolia/react-instantsearch/issues/3146)) ([0d2c7f9](https://github.com/algolia/react-instantsearch/commit/0d2c7f9bd25b88cf725a1babd3b228ac804644c7)) -- **hooks:** trigger single network request on load ([#3167](https://github.com/algolia/react-instantsearch/issues/3167)) ([ff1ea49](https://github.com/algolia/react-instantsearch/commit/ff1ea49079a7800fd61ba99ceeb74b9f513eb99d)) -- **hooks:** type `useConnector()` return as render state ([#3169](https://github.com/algolia/react-instantsearch/issues/3169)) ([a801468](https://github.com/algolia/react-instantsearch/commit/a80146860164a092d2c90ee0aa4fcef88d5b675f)) -- **hooks:** update GitHub bug reports link ([#3157](https://github.com/algolia/react-instantsearch/issues/3157)) ([568b5c8](https://github.com/algolia/react-instantsearch/commit/568b5c83849a3927417907706656c3835163f216)) - - - -## [6.12.1](https://github.com/algolia/react-instantsearch/compare/v6.12.0...v6.12.1) (2021-08-02) - - -### Bug Fixes - -* **server side rendering:** return a value from mock currentRefinement/metadata ([#3078](https://github.com/algolia/react-instantsearch/issues/3078)) ([09f802b](https://github.com/algolia/react-instantsearch/commit/09f802b)) - - - -# [6.12.0](https://github.com/algolia/react-instantsearch/compare/v6.11.2...v6.12.0) (2021-07-06) - - -### Bug Fixes - -* **HitsPerPage:** Adds id prop to HitsPerPage, Select components ([#3072](https://github.com/algolia/react-instantsearch/issues/3072)) ([bc75d75](https://github.com/algolia/react-instantsearch/commit/bc75d75)) -* **MenuSelect:** Adds id prop to MenuSelect ([#3073](https://github.com/algolia/react-instantsearch/issues/3073)) ([fddaaef](https://github.com/algolia/react-instantsearch/commit/fddaaef)) -* **SearchBox:** Adds inputId prop to SearchBox ([#3074](https://github.com/algolia/react-instantsearch/issues/3074)) ([a05f6a4](https://github.com/algolia/react-instantsearch/commit/a05f6a4)) -* **SortBy:** Adds `id` prop to `SortBy`, `Select` components ([#3068](https://github.com/algolia/react-instantsearch/issues/3068)) ([1f2797f](https://github.com/algolia/react-instantsearch/commit/1f2797f)) - - -### Features - -* **DynamicWidgets:** add implementation ([#3056](https://github.com/algolia/react-instantsearch/issues/3056)) ([691ef87](https://github.com/algolia/react-instantsearch/commit/691ef87)) -* **facets:** add a new option "facetOrdering" to Menu, RefinementList & HierarchicalMenu ([#3067](https://github.com/algolia/react-instantsearch/issues/3067)) ([731d9ba](https://github.com/algolia/react-instantsearch/commit/731d9ba)) - - - -## [6.11.2](https://github.com/algolia/react-instantsearch/compare/v6.11.1...v6.11.2) (2021-06-28) - - -### Bug Fixes - -* **maps:** leave zoom in place if the bounds did not change ([#3050](https://github.com/algolia/react-instantsearch/issues/3050)) ([c430598](https://github.com/algolia/react-instantsearch/commit/c430598)) - - - -## [6.11.1](https://github.com/algolia/react-instantsearch/compare/v6.11.0...v6.11.1) (2021-06-09) - - -### Bug Fixes - -* **RefinementList:** prevent searchable component to refine on empty list ([#3059](https://github.com/algolia/react-instantsearch/issues/3059)) ([04f3644](https://github.com/algolia/react-instantsearch/commit/04f3644)) - - - -# [6.11.0](https://github.com/algolia/react-instantsearch/compare/v6.10.3...v6.11.0) (2021-05-04) - - -### Features - -* **connectNumericMenu:** add support for floating point values ([#3047](https://github.com/algolia/react-instantsearch/issues/3047)) ([091bf57](https://github.com/algolia/react-instantsearch/commit/091bf57)) - - - -## [6.10.3](https://github.com/algolia/react-instantsearch/compare/v6.10.2...v6.10.3) (2021-03-03) - - -### Bug Fixes - -* **RelevantSort:** Rename `SmartSort` widget to `RelevantSort` ([#3026](https://github.com/algolia/react-instantsearch/issues/3026)) ([47d11bf](https://github.com/algolia/react-instantsearch/commit/47d11bf)) - - - -## [6.10.2](https://github.com/algolia/react-instantsearch/compare/v6.10.1...v6.10.2) (2021-03-03) - - -### Bug Fixes - -* **infiniteHits:** fix stale hits issue ([#3021](https://github.com/algolia/react-instantsearch/issues/3021)) ([a9a29c7](https://github.com/algolia/react-instantsearch/commit/a9a29c7)) - - - -## [6.10.1](https://github.com/algolia/react-instantsearch/compare/v6.10.0...v6.10.1) (2021-03-02) - - -### Bug Fixes - -* **SmartSort:** make `textComponent` and `buttonTextComponent` optional ([#3014](https://github.com/algolia/react-instantsearch/issues/3014)) ([682ee6d](https://github.com/algolia/react-instantsearch/commit/682ee6d)) - - - -# [6.10.0](https://github.com/algolia/react-instantsearch/compare/v6.9.0...v6.10.0) (2021-02-23) - - -### Bug Fixes - -* **infiniteHits:** do not cache the cached hits ([#3011](https://github.com/algolia/react-instantsearch/issues/3011)) ([b56f5f7](https://github.com/algolia/react-instantsearch/commit/b56f5f7)) - - -### Features - -* **smartSort:** add widget ([#3009](https://github.com/algolia/react-instantsearch/issues/3009)) ([4cc8412](https://github.com/algolia/react-instantsearch/commit/4cc8412)), closes [#3010](https://github.com/algolia/react-instantsearch/issues/3010) - - - -# [6.9.0](https://github.com/algolia/react-instantsearch/compare/v6.8.3...v6.9.0) (2021-02-03) - - -### Features - -* **answers:** add `EXPERIMENTAL_Answers` widget ([#2996](https://github.com/algolia/react-instantsearch/issues/2996)) ([55e4191](https://github.com/algolia/react-instantsearch/commit/55e4191)), closes [#3005](https://github.com/algolia/react-instantsearch/issues/3005) - - - -## [6.8.3](https://github.com/algolia/react-instantsearch/compare/v6.8.2...v6.8.3) (2021-01-22) - - -### Bug Fixes - -* upgrade prop-types dependency to 15.6+ ([#3003](https://github.com/algolia/react-instantsearch/issues/3003)) ([fc03496](https://github.com/algolia/react-instantsearch/commit/fc03496)) - - - -## [6.8.2](https://github.com/algolia/react-instantsearch/compare/v6.8.1...v6.8.2) (2020-10-21) - - -### Bug Fixes - -* **ssr:** provide metadata default value ([0a2f34c](https://github.com/algolia/react-instantsearch/commit/0a2f34c)) - - - -## [6.8.1](https://github.com/algolia/react-instantsearch/compare/v6.8.0...v6.8.1) (2020-10-14) - - -### Bug Fixes - -* **ssr:** hydrate metadata with a value ([9249c19](https://github.com/algolia/react-instantsearch/commit/9249c19)) - - - -# [6.8.0](https://github.com/algolia/react-instantsearch/compare/v6.7.0...v6.8.0) (2020-10-14) - - -### Bug Fixes - -* **ssr:** make sure metadata is available on initial render ([#2973](https://github.com/algolia/react-instantsearch/issues/2973)) ([be43b65](https://github.com/algolia/react-instantsearch/commit/be43b65)), closes [#2972](https://github.com/algolia/react-instantsearch/issues/2972) -* add missing dependencies ([#2975](https://github.com/algolia/react-instantsearch/issues/2975)) ([22ecb3c](https://github.com/algolia/react-instantsearch/commit/22ecb3c)) -* **ConfigureRelatedItems:** support nested attributes ([#2967](https://github.com/algolia/react-instantsearch/issues/2967)) ([86dfe86](https://github.com/algolia/react-instantsearch/commit/86dfe86)) -* **ssr:** allow "params" to be optional in custom clients ([#2961](https://github.com/algolia/react-instantsearch/issues/2961)) ([c3e3d2e](https://github.com/algolia/react-instantsearch/commit/c3e3d2e)), closes [#2958](https://github.com/algolia/react-instantsearch/issues/2958) - - - -# [6.7.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.7.0) (2020-07-20) - - -### Bug Fixes - -* **core:** appending successful index search results by returning new object reference ([#2953](https://github.com/algolia/react-instantsearch/issues/2953)) ([0a711a7](https://github.com/algolia/react-instantsearch/commit/0a711a7)) -* **ssr:** remove second instance of "query" in the response "params" for SSR ([#2945](https://github.com/algolia/react-instantsearch/issues/2945)) ([bf837c5](https://github.com/algolia/react-instantsearch/commit/bf837c5)), closes [#2941](https://github.com/algolia/react-instantsearch/issues/2941) - - -### Features - -* **infinite-hits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.6.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.6.0) (2020-06-15) - - -### Features - -* **infiniteHits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.5.0](https://github.com/algolia/react-instantsearch/compare/v6.4.0...v6.5.0) (2020-05-18) - - -### Bug Fixes - -* **connectQueryRules:** fix crash when using connectQueryRules with multiple indexes ([#2903](https://github.com/algolia/react-instantsearch/issues/2903)) ([c66d612](https://github.com/algolia/react-instantsearch/commit/c66d612)) -* **core:** fix maximum call stack size exceeded ([#2926](https://github.com/algolia/react-instantsearch/issues/2926)) ([7e883df](https://github.com/algolia/react-instantsearch/commit/7e883df)) - - -### Features - -* **SearchBox:** provide input element ref ([#2913](https://github.com/algolia/react-instantsearch/issues/2913)) ([b41bff2](https://github.com/algolia/react-instantsearch/commit/b41bff2)) - - - -# [6.4.0](https://github.com/algolia/react-instantsearch/compare/v6.3.0...v6.4.0) (2020-03-18) - - -### Bug Fixes - -* **deps:** fix "too much recursion" error with circular deps ([#2899](https://github.com/algolia/react-instantsearch/issues/2899)) ([c5f27a1](https://github.com/algolia/react-instantsearch/commit/c5f27a1)) - - - -# [6.3.0](https://github.com/algolia/react-instantsearch/compare/v6.2.0...v6.3.0) (2020-01-30) - - -### Features - -* **algoliasearch:** add support for algoliasearch v4 ([#2890](https://github.com/algolia/react-instantsearch/issues/2890)) ([c6c7382](https://github.com/algolia/react-instantsearch/commit/c6c7382)) - - - -# [6.2.0](https://github.com/algolia/react-instantsearch/compare/v6.1.0...v6.2.0) (2020-01-20) - - -### Bug Fixes - -* **deps:** update dependency algoliasearch to v3.35.1 ([#2802](https://github.com/algolia/react-instantsearch/issues/2802)) ([cfb91f0](https://github.com/algolia/react-instantsearch/commit/cfb91f0)) -* **widgets:** rename `ExperimentalConfigureRelatedItems` compon… ([#2891](https://github.com/algolia/react-instantsearch/issues/2891)) ([b910df2](https://github.com/algolia/react-instantsearch/commit/b910df2)) - - -### Features - -* **insights:** add getInsightsAnonymousUserToken helper ([#2887](https://github.com/algolia/react-instantsearch/issues/2887)) ([b5fe4f7](https://github.com/algolia/react-instantsearch/commit/b5fe4f7)) -* **widgets:** introduce `ConfigureRelatedItems` as experimental ([#2880](https://github.com/algolia/react-instantsearch/issues/2880)) ([923cd43](https://github.com/algolia/react-instantsearch/commit/923cd43)) - - - -# [6.1.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0...v6.1.0) (2019-12-17) - - -### Bug Fixes - -* **connectNumericMenu:** support numeric refinement 0 ([#2882](https://github.com/algolia/react-instantsearch/issues/2882)) ([30bd9fd](https://github.com/algolia/react-instantsearch/commit/30bd9fd)) -* **deps:** update dependency next to v9.1.1 ([9d49d33](https://github.com/algolia/react-instantsearch/commit/9d49d33)) -* **helper:** rely on stable version of algoliasearch-helper ([#2871](https://github.com/algolia/react-instantsearch/issues/2871)) ([e3531a1](https://github.com/algolia/react-instantsearch/commit/e3531a1)) - - -### Features - -* **insights:** show an error when 'clickAnalytics: true' is missing. ([#2877](https://github.com/algolia/react-instantsearch/issues/2877)) ([621656a](https://github.com/algolia/react-instantsearch/commit/621656a)) -* **voice:** add additionalQueryParameters ([#2366](https://github.com/algolia/react-instantsearch/issues/2366)) ([3a45b2c](https://github.com/algolia/react-instantsearch/commit/3a45b2c)) - - - -# [6.0.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.2...v6.0.0) (2019-10-28) - - - -# [6.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.1...v6.0.0-beta.2) (2019-10-25) - - -### Bug Fixes - -* serialize cache value on hydrate ([#2862](https://github.com/algolia/react-instantsearch/issues/2862)) ([3319665](https://github.com/algolia/react-instantsearch/commit/3319665)), closes [#2828](https://github.com/algolia/react-instantsearch/issues/2828) - - - -# [6.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.1) (2019-10-18) - - -### Bug Fixes - -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **deps:** update dependency antd to v3.19.3 ([#2530](https://github.com/algolia/react-instantsearch/issues/2530)) ([73636c5](https://github.com/algolia/react-instantsearch/commit/73636c5)) -* **deps:** update dependency antd to v3.19.4 ([#2559](https://github.com/algolia/react-instantsearch/issues/2559)) ([c3e8267](https://github.com/algolia/react-instantsearch/commit/c3e8267)) -* **deps:** update dependency antd to v3.19.5 ([#2560](https://github.com/algolia/react-instantsearch/issues/2560)) ([72efd31](https://github.com/algolia/react-instantsearch/commit/72efd31)) -* **deps:** update dependency antd to v3.19.6 ([#2564](https://github.com/algolia/react-instantsearch/issues/2564)) ([654f986](https://github.com/algolia/react-instantsearch/commit/654f986)) -* **deps:** update dependency antd to v3.19.7 ([#2573](https://github.com/algolia/react-instantsearch/issues/2573)) ([7e963ad](https://github.com/algolia/react-instantsearch/commit/7e963ad)) -* **deps:** update dependency antd to v3.19.8 ([#2584](https://github.com/algolia/react-instantsearch/issues/2584)) ([34dd9b2](https://github.com/algolia/react-instantsearch/commit/34dd9b2)) -* **deps:** update dependency antd to v3.20.0 ([#2611](https://github.com/algolia/react-instantsearch/issues/2611)) ([b976c67](https://github.com/algolia/react-instantsearch/commit/b976c67)) -* **deps:** update dependency antd to v3.20.1 ([#2635](https://github.com/algolia/react-instantsearch/issues/2635)) ([792ad9c](https://github.com/algolia/react-instantsearch/commit/792ad9c)) -* **deps:** update dependency antd to v3.20.2 ([#2655](https://github.com/algolia/react-instantsearch/issues/2655)) ([301c2d8](https://github.com/algolia/react-instantsearch/commit/301c2d8)) -* **deps:** update dependency antd to v3.20.3 ([#2658](https://github.com/algolia/react-instantsearch/issues/2658)) ([d078e70](https://github.com/algolia/react-instantsearch/commit/d078e70)) -* **deps:** update dependency antd to v3.20.5 ([#2686](https://github.com/algolia/react-instantsearch/issues/2686)) ([42ef821](https://github.com/algolia/react-instantsearch/commit/42ef821)) -* **deps:** update dependency antd to v3.20.6 ([#2711](https://github.com/algolia/react-instantsearch/issues/2711)) ([927fbfe](https://github.com/algolia/react-instantsearch/commit/927fbfe)) -* **deps:** update dependency antd to v3.20.7 ([#2712](https://github.com/algolia/react-instantsearch/issues/2712)) ([1830952](https://github.com/algolia/react-instantsearch/commit/1830952)) -* **deps:** update dependency antd to v3.21.1 ([#2736](https://github.com/algolia/react-instantsearch/issues/2736)) ([39a51a6](https://github.com/algolia/react-instantsearch/commit/39a51a6)) -* **deps:** update dependency antd to v3.21.2 ([#2738](https://github.com/algolia/react-instantsearch/issues/2738)) ([a7a998a](https://github.com/algolia/react-instantsearch/commit/a7a998a)) -* **deps:** update dependency antd to v3.21.4 ([#2747](https://github.com/algolia/react-instantsearch/issues/2747)) ([60012be](https://github.com/algolia/react-instantsearch/commit/60012be)) -* **deps:** update dependency antd to v3.22.0 ([#2758](https://github.com/algolia/react-instantsearch/issues/2758)) ([9cda468](https://github.com/algolia/react-instantsearch/commit/9cda468)) -* **deps:** update dependency antd to v3.22.2 ([#2791](https://github.com/algolia/react-instantsearch/issues/2791)) ([ff1f5d9](https://github.com/algolia/react-instantsearch/commit/ff1f5d9)) -* **deps:** update dependency antd to v3.23.2 ([#2814](https://github.com/algolia/react-instantsearch/issues/2814)) ([a190410](https://github.com/algolia/react-instantsearch/commit/a190410)) -* **deps:** update dependency lodash to v4.17.13 ([c4974cf](https://github.com/algolia/react-instantsearch/commit/c4974cf)) -* **deps:** update dependency lodash to v4.17.14 ([#2647](https://github.com/algolia/react-instantsearch/issues/2647)) ([a2d2dd5](https://github.com/algolia/react-instantsearch/commit/a2d2dd5)) -* **deps:** update dependency lodash to v4.17.15 ([#2684](https://github.com/algolia/react-instantsearch/issues/2684)) ([354143f](https://github.com/algolia/react-instantsearch/commit/354143f)) -* **deps:** update dependency next to v9 ([#2638](https://github.com/algolia/react-instantsearch/issues/2638)) ([d22f61d](https://github.com/algolia/react-instantsearch/commit/d22f61d)) -* **deps:** update dependency next to v9.0.1 ([#2652](https://github.com/algolia/react-instantsearch/issues/2652)) ([2c2dab9](https://github.com/algolia/react-instantsearch/commit/2c2dab9)) -* **deps:** update dependency next to v9.0.2 ([#2662](https://github.com/algolia/react-instantsearch/issues/2662)) ([6fa4c5e](https://github.com/algolia/react-instantsearch/commit/6fa4c5e)) -* **deps:** update dependency next to v9.0.3 ([#2724](https://github.com/algolia/react-instantsearch/issues/2724)) ([f51b04b](https://github.com/algolia/react-instantsearch/commit/f51b04b)) -* **deps:** update dependency next to v9.0.4 ([#2767](https://github.com/algolia/react-instantsearch/issues/2767)) ([9af9180](https://github.com/algolia/react-instantsearch/commit/9af9180)) -* **deps:** update dependency next to v9.0.5 ([#2789](https://github.com/algolia/react-instantsearch/issues/2789)) ([0a75f41](https://github.com/algolia/react-instantsearch/commit/0a75f41)) -* **deps:** update dependency qs to v6.8.0 ([#2757](https://github.com/algolia/react-instantsearch/issues/2757)) ([8bffb87](https://github.com/algolia/react-instantsearch/commit/8bffb87)) -* **deps:** update dependency react-compound-slider to v2.1.0 ([#2610](https://github.com/algolia/react-instantsearch/issues/2610)) ([3389ee5](https://github.com/algolia/react-instantsearch/commit/3389ee5)) -* **deps:** update dependency react-compound-slider to v2.2.0 ([#2649](https://github.com/algolia/react-instantsearch/issues/2649)) ([7b81af1](https://github.com/algolia/react-instantsearch/commit/7b81af1)) -* **deps:** update dependency react-native-vector-icons to v6.5.0 ([#2520](https://github.com/algolia/react-instantsearch/issues/2520)) ([5f7f5b6](https://github.com/algolia/react-instantsearch/commit/5f7f5b6)) -* **deps:** update dependency react-native-vector-icons to v6.6.0 ([#2599](https://github.com/algolia/react-instantsearch/issues/2599)) ([b6bb199](https://github.com/algolia/react-instantsearch/commit/b6bb199)) -* **deps:** update dependency react-router-dom to v5.0.1 ([#2506](https://github.com/algolia/react-instantsearch/issues/2506)) ([d762230](https://github.com/algolia/react-instantsearch/commit/d762230)) -* **highlight:** switch to index as key ([#2691](https://github.com/algolia/react-instantsearch/issues/2691)) ([17e75d1](https://github.com/algolia/react-instantsearch/commit/17e75d1)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### chore - -* **release:** 6.0.0-beta.1 ([#2861](https://github.com/algolia/react-instantsearch/issues/2861)) ([cb56ca0](https://github.com/algolia/react-instantsearch/commit/cb56ca0)), closes [#2023](https://github.com/algolia/react-instantsearch/issues/2023) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2203](https://github.com/algolia/react-instantsearch/issues/2203) [#2432](https://github.com/algolia/react-instantsearch/issues/2432) [#2444](https://github.com/algolia/react-instantsearch/issues/2444) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2454](https://github.com/algolia/react-instantsearch/issues/2454) [#2455](https://github.com/algolia/react-instantsearch/issues/2455) [#2459](https://github.com/algolia/react-instantsearch/issues/2459) [#2458](https://github.com/algolia/react-instantsearch/issues/2458) [#2460](https://github.com/algolia/react-instantsearch/issues/2460) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2446](https://github.com/algolia/react-instantsearch/issues/2446) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2466](https://github.com/algolia/react-instantsearch/issues/2466) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2555](https://github.com/algolia/react-instantsearch/issues/2555) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2349](https://github.com/algolia/react-instantsearch/issues/2349) [#2570](https://github.com/algolia/react-instantsearch/issues/2570) [#2462](https://github.com/algolia/react-instantsearch/issues/2462) [#2600](https://github.com/algolia/react-instantsearch/issues/2600) [#2468](https://github.com/algolia/react-instantsearch/issues/2468) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2621](https://github.com/algolia/react-instantsearch/issues/2621) [#2627](https://github.com/algolia/react-instantsearch/issues/2627) [#2644](https://github.com/algolia/react-instantsearch/issues/2644) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2645](https://github.com/algolia/react-instantsearch/issues/2645) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2643](https://github.com/algolia/react-instantsearch/issues/2643) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2722](https://github.com/algolia/react-instantsearch/issues/2722) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2701](https://github.com/algolia/react-instantsearch/issues/2701) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2530](https://github.com/algolia/react-instantsearch/issues/2530) [#2559](https://github.com/algolia/react-instantsearch/issues/2559) [#2560](https://github.com/algolia/react-instantsearch/issues/2560) [#2564](https://github.com/algolia/react-instantsearch/issues/2564) [#2573](https://github.com/algolia/react-instantsearch/issues/2573) [#2584](https://github.com/algolia/react-instantsearch/issues/2584) [#2611](https://github.com/algolia/react-instantsearch/issues/2611) [#2635](https://github.com/algolia/react-instantsearch/issues/2635) [#2655](https://github.com/algolia/react-instantsearch/issues/2655) [#2658](https://github.com/algolia/react-instantsearch/issues/2658) [#2686](https://github.com/algolia/react-instantsearch/issues/2686) [#2711](https://github.com/algolia/react-instantsearch/issues/2711) [#2712](https://github.com/algolia/react-instantsearch/issues/2712) [#2736](https://github.com/algolia/react-instantsearch/issues/2736) [#2738](https://github.com/algolia/react-instantsearch/issues/2738) [#2747](https://github.com/algolia/react-instantsearch/issues/2747) [#2758](https://github.com/algolia/react-instantsearch/issues/2758) [#2647](https://github.com/algolia/react-instantsearch/issues/2647) [#2684](https://github.com/algolia/react-instantsearch/issues/2684) [#2638](https://github.com/algolia/react-instantsearch/issues/2638) [#2652](https://github.com/algolia/react-instantsearch/issues/2652) [#2662](https://github.com/algolia/react-instantsearch/issues/2662) [#2724](https://github.com/algolia/react-instantsearch/issues/2724) [#2767](https://github.com/algolia/react-instantsearch/issues/2767) [#2757](https://github.com/algolia/react-instantsearch/issues/2757) [#2610](https://github.com/algolia/react-instantsearch/issues/2610) [#2649](https://github.com/algolia/react-instantsearch/issues/2649) [#2520](https://github.com/algolia/react-instantsearch/issues/2520) [#2599](https://github.com/algolia/react-instantsearch/issues/2599) [#2506](https://github.com/algolia/react-instantsearch/issues/2506) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2688](https://github.com/algolia/react-instantsearch/issues/2688) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2535](https://github.com/algolia/react-instantsearch/issues/2535) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2834](https://github.com/algolia/react-instantsearch/issues/2834) [#2845](https://github.com/algolia/react-instantsearch/issues/2845) [#2842](https://github.com/algolia/react-instantsearch/issues/2842) [#2852](https://github.com/algolia/react-instantsearch/issues/2852) [#2853](https://github.com/algolia/react-instantsearch/issues/2853) - - -### BREAKING CHANGES - -* **release:** translation will render default value if passed undefined as value - -* chore(lodash): remove imports - -* fix(translation): allow undefined value to be passed on purpose -* **release:** no longer do we allow paths like `attribute[5].something`, or other indexed forms, only `.` is allowed as special key. - -All existing tests still pass, and we never documented you could use `lodash.get` patterns other than `.`. - -* feat(get): accept array & bracked-separated string - -moved to utils at the same time - -* fix typo - -* feedback: test for undefined behaviour - -* chore(size): update expectation - -this will go down afterwards, but for now there's some more duplication - - - -# [6.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.0) (2019-08-21) - -[Migration guide](MIGRATION.md) - -### Bug Fixes - -* **react 17 compat:** upgrade RangeInput lifecycle ([#2289](https://github.com/algolia/react-instantsearch/issues/2289)) ([110b1af](https://github.com/algolia/react-instantsearch/commit/110b1af)) -* **react 17 compat:** upgrade RangeSlider lifecycle ([#2290](https://github.com/algolia/react-instantsearch/issues/2290)) ([69a7f53](https://github.com/algolia/react-instantsearch/commit/69a7f53)) -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **core:** searchState can be non-Object object ([#2722](https://github.com/algolia/react-instantsearch/issues/2722)) ([dea493c](https://github.com/algolia/react-instantsearch/commit/dea493c)), closes [#2568](https://github.com/algolia/react-instantsearch/issues/2568) -* **createConnector:** new React life cycles ([#2357](https://github.com/algolia/react-instantsearch/issues/2357)) ([fc10640](https://github.com/algolia/react-instantsearch/commit/fc10640)) -* **createInstantSearchManager:** do not trigger search on index update ([#2552](https://github.com/algolia/react-instantsearch/issues/2552)) ([e209362](https://github.com/algolia/react-instantsearch/commit/e209362)) -* **geo:** check for undefined in isEqual ([#2643](https://github.com/algolia/react-instantsearch/issues/2643)) ([a544231](https://github.com/algolia/react-instantsearch/commit/a544231)), closes [#2467](https://github.com/algolia/react-instantsearch/issues/2467) -* **geo:** remove lifecycle compat ([#2644](https://github.com/algolia/react-instantsearch/issues/2644)) ([2b2b898](https://github.com/algolia/react-instantsearch/commit/2b2b898)), closes [#2626](https://github.com/algolia/react-instantsearch/issues/2626) -* **highlight:** switch to index as key ([#2690](https://github.com/algolia/react-instantsearch/issues/2690)) ([51de682](https://github.com/algolia/react-instantsearch/commit/51de682)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **peerDependencies:** update React ([#2626](https://github.com/algolia/react-instantsearch/issues/2626)) ([6ccad49](https://github.com/algolia/react-instantsearch/commit/6ccad49)) -* **ssr:** avoid duplicate serializing ([#2726](https://github.com/algolia/react-instantsearch/issues/2726)) ([c768b1a](https://github.com/algolia/react-instantsearch/commit/c768b1a)) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### Code Refactoring - -* **lodash:** get ([#2461](https://github.com/algolia/react-instantsearch/issues/2461)) ([527b879](https://github.com/algolia/react-instantsearch/commit/527b879)) -* **lodash:** has ([#2434](https://github.com/algolia/react-instantsearch/issues/2434)) ([75a4a15](https://github.com/algolia/react-instantsearch/commit/75a4a15)) -* **lodash:** has been fully removed - -### Features - -* **autocomplete:** add queryID & position to provided hits ([#2687](https://github.com/algolia/react-instantsearch/issues/2687)) ([e453dab](https://github.com/algolia/react-instantsearch/commit/e453dab)) -* **client:** remove algoliaClient, appId & apiKey ([#2338](https://github.com/algolia/react-instantsearch/issues/2338)) ([b84a0b5](https://github.com/algolia/react-instantsearch/commit/b84a0b5)) (use searchClient exclusively now) -* **context:** migrate to new React context ([#2178](https://github.com/algolia/react-instantsearch/issues/2178)) ([0a1abea](https://github.com/algolia/react-instantsearch/commit/0a1abea)), closes [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) -* **ssr:** update the SSR API ([#2555](https://github.com/algolia/react-instantsearch/issues/2555)) ([925bdb8](https://github.com/algolia/react-instantsearch/commit/925bdb8)), closes [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) - - -### BREAKING CHANGES - -* **searchClient:** argument is the only option now. - -Previously there were three options to pass a search client: searchClient, appId & apiKey, algoliaClient. The latter two have been removed, and now only `searchClient` is accepted. This searchClient is an instance of the `algoliasearch` module: - -```js -import algoliasearch from 'algoliasearch/lite'; - -const searchClient = algoliasearch( - 'myAppId', - 'myApiKey', - { _useRequestCache: true } -); - -// ... - -``` - -If you were relying on duplicate requests not being fired when using appId & apiKey before, you need to enable the `_useRequestCache` option now. - -* **SSR:** imports have changed - -In the server, you now directly import `findResultsState`, which now requires a `searchClient` in the second argument. - -In the App, you now use a regular `InstantSearch` component. - -* **Index & InstantSearch:** Remove `root` DOM element - -These elements now are pure containers for their children, and don't add a `div` to the DOM anymore. If you were relying on those for styling, wrap the `InstantSearch` and `Index` element with a `div` with an appropriate class. - -* **Index & InstantSearch:** Remove support for `root` prop - -Since these two arguments now no longer wrap their children in an element, they no longer accept a `root` prop. - -* **Highlight:** some paths will no longer be accepted - -We only accept paths separated with a dot or bracket now, like before. It's possible that a different type of path worked undocumented, but no longer does. - -* **algoliasearch-helper:** updating to the next major version - -This library is mostly internal, but it has had a major refactor (including removing lodash). This has no impact, unless you are dealing with it using `createConnector`. See the [migration guide](https://github.com/algolia/algoliasearch-helper-js/blob/next/documentation-src/metalsmith/content/upgrade.md) for the v3 of algoliasearch-helper for more information. - -# [5.7.0](https://github.com/algolia/react-instantsearch/compare/v5.6.0...v5.7.0) (2019-06-04) - - -### Bug Fixes - -* **highlight:** allow array as "attribute" ([#2474](https://github.com/algolia/react-instantsearch/issues/2474)) ([9dc829a](https://github.com/algolia/react-instantsearch/commit/9dc829a)), closes [#2461](https://github.com/algolia/react-instantsearch/issues/2461) -* **indexUtils:** allow index with dots in it ([#2350](https://github.com/algolia/react-instantsearch/issues/2350)) ([f91906f](https://github.com/algolia/react-instantsearch/commit/f91906f)) - - -### Features - -* **voiceSearch:** add voice search widget ([#2316](https://github.com/algolia/react-instantsearch/issues/2316)) ([0e3b124](https://github.com/algolia/react-instantsearch/commit/0e3b124)) - - - -# [5.6.0](https://github.com/algolia/react-instantsearch/compare/v5.5.0...v5.6.0) (2019-05-15) - - -### Bug Fixes - -* **connectQueryRules:** avoid to throw an error with undefined values ([#2436](https://github.com/algolia/react-instantsearch/issues/2436)) ([1e18287](https://github.com/algolia/react-instantsearch/commit/1e18287)) - - -### Features - -* **infiniteHits:** add previous button ([#2296](https://github.com/algolia/react-instantsearch/issues/2296)) ([010a69a](https://github.com/algolia/react-instantsearch/commit/010a69a)) - - - -# [5.5.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0...v5.5.0) (2019-04-23) - - -### Bug Fixes - -* **createInstantSearch:** change the User-Agent to use the new specs ([#2209](https://github.com/algolia/react-instantsearch/issues/2209)) ([642ba0b](https://github.com/algolia/react-instantsearch/commit/642ba0b)) - - -### Features - -* **DOMMaps:** expose withGoogleMaps HOC [PART-1] ([#2000](https://github.com/algolia/react-instantsearch/issues/2000)) ([2ae1dea](https://github.com/algolia/react-instantsearch/commit/2ae1dea)) -* **queryRules:** add Query Rules features ([#2286](https://github.com/algolia/react-instantsearch/issues/2286)) ([3ae9c01](https://github.com/algolia/react-instantsearch/commit/3ae9c01)) -* **insights:** add insights features ([#2215](https://github.com/algolia/react-instantsearch/pull/2215)) ([961e7a7](https://github.com/algolia/react-instantsearch/commit/961e7a7)) - - - -# [5.4.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.1...v5.4.0) (2019-02-05) - - -### Bug Fixes - -* **DOMMaps:** set React & React DOM as peer deps ([#1922](https://github.com/algolia/react-instantsearch/issues/1922)) ([2f2cefd](https://github.com/algolia/react-instantsearch/commit/2f2cefd)) - - - -# [5.4.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.0...v5.4.0-beta.1) (2019-01-10) - - -### Bug Fixes - -* **deps:** sync algoliasearch version ([#1879](https://github.com/algolia/react-instantsearch/issues/1879)) ([40f9c26](https://github.com/algolia/react-instantsearch/commit/40f9c26)) -* **maps:** use stable version for peer deps ([#1887](https://github.com/algolia/react-instantsearch/issues/1887)) ([9055167](https://github.com/algolia/react-instantsearch/commit/9055167)) - - - -# [5.4.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.3.2...v5.4.0-beta.0) (2019-01-08) - - -### Features - -* **Index:** introduce `indexId` prop ([#1833](https://github.com/algolia/react-instantsearch/issues/1833)) ([ec9e0fb](https://github.com/algolia/react-instantsearch/commit/ec9e0fb)) - - - - -## [5.3.2](https://github.com/algolia/react-instantsearch/compare/v5.3.1...v5.3.2) (2018-10-30) - - -### Bug Fixes - -* **sffv:** clamp maxFacetHits to the allowed range ([#1696](https://github.com/algolia/react-instantsearch/issues/1696)) ([83ce245](https://github.com/algolia/react-instantsearch/commit/83ce245)) - - - - -## [5.3.1](https://github.com/algolia/react-instantsearch/compare/v5.3.0...v5.3.1) (2018-09-26) - - -### Bug Fixes - -* **connector:** ensure canRefine is computed on the transformed items ([#1568](https://github.com/algolia/react-instantsearch/pull/1568)) ([c95384f](https://github.com/algolia/react-instantsearch/commit/c95384f)) -* **toggle:** ensure facet is present ([#1613](https://github.com/algolia/react-instantsearch/issues/1613)) ([e914ff6](https://github.com/algolia/react-instantsearch/commit/e914ff6)) - - - - -# [5.3.0](https://github.com/algolia/react-instantsearch/compare/v5.2.3...v5.3.0) (2018-09-24) - - -### Bug Fixes - -* **SSR:** bind getSearchParmaters to the component instance ([f34cb3d](https://github.com/algolia/react-instantsearch/commit/f34cb3d)) -* **GoogleMapsLoader:** pick google maps version ([#1540](https://github.com/algolia/react-instantsearch/issues/1540)) ([b14efcf](https://github.com/algolia/react-instantsearch/commit/b14efcf)) - - -### Features - -* **connectToggleRefinement:** implement canRefine & count ([#1588](https://github.com/algolia/react-instantsearch/issues/1588)) ([40672dd](https://github.com/algolia/react-instantsearch/commit/40672dd)) - - - - -## [5.2.3](https://github.com/algolia/react-instantsearch/compare/v5.2.2...v5.2.3) (2018-08-16) - - -### Bug Fixes - -* Allow object as type for Root (closes [#1446](https://github.com/algolia/react-instantsearch/issues/1446)) ([#1461](https://github.com/algolia/react-instantsearch/issues/1461)) ([7c2317b](https://github.com/algolia/react-instantsearch/commit/7c2317b)) -* **List:** render children list only when required ([#1472](https://github.com/algolia/react-instantsearch/issues/1472)) ([9eb2cbb](https://github.com/algolia/react-instantsearch/commit/9eb2cbb)), closes [#1459](https://github.com/algolia/react-instantsearch/issues/1459) - - - - -## [5.2.2](https://github.com/algolia/react-instantsearch/compare/v5.2.1...v5.2.2) (2018-07-16) - - -Publish the previous version on `stable`. - - - - -## [5.2.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0...v5.2.1) (2018-07-16) - - -### Bug Fixes - -* **GoogleMapsLoader:** inline the import to scriptjs ([#1427](https://github.com/algolia/react-instantsearch/issues/1427)) ([8019416](https://github.com/algolia/react-instantsearch/commit/8019416)) - - - - -# [5.2.0](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.2...v5.2.0) (2018-07-04) - - -### Bug Fixes - -* **translatable:** avoid create a new function on every render ([#1383](https://github.com/algolia/react-instantsearch/issues/1383)) ([1285b3b](https://github.com/algolia/react-instantsearch/commit/1285b3b)) - - -### Features - -* **core:** export translatable ([#1351](https://github.com/algolia/react-instantsearch/issues/1351)) ([6d5a89d](https://github.com/algolia/react-instantsearch/commit/6d5a89d)) -* **maps:** add connector & widget ([#1171](https://github.com/algolia/react-instantsearch/issues/1171)) ([16e288a](https://github.com/algolia/react-instantsearch/commit/16e288a)) - - - - -# [5.2.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.1...v5.2.0-beta.2) (2018-06-19) - - -### Features - -* export highlight tags from DOM / native ([#1342](https://github.com/algolia/react-instantsearch/issues/1342)) ([28a699e](https://github.com/algolia/react-instantsearch/commit/28a699e)) -* **createInstantSearch:** enable _useRequestCache ([#1346](https://github.com/algolia/react-instantsearch/issues/1346)) ([f772600](https://github.com/algolia/react-instantsearch/commit/f772600)) -* **dom:** export create class name ([#1348](https://github.com/algolia/react-instantsearch/issues/1348)) ([9017468](https://github.com/algolia/react-instantsearch/commit/9017468)) - - - - -# [5.2.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.0...v5.2.0-beta.1) (2018-06-04) - - -### Bug Fixes - -* **dom:** publish server file ([#1305](https://github.com/algolia/react-instantsearch/issues/1305)) ([bd79693](https://github.com/algolia/react-instantsearch/commit/bd79693)) - - - - -# [5.2.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.1.0...v5.2.0-beta.0) (2018-06-04) - - -This new version introduce a complete revamp of the package structure, but it should be completely transparent for the users. - -If you have any troubles with this version please open a issue on [Github](https://github.com/algolia/react-instantsearch/issues/new), thanks! - - - - -# [5.1.0](https://github.com/algolia/react-instantsearch/compare/v5.0.3...v5.1.0) (2018-05-28) - - -### Bug Fixes - -* **connectInfiniteHits:** always set a value for previous page ([#1195](https://github.com/algolia/react-instantsearch/issues/1195)) ([4c218d5](https://github.com/algolia/react-instantsearch/commit/4c218d5)) -* **indexUtils:** avoid throw an error on cleanUp multi indices ([#1265](https://github.com/algolia/react-instantsearch/issues/1265)) ([12f5ace](https://github.com/algolia/react-instantsearch/commit/12f5ace)) - - -### Features - -* **searchClient:** Add support for custom Search Clients ([#1216](https://github.com/algolia/react-instantsearch/issues/1216)) ([174cc28](https://github.com/algolia/react-instantsearch/commit/174cc28)) - - - - -## [5.0.3](https://github.com/algolia/react-instantsearch/compare/v5.0.2...v5.0.3) (2018-04-03) - - -### Bug Fixes - -* revert dependencies as devDependencies ([#1135](https://github.com/algolia/react-instantsearch/issues/1135)) ([6b627bb](https://github.com/algolia/react-instantsearch/commit/6b627bb)) - - - - -## [5.0.2](https://github.com/algolia/react-instantsearch/compare/v5.0.1...v5.0.2) (2018-04-03) - - -### Bug Fixes - -* use lodash version of unsupported Array.{fill, find} ([#1118](https://github.com/algolia/react-instantsearch/issues/1118)) ([ea7bf42](https://github.com/algolia/react-instantsearch/commit/ea7bf42)) - - - - -## [5.0.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0...v5.0.1) (2018-03-12) - - -### Bug Fixes - -* **connectInfiniteHits:** always provide an array for hits ([#1064](https://github.com/algolia/react-instantsearch/issues/1064)) ([c75e38b](https://github.com/algolia/react-instantsearch/commit/c75e38b)) - - - - -# [5.0.0](https://github.com/algolia/react-instantsearch/compare/v4.5.2...v5.0.0) (2018-03-06) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.5.2](https://github.com/algolia/react-instantsearch/compare/v4.5.1...v4.5.2) (2018-03-06) - - -### Bug Fixes - -* **connectRange:** update default refinement propTypes ([#978](https://github.com/algolia/react-instantsearch/issues/978)) ([c065fb1](https://github.com/algolia/react-instantsearch/commit/c065fb1)) -* **IndexUtils:** avoid throw an error when cleanUp multi index ([#1019](https://github.com/algolia/react-instantsearch/issues/1019)) ([865a3c3](https://github.com/algolia/react-instantsearch/commit/865a3c3)) -* **SearchBox:** avoid to bind click on reset button ([#979](https://github.com/algolia/react-instantsearch/issues/979)) ([ea3063a](https://github.com/algolia/react-instantsearch/commit/ea3063a)) - - - - -# [5.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2018-02-06) - - -Apply features & bug fixes from [v4.5.0](#450-2018-02-06) & [v4.5.1](#451-2018-02-06) on the v5. - -See their CHANGELOG for more details. - - - - -## [4.5.1](https://github.com/algolia/react-instantsearch/compare/v4.5.0...v4.5.1) (2018-02-06) - - -### Bug Fixes - -* **StarRating:** move to 1 based instead of 0 ([#949](https://github.com/algolia/react-instantsearch/issues/949)) ([eb0152d](https://github.com/algolia/react-instantsearch/commit/eb0152d)) - - - - -# [4.5.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v4.5.0) (2018-02-06) - - -### Bug Fixes - -* **connectRange:** use the same behaviour for currentRefinement in getMetadata ([#923](https://github.com/algolia/react-instantsearch/issues/923)) ([08917b6](https://github.com/algolia/react-instantsearch/commit/08917b6)) -* **connectToggle:** use currentRefinement in metadata instead of the label ([#909](https://github.com/algolia/react-instantsearch/issues/909)) ([89cae2b](https://github.com/algolia/react-instantsearch/commit/89cae2b)) -* **StarRatings:** always show the stars below ([#929](https://github.com/algolia/react-instantsearch/issues/929)) ([22bf93a](https://github.com/algolia/react-instantsearch/commit/22bf93a)) - - -### Features - -* **connectStateResults:** expose isSearchStalled ([#933](https://github.com/algolia/react-instantsearch/issues/933)) ([f45ba27](https://github.com/algolia/react-instantsearch/commit/f45ba27)) - - - -# [5.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v5.0.0-beta.0) (2018-01-30) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.4.2](https://github.com/algolia/react-instantsearch/compare/v4.4.1...v4.4.2) (2018-01-24) - - -### Bug Fixes - -* **currentRefinements:** give access to id and index from transformItems for deduplication ([#830](https://github.com/algolia/react-instantsearch/issues/830)) ([316b8f5](https://github.com/algolia/react-instantsearch/commit/316b8f5)) -* pass maxFacetHits to SFFV ([#863](https://github.com/algolia/react-instantsearch/issues/863)) ([de23a46](https://github.com/algolia/react-instantsearch/commit/de23a46)) - - - - -## [4.4.1](https://github.com/algolia/react-instantsearch/compare/v4.4.0...v4.4.1) (2018-01-09) - - -### Bug Fixes - -* **SearchBox**: clear SearchBox without search as you type ([#802](https://github.com/algolia/react-instantsearch/issues/802)) ([c49b2b6](https://github.com/algolia/react-instantsearch/commit/c49b2b6)) -* **connectRange:** check if facet exist before access ([#797](https://github.com/algolia/react-instantsearch/issues/797)) ([6520760](https://github.com/algolia/react-instantsearch/commit/6520760)) -* **stories:** avoid to use linear-background it breaks Argos every time ([#804](https://github.com/algolia/react-instantsearch/issues/804)) ([0beded7](https://github.com/algolia/react-instantsearch/commit/0beded7)) -* **stories:** limit hits per page on Index ([#806](https://github.com/algolia/react-instantsearch/issues/806)) ([6eb14d3](https://github.com/algolia/react-instantsearch/commit/6eb14d3)) - - -### Features - -* **Index:** allow custom root ([#792](https://github.com/algolia/react-instantsearch/issues/792)) ([d793b0a](https://github.com/algolia/react-instantsearch/commit/d793b0a)) - - - - -# [4.4.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0...v4.4.0) (2018-01-03) - - -### Bug Fixes - -* **createInstantSearch:** remove the client from the Snapshot ([#749](https://github.com/algolia/react-instantsearch/issues/749)) ([700d8f4](https://github.com/algolia/react-instantsearch/commit/700d8f4)) -* refresh cache memory leak example ([#784](https://github.com/algolia/react-instantsearch/issues/784)) ([cf228ac](https://github.com/algolia/react-instantsearch/commit/cf228ac)) -* **stories:** rename InstantSearch to `` ([#789](https://github.com/algolia/react-instantsearch/issues/789)) ([05efda5](https://github.com/algolia/react-instantsearch/commit/05efda5)) - - -### Features - -* InstantSearch root props ([#770](https://github.com/algolia/react-instantsearch/issues/770)) ([2d458f8](https://github.com/algolia/react-instantsearch/commit/2d458f8)) - - - - -# [4.3.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0-beta.0...v4.3.0) (2017-12-20) - - -### Bug Fixes - -* reset page with multi index ([#665](https://github.com/algolia/react-instantsearch/issues/665)) ([865b7dc](https://github.com/algolia/react-instantsearch/commit/865b7dc)) -* track all index in the manager ([#660](https://github.com/algolia/react-instantsearch/issues/660)) ([793502b](https://github.com/algolia/react-instantsearch/commit/793502b)) - - -### Features - -* **SearchBox:** provide a loading indicator ([#544](https://github.com/algolia/react-instantsearch/issues/544)) ([189659e](https://github.com/algolia/react-instantsearch/commit/189659e)) -* **Highlight:** support array of strings ([#715](https://github.com/algolia/react-instantsearch/issues/715)) ([8e93c6a](https://github.com/algolia/react-instantsearch/commit/8e93c6a)) - - - - -# [4.3.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.2.0...v4.3.0-beta.0) (2017-11-27) - - -### Bug Fixes - -* **babelrc:** add a key for each env development, production, es ([#547](https://github.com/algolia/react-instantsearch/issues/547)) ([fa9528d](https://github.com/algolia/react-instantsearch/commit/fa9528d)) -* **localizecount:** allow localized string for count in MenuSelect ([#657](https://github.com/algolia/react-instantsearch/issues/657)) ([67ebd34](https://github.com/algolia/react-instantsearch/commit/67ebd34)) -* **react-router-example:** Properly update search query when using browser navigation ([#604](https://github.com/algolia/react-instantsearch/issues/604)) ([9ee6600](https://github.com/algolia/react-instantsearch/commit/9ee6600)) - - -### Features - -* **refreshcache:** add prop refresh to InstantSearch instance ([#619](https://github.com/algolia/react-instantsearch/issues/619)) ([19f6de0](https://github.com/algolia/react-instantsearch/commit/19f6de0)) - - - - -# [4.2.0](https://github.com/algolia/react-instantsearch/compare/v4.1.3...v4.2.0) (2017-11-02) - - -### Bug Fixes - -* **connectRange:** handle boundaries on first call ([9f14dc0](https://github.com/algolia/react-instantsearch/commit/9f14dc0)) -* **connectRange:** use refine instead of cleanUp in metadata ([#526](https://github.com/algolia/react-instantsearch/issues/526)) ([1861235](https://github.com/algolia/react-instantsearch/commit/1861235)) -* **hierarchicaMenu:** allow sorting and using limit ([fe178ed](https://github.com/algolia/react-instantsearch/commit/fe178ed)), closes [#92](https://github.com/algolia/react-instantsearch/issues/92) -* **InfiniteHits:** add disabled style to the LoadMore button ([#477](https://github.com/algolia/react-instantsearch/issues/477)) ([faba1ad](https://github.com/algolia/react-instantsearch/commit/faba1ad)) -* **Range:** handle float, allow reset and respect boundaries ([75969b8](https://github.com/algolia/react-instantsearch/commit/75969b8)) -* **RangeInput:** fix compatibility with React 16 & Panel ([3f218db](https://github.com/algolia/react-instantsearch/commit/3f218db)) -* **searchbox:** add maxlength 512 ([#542](https://github.com/algolia/react-instantsearch/issues/542)) ([5bd4033](https://github.com/algolia/react-instantsearch/commit/5bd4033)), closes [#510](https://github.com/algolia/react-instantsearch/issues/510) - - -### Features - -* **MenuSelect:** add component and connector ([cc6e0d7](https://github.com/algolia/react-instantsearch/commit/cc6e0d7)) - - - - -## [4.1.3](https://github.com/algolia/react-instantsearch/compare/v4.1.2...v4.1.3) (2017-10-09) - - -### Bug Fixes - -* **List:** remove React16 warning ([#442](https://github.com/algolia/react-instantsearch/issues/442)) ([8d6cf18](https://github.com/algolia/react-instantsearch/commit/8d6cf18)) - - -### Features - -* **connectStateResults:** add component props ([#434](https://github.com/algolia/react-instantsearch/issues/434)) ([c629b97](https://github.com/algolia/react-instantsearch/commit/c629b97)) - - - - -## [4.1.2](https://github.com/algolia/react-instantsearch/compare/v4.1.1...v4.1.2) (2017-09-26) - - -### Features - -* **Conditional:** add connectStateResults connector ([#357](https://github.com/algolia/react-instantsearch/issues/357)) ([462df5f](https://github.com/algolia/react-instantsearch/commit/462df5f)) - - - - -## [4.1.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0...v4.1.1) (2017-09-13) - - -### Bug Fixes - -* **MultiIndex:** reset page to 1 when share widgets refine (#312) ([c85a7bf](https://github.com/algolia/react-instantsearch/commit/c85a7bf)) -* **MultiIndex:** Trigger new search when `` props are updated (#318) ([bb11965](https://github.com/algolia/react-instantsearch/commit/bb11965)) - - - - -# [4.1.0](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.5...v4.1.0) (2017-08-28) - - -### Bug Fixes - -* **Highlighting:** revert breaking change (#245) ([045ee06](https://github.com/algolia/react-instantsearch/commit/045ee06)) -* **List:** adds support for any type of renderable element (#266) ([d848bb6](https://github.com/algolia/react-instantsearch/commit/d848bb6)) -* **Pagination:** fixed the offset ([3c0fff2](https://github.com/algolia/react-instantsearch/commit/3c0fff2)) -* **PoweredBy:** aria-* tags are not camelcased (#261) ([dc4a5bb](https://github.com/algolia/react-instantsearch/commit/dc4a5bb)) - - - - -# [4.1.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2017-08-08) - - -### Bug Fixes - -* **SSR:** clean SP before rendering agan (#238) ([e765886](https://github.com/algolia/react-instantsearch/commit/e765886)) - - -### Features - -* **Breadcrumb:** add a new widget & connector (#228) ([7f8f3ae](https://github.com/algolia/react-instantsearch/commit/7f8f3ae)) - - - - -# [4.1.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2017-08-03) - - -### Bug Fixes - -* **deps:** Update dependency lint-staged to version ^4.0.0 (#201) ([6867a1b](https://github.com/algolia/react-instantsearch/commit/6867a1b)) -* **nextjs/ssr:** parse `params.asPath` (#189) ([ae17da0](https://github.com/algolia/react-instantsearch/commit/ae17da0)) -* **PoweredBy:** add a label to the Algolia logo (#216) ([cd235bd](https://github.com/algolia/react-instantsearch/commit/cd235bd)) -* **react:** remove typo around `"" 2` (#220) ([f73eb04](https://github.com/algolia/react-instantsearch/commit/f73eb04)) -* **ScrollTo:** scroll to only if change triggered by the widget observed (#202) ([2d76022](https://github.com/algolia/react-instantsearch/commit/2d76022)) -* **theme:** format the count of items appearing in a refinement (#217) ([2225c24](https://github.com/algolia/react-instantsearch/commit/2225c24)) - - - - -# [4.1.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2017-07-25) - - -### Bug Fixes - -* **error:** reset error when receiving results of a query (not when sending it) (#179) ([bb12c29](https://github.com/algolia/react-instantsearch/commit/bb12c29)) -* **highlight:** wrong parsing between client and server (#183) ([2daae70](https://github.com/algolia/react-instantsearch/commit/2daae70)) -* **poweredBy:** SSR compatibility (#181) ([ec0fa8a](https://github.com/algolia/react-instantsearch/commit/ec0fa8a)) - - -### BREAKING CHANGES - -* **highlight:** We remove the timestamp present in our highlight preTag and postTag. If you were using regex to parse the -highlighting results then you'll need to adapt it as now it's only "ais-highlight". - - - - -# [4.1.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2017-07-20) - - -### Bug Fixes - -* **error:** reset error if next query is successful (#175) ([ff50a07](https://github.com/algolia/react-instantsearch/commit/ff50a07)) - - - - -# [4.1.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2017-07-12) - - - - -# [4.1.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.0.7...v4.1.0-beta.0) (2017-07-10) - - -### Bug Fixes - -* **argos:** address flakyness (#152) ([84ef8f1](https://github.com/algolia/react-instantsearch/commit/84ef8f1)) - - -### Features - -* **server-side rendering:** Add API features for server-side rendering ([86b14d1](https://github.com/algolia/react-instantsearch/commit/86b14d1)) - - - - -## [4.0.7](https://github.com/algolia/react-instantsearch/compare/v4.0.6...v4.0.7) (2017-07-06) - - -### Bug Fixes - -* **results:** revert commit that ensure hits are returned only if right indices (#149) ([df9aa25](https://github.com/algolia/react-instantsearch/commit/df9aa25)) - - - - -## [4.0.6](https://github.com/algolia/react-instantsearch/compare/v4.0.5...v4.0.6) (2017-06-29) - - -### Bug Fixes - -* **store:** delay call to listener to prevent infinite loops (#143) ([0945958](https://github.com/algolia/react-instantsearch/commit/0945958)) - - - - -## [4.0.5](https://github.com/algolia/react-instantsearch/compare/v4.0.4...v4.0.5) (2017-06-26) - - -### Bug Fixes - -* **MultiIndex:** ensure getResults return only hits matching index in the context (#136) ([124ffe6](https://github.com/algolia/react-instantsearch/commit/124ffe6)) -* **MultiIndex:** handle if namespace isn't in search state (#139) ([1aab324](https://github.com/algolia/react-instantsearch/commit/1aab324)) -* **storybook:** process CSS through autoprefixer (#138) ([62cf512](https://github.com/algolia/react-instantsearch/commit/62cf512)) - - - - -## [4.0.4](https://github.com/algolia/react-instantsearch/compare/v4.0.3...v4.0.4) (2017-06-19) - - -### Bug Fixes - -* **MultiIndex:** handle switch between mono and multi index (#132) ([e161921](https://github.com/algolia/react-instantsearch/commit/e161921)) - - - - -## [4.0.3](https://github.com/algolia/react-instantsearch/compare/v4.0.2...v4.0.3) (2017-06-14) - - -### Bug Fixes - -* **SFFV:** search status we're not inside search state (#125) ([5f3e670](https://github.com/algolia/react-instantsearch/commit/5f3e670)) - - - - -## [4.0.2](https://github.com/algolia/react-instantsearch/compare/v4.0.1...v4.0.2) (2017-05-30) - - - - -## [4.0.1](https://github.com/algolia/react-instantsearch/compare/v4.0.0...v4.0.1) (2017-05-17) - - -### Bug Fixes - -* **state:** nested attributes for faceting were not handled ([11bd122](https://github.com/algolia/react-instantsearch/commit/11bd122)) - - - - -# [4.0.0](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.6...v4.0.0) (2017-05-15) - -### Features and migration guide - -You can find all the details of the release and the migration guide from v3 to v4 here: https://discourse.algolia.com/t/react-instantsearch-v4/1329. - - - -# [4.0.0-beta.6](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.5...v4.0.0-beta.6) (2017-05-04) - - - - -# [4.0.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.4...v4.0.0-beta.5) (2017-05-02) - - -### Bug Fixes - -* **connectAutoComplete:** allow usage with hits from a single index (#75) ([8b3b358](https://github.com/algolia/react-instantsearch/commit/8b3b358)), closes [#74](https://github.com/algolia/react-instantsearch/issues/74) -* **InstantSearch:** update algoliaClient when it change (#70) ([9e97dbd](https://github.com/algolia/react-instantsearch/commit/9e97dbd)) - - - - -# [4.0.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.3...v4.0.0-beta.4) (2017-04-25) - - -### Bug Fixes - -* **MultIndex:** no need to nest hits, if those are from main index. (#56) ([86e0bd7](https://github.com/algolia/react-instantsearch/commit/86e0bd7)) - - -### Features - -* **MultiIndex:** remove the need for virtual hits when using connectAutoComplete (#45) ([7549019](https://github.com/algolia/react-instantsearch/commit/7549019)) - - - - -# [4.0.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.2...v4.0.0-beta.3) (2017-04-21) - - -### Bug Fixes - -* replace usage of Object.values (#47) ([4c79e3e](https://github.com/algolia/react-instantsearch/commit/4c79e3e)) - - - - -# [4.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2017-04-18) - - -### Bug Fixes - -* **InstantSearch:** dont fire request/onsearchStateChange when unmounting (#26) ([9a1487a](https://github.com/algolia/react-instantsearch/commit/9a1487a)) -* **MultiIndex:** derived helper were using main index specifics params (#36) ([991fea6](https://github.com/algolia/react-instantsearch/commit/991fea6)) -* **MultiIndex:** revert breaking change if no multiple index (#32) ([44f7de0](https://github.com/algolia/react-instantsearch/commit/44f7de0)) -* **util:** remove empty key was removing non object key (#29) ([9f795c7](https://github.com/algolia/react-instantsearch/commit/9f795c7)) - - -### Features - -* **Highlighter:** allow rendering to custom tag (#11) ([52a1212](https://github.com/algolia/react-instantsearch/commit/52a1212)) -* **SearchBox:** add default width and height to buttons. (#34) ([bcabf9b](https://github.com/algolia/react-instantsearch/commit/bcabf9b)) - - - - -# [4.0.0-beta.1](https://github.com/algolia/instantsearch.js/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2017-04-03) - - -### Bug Fixes - -* **SFFV:** fix wrong query behaviour with slow network (#2086) ([c251e8f](https://github.com/algolia/instantsearch.js/commit/c251e8f)), closes [#2086](https://github.com/algolia/instantsearch.js/issues/2086) - - - - -# [4.0.0-beta.0](https://github.com/algolia/instantsearch.js/compare/v3.3.0...v4.0.0-beta.0) (2017-03-28) - - -### Features - -* **multi-index:** ease multi index and auto complete ([09a4e1d](https://github.com/algolia/instantsearch.js/commit/09a4e1d)) - - -### BREAKING CHANGES - -* multi-index: * Reseting the pagination should be done at each connector level inside the "refine" function when returning the search state. -* The current page now appears inside the search state when a widget is used -* Query values are part of the items prop of the connectCurrentRefinements connector. Behaviour is unchanged, query will be filtered if clearsQuery prop is false. -* Add the index name to all the current refinements items. (not used by our widgets yet, but available if needed). - - - - -# [3.3.0](https://github.com/algolia/instantsearch.js/compare/v3.2.2-beta0...v3.3.0) (2017-03-22) - - -### Bug Fixes - -* **example:** Fix access to props in react-router example ([1417d6f](https://github.com/algolia/instantsearch.js/commit/1417d6f)) - - - - -## [3.2.2-beta0](https://github.com/algolia/instantsearch.js/compare/v3.2.1...v3.2.2-beta0) (2017-03-20) - - -### Bug Fixes - -* **InfiniteHits:** provide translation key for `Load More` (#2048) ([6130bf2](https://github.com/algolia/instantsearch.js/commit/6130bf2)) -* **SearchBox:** better mobile behaviour by default ([ea968b3](https://github.com/algolia/instantsearch.js/commit/ea968b3)) -* **example:** link to instantsearch/react (#2007) ([5e674cd](https://github.com/algolia/instantsearch.js/commit/5e674cd)) -* **recipes:** react router v4 ([de673bf](https://github.com/algolia/instantsearch.js/commit/de673bf)) - - -### Features - -* **SearchBox:** add role=search to the form (#2046) ([d1e90f3](https://github.com/algolia/instantsearch.js/commit/d1e90f3)) -* **SearchBox:** allow custom reset and submit components (#1991) ([cd303d7](https://github.com/algolia/instantsearch.js/commit/cd303d7)) -* **searchBox:** add event handling ([e267ab6](https://github.com/algolia/instantsearch.js/commit/e267ab6)), closes [#2017](https://github.com/algolia/instantsearch.js/issues/2017) - - - - -## [3.2.1](https://github.com/algolia/instantsearch.js/compare/v3.2.0...v3.2.1) (2017-02-22) - - -### Bug Fixes - -* **umd:** Add connectors to UMD build (#1988) ([23ac5e6](https://github.com/algolia/instantsearch.js/commit/23ac5e6)), closes [#1987](https://github.com/algolia/instantsearch.js/issues/1987) - - - - -# [3.2.0](https://github.com/algolia/instantsearch.js/compare/v3.1.0...v3.2.0) (2017-02-15) - - -### Bug Fixes - -* **Configure:** use props a unique source of truth (#1967) ([9d53d86](https://github.com/algolia/instantsearch.js/commit/9d53d86)) -* **SearchBox:** Safari can only have with xlinkHref (#1970) ([7ab00bd](https://github.com/algolia/instantsearch.js/commit/7ab00bd)), closes [#1968](https://github.com/algolia/instantsearch.js/issues/1968) - - -### Features - -* **MultiRange:** add an all range (#1959) ([a3dc950](https://github.com/algolia/instantsearch.js/commit/a3dc950)) - - -### BREAKING CHANGES - -* MultiRange: - MultiRange/connectMultiRange: will add a "All" range to allow unselection of range without the usage of CurrentRefinements. This range can be either filtered or ramove via CSS if not needed. The label can be changed by using our translations system. - - - - -# [3.1.0](https://github.com/algolia/instantsearch.js/compare/v3.0.0...v3.1.0) (2017-02-08) - - -### Bug Fixes - -* **Configure:** call onSearchStateChange when props are updated (#1953) ([7e151db](https://github.com/algolia/instantsearch.js/commit/7e151db)), closes [#1950](https://github.com/algolia/instantsearch.js/issues/1950) -* **Configure:** trigger onSearchStateChange with the right data ([11e5af8](https://github.com/algolia/instantsearch.js/commit/11e5af8)) -* **createConnector:** updates with latest props on state change (#1951) ([cd3a82c](https://github.com/algolia/instantsearch.js/commit/cd3a82c)) - - -### Features - -* **ClearAll:** add withQuery to also clear the search query (#1958) ([c0e695b](https://github.com/algolia/instantsearch.js/commit/c0e695b)) - - - - -# [3.0.0](https://github.com/algolia/instantsearch.js/compare/v2.2.5...v3.0.0) (2017-02-06) - - -### Bug Fixes - -* ***List:** disable shortcuts in *List SearchBoxes (#1921) ([51a76ae](https://github.com/algolia/instantsearch.js/commit/51a76ae)), closes [#1920](https://github.com/algolia/instantsearch.js/issues/1920) -* **Configure:** add configure parameters in search state (#1935) ([0971330](https://github.com/algolia/instantsearch.js/commit/0971330)), closes [#1863](https://github.com/algolia/instantsearch.js/issues/1863) -* **Hits:** limit the hitComponent to be only a function (#1912) ([b3c9578](https://github.com/algolia/instantsearch.js/commit/b3c9578)) -* **Pagination:** fix and indicate when pagination is disabled ([5f20199](https://github.com/algolia/instantsearch.js/commit/5f20199)), closes [#1938](https://github.com/algolia/instantsearch.js/issues/1938) -* **StarRating:** usage with filters (#1933) ([667e9d5](https://github.com/algolia/instantsearch.js/commit/667e9d5)) -* **withSearchBox:** keep displaying searchBox when no items found (#1930) ([30de4cd](https://github.com/algolia/instantsearch.js/commit/30de4cd)) - - -### Features - -* **MultiRange:** indicate if a range has no refinements (#1926) ([80b6450](https://github.com/algolia/instantsearch.js/commit/80b6450)) -* **panel:** add a panel widget (#1889) ([594e1a1](https://github.com/algolia/instantsearch.js/commit/594e1a1)) -* **starRating:** indicate when any refinement has no effect ([c547ae5](https://github.com/algolia/instantsearch.js/commit/c547ae5)) -* **widgets:** default design for disabled states (#1929) ([31f010b](https://github.com/algolia/instantsearch.js/commit/31f010b)) - -### Migration guide - -The migration to V3.0.0 should be safe and you should do it. - -There are two breaking changes that you will need to handle in your codebase: -- Anytime you are using a connector, when there are no more items in it or no more hits, we will still call your Component. Thus you will have to handle cases like dealing with empty arrays and decide if you want to unmount or hide the widget. -- Anytime you are using a widget, when there are no more items in it or no more hits, we will still display the widget. You can then decide to hide it with CSS. - - -## [2.2.5](https://github.com/algolia/instantsearch.js/compare/v2.2.4...v2.2.5) (2017-01-23) - - -### Bug Fixes - -* **currentRefinements:** make removing a toggle refinement work ([8995e64](https://github.com/algolia/instantsearch.js/commit/8995e64)) - - - - -## [2.2.4](https://github.com/algolia/instantsearch.js/compare/v2.2.3...v2.2.4) (2017-01-20) - - -### Bug Fixes - -* **publish:** publish react-instantsearch/dist instead of root (#1884) ([64414e0](https://github.com/algolia/instantsearch.js/commit/64414e0)) - - - - -## [2.2.3](https://github.com/algolia/instantsearch.js/compare/v2.2.2...v2.2.3) (2017-01-20) - - -### Bug Fixes - -* **SFFV:** translations for searchbox were not applied (#1879) ([e9b4ee1](https://github.com/algolia/instantsearch.js/commit/e9b4ee1)) - - - - -## [2.2.2](https://github.com/algolia/instantsearch.js/compare/v2.2.1...v2.2.2) (2017-01-18) - - -### Bug Fixes - -* **react-router:** search was triggered two many times (#1840) ([25e9db5](https://github.com/algolia/instantsearch.js/commit/25e9db5)) -* **SFFV:** empty query triggered a new SFFV (#1875) ([6c8259a](https://github.com/algolia/instantsearch.js/commit/6c8259a)) - - - - -## [2.2.1](https://github.com/algolia/instantsearch.js/compare/v2.2.0...v2.2.1) (2017-01-18) - - -### Bug Fixes - -* **createInstantsearch:** fix missing props (#1867) ([8d319b5](https://github.com/algolia/instantsearch.js/commit/8d319b5)), closes [#1867](https://github.com/algolia/instantsearch.js/issues/1867) - - - - -# [2.2.0](https://github.com/algolia/instantsearch.js/compare/v2.1.0...v2.2.0) (2017-01-17) - - -### Bug Fixes - -* **clear:** clearing wasn't working with too+ same type facets selected (#1820) ([a9a2364](https://github.com/algolia/instantsearch.js/commit/a9a2364)) -* **connectSearchBox:** handle `defaultRefinement` (#1829) ([7a730e2](https://github.com/algolia/instantsearch.js/commit/7a730e2)), closes [#1826](https://github.com/algolia/instantsearch.js/issues/1826) -* **Instantsearch:** Update all props on InstantSearch (#1828) ([2ed9b49](https://github.com/algolia/instantsearch.js/commit/2ed9b49)) -* **InstantSearch:** add specific `react-instantsearch ${version}` agent (#1844) ([a1113bc](https://github.com/algolia/instantsearch.js/commit/a1113bc)) -* **SFFV:** correct propTypes and add missing default values (#1845) ([a4c1b31](https://github.com/algolia/instantsearch.js/commit/a4c1b31)) -* **test:** add missing Snippet and Highliter snapshot ([4accce5](https://github.com/algolia/instantsearch.js/commit/4accce5)) -* **widgets:** replace setImmediate use with Promise use when update is needed (#1811) ([17e2497](https://github.com/algolia/instantsearch.js/commit/17e2497)) - - -### Features - -* **Menu, connectMenu:** add search for facet values (#1822) ([a6c513e](https://github.com/algolia/instantsearch.js/commit/a6c513e)) -* **snippet:** add a snippet widget to be able to highlight snippet results (#1797) ([2aecc40](https://github.com/algolia/instantsearch.js/commit/2aecc40)) -* **widgets:** add transformItems to be able to sort and filter (#1809) ([ba539f0](https://github.com/algolia/instantsearch.js/commit/ba539f0)) - - - - -# [2.1.0](https://github.com/algolia/instantsearch.js/compare/v2.0.1...v2.1.0) (2017-01-04) - - -### Bug Fixes - -* **createInstantSearchManager:** drop outdated response (#1765) ([76c5312](https://github.com/algolia/instantsearch.js/commit/76c5312)) -* **highlight:** highlight should work even if the attribute is missing (#1791) ([5b79b15](https://github.com/algolia/instantsearch.js/commit/5b79b15)), closes [#1790](https://github.com/algolia/instantsearch.js/issues/1790) -* **InfiniteHits:** better classname to loadmore btn (#1789) ([ad2ded3](https://github.com/algolia/instantsearch.js/commit/ad2ded3)) -* **starRatings:** click on selected range doesn't unselect it (#1766) ([beacc72](https://github.com/algolia/instantsearch.js/commit/beacc72)) -* **website:** broken demo links (#1802) ([0abe2f5](https://github.com/algolia/instantsearch.js/commit/0abe2f5)) -* **widgets:** add 300px width for the default SearchBox (#1803) ([bf5d791](https://github.com/algolia/instantsearch.js/commit/bf5d791)) - - -### Features - -* **InfiniteHits:** Add class to load more button (#1787) ([416febd](https://github.com/algolia/instantsearch.js/commit/416febd)) -* **RefinementList, connectRefinementList:** allow to search for facet values ([e086a81](https://github.com/algolia/instantsearch.js/commit/e086a81)) - - - - -## [2.0.1](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.1) (2016-12-15) - - -### Bug Fixes - -* **connectRange:** when unfinite numbers are passed throw ([75bec0d](https://github.com/algolia/instantsearch.js/commit/75bec0d)) -* **react-native:** use View as a container for react-native (#1729) ([5b76f75](https://github.com/algolia/instantsearch.js/commit/5b76f75)), closes [#1730](https://github.com/algolia/instantsearch.js/issues/1730) -* **SearchBox:** autocomplete was not disabled by default (#1742) ([bc76618](https://github.com/algolia/instantsearch.js/commit/bc76618)) -* **starRating:** call createURL with the right interface (min/max) (#1747) ([f9ab9b6](https://github.com/algolia/instantsearch.js/commit/f9ab9b6)) - - - - -## [2.0.0](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.0) (2016-12-08) - -First release of `react-instantsearch` diff --git a/packages/react-instantsearch-hooks-server/README.md b/packages/react-instantsearch-hooks-server/README.md deleted file mode 100644 index a2c754b298..0000000000 --- a/packages/react-instantsearch-hooks-server/README.md +++ /dev/null @@ -1,74 +0,0 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [react-instantsearch-hooks-server](#react-instantsearch-hooks-server) - - [Installation](#installation) - - [Getting started](#getting-started) - - [API reference](#api-reference) - - [Documentation](#documentation) - - [Contributing](#contributing) - - [License](#license) - - - -# react-instantsearch-hooks-server - -React InstantSearch Hooks is an open-source, **UI library** for React that lets you server-side render a search interface. - -Server-side rendering (SSR) lets you generate HTML from InstantSearch components on the server. React InstantSearch Hooks is compatible with server-side rendering. The library provides an API that works with any SSR solution. - -## Installation - -React InstantSearch Hooks Server is available on the npm registry. - -```sh -yarn add react-instantsearch-hooks-server -# or -npm install react-instantsearch-hooks-server -``` - -## Getting started - -React InstantSearch Hooks is compatible with server-side rendering. The library provides an API that works with any SSR solution. You can, for example, use it with an [express](https://expressjs.com/) server or with [Next.js](https://nextjs.org/). - -Check out our [**Server-side rendering guide**](https://algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react-hooks/) to start rendering your React InstantSearch Hooks app on the server. - -## API reference - -- [`getServerState()`](https://algolia.com/doc/api-reference/widgets/server-state/react-hooks/) -- [``](https://algolia.com/doc/api-reference/widgets/ssr-provider/react-hooks/) - -## Documentation - -The full documentation is available on [algolia.com/doc](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/). - -## Contributing - -We welcome all contributors, from casual to regular 💙 - -- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport]. -- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest]. -- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it. -- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore]. - -To start contributing to code, you need to: - -1. [Fork the project](https://help.github.com/articles/fork-a-repo/) -1. [Clone the repository](https://help.github.com/articles/cloning-a-repository/) -1. Install the dependencies: `yarn` - -Please read [our contribution process](https://github.com/algolia/instantsearch.js/blob/master/CONTRIBUTING.md) to learn more. - -## License - -React InstantSearch Hooks is [MIT licensed](../../LICENSE). - - - -[contributing-bugreport]: https://github.com/algolia/instantsearch.js/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-featurerequest]: https://github.com/algolia/instantsearch.js/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch+Hooks&title=Feature%20request%3A%20 -[contributing-newissue]: https://github.com/algolia/instantsearch.js/issues/new?labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-label-easy]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-bug]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-chore]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 diff --git a/packages/react-instantsearch-hooks-server/package.json b/packages/react-instantsearch-hooks-server/package.json deleted file mode 100644 index c4c0f18f5a..0000000000 --- a/packages/react-instantsearch-hooks-server/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "react-instantsearch-hooks-server", - "version": "6.47.3", - "description": "⚡ Server-side support for React InstantSearch Hooks, by Algolia", - "source": "src/index.ts", - "types": "dist/es/index.d.ts", - "main": "dist/cjs/index.js", - "module": "dist/es/index.js", - "type": "module", - "exports": { - ".": { - "import": "./dist/es/index.js", - "require": "./dist/cjs/index.js" - } - }, - "sideEffects": false, - "license": "MIT", - "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/", - "repository": { - "type": "git", - "url": "https://github.com/algolia/instantsearch.js" - }, - "author": { - "name": "Algolia, Inc.", - "url": "https://www.algolia.com" - }, - "keywords": [ - "algolia", - "components", - "fast", - "instantsearch", - "react", - "search" - ], - "files": [ - "README.md", - "dist" - ], - "scripts": { - "clean": "rm -rf dist", - "watch": "yarn build:cjs --watch", - "build": "yarn build:cjs && yarn build:es && yarn build:types", - "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet && ../../scripts/prepare-cjs.sh", - "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", - "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es", - "test:exports": "node ./test/module/is-es-module.mjs && node ./test/module/is-cjs-module.cjs" - }, - "dependencies": { - "@babel/runtime": "^7.1.2", - "instantsearch.js": "4.56.8", - "react-instantsearch-hooks": "6.47.3" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 5", - "react": ">= 16.8.0 < 19", - "react-dom": ">= 16.8.0 < 19" - } -} diff --git a/packages/react-instantsearch-hooks-server/test/module/is-cjs-module.cjs b/packages/react-instantsearch-hooks-server/test/module/is-cjs-module.cjs deleted file mode 100644 index e3718e6fee..0000000000 --- a/packages/react-instantsearch-hooks-server/test/module/is-cjs-module.cjs +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable no-console */ - -const assert = require('assert'); - -const ReactInstantSearchHooksServer = require('react-instantsearch-hooks-server'); - -assert.ok(ReactInstantSearchHooksServer); - -console.log('react-instantsearch-hooks-server is valid CJS'); diff --git a/packages/react-instantsearch-hooks-server/test/module/is-es-module.mjs b/packages/react-instantsearch-hooks-server/test/module/is-es-module.mjs deleted file mode 100644 index 943c653199..0000000000 --- a/packages/react-instantsearch-hooks-server/test/module/is-es-module.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-disable no-console */ -import assert from 'assert'; - -import * as ReactInstantSearchHooksServer from 'react-instantsearch-hooks-server'; - -assert.ok(ReactInstantSearchHooksServer); - -console.log('react-instantsearch-hooks-server is valid ESM'); diff --git a/packages/react-instantsearch-hooks-web/CHANGELOG.md b/packages/react-instantsearch-hooks-web/CHANGELOG.md deleted file mode 100644 index 2b4129ab53..0000000000 --- a/packages/react-instantsearch-hooks-web/CHANGELOG.md +++ /dev/null @@ -1,2042 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [6.47.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.47.2...react-instantsearch-hooks-web@6.47.3) (2023-07-27) - - -### Bug Fixes - -* add a future warning when the package name changes ([#5778](https://github.com/algolia/instantsearch.js/issues/5778)) ([3d22ee4](https://github.com/algolia/instantsearch.js/commit/3d22ee45e1f03a443323a371621262f1fe45e664)) - - - - - -## [6.47.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.47.1...react-instantsearch-hooks-web@6.47.2) (2023-07-25) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.47.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.47.0...react-instantsearch-hooks-web@6.47.1) (2023-07-19) - - -### Bug Fixes - -* **instantsearch:** keep algoliasearch-helper as external dependency during build ([#5765](https://github.com/algolia/instantsearch.js/issues/5765)) ([550fefa](https://github.com/algolia/instantsearch.js/commit/550fefa1401773f38dedc20322513ae662faa25d)) - - - - - -# [6.47.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.46.0...react-instantsearch-hooks-web@6.47.0) (2023-07-18) - - -### Features - -* **react-instantsearch-hooks web:** rename `` translation ([#5756](https://github.com/algolia/instantsearch.js/issues/5756)) ([6c70035](https://github.com/algolia/instantsearch.js/commit/6c700350377d69a71b8ed44f70952d33ed81d085)) - - - - - -## [6.46.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.45.1...react-instantsearch-hooks-web@6.46.0) (2023-07-10) - - -### Bug Fixes - -* **url:** base createURL on UiState instead of SearchParameters ([#5696](https://github.com/algolia/instantsearch.js/issues/5696)) ([7e2c8a2](https://github.com/algolia/instantsearch.js/commit/7e2c8a295a6fc5ba36d9482f645ef55b422d5e75)), closes [#5694](https://github.com/algolia/instantsearch.js/issues/5694) - - - - - -## [6.45.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.45.0...react-instantsearch-hooks-web@6.45.1) (2023-07-04) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.45.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.3...react-instantsearch-hooks-web@6.45.0) (2023-06-20) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.44.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.2...react-instantsearch-hooks-web@6.44.3) (2023-06-13) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.44.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.1...react-instantsearch-hooks-web@6.44.2) (2023-06-05) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.44.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.0...react-instantsearch-hooks-web@6.44.1) (2023-05-30) - - -### Bug Fixes - -* **insights:** send default click event when using auxiliary pointer button ([#5634](https://github.com/algolia/instantsearch.js/issues/5634)) ([7e4a216](https://github.com/algolia/instantsearch.js/commit/7e4a2162f87596a384b35c97efa51db9bb6f8973)) - - - - - -## [6.43.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.43.0...react-instantsearch-hooks-web@6.43.1) (2023-05-16) - - -### Bug Fixes - -* **rangeinput:** allow input of numbers with precision ([#5541](https://github.com/algolia/instantsearch.js/issues/5541)) ([fb48951](https://github.com/algolia/instantsearch.js/commit/fb489513a8550528f3e2867be30fb380229ad188)) -* **this:** ensure all functions are able to be destructured ([#5611](https://github.com/algolia/instantsearch.js/issues/5611)) ([a8b5c1e](https://github.com/algolia/instantsearch.js/commit/a8b5c1e5bbd6afac39fce523f7d7c2ec02f51153)), closes [#5589](https://github.com/algolia/instantsearch.js/issues/5589) - - - - - -# [6.43.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.42.2...react-instantsearch-hooks-web@6.43.0) (2023-04-24) - - -### Bug Fixes - -* **lifecycle:** prevent extra network requests when unmounting multiple widgets ([#5602](https://github.com/algolia/instantsearch.js/issues/5602)) ([11458ee](https://github.com/algolia/instantsearch.js/commit/11458eee7e7f0f3e9c5f368584a16f58646b1cdd)) - - -### Features - - -* **insights:** add insights option to InstantSearch ([#5488](https://github.com/algolia/instantsearch.js/issues/5488)) ([9031573](https://github.com/algolia/instantsearch.js/commit/9031573807fa6803dcfae9f33d61b8f111f68423)) ([#5578](https://github.com/algolia/instantsearch.js/issues/5578)) ([8fb517f](https://github.com/algolia/instantsearch.js/commit/8fb517f15381ecb32ea00cf4b01a0fd5e70e1d17)) ([#5545](https://github.com/algolia/instantsearch.js/issues/5545)) ([99a0972](https://github.com/algolia/instantsearch.js/commit/99a0972663b8f3284cac3b5621571ced7a33908f)) ([#5493](https://github.com/algolia/instantsearch.js/issues/5493)) ([cff723f](https://github.com/algolia/instantsearch.js/commit/cff723fc95a90ebb2ed14c46c51ab05764835a47)) -* **insights:** always pass Algolia credentials locally ([#5554](https://github.com/algolia/instantsearch.js/issues/5554)) ([654ab81](https://github.com/algolia/instantsearch.js/commit/654ab81e1669354c249710b6756610fba35d54b4)) ([#5558](https://github.com/algolia/instantsearch.js/issues/5558)) ([82144c0](https://github.com/algolia/instantsearch.js/commit/82144c0a0b18e6b47d6f508e5c670a9de274c121)) ([#5529](https://github.com/algolia/instantsearch.js/issues/5529)) ([8537f8f](https://github.com/algolia/instantsearch.js/commit/8537f8f7a10bcaf053ff62180c082e077b1b052d)) -* **insights:** annotate events with algoliaSource ([#5580](https://github.com/algolia/instantsearch.js/issues/5580)) ([c419307](https://github.com/algolia/instantsearch.js/commit/c419307a5f7fe46d5032c9437a17c8e3dad57fe5)) -* **insights:** automatically load search-insights if not passed ([#5484](https://github.com/algolia/instantsearch.js/issues/5484)) ([a85797b](https://github.com/algolia/instantsearch.js/commit/a85797b503edc94e001c5bfb3b754db6cb556943)) -* **insights:** enable default click events on hits and infinite hits ([#5522](https://github.com/algolia/instantsearch.js/issues/5522)) ([271bd12](https://github.com/algolia/instantsearch.js/commit/271bd12e34bc55656976bb53c90282193083eb86)) ([#5527](https://github.com/algolia/instantsearch.js/issues/5527)) ([0e55821](https://github.com/algolia/instantsearch.js/commit/0e558213c807cd17d592fadec052f3d1fc692e6c)) -* **insights:** prevent potential errors ([#5487](https://github.com/algolia/instantsearch.js/issues/5487)) ([33fe510](https://github.com/algolia/instantsearch.js/commit/33fe510307e4b382a5ba1153a0eaf160420acd11)) ([#5606](https://github.com/algolia/instantsearch.js/issues/5606)) ([bdd9290](https://github.com/algolia/instantsearch.js/commit/bdd92901b59ae4e5d7311eadfbf53ed656bbaf4a)) ([#5512](https://github.com/algolia/instantsearch.js/issues/5512)) ([85dfbc9](https://github.com/algolia/instantsearch.js/commit/85dfbc9ebd722fbe6a7e1bd056950fdbcc16d8d9)) -* **metadata:** register metadata around middleware ([#5492](https://github.com/algolia/instantsearch.js/issues/5492)) ([3e72ec8](https://github.com/algolia/instantsearch.js/commit/3e72ec82894a05a071328a4802d2f764233fe005)) - - - - - -## [6.42.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.42.1...react-instantsearch-hooks-web@6.42.2) (2023-04-11) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.42.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.42.0...react-instantsearch-hooks-web@6.42.1) (2023-03-28) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.42.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.41.0...react-instantsearch-hooks-web@6.42.0) (2023-03-21) - - -### Bug Fixes - -* **searchbox:** add aria-hidden to svg icons ([#5547](https://github.com/algolia/instantsearch.js/issues/5547)) ([50344e3](https://github.com/algolia/instantsearch.js/commit/50344e3b14c22c886415c0e7d799aca778dc39ab)), closes [#5546](https://github.com/algolia/instantsearch.js/issues/5546) - - - - - -## [6.41.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.40.1...react-instantsearch-hooks-web@6.41.0) (2023-03-07) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.40.1...react-instantsearch-hooks-web@6.40.2) (2023-02-28) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.40.0...react-instantsearch-hooks-web@6.40.1) (2023-02-21) - - -### Bug Fixes - -* **breadcrumb:** guard against undefined facets ([#5482](https://github.com/algolia/instantsearch.js/issues/5482)) ([3159afe](https://github.com/algolia/instantsearch.js/commit/3159afe57472fe2b669dceb5f1ee638b658f7f52)) - - - - - -## [6.40.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.3...react-instantsearch-hooks-web@6.40.0) (2023-02-14) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.39.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.2...react-instantsearch-hooks-web@6.39.3) (2023-02-07) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.39.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.1...react-instantsearch-hooks-web@6.39.2) (2023-01-30) - - -### Bug Fixes - -* **infiniteHits:** read cache correctly when search is loading ([#5461](https://github.com/algolia/instantsearch.js/issues/5461)) ([bfabe00](https://github.com/algolia/instantsearch.js/commit/bfabe002a26e15e13b33200c355379f4e3c60f21)) - - - - - -## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.0...react-instantsearch-hooks-web@6.39.1) (2023-01-26) - - -### Bug Fixes - -* **dependencies:** update typescript ([#5454](https://github.com/algolia/instantsearch.js/issues/5454)) ([0e6bb48](https://github.com/algolia/instantsearch.js/commit/0e6bb485a31cd3294436ac9902c2c2662dfcdf8b)) -* **HierarchicalMenu:** don't give --parent class if data is empty ([#5458](https://github.com/algolia/instantsearch.js/issues/5458)) ([1d1a209](https://github.com/algolia/instantsearch.js/commit/1d1a209992e86b720939607cd22e37a04e865195)), closes [/github.com/algolia/instantsearch/blob/f84c01b2f66ac279f7e33fafe5f1cd559436edef/packages/instantsearch.js/src/components/RefinementList/RefinementList.tsx#L175-L179](https://github.com//github.com/algolia/instantsearch/blob/f84c01b2f66ac279f7e33fafe5f1cd559436edef/packages/instantsearch.js/src/components/RefinementList/RefinementList.tsx/issues/L175-L179) - - - - - -# [6.39.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.38.3...react-instantsearch-hooks-web@6.39.0) (2023-01-25) - - -### Features - -* **react-instantsearch-hooks-web:** Add stats widget and ui component ([#5427](https://github.com/algolia/instantsearch.js/issues/5427)) ([d07cf0d](https://github.com/algolia/instantsearch.js/commit/d07cf0d0310bf4e49d4a4c2142b3783d9bcda79d)) -* **rendering:** always render with current state ([#5429](https://github.com/algolia/instantsearch.js/issues/5429)) ([920e951](https://github.com/algolia/instantsearch.js/commit/920e951f03aada0e6a1ce16bc389a82a2f00b202)) -* **rendering:** revert search state on error ([#5438](https://github.com/algolia/instantsearch.js/issues/5438)) ([732fcac](https://github.com/algolia/instantsearch.js/commit/732fcac79ea1f51b19f62d5c4bf1fdf22619fa73)) - - - - - -## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.38.2...react-instantsearch-hooks-web@6.38.3) (2023-01-09) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.38.1...react-instantsearch-hooks-web@6.38.2) (2023-01-03) - -**Note:** Version bump only for package react-instantsearch-hooks-web - - - - - -## [6.38.1](https://github.com/algolia/react-instantsearch/compare/v6.38.0...v6.38.1) (2022-11-08) - - -### Bug Fixes - -* **hooks:** avoid effect in useStableValue ([#3670](https://github.com/algolia/react-instantsearch/issues/3670)) ([d1f53ae](https://github.com/algolia/react-instantsearch/commit/d1f53ae815b75f13c18fd245e0403d57e7ae391c)) - - - -# [6.38.0](https://github.com/algolia/react-instantsearch/compare/v6.37.0...v6.38.0) (2022-10-25) - - -### Features - -* **getServerState:** allow users to inject renderToString ([#3658](https://github.com/algolia/react-instantsearch/issues/3658)) ([9c10449](https://github.com/algolia/react-instantsearch/commit/9c104497b9b32337f288d70a2582c41cafb13cd6)), closes [#3633](https://github.com/algolia/react-instantsearch/issues/3633) [#3618](https://github.com/algolia/react-instantsearch/issues/3618) [vercel/next.js#40067](https://github.com/vercel/next.js/issues/40067) - -* **PoweredBy:** update component logo ([#3661](https://github.com/algolia/react-instantsearch/issues/3661)) ([69c1b2a](https://github.com/algolia/react-instantsearch/commit/69c1b2acef64d972dfa6c6beb8967032119ad2d5)) - - - -# [6.37.0](https://github.com/algolia/react-instantsearch/compare/v6.36.0...v6.37.0) (2022-10-18) - - -### Features - -* **core:** support react 18 strict mode ([#3653](https://github.com/algolia/react-instantsearch/issues/3653)) ([9174806](https://github.com/algolia/react-instantsearch/commit/9174806a7997a45893a24d149027119f4a0709c3)) - - - -# [6.36.0](https://github.com/algolia/react-instantsearch/compare/v6.35.0...v6.36.0) (2022-10-04) - - -### Features - -* **HierarchicalMenu:** add css class for link of selected menu item ([#3646](https://github.com/algolia/react-instantsearch/issues/3646)) ([980ad70](https://github.com/algolia/react-instantsearch/commit/980ad70c75e970c35c11a10a534dbe3996d6c54c)) -* **useInstantSearch:** expose status & error ([#3645](https://github.com/algolia/react-instantsearch/issues/3645)) ([f436d31](https://github.com/algolia/react-instantsearch/commit/f436d31184f3f75b33a1fdaa19c665e77948df28)) - - - -# [6.35.0](https://github.com/algolia/react-instantsearch/compare/v6.34.0...v6.35.0) (2022-09-29) - - -### Features - -* **hooks-web:** introduce Translations API ([#3638](https://github.com/algolia/react-instantsearch/issues/3638)) ([63b506f](https://github.com/algolia/react-instantsearch/commit/63b506f9dbad284f45ac17210e17c4a2a8f099b6)) - -### Fixes -* **hooks-web:** when searchAsYouType=false, pressing the reset button searches (previously only reset the query) ([#3642](https://github.com/algolia/react-instantsearch/issues/3642)) ([f969deb](https://github.com/algolia/react-instantsearch/commit/f969deb05fd4f53aaa251ff88b52db2224ce0786)) - -# [6.34.0](https://github.com/algolia/react-instantsearch/compare/v6.33.0...v6.34.0) (2022-09-27) - - -### Features - -* **SearchBox:** expose formRef ([#3565](https://github.com/algolia/react-instantsearch/issues/3565)) ([1c2f46d](https://github.com/algolia/react-instantsearch/commit/1c2f46da2d1cf705cfd3946c52aef4ca9ec943d7)) - - - -# [6.33.0](https://github.com/algolia/react-instantsearch/compare/v6.32.1...v6.33.0) (2022-09-15) - - -### Bug Fixes - -* **react-native:** mark as compatible with react 18 ([#3614](https://github.com/algolia/react-instantsearch/issues/3614)) ([2a191a8](https://github.com/algolia/react-instantsearch/commit/2a191a84751127de5a3eb34b59b460a1d1bfe594)) -* **rish:** hide reset button when search is stalled in `SearchBox` ([#3617](https://github.com/algolia/react-instantsearch/issues/3617)) ([93ee9d0](https://github.com/algolia/react-instantsearch/commit/93ee9d0212893cef4842c86b7c78f285aa136be8)) - - -### Features - -* **dependencies:** update instantsearch and helper ([#3622](https://github.com/algolia/react-instantsearch/issues/3622)) ([6773ab1](https://github.com/algolia/react-instantsearch/commit/6773ab169cd74dfe1133e255daade4d57e99d9a4)) - - - -## [6.32.1](https://github.com/algolia/react-instantsearch/compare/v6.32.0...v6.32.1) (2022-08-26) - - -### Bug Fixes - -* **useInstantSearch:** prevent `results` from being `null` when first search is stalled ([#3597](https://github.com/algolia/react-instantsearch/issues/3597)) ([6f71d78](https://github.com/algolia/react-instantsearch/commit/6f71d78868fde55a3f9c4460edc337a1e99df4f9)) - - - -# [6.32.0](https://github.com/algolia/react-instantsearch/compare/v6.31.1...v6.32.0) (2022-08-22) - - -### Features - -* **SearchBox:** introduce `autoFocus` prop ([#3599](https://github.com/algolia/react-instantsearch/issues/3599)) ([99121b9](https://github.com/algolia/react-instantsearch/commit/99121b952fd002cb6dae52af41f08beed8f6c3e2)) - - - -## [6.31.1](https://github.com/algolia/react-instantsearch/compare/v6.31.0...v6.31.1) (2022-08-08) - - -### Bug Fixes - -* **hooks:** prevent widget cleanup on `` unmount ([#3590](https://github.com/algolia/react-instantsearch/issues/3590)) ([d94899d](https://github.com/algolia/react-instantsearch/commit/d94899d1264134f0cb1ca2d266a660f1fb2a588c)) - - - -# [6.31.0](https://github.com/algolia/react-instantsearch/compare/v6.30.3...v6.31.0) (2022-08-03) - - -### Features - -* **hooks:** add `searchAsYouType` option to `` ([#3585](https://github.com/algolia/react-instantsearch/issues/3585)) ([c610385](https://github.com/algolia/react-instantsearch/commit/c610385cb9688b23b3e041e31b9edd60392b341d)) - - - -## [6.30.3](https://github.com/algolia/react-instantsearch/compare/v6.30.2...v6.30.3) (2022-08-01) - - -### Bug Fixes - -* **hooks:** replace `labelText` CSS class with `label` in `` to align with InstantSearch's CSS specifications ([#3583](https://github.com/algolia/react-instantsearch/issues/3583)) ([3e030ae](https://github.com/algolia/react-instantsearch/commit/3e030aedb9f285ff449eb82589bc6fea60b160cb)) - - - -## [6.30.2](https://github.com/algolia/react-instantsearch/compare/v6.30.1...v6.30.2) (2022-07-18) - - -### Bug Fixes - -* **hooks:** type of DynamicWidgets props ([#3566](https://github.com/algolia/react-instantsearch/issues/3566)) ([612c98b](https://github.com/algolia/react-instantsearch/commit/612c98b5a77fb9037185c4b5efda8c07663dbd1a)), closes [#3563](https://github.com/algolia/react-instantsearch/issues/3563) -* **hooks:** use single instance in ([#3561](https://github.com/algolia/react-instantsearch/issues/3561)) ([4c358be](https://github.com/algolia/react-instantsearch/commit/4c358bebfc91451b1610f677f89c595d7a427f1f)) - - - -## [6.30.1](https://github.com/algolia/react-instantsearch/compare/v6.30.0...v6.30.1) (2022-07-12) - - -### Bug Fixes - -* **hooks:** provide state and results APIs from "render" event ([#3554](https://github.com/algolia/react-instantsearch/issues/3554)) ([67d4788](https://github.com/algolia/react-instantsearch/commit/67d4788ab09ec2a57b43d53e8093b8c11120b761)) -* **hooks**: update instantsearch.js dependency ([#3557](https://github.com/algolia/react-instantsearch/issues/3557)) - - - -# [6.30.0](https://github.com/algolia/react-instantsearch/compare/v6.29.0...v6.30.0) (2022-07-05) - - -### Features - -* **core:** update instantsearch and helper ([#3539](https://github.com/algolia/react-instantsearch/issues/3539)) ([0ac2c7a](https://github.com/algolia/react-instantsearch/commit/0ac2c7a3f2e2a827721f5b2b7c69c54560f8574f)) - - - -# [6.29.0](https://github.com/algolia/react-instantsearch/compare/v6.28.0...v6.29.0) (2022-06-21) - - -### Bug Fixes - -* **HierarchicalMenu:** show full hierarchical parent values ([#3521](https://github.com/algolia/react-instantsearch/issues/3521)) ([79c3890](https://github.com/algolia/react-instantsearch/commit/79c3890848175a4d70311e5c3929c902bb953c10)) - - -### Features - -* **core:** sort parameters for improved cache rate ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) -* **core:** support client.search for sffv ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) - - - -# [6.28.0](https://github.com/algolia/react-instantsearch/compare/v6.27.0...v6.28.0) (2022-06-15) - - -### Bug Fixes - -* **hooks-server:** import react server via an expression ([#3515](https://github.com/algolia/react-instantsearch/issues/3515)) ([91b96f7](https://github.com/algolia/react-instantsearch/commit/91b96f743b9315ed5ea781681b77fc7f5604ab6e)), closes [#3512](https://github.com/algolia/react-instantsearch/issues/3512) -* **hooks-web:** fix duplicated key in ([#3513](https://github.com/algolia/react-instantsearch/issues/3513)) ([fc94d80](https://github.com/algolia/react-instantsearch/commit/fc94d806daf139f58b234cdc0b450da2efe861ee)) -* **hooks:** mount widgets in SSR to retrieve HTML ([#3518](https://github.com/algolia/react-instantsearch/issues/3518)) ([aa5f9d8](https://github.com/algolia/react-instantsearch/commit/aa5f9d84ddb6e97d05e6ad1baf2c6caa23891281)) -* **types:** allow useInstantSearch to be generic ([#3508](https://github.com/algolia/react-instantsearch/issues/3508)) ([6807232](https://github.com/algolia/react-instantsearch/commit/68072324cf302801502a1b4c3d06703e57b55a97)), closes [algolia/instantsearch.js#5060](https://github.com/algolia/instantsearch.js/issues/5060) -* **types:** support React 18 types ([#3481](https://github.com/algolia/react-instantsearch/issues/3481)) ([74cf8cb](https://github.com/algolia/react-instantsearch/commit/74cf8cb9be8ff3d113b57a50e7083df0d1bc94f2)) - - -### Features - -* **hooks:** introduce `useInstantSearch()` ([#3494](https://github.com/algolia/react-instantsearch/issues/3494)) ([74d522c](https://github.com/algolia/react-instantsearch/commit/74d522c032326658f2a0b8f0001bd593e0085208)) -* **hooks:** support React 18 Strict Mode ([#3514](https://github.com/algolia/react-instantsearch/issues/3514)) ([eeb67c7](https://github.com/algolia/react-instantsearch/commit/eeb67c7b5dc08c696c46d9538f104eeceecef388)) - - - -# [6.27.0](https://github.com/algolia/react-instantsearch/compare/v6.26.0...v6.27.0) (2022-06-07) - - -### Bug Fixes - -* **hooks-web:** don't pass widget props to ui components ([#3501](https://github.com/algolia/react-instantsearch/issues/3501)) ([5bd53c1](https://github.com/algolia/react-instantsearch/commit/5bd53c128ddeeea87f75ae89eb8f2324d476c70e)), closes [#3499](https://github.com/algolia/react-instantsearch/issues/3499) -* **SearchBox-hooks:** correctly pass widget props ([#3499](https://github.com/algolia/react-instantsearch/issues/3499)) ([2cdf906](https://github.com/algolia/react-instantsearch/commit/2cdf90602b7c2c5895124ef64c389ce574154386)), closes [#3498](https://github.com/algolia/react-instantsearch/issues/3498) - - -### Features - -* **hooks:** migrate to `useSyncExternalStore()` ([#3489](https://github.com/algolia/react-instantsearch/issues/3489)) ([81bbdf2](https://github.com/algolia/react-instantsearch/commit/81bbdf28f2d28d8b0081cfd7d9e84c3e33038dd2)) -* **hooks:** upgrade to InstantSearch.js 4.41.0 ([#3502](https://github.com/algolia/react-instantsearch/issues/3502)) ([0b76792](https://github.com/algolia/react-instantsearch/commit/0b76792ea0c4e2ac9fe742810d70ba1aee2a3e79)) - - - -# [6.26.0](https://github.com/algolia/react-instantsearch/compare/v6.25.0...v6.26.0) (2022-05-19) - - -### Features - -* **hooks-web:** expose `sendEvent` to `hitComponent` ([#3476](https://github.com/algolia/react-instantsearch/issues/3476)) ([5cc18d1](https://github.com/algolia/react-instantsearch/commit/5cc18d19d9f22305f33d92e43fd0aca2a5cb949a)) -* **react-instantsearch-core:** allow widgets to set their `$$widgetType` ([#3472](https://github.com/algolia/react-instantsearch/issues/3472)) ([1c436e1](https://github.com/algolia/react-instantsearch/commit/1c436e1429ab4230bbfea7c6d2474d141f5c5c64)) - - - -# [6.25.0](https://github.com/algolia/react-instantsearch/compare/v6.24.3...v6.25.0) (2022-05-17) - - -### Bug Fixes - -* **hooks-highlight:** make sure highlight and snippet don't show html-escaped content ([#3471](https://github.com/algolia/react-instantsearch/issues/3471)) ([c18ddd2](https://github.com/algolia/react-instantsearch/commit/c18ddd25faca37d6bfa3d1c28f6fc22ec5fcf6d8)) -* **hooks-server:** remove faulty UMD build ([#3465](https://github.com/algolia/react-instantsearch/issues/3465)) ([c1ddfe4](https://github.com/algolia/react-instantsearch/commit/c1ddfe408b411551ac8524877a9d65ded8133c42)) - - -### Features - -* **dom-maps:** expose GeoSearchContext ([#3468](https://github.com/algolia/react-instantsearch/issues/3468)) ([a61ff96](https://github.com/algolia/react-instantsearch/commit/a61ff96222bfd4f6301cf93bf95e2fa18b263d3c)), closes [#3448](https://github.com/algolia/react-instantsearch/issues/3448) -* **hooks-server:** support import from React 18 ([#3464](https://github.com/algolia/react-instantsearch/issues/3464)) ([0a13867](https://github.com/algolia/react-instantsearch/commit/0a13867f7dd5a8a18e0957b2072bbde3b02d6490)), closes [#3453](https://github.com/algolia/react-instantsearch/issues/3453) -* **hooks:** make InstantSearch type generic ([#3466](https://github.com/algolia/react-instantsearch/issues/3466)) ([b0905b7](https://github.com/algolia/react-instantsearch/commit/b0905b73bed558c62eedb7ae701be20c2ebe25c9)) - - - -## [6.24.3](https://github.com/algolia/react-instantsearch/compare/v6.24.2...v6.24.3) (2022-05-10) - - -### Bug Fixes - -* **numericmenu:** include range values in comparison with minmax bounds ([#3461](https://github.com/algolia/react-instantsearch/issues/3461)) ([e4c2682](https://github.com/algolia/react-instantsearch/commit/e4c268261dc42a6aa43d985934b53c82f8b71089)) - - - -## [6.24.2](https://github.com/algolia/react-instantsearch/compare/v6.24.1...v6.24.2) (2022-05-05) - - -### Bug Fixes - -* **hooks:** prevent infinite loops from render state ([#3455](https://github.com/algolia/react-instantsearch/issues/3455)) ([1799fc9](https://github.com/algolia/react-instantsearch/commit/1799fc9f78a4a5aafb54df339c3e211ff9187748)) - - - -## [6.24.1](https://github.com/algolia/react-instantsearch/compare/v6.24.0...v6.24.1) (2022-05-03) - - -### Bug Fixes - -* **types:** export correct types for react-instantsearch-hooks-web ([#3454](https://github.com/algolia/react-instantsearch/issues/3454)) ([a863430](https://github.com/algolia/react-instantsearch/commit/a8634306621f7a05a2b3056a6db25ccf8d9eabf0)) - - - -# [6.24.0](https://github.com/algolia/react-instantsearch/compare/v6.23.4...v6.24.0) (2022-04-28) - - -### Features - -* **hooks:** expose DOM components ([#3450](https://github.com/algolia/react-instantsearch/issues/3450)) ([5732e3d](https://github.com/algolia/react-instantsearch/commit/5732e3de732275911f94b26ba9e2c4165bdf77e7)) -* **hooks:** remove experimental warning ([#3446](https://github.com/algolia/react-instantsearch/issues/3446)) ([84c99fe](https://github.com/algolia/react-instantsearch/commit/84c99fe91d6906a877bec620b44c61d762f0ea57)) - - - -## [6.23.4](https://github.com/algolia/react-instantsearch/compare/v6.23.3...v6.23.4) (2022-04-21) - - -### Bug Fixes - -* **hooks:** forbid importing from instantsearch.js root path ([#3437](https://github.com/algolia/react-instantsearch/issues/3437)) ([82ef9ea](https://github.com/algolia/react-instantsearch/commit/82ef9eaaec42bc54f15796b5b031a8656330a45c)) -* **packages:** correctly mark peer dependency ([#3439](https://github.com/algolia/react-instantsearch/issues/3439)) ([51e8818](https://github.com/algolia/react-instantsearch/commit/51e8818fce224819230c8bf6dea2a08d71d9be29)), closes [#3428](https://github.com/algolia/react-instantsearch/issues/3428) - - - -## [6.23.3](https://github.com/algolia/react-instantsearch/compare/v6.23.2...v6.23.3) (2022-04-05) - - -### Bug Fixes - -* **facets:** show raw value in currentRefinements ([#3420](https://github.com/algolia/react-instantsearch/issues/3420)) ([1199ce6](https://github.com/algolia/react-instantsearch/commit/1199ce6bd4152e4b54bd2ee0e1f0c9d81021c7d5)), closes [#3412](https://github.com/algolia/react-instantsearch/issues/3412) -* **multi-index:** correctly set `searching` prop in multi-index result states ([#3419](https://github.com/algolia/react-instantsearch/issues/3419)) ([7f8e97e](https://github.com/algolia/react-instantsearch/commit/7f8e97e31b3dd5e49d3febef673f41f7dfa6d45d)) - - - -## [6.23.2](https://github.com/algolia/react-instantsearch/compare/v6.23.1...v6.23.2) (2022-04-04) - - -### Bug Fixes - -* **refinements:** use escaped value for refining ([#3412](https://github.com/algolia/react-instantsearch/issues/3412)) ([f2f5f6c](https://github.com/algolia/react-instantsearch/commit/f2f5f6cbfaed48a5c494daeb8789e8deebc64293)) -* support React 18 as peer dependency ([#3411](https://github.com/algolia/react-instantsearch/issues/3411)) ([38eb5a6](https://github.com/algolia/react-instantsearch/commit/38eb5a6a8fe92cb763a25f452bea78b189a6a82a)) - - -## [6.23.1](https://algolia/compare/v6.23.0...v6.23.1) (2022-03-30) - - -### Bug Fixes - -* **hooks:** compute initial search parameters from widget ([#3399](https://algolia/issues/3399)) ([b4bd933](https://algolia/commits/b4bd93358598bdc77a1aa858252e6eee23441345)) - - - -# [6.23.0](https://algolia/compare/v6.22.0...v6.23.0) (2022-03-23) - - -### Bug Fixes - -* **ssr:** perform initial multi-index search using a single request ([#3385](https://algolia/issues/3385)) ([b96809e](https://algolia/commits/b96809e5748d298350890647956cb7adbbb55650)) - - -### Features - -* **hooks:** allow additional widget properties to be passed from hooks ([#3359](https://algolia/issues/3359)) ([a047be4](https://algolia/commits/a047be45c7fce7ee28f7d6f61d2fbfa79e3ed2d0)) -* **hooks:** allow useHits and useInfiniteHit to be generic ([#3364](https://algolia/issues/3364)) ([8e66ad3](https://algolia/commits/8e66ad3ad587197c4811c60a5cab475137ca5afb)) -* **hooks:** mark initial results as "artificial" ([#3384](https://algolia/issues/3384)) ([937efdc](https://algolia/commits/937efdc71bae1d99270f8ecb5c5c9c501b3d7769)) - - - -# [6.22.0](https://github.com/algolia/react-instantsearch/compare/v6.21.1...v6.22.0) (2022-02-01) - - -### Bug Fixes - -* **hooks:** enable pause on exceptions on warning ([#3283](https://github.com/algolia/react-instantsearch/issues/3283)) ([ce3a6c3](https://github.com/algolia/react-instantsearch/commit/ce3a6c3f2700a05ae54a91278fd23fa64a97d733)) - - -### Features - -* **hooks:** introduce `useBreadcrumb()` ([#3245](https://github.com/algolia/react-instantsearch/issues/3245)) ([5bfbaaf](https://github.com/algolia/react-instantsearch/commit/5bfbaaf746e7632968ac9b30b73357bcb0b37e91)) - - - -## [6.21.1](https://github.com/algolia/react-instantsearch/compare/v6.21.0...v6.21.1) (2022-01-25) - - -### Bug Fixes - -* **hooks:** apply initial search parameters in useConnector ([#3276](https://github.com/algolia/react-instantsearch/issues/3276)) ([f85d679](https://github.com/algolia/react-instantsearch/commit/f85d6794c31eac61521fd8fc16b75673f02ed75b)) - - - -# [6.21.0](https://github.com/algolia/react-instantsearch/compare/v6.20.0...v6.21.0) (2022-01-24) - - -### Features - -* **hooks-server:** load data twice in the case of dynamic widget usage ([#3259](https://github.com/algolia/react-instantsearch/issues/3259)) ([9b4903b](https://github.com/algolia/react-instantsearch/commit/9b4903b2ea73d9d7e33729b87d9d55990e64312c)) -* **server:** load data twice in the case of dynamic widget usage ([#3268](https://github.com/algolia/react-instantsearch/issues/3268)) ([91cd085](https://github.com/algolia/react-instantsearch/commit/91cd085f9a323ed6b872f3a098f561007a72d0d2)), closes [/github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js#L14-L16](https://github.com//github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js/issues/L14-L16) - - - -# [6.20.0](https://github.com/algolia/react-instantsearch/compare/v6.19.0...v6.20.0) (2022-01-18) - - -### Bug Fixes - -* **hooks:** allow importing via require ([#3257](https://github.com/algolia/react-instantsearch/issues/3257)) ([6aa80b3](https://github.com/algolia/react-instantsearch/commit/6aa80b3647567199c3df1b90a07d708b223ce1fd)) - - -### Features - -* **hooks:** implement `useClearRefinements()` ([#3256](https://github.com/algolia/react-instantsearch/issues/3256)) ([930b83d](https://github.com/algolia/react-instantsearch/commit/930b83df4c4bbccbc3118f6ea1001f6a30a3d464)), closes [#3252](https://github.com/algolia/react-instantsearch/issues/3252) -* **hooks:** introduce `` ([#3261](https://github.com/algolia/react-instantsearch/issues/3261)) ([3527b94](https://github.com/algolia/react-instantsearch/commit/3527b9422de48a4a6b4bd752bd643e01cd5011d8)) -* **hooks:** introduce `usePoweredBy()` ([#3251](https://github.com/algolia/react-instantsearch/issues/3251)) ([a97230b](https://github.com/algolia/react-instantsearch/commit/a97230b89e3ba1df9bf2d21a6a9f9a70b45f41b8)) -* **hooks:** introduce `useToggleRefinement()` ([#3248](https://github.com/algolia/react-instantsearch/issues/3248)) ([a561c09](https://github.com/algolia/react-instantsearch/commit/a561c090a268e1c6ca1fa2d5bf72263cc47aa273)) - - - -# [6.19.0](https://github.com/algolia/react-instantsearch/compare/v6.18.0...v6.19.0) (2022-01-05) - - -### Features - -* **hooks:** bundle as es-module ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) -* **dependencies:** update to latest algoliasearch-helper ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) - - - -# [6.18.0](https://github.com/algolia/react-instantsearch/compare/v6.17.0...v6.18.0) (2021-12-16) - - -### Features - -* **dynamicWidgets:** send facets * and maxValuesPerFacet by default ([#3242](https://github.com/algolia/react-instantsearch/issues/3242)) ([c071776](https://github.com/algolia/react-instantsearch/commit/c07177670ac30dced55250774654e8b2a77b6739)) -* **hooks:** introduce `useNumericMenu()` ([#3237](https://github.com/algolia/react-instantsearch/issues/3237)) ([e3056c9](https://github.com/algolia/react-instantsearch/commit/e3056c9e2c64b5afafb7a736599a5cbf6137575b)) -* **hooks:** introduce `useInfiniteHits()` ([#3224](https://github.com/algolia/react-instantsearch/issues/3224)) ([177ec56](https://github.com/algolia/react-instantsearch/commit/177ec56af274670c2bf8599ba104b5544979bbe8)) - - - -# [6.17.0](https://github.com/algolia/react-instantsearch/compare/v6.16.0...v6.17.0) (2021-12-08) - - -### Bug Fixes - -* **hooks:** throw invariant violations in production ([#3217](https://github.com/algolia/react-instantsearch/issues/3217)) ([6d3f99c](https://github.com/algolia/react-instantsearch/commit/6d3f99ca91f470ee742ddc55e95f57b1f1801d7b)) - - -### Features - -* **hooks:** introduce `useMenu()` ([#3197](https://github.com/algolia/react-instantsearch/issues/3197)) ([15d1cc9](https://github.com/algolia/react-instantsearch/commit/15d1cc993437b111cd5a32f43ee1d2065c639ed4)) -* **hooks:** introduce `useRange()` ([#3198](https://github.com/algolia/react-instantsearch/issues/3198)) ([df1f1c8](https://github.com/algolia/react-instantsearch/commit/df1f1c8109dc684e74d3aee1bf0359f2a0e1b9f4)) -* **hooks:** introduce `` ([#3216](https://github.com/algolia/react-instantsearch/issues/3216)) ([d99aea6](https://github.com/algolia/react-instantsearch/commit/d99aea6cfe9dea86ae6b98ee3762373f4b3843f1)) -* **hooks:** introduce `useDynamicWidgets()` ([#3210](https://github.com/algolia/react-instantsearch/issues/3210)) ([29c2ea2](https://github.com/algolia/react-instantsearch/commit/29c2ea22b91a39d9eb40a044ae9aab85f2612db8)) -* **hooks:** introduce `useQueryRules()` ([#3212](https://github.com/algolia/react-instantsearch/issues/3212)) ([3ef1e1e](https://github.com/algolia/react-instantsearch/commit/3ef1e1e4116b3e58b2c2134d0c60fbb9f40c1501)) -* **hooks:** introduce SSR support ([#3221](https://github.com/algolia/react-instantsearch/issues/3221)) ([0a6b3ec](https://github.com/algolia/react-instantsearch/commit/0a6b3ec61942ad3849c6f078e21b3328679bffff)) -* **hooks:** introduce `useCurrentRefinements()` ([#3222](https://github.com/algolia/react-instantsearch/issues/3222)) ([7ebd8c3](https://github.com/algolia/react-instantsearch/commit/7ebd8c3da8c17b0bd7e0f8deab633b98fa052e7f)) - - - -# [6.16.0](https://github.com/algolia/react-instantsearch/compare/v6.15.0...v6.16.0) (2021-11-22) - - -### Bug Fixes - -* **PoweredBy:** support environments with `window` but no `location` ([#3186](https://github.com/algolia/react-instantsearch/issues/3186)) ([22ff23b](https://github.com/algolia/react-instantsearch/commit/22ff23b67554683567393114c2f53cacec44f4a6)) - - -### Features - -* **DynamicWidgets:** release as stable ([#3090](https://github.com/algolia/react-instantsearch/issues/3090)) ([a4a1d9e](https://github.com/algolia/react-instantsearch/commit/a4a1d9e032b31c611d5d73fdda3a03ad705f5c68)) -* **hooks:** introduce `usePagination()` ([#3182](https://github.com/algolia/react-instantsearch/issues/3182)) ([d8b1b86](https://github.com/algolia/react-instantsearch/commit/d8b1b867bb598e801f1350e81b4a4220a8e528d7)) -* **hooks:** introduce `useSortBy()` ([#3190](https://github.com/algolia/react-instantsearch/issues/3190)) ([5cce33b](https://github.com/algolia/react-instantsearch/commit/5cce33b48032548fed76b592ee0201e3c42fc3c4)) -* **hooks:** introduce `useHierarchicalMenu()` ([#3199](https://github.com/algolia/react-instantsearch/issues/3199)) ([b347061](https://github.com/algolia/react-instantsearch/commit/b3470611b962ef55c55576c65a6307abc54e5efd)) - - - -# [6.15.0](https://github.com/algolia/react-instantsearch/compare/v6.14.0...v6.15.0) (2021-10-27) - - -### Bug Fixes - -* **metadata:** stricter detection of user agent ([#3184](https://github.com/algolia/react-instantsearch/issues/3184)) ([994c8ae](https://github.com/algolia/react-instantsearch/commit/994c8ae055fc23a1a067d111d2f4727b1c7bf8ca)) - - -### Features - -* **hooks:** introduce `useConfigure()` ([#3181](https://github.com/algolia/react-instantsearch/issues/3181)) ([aa2eb9b](https://github.com/algolia/react-instantsearch/commit/aa2eb9baec6335f8a3523ee8b9b761a217cfc734)) - - - -# [6.14.0](https://github.com/algolia/react-instantsearch/compare/v6.13.0...v6.14.0) (2021-10-26) - - -### Features - -* **dependencies:** update algoliasearch-helper ([#3176](https://github.com/algolia/react-instantsearch/issues/3176)) ([a8708a3](https://github.com/algolia/react-instantsearch/commit/a8708a33f31632000bc827b076539b1cca7adf6f)) -* **metadata:** expose widget information ([#3145](https://github.com/algolia/react-instantsearch/issues/3145)) ([46cddf8](https://github.com/algolia/react-instantsearch/commit/46cddf8fcc0291beaa34b04da7aaaa7f2566e52e)) - - - -# [6.13.0](https://github.com/algolia/react-instantsearch/compare/v6.12.1...v6.13.0) (2021-10-19) - -This is the initial release of the experimental **React InstantSearch Hooks** package. Check out the [**Getting Started**](https://github.com/algolia/react-instantsearch/blob/e8d72cb1c7c45300ef7c273f1f163beb6dc57622/packages/react-instantsearch-hooks/README.md#getting-started) guide. - -### Bug Fixes - -- **core:** accept objects for `hitComponent` ([#3087](https://github.com/algolia/react-instantsearch/issues/3087)) ([4ae23d4](https://github.com/algolia/react-instantsearch/commit/4ae23d432a01ccbefff1fcdc865120aeca4d3efc)) - -### Features - -- **hooks:** add InstantSearch and Index components ([#3133](https://github.com/algolia/react-instantsearch/issues/3133)) ([8e3370d](https://github.com/algolia/react-instantsearch/commit/8e3370ddb7d5e42b8b9a5ff6a1e4255490de7dbe)) -- **hooks:** bootstrap Core package ([#3132](https://github.com/algolia/react-instantsearch/issues/3132)) ([d459e62](https://github.com/algolia/react-instantsearch/commit/d459e62f5cae4c98427ab302531873f5ee23d149)) -- **hooks:** display experimental warning ([#3149](https://github.com/algolia/react-instantsearch/issues/3149)) ([623577c](https://github.com/algolia/react-instantsearch/commit/623577c50cd0c04cd87f719edafdcfa04b5527b6)) -- **hooks:** export types ([#3159](https://github.com/algolia/react-instantsearch/issues/3159)) ([182348b](https://github.com/algolia/react-instantsearch/commit/182348b4a901823a7a41aee5d2b3bdc025cce48f)) -- **hooks:** expose `displayName` on Contexts ([#3168](https://github.com/algolia/react-instantsearch/issues/3168)) ([dafd3c6](https://github.com/algolia/react-instantsearch/commit/dafd3c66d05fbec09ebf907209ac25f55804e6f5)) -- **hooks:** friendly error when using Hooks with Core ([#3150](https://github.com/algolia/react-instantsearch/issues/3150)) ([d547ccf](https://github.com/algolia/react-instantsearch/commit/d547ccf7951299e2f6b1771e02fce052696ff65a)) -- **hooks:** introduce `useConnector()` ([#3137](https://github.com/algolia/react-instantsearch/issues/3137)) ([53e8afd](https://github.com/algolia/react-instantsearch/commit/53e8afd093b9950351467a16b82d528207ac34d2)) -- **hooks:** introduce `useHits()` ([#3147](https://github.com/algolia/react-instantsearch/issues/3147)) ([cc25cff](https://github.com/algolia/react-instantsearch/commit/cc25cff06e5ec216cba40fb8261372bc327001b6)) -- **hooks:** introduce `useRefinementList()` ([#3152](https://github.com/algolia/react-instantsearch/issues/3152)) ([0385cd9](https://github.com/algolia/react-instantsearch/commit/0385cd971635a8423ad687fab451d0778358389e)) -- **hooks:** introduce `useSearchBox()` ([#3146](https://github.com/algolia/react-instantsearch/issues/3146)) ([0d2c7f9](https://github.com/algolia/react-instantsearch/commit/0d2c7f9bd25b88cf725a1babd3b228ac804644c7)) -- **hooks:** trigger single network request on load ([#3167](https://github.com/algolia/react-instantsearch/issues/3167)) ([ff1ea49](https://github.com/algolia/react-instantsearch/commit/ff1ea49079a7800fd61ba99ceeb74b9f513eb99d)) -- **hooks:** type `useConnector()` return as render state ([#3169](https://github.com/algolia/react-instantsearch/issues/3169)) ([a801468](https://github.com/algolia/react-instantsearch/commit/a80146860164a092d2c90ee0aa4fcef88d5b675f)) -- **hooks:** update GitHub bug reports link ([#3157](https://github.com/algolia/react-instantsearch/issues/3157)) ([568b5c8](https://github.com/algolia/react-instantsearch/commit/568b5c83849a3927417907706656c3835163f216)) - - - -## [6.12.1](https://github.com/algolia/react-instantsearch/compare/v6.12.0...v6.12.1) (2021-08-02) - - -### Bug Fixes - -* **server side rendering:** return a value from mock currentRefinement/metadata ([#3078](https://github.com/algolia/react-instantsearch/issues/3078)) ([09f802b](https://github.com/algolia/react-instantsearch/commit/09f802b)) - - - -# [6.12.0](https://github.com/algolia/react-instantsearch/compare/v6.11.2...v6.12.0) (2021-07-06) - - -### Bug Fixes - -* **HitsPerPage:** Adds id prop to HitsPerPage, Select components ([#3072](https://github.com/algolia/react-instantsearch/issues/3072)) ([bc75d75](https://github.com/algolia/react-instantsearch/commit/bc75d75)) -* **MenuSelect:** Adds id prop to MenuSelect ([#3073](https://github.com/algolia/react-instantsearch/issues/3073)) ([fddaaef](https://github.com/algolia/react-instantsearch/commit/fddaaef)) -* **SearchBox:** Adds inputId prop to SearchBox ([#3074](https://github.com/algolia/react-instantsearch/issues/3074)) ([a05f6a4](https://github.com/algolia/react-instantsearch/commit/a05f6a4)) -* **SortBy:** Adds `id` prop to `SortBy`, `Select` components ([#3068](https://github.com/algolia/react-instantsearch/issues/3068)) ([1f2797f](https://github.com/algolia/react-instantsearch/commit/1f2797f)) - - -### Features - -* **DynamicWidgets:** add implementation ([#3056](https://github.com/algolia/react-instantsearch/issues/3056)) ([691ef87](https://github.com/algolia/react-instantsearch/commit/691ef87)) -* **facets:** add a new option "facetOrdering" to Menu, RefinementList & HierarchicalMenu ([#3067](https://github.com/algolia/react-instantsearch/issues/3067)) ([731d9ba](https://github.com/algolia/react-instantsearch/commit/731d9ba)) - - - -## [6.11.2](https://github.com/algolia/react-instantsearch/compare/v6.11.1...v6.11.2) (2021-06-28) - - -### Bug Fixes - -* **maps:** leave zoom in place if the bounds did not change ([#3050](https://github.com/algolia/react-instantsearch/issues/3050)) ([c430598](https://github.com/algolia/react-instantsearch/commit/c430598)) - - - -## [6.11.1](https://github.com/algolia/react-instantsearch/compare/v6.11.0...v6.11.1) (2021-06-09) - - -### Bug Fixes - -* **RefinementList:** prevent searchable component to refine on empty list ([#3059](https://github.com/algolia/react-instantsearch/issues/3059)) ([04f3644](https://github.com/algolia/react-instantsearch/commit/04f3644)) - - - -# [6.11.0](https://github.com/algolia/react-instantsearch/compare/v6.10.3...v6.11.0) (2021-05-04) - - -### Features - -* **connectNumericMenu:** add support for floating point values ([#3047](https://github.com/algolia/react-instantsearch/issues/3047)) ([091bf57](https://github.com/algolia/react-instantsearch/commit/091bf57)) - - - -## [6.10.3](https://github.com/algolia/react-instantsearch/compare/v6.10.2...v6.10.3) (2021-03-03) - - -### Bug Fixes - -* **RelevantSort:** Rename `SmartSort` widget to `RelevantSort` ([#3026](https://github.com/algolia/react-instantsearch/issues/3026)) ([47d11bf](https://github.com/algolia/react-instantsearch/commit/47d11bf)) - - - -## [6.10.2](https://github.com/algolia/react-instantsearch/compare/v6.10.1...v6.10.2) (2021-03-03) - - -### Bug Fixes - -* **infiniteHits:** fix stale hits issue ([#3021](https://github.com/algolia/react-instantsearch/issues/3021)) ([a9a29c7](https://github.com/algolia/react-instantsearch/commit/a9a29c7)) - - - -## [6.10.1](https://github.com/algolia/react-instantsearch/compare/v6.10.0...v6.10.1) (2021-03-02) - - -### Bug Fixes - -* **SmartSort:** make `textComponent` and `buttonTextComponent` optional ([#3014](https://github.com/algolia/react-instantsearch/issues/3014)) ([682ee6d](https://github.com/algolia/react-instantsearch/commit/682ee6d)) - - - -# [6.10.0](https://github.com/algolia/react-instantsearch/compare/v6.9.0...v6.10.0) (2021-02-23) - - -### Bug Fixes - -* **infiniteHits:** do not cache the cached hits ([#3011](https://github.com/algolia/react-instantsearch/issues/3011)) ([b56f5f7](https://github.com/algolia/react-instantsearch/commit/b56f5f7)) - - -### Features - -* **smartSort:** add widget ([#3009](https://github.com/algolia/react-instantsearch/issues/3009)) ([4cc8412](https://github.com/algolia/react-instantsearch/commit/4cc8412)), closes [#3010](https://github.com/algolia/react-instantsearch/issues/3010) - - - -# [6.9.0](https://github.com/algolia/react-instantsearch/compare/v6.8.3...v6.9.0) (2021-02-03) - - -### Features - -* **answers:** add `EXPERIMENTAL_Answers` widget ([#2996](https://github.com/algolia/react-instantsearch/issues/2996)) ([55e4191](https://github.com/algolia/react-instantsearch/commit/55e4191)), closes [#3005](https://github.com/algolia/react-instantsearch/issues/3005) - - - -## [6.8.3](https://github.com/algolia/react-instantsearch/compare/v6.8.2...v6.8.3) (2021-01-22) - - -### Bug Fixes - -* upgrade prop-types dependency to 15.6+ ([#3003](https://github.com/algolia/react-instantsearch/issues/3003)) ([fc03496](https://github.com/algolia/react-instantsearch/commit/fc03496)) - - - -## [6.8.2](https://github.com/algolia/react-instantsearch/compare/v6.8.1...v6.8.2) (2020-10-21) - - -### Bug Fixes - -* **ssr:** provide metadata default value ([0a2f34c](https://github.com/algolia/react-instantsearch/commit/0a2f34c)) - - - -## [6.8.1](https://github.com/algolia/react-instantsearch/compare/v6.8.0...v6.8.1) (2020-10-14) - - -### Bug Fixes - -* **ssr:** hydrate metadata with a value ([9249c19](https://github.com/algolia/react-instantsearch/commit/9249c19)) - - - -# [6.8.0](https://github.com/algolia/react-instantsearch/compare/v6.7.0...v6.8.0) (2020-10-14) - - -### Bug Fixes - -* **ssr:** make sure metadata is available on initial render ([#2973](https://github.com/algolia/react-instantsearch/issues/2973)) ([be43b65](https://github.com/algolia/react-instantsearch/commit/be43b65)), closes [#2972](https://github.com/algolia/react-instantsearch/issues/2972) -* add missing dependencies ([#2975](https://github.com/algolia/react-instantsearch/issues/2975)) ([22ecb3c](https://github.com/algolia/react-instantsearch/commit/22ecb3c)) -* **ConfigureRelatedItems:** support nested attributes ([#2967](https://github.com/algolia/react-instantsearch/issues/2967)) ([86dfe86](https://github.com/algolia/react-instantsearch/commit/86dfe86)) -* **ssr:** allow "params" to be optional in custom clients ([#2961](https://github.com/algolia/react-instantsearch/issues/2961)) ([c3e3d2e](https://github.com/algolia/react-instantsearch/commit/c3e3d2e)), closes [#2958](https://github.com/algolia/react-instantsearch/issues/2958) - - - -# [6.7.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.7.0) (2020-07-20) - - -### Bug Fixes - -* **core:** appending successful index search results by returning new object reference ([#2953](https://github.com/algolia/react-instantsearch/issues/2953)) ([0a711a7](https://github.com/algolia/react-instantsearch/commit/0a711a7)) -* **ssr:** remove second instance of "query" in the response "params" for SSR ([#2945](https://github.com/algolia/react-instantsearch/issues/2945)) ([bf837c5](https://github.com/algolia/react-instantsearch/commit/bf837c5)), closes [#2941](https://github.com/algolia/react-instantsearch/issues/2941) - - -### Features - -* **infinite-hits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.6.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.6.0) (2020-06-15) - - -### Features - -* **infiniteHits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.5.0](https://github.com/algolia/react-instantsearch/compare/v6.4.0...v6.5.0) (2020-05-18) - - -### Bug Fixes - -* **connectQueryRules:** fix crash when using connectQueryRules with multiple indexes ([#2903](https://github.com/algolia/react-instantsearch/issues/2903)) ([c66d612](https://github.com/algolia/react-instantsearch/commit/c66d612)) -* **core:** fix maximum call stack size exceeded ([#2926](https://github.com/algolia/react-instantsearch/issues/2926)) ([7e883df](https://github.com/algolia/react-instantsearch/commit/7e883df)) - - -### Features - -* **SearchBox:** provide input element ref ([#2913](https://github.com/algolia/react-instantsearch/issues/2913)) ([b41bff2](https://github.com/algolia/react-instantsearch/commit/b41bff2)) - - - -# [6.4.0](https://github.com/algolia/react-instantsearch/compare/v6.3.0...v6.4.0) (2020-03-18) - - -### Bug Fixes - -* **deps:** fix "too much recursion" error with circular deps ([#2899](https://github.com/algolia/react-instantsearch/issues/2899)) ([c5f27a1](https://github.com/algolia/react-instantsearch/commit/c5f27a1)) - - - -# [6.3.0](https://github.com/algolia/react-instantsearch/compare/v6.2.0...v6.3.0) (2020-01-30) - - -### Features - -* **algoliasearch:** add support for algoliasearch v4 ([#2890](https://github.com/algolia/react-instantsearch/issues/2890)) ([c6c7382](https://github.com/algolia/react-instantsearch/commit/c6c7382)) - - - -# [6.2.0](https://github.com/algolia/react-instantsearch/compare/v6.1.0...v6.2.0) (2020-01-20) - - -### Bug Fixes - -* **deps:** update dependency algoliasearch to v3.35.1 ([#2802](https://github.com/algolia/react-instantsearch/issues/2802)) ([cfb91f0](https://github.com/algolia/react-instantsearch/commit/cfb91f0)) -* **widgets:** rename `ExperimentalConfigureRelatedItems` compon… ([#2891](https://github.com/algolia/react-instantsearch/issues/2891)) ([b910df2](https://github.com/algolia/react-instantsearch/commit/b910df2)) - - -### Features - -* **insights:** add getInsightsAnonymousUserToken helper ([#2887](https://github.com/algolia/react-instantsearch/issues/2887)) ([b5fe4f7](https://github.com/algolia/react-instantsearch/commit/b5fe4f7)) -* **widgets:** introduce `ConfigureRelatedItems` as experimental ([#2880](https://github.com/algolia/react-instantsearch/issues/2880)) ([923cd43](https://github.com/algolia/react-instantsearch/commit/923cd43)) - - - -# [6.1.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0...v6.1.0) (2019-12-17) - - -### Bug Fixes - -* **connectNumericMenu:** support numeric refinement 0 ([#2882](https://github.com/algolia/react-instantsearch/issues/2882)) ([30bd9fd](https://github.com/algolia/react-instantsearch/commit/30bd9fd)) -* **deps:** update dependency next to v9.1.1 ([9d49d33](https://github.com/algolia/react-instantsearch/commit/9d49d33)) -* **helper:** rely on stable version of algoliasearch-helper ([#2871](https://github.com/algolia/react-instantsearch/issues/2871)) ([e3531a1](https://github.com/algolia/react-instantsearch/commit/e3531a1)) - - -### Features - -* **insights:** show an error when 'clickAnalytics: true' is missing. ([#2877](https://github.com/algolia/react-instantsearch/issues/2877)) ([621656a](https://github.com/algolia/react-instantsearch/commit/621656a)) -* **voice:** add additionalQueryParameters ([#2366](https://github.com/algolia/react-instantsearch/issues/2366)) ([3a45b2c](https://github.com/algolia/react-instantsearch/commit/3a45b2c)) - - - -# [6.0.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.2...v6.0.0) (2019-10-28) - - - -# [6.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.1...v6.0.0-beta.2) (2019-10-25) - - -### Bug Fixes - -* serialize cache value on hydrate ([#2862](https://github.com/algolia/react-instantsearch/issues/2862)) ([3319665](https://github.com/algolia/react-instantsearch/commit/3319665)), closes [#2828](https://github.com/algolia/react-instantsearch/issues/2828) - - - -# [6.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.1) (2019-10-18) - - -### Bug Fixes - -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **deps:** update dependency antd to v3.19.3 ([#2530](https://github.com/algolia/react-instantsearch/issues/2530)) ([73636c5](https://github.com/algolia/react-instantsearch/commit/73636c5)) -* **deps:** update dependency antd to v3.19.4 ([#2559](https://github.com/algolia/react-instantsearch/issues/2559)) ([c3e8267](https://github.com/algolia/react-instantsearch/commit/c3e8267)) -* **deps:** update dependency antd to v3.19.5 ([#2560](https://github.com/algolia/react-instantsearch/issues/2560)) ([72efd31](https://github.com/algolia/react-instantsearch/commit/72efd31)) -* **deps:** update dependency antd to v3.19.6 ([#2564](https://github.com/algolia/react-instantsearch/issues/2564)) ([654f986](https://github.com/algolia/react-instantsearch/commit/654f986)) -* **deps:** update dependency antd to v3.19.7 ([#2573](https://github.com/algolia/react-instantsearch/issues/2573)) ([7e963ad](https://github.com/algolia/react-instantsearch/commit/7e963ad)) -* **deps:** update dependency antd to v3.19.8 ([#2584](https://github.com/algolia/react-instantsearch/issues/2584)) ([34dd9b2](https://github.com/algolia/react-instantsearch/commit/34dd9b2)) -* **deps:** update dependency antd to v3.20.0 ([#2611](https://github.com/algolia/react-instantsearch/issues/2611)) ([b976c67](https://github.com/algolia/react-instantsearch/commit/b976c67)) -* **deps:** update dependency antd to v3.20.1 ([#2635](https://github.com/algolia/react-instantsearch/issues/2635)) ([792ad9c](https://github.com/algolia/react-instantsearch/commit/792ad9c)) -* **deps:** update dependency antd to v3.20.2 ([#2655](https://github.com/algolia/react-instantsearch/issues/2655)) ([301c2d8](https://github.com/algolia/react-instantsearch/commit/301c2d8)) -* **deps:** update dependency antd to v3.20.3 ([#2658](https://github.com/algolia/react-instantsearch/issues/2658)) ([d078e70](https://github.com/algolia/react-instantsearch/commit/d078e70)) -* **deps:** update dependency antd to v3.20.5 ([#2686](https://github.com/algolia/react-instantsearch/issues/2686)) ([42ef821](https://github.com/algolia/react-instantsearch/commit/42ef821)) -* **deps:** update dependency antd to v3.20.6 ([#2711](https://github.com/algolia/react-instantsearch/issues/2711)) ([927fbfe](https://github.com/algolia/react-instantsearch/commit/927fbfe)) -* **deps:** update dependency antd to v3.20.7 ([#2712](https://github.com/algolia/react-instantsearch/issues/2712)) ([1830952](https://github.com/algolia/react-instantsearch/commit/1830952)) -* **deps:** update dependency antd to v3.21.1 ([#2736](https://github.com/algolia/react-instantsearch/issues/2736)) ([39a51a6](https://github.com/algolia/react-instantsearch/commit/39a51a6)) -* **deps:** update dependency antd to v3.21.2 ([#2738](https://github.com/algolia/react-instantsearch/issues/2738)) ([a7a998a](https://github.com/algolia/react-instantsearch/commit/a7a998a)) -* **deps:** update dependency antd to v3.21.4 ([#2747](https://github.com/algolia/react-instantsearch/issues/2747)) ([60012be](https://github.com/algolia/react-instantsearch/commit/60012be)) -* **deps:** update dependency antd to v3.22.0 ([#2758](https://github.com/algolia/react-instantsearch/issues/2758)) ([9cda468](https://github.com/algolia/react-instantsearch/commit/9cda468)) -* **deps:** update dependency antd to v3.22.2 ([#2791](https://github.com/algolia/react-instantsearch/issues/2791)) ([ff1f5d9](https://github.com/algolia/react-instantsearch/commit/ff1f5d9)) -* **deps:** update dependency antd to v3.23.2 ([#2814](https://github.com/algolia/react-instantsearch/issues/2814)) ([a190410](https://github.com/algolia/react-instantsearch/commit/a190410)) -* **deps:** update dependency lodash to v4.17.13 ([c4974cf](https://github.com/algolia/react-instantsearch/commit/c4974cf)) -* **deps:** update dependency lodash to v4.17.14 ([#2647](https://github.com/algolia/react-instantsearch/issues/2647)) ([a2d2dd5](https://github.com/algolia/react-instantsearch/commit/a2d2dd5)) -* **deps:** update dependency lodash to v4.17.15 ([#2684](https://github.com/algolia/react-instantsearch/issues/2684)) ([354143f](https://github.com/algolia/react-instantsearch/commit/354143f)) -* **deps:** update dependency next to v9 ([#2638](https://github.com/algolia/react-instantsearch/issues/2638)) ([d22f61d](https://github.com/algolia/react-instantsearch/commit/d22f61d)) -* **deps:** update dependency next to v9.0.1 ([#2652](https://github.com/algolia/react-instantsearch/issues/2652)) ([2c2dab9](https://github.com/algolia/react-instantsearch/commit/2c2dab9)) -* **deps:** update dependency next to v9.0.2 ([#2662](https://github.com/algolia/react-instantsearch/issues/2662)) ([6fa4c5e](https://github.com/algolia/react-instantsearch/commit/6fa4c5e)) -* **deps:** update dependency next to v9.0.3 ([#2724](https://github.com/algolia/react-instantsearch/issues/2724)) ([f51b04b](https://github.com/algolia/react-instantsearch/commit/f51b04b)) -* **deps:** update dependency next to v9.0.4 ([#2767](https://github.com/algolia/react-instantsearch/issues/2767)) ([9af9180](https://github.com/algolia/react-instantsearch/commit/9af9180)) -* **deps:** update dependency next to v9.0.5 ([#2789](https://github.com/algolia/react-instantsearch/issues/2789)) ([0a75f41](https://github.com/algolia/react-instantsearch/commit/0a75f41)) -* **deps:** update dependency qs to v6.8.0 ([#2757](https://github.com/algolia/react-instantsearch/issues/2757)) ([8bffb87](https://github.com/algolia/react-instantsearch/commit/8bffb87)) -* **deps:** update dependency react-compound-slider to v2.1.0 ([#2610](https://github.com/algolia/react-instantsearch/issues/2610)) ([3389ee5](https://github.com/algolia/react-instantsearch/commit/3389ee5)) -* **deps:** update dependency react-compound-slider to v2.2.0 ([#2649](https://github.com/algolia/react-instantsearch/issues/2649)) ([7b81af1](https://github.com/algolia/react-instantsearch/commit/7b81af1)) -* **deps:** update dependency react-native-vector-icons to v6.5.0 ([#2520](https://github.com/algolia/react-instantsearch/issues/2520)) ([5f7f5b6](https://github.com/algolia/react-instantsearch/commit/5f7f5b6)) -* **deps:** update dependency react-native-vector-icons to v6.6.0 ([#2599](https://github.com/algolia/react-instantsearch/issues/2599)) ([b6bb199](https://github.com/algolia/react-instantsearch/commit/b6bb199)) -* **deps:** update dependency react-router-dom to v5.0.1 ([#2506](https://github.com/algolia/react-instantsearch/issues/2506)) ([d762230](https://github.com/algolia/react-instantsearch/commit/d762230)) -* **highlight:** switch to index as key ([#2691](https://github.com/algolia/react-instantsearch/issues/2691)) ([17e75d1](https://github.com/algolia/react-instantsearch/commit/17e75d1)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### chore - -* **release:** 6.0.0-beta.1 ([#2861](https://github.com/algolia/react-instantsearch/issues/2861)) ([cb56ca0](https://github.com/algolia/react-instantsearch/commit/cb56ca0)), closes [#2023](https://github.com/algolia/react-instantsearch/issues/2023) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2203](https://github.com/algolia/react-instantsearch/issues/2203) [#2432](https://github.com/algolia/react-instantsearch/issues/2432) [#2444](https://github.com/algolia/react-instantsearch/issues/2444) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2454](https://github.com/algolia/react-instantsearch/issues/2454) [#2455](https://github.com/algolia/react-instantsearch/issues/2455) [#2459](https://github.com/algolia/react-instantsearch/issues/2459) [#2458](https://github.com/algolia/react-instantsearch/issues/2458) [#2460](https://github.com/algolia/react-instantsearch/issues/2460) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2446](https://github.com/algolia/react-instantsearch/issues/2446) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2466](https://github.com/algolia/react-instantsearch/issues/2466) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2555](https://github.com/algolia/react-instantsearch/issues/2555) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2349](https://github.com/algolia/react-instantsearch/issues/2349) [#2570](https://github.com/algolia/react-instantsearch/issues/2570) [#2462](https://github.com/algolia/react-instantsearch/issues/2462) [#2600](https://github.com/algolia/react-instantsearch/issues/2600) [#2468](https://github.com/algolia/react-instantsearch/issues/2468) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2621](https://github.com/algolia/react-instantsearch/issues/2621) [#2627](https://github.com/algolia/react-instantsearch/issues/2627) [#2644](https://github.com/algolia/react-instantsearch/issues/2644) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2645](https://github.com/algolia/react-instantsearch/issues/2645) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2643](https://github.com/algolia/react-instantsearch/issues/2643) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2722](https://github.com/algolia/react-instantsearch/issues/2722) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2701](https://github.com/algolia/react-instantsearch/issues/2701) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2530](https://github.com/algolia/react-instantsearch/issues/2530) [#2559](https://github.com/algolia/react-instantsearch/issues/2559) [#2560](https://github.com/algolia/react-instantsearch/issues/2560) [#2564](https://github.com/algolia/react-instantsearch/issues/2564) [#2573](https://github.com/algolia/react-instantsearch/issues/2573) [#2584](https://github.com/algolia/react-instantsearch/issues/2584) [#2611](https://github.com/algolia/react-instantsearch/issues/2611) [#2635](https://github.com/algolia/react-instantsearch/issues/2635) [#2655](https://github.com/algolia/react-instantsearch/issues/2655) [#2658](https://github.com/algolia/react-instantsearch/issues/2658) [#2686](https://github.com/algolia/react-instantsearch/issues/2686) [#2711](https://github.com/algolia/react-instantsearch/issues/2711) [#2712](https://github.com/algolia/react-instantsearch/issues/2712) [#2736](https://github.com/algolia/react-instantsearch/issues/2736) [#2738](https://github.com/algolia/react-instantsearch/issues/2738) [#2747](https://github.com/algolia/react-instantsearch/issues/2747) [#2758](https://github.com/algolia/react-instantsearch/issues/2758) [#2647](https://github.com/algolia/react-instantsearch/issues/2647) [#2684](https://github.com/algolia/react-instantsearch/issues/2684) [#2638](https://github.com/algolia/react-instantsearch/issues/2638) [#2652](https://github.com/algolia/react-instantsearch/issues/2652) [#2662](https://github.com/algolia/react-instantsearch/issues/2662) [#2724](https://github.com/algolia/react-instantsearch/issues/2724) [#2767](https://github.com/algolia/react-instantsearch/issues/2767) [#2757](https://github.com/algolia/react-instantsearch/issues/2757) [#2610](https://github.com/algolia/react-instantsearch/issues/2610) [#2649](https://github.com/algolia/react-instantsearch/issues/2649) [#2520](https://github.com/algolia/react-instantsearch/issues/2520) [#2599](https://github.com/algolia/react-instantsearch/issues/2599) [#2506](https://github.com/algolia/react-instantsearch/issues/2506) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2688](https://github.com/algolia/react-instantsearch/issues/2688) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2535](https://github.com/algolia/react-instantsearch/issues/2535) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2834](https://github.com/algolia/react-instantsearch/issues/2834) [#2845](https://github.com/algolia/react-instantsearch/issues/2845) [#2842](https://github.com/algolia/react-instantsearch/issues/2842) [#2852](https://github.com/algolia/react-instantsearch/issues/2852) [#2853](https://github.com/algolia/react-instantsearch/issues/2853) - - -### BREAKING CHANGES - -* **release:** translation will render default value if passed undefined as value - -* chore(lodash): remove imports - -* fix(translation): allow undefined value to be passed on purpose -* **release:** no longer do we allow paths like `attribute[5].something`, or other indexed forms, only `.` is allowed as special key. - -All existing tests still pass, and we never documented you could use `lodash.get` patterns other than `.`. - -* feat(get): accept array & bracked-separated string - -moved to utils at the same time - -* fix typo - -* feedback: test for undefined behaviour - -* chore(size): update expectation - -this will go down afterwards, but for now there's some more duplication - - - -# [6.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.0) (2019-08-21) - -[Migration guide](MIGRATION.md) - -### Bug Fixes - -* **react 17 compat:** upgrade RangeInput lifecycle ([#2289](https://github.com/algolia/react-instantsearch/issues/2289)) ([110b1af](https://github.com/algolia/react-instantsearch/commit/110b1af)) -* **react 17 compat:** upgrade RangeSlider lifecycle ([#2290](https://github.com/algolia/react-instantsearch/issues/2290)) ([69a7f53](https://github.com/algolia/react-instantsearch/commit/69a7f53)) -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **core:** searchState can be non-Object object ([#2722](https://github.com/algolia/react-instantsearch/issues/2722)) ([dea493c](https://github.com/algolia/react-instantsearch/commit/dea493c)), closes [#2568](https://github.com/algolia/react-instantsearch/issues/2568) -* **createConnector:** new React life cycles ([#2357](https://github.com/algolia/react-instantsearch/issues/2357)) ([fc10640](https://github.com/algolia/react-instantsearch/commit/fc10640)) -* **createInstantSearchManager:** do not trigger search on index update ([#2552](https://github.com/algolia/react-instantsearch/issues/2552)) ([e209362](https://github.com/algolia/react-instantsearch/commit/e209362)) -* **geo:** check for undefined in isEqual ([#2643](https://github.com/algolia/react-instantsearch/issues/2643)) ([a544231](https://github.com/algolia/react-instantsearch/commit/a544231)), closes [#2467](https://github.com/algolia/react-instantsearch/issues/2467) -* **geo:** remove lifecycle compat ([#2644](https://github.com/algolia/react-instantsearch/issues/2644)) ([2b2b898](https://github.com/algolia/react-instantsearch/commit/2b2b898)), closes [#2626](https://github.com/algolia/react-instantsearch/issues/2626) -* **highlight:** switch to index as key ([#2690](https://github.com/algolia/react-instantsearch/issues/2690)) ([51de682](https://github.com/algolia/react-instantsearch/commit/51de682)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **peerDependencies:** update React ([#2626](https://github.com/algolia/react-instantsearch/issues/2626)) ([6ccad49](https://github.com/algolia/react-instantsearch/commit/6ccad49)) -* **ssr:** avoid duplicate serializing ([#2726](https://github.com/algolia/react-instantsearch/issues/2726)) ([c768b1a](https://github.com/algolia/react-instantsearch/commit/c768b1a)) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### Code Refactoring - -* **lodash:** get ([#2461](https://github.com/algolia/react-instantsearch/issues/2461)) ([527b879](https://github.com/algolia/react-instantsearch/commit/527b879)) -* **lodash:** has ([#2434](https://github.com/algolia/react-instantsearch/issues/2434)) ([75a4a15](https://github.com/algolia/react-instantsearch/commit/75a4a15)) -* **lodash:** has been fully removed - -### Features - -* **autocomplete:** add queryID & position to provided hits ([#2687](https://github.com/algolia/react-instantsearch/issues/2687)) ([e453dab](https://github.com/algolia/react-instantsearch/commit/e453dab)) -* **client:** remove algoliaClient, appId & apiKey ([#2338](https://github.com/algolia/react-instantsearch/issues/2338)) ([b84a0b5](https://github.com/algolia/react-instantsearch/commit/b84a0b5)) (use searchClient exclusively now) -* **context:** migrate to new React context ([#2178](https://github.com/algolia/react-instantsearch/issues/2178)) ([0a1abea](https://github.com/algolia/react-instantsearch/commit/0a1abea)), closes [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) -* **ssr:** update the SSR API ([#2555](https://github.com/algolia/react-instantsearch/issues/2555)) ([925bdb8](https://github.com/algolia/react-instantsearch/commit/925bdb8)), closes [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) - - -### BREAKING CHANGES - -* **searchClient:** argument is the only option now. - -Previously there were three options to pass a search client: searchClient, appId & apiKey, algoliaClient. The latter two have been removed, and now only `searchClient` is accepted. This searchClient is an instance of the `algoliasearch` module: - -```js -import algoliasearch from 'algoliasearch/lite'; - -const searchClient = algoliasearch( - 'myAppId', - 'myApiKey', - { _useRequestCache: true } -); - -// ... - -``` - -If you were relying on duplicate requests not being fired when using appId & apiKey before, you need to enable the `_useRequestCache` option now. - -* **SSR:** imports have changed - -In the server, you now directly import `findResultsState`, which now requires a `searchClient` in the second argument. - -In the App, you now use a regular `InstantSearch` component. - -* **Index & InstantSearch:** Remove `root` DOM element - -These elements now are pure containers for their children, and don't add a `div` to the DOM anymore. If you were relying on those for styling, wrap the `InstantSearch` and `Index` element with a `div` with an appropriate class. - -* **Index & InstantSearch:** Remove support for `root` prop - -Since these two arguments now no longer wrap their children in an element, they no longer accept a `root` prop. - -* **Highlight:** some paths will no longer be accepted - -We only accept paths separated with a dot or bracket now, like before. It's possible that a different type of path worked undocumented, but no longer does. - -* **algoliasearch-helper:** updating to the next major version - -This library is mostly internal, but it has had a major refactor (including removing lodash). This has no impact, unless you are dealing with it using `createConnector`. See the [migration guide](https://github.com/algolia/algoliasearch-helper-js/blob/next/documentation-src/metalsmith/content/upgrade.md) for the v3 of algoliasearch-helper for more information. - -# [5.7.0](https://github.com/algolia/react-instantsearch/compare/v5.6.0...v5.7.0) (2019-06-04) - - -### Bug Fixes - -* **highlight:** allow array as "attribute" ([#2474](https://github.com/algolia/react-instantsearch/issues/2474)) ([9dc829a](https://github.com/algolia/react-instantsearch/commit/9dc829a)), closes [#2461](https://github.com/algolia/react-instantsearch/issues/2461) -* **indexUtils:** allow index with dots in it ([#2350](https://github.com/algolia/react-instantsearch/issues/2350)) ([f91906f](https://github.com/algolia/react-instantsearch/commit/f91906f)) - - -### Features - -* **voiceSearch:** add voice search widget ([#2316](https://github.com/algolia/react-instantsearch/issues/2316)) ([0e3b124](https://github.com/algolia/react-instantsearch/commit/0e3b124)) - - - -# [5.6.0](https://github.com/algolia/react-instantsearch/compare/v5.5.0...v5.6.0) (2019-05-15) - - -### Bug Fixes - -* **connectQueryRules:** avoid to throw an error with undefined values ([#2436](https://github.com/algolia/react-instantsearch/issues/2436)) ([1e18287](https://github.com/algolia/react-instantsearch/commit/1e18287)) - - -### Features - -* **infiniteHits:** add previous button ([#2296](https://github.com/algolia/react-instantsearch/issues/2296)) ([010a69a](https://github.com/algolia/react-instantsearch/commit/010a69a)) - - - -# [5.5.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0...v5.5.0) (2019-04-23) - - -### Bug Fixes - -* **createInstantSearch:** change the User-Agent to use the new specs ([#2209](https://github.com/algolia/react-instantsearch/issues/2209)) ([642ba0b](https://github.com/algolia/react-instantsearch/commit/642ba0b)) - - -### Features - -* **DOMMaps:** expose withGoogleMaps HOC [PART-1] ([#2000](https://github.com/algolia/react-instantsearch/issues/2000)) ([2ae1dea](https://github.com/algolia/react-instantsearch/commit/2ae1dea)) -* **queryRules:** add Query Rules features ([#2286](https://github.com/algolia/react-instantsearch/issues/2286)) ([3ae9c01](https://github.com/algolia/react-instantsearch/commit/3ae9c01)) -* **insights:** add insights features ([#2215](https://github.com/algolia/react-instantsearch/pull/2215)) ([961e7a7](https://github.com/algolia/react-instantsearch/commit/961e7a7)) - - - -# [5.4.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.1...v5.4.0) (2019-02-05) - - -### Bug Fixes - -* **DOMMaps:** set React & React DOM as peer deps ([#1922](https://github.com/algolia/react-instantsearch/issues/1922)) ([2f2cefd](https://github.com/algolia/react-instantsearch/commit/2f2cefd)) - - - -# [5.4.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.0...v5.4.0-beta.1) (2019-01-10) - - -### Bug Fixes - -* **deps:** sync algoliasearch version ([#1879](https://github.com/algolia/react-instantsearch/issues/1879)) ([40f9c26](https://github.com/algolia/react-instantsearch/commit/40f9c26)) -* **maps:** use stable version for peer deps ([#1887](https://github.com/algolia/react-instantsearch/issues/1887)) ([9055167](https://github.com/algolia/react-instantsearch/commit/9055167)) - - - -# [5.4.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.3.2...v5.4.0-beta.0) (2019-01-08) - - -### Features - -* **Index:** introduce `indexId` prop ([#1833](https://github.com/algolia/react-instantsearch/issues/1833)) ([ec9e0fb](https://github.com/algolia/react-instantsearch/commit/ec9e0fb)) - - - - -## [5.3.2](https://github.com/algolia/react-instantsearch/compare/v5.3.1...v5.3.2) (2018-10-30) - - -### Bug Fixes - -* **sffv:** clamp maxFacetHits to the allowed range ([#1696](https://github.com/algolia/react-instantsearch/issues/1696)) ([83ce245](https://github.com/algolia/react-instantsearch/commit/83ce245)) - - - - -## [5.3.1](https://github.com/algolia/react-instantsearch/compare/v5.3.0...v5.3.1) (2018-09-26) - - -### Bug Fixes - -* **connector:** ensure canRefine is computed on the transformed items ([#1568](https://github.com/algolia/react-instantsearch/pull/1568)) ([c95384f](https://github.com/algolia/react-instantsearch/commit/c95384f)) -* **toggle:** ensure facet is present ([#1613](https://github.com/algolia/react-instantsearch/issues/1613)) ([e914ff6](https://github.com/algolia/react-instantsearch/commit/e914ff6)) - - - - -# [5.3.0](https://github.com/algolia/react-instantsearch/compare/v5.2.3...v5.3.0) (2018-09-24) - - -### Bug Fixes - -* **SSR:** bind getSearchParmaters to the component instance ([f34cb3d](https://github.com/algolia/react-instantsearch/commit/f34cb3d)) -* **GoogleMapsLoader:** pick google maps version ([#1540](https://github.com/algolia/react-instantsearch/issues/1540)) ([b14efcf](https://github.com/algolia/react-instantsearch/commit/b14efcf)) - - -### Features - -* **connectToggleRefinement:** implement canRefine & count ([#1588](https://github.com/algolia/react-instantsearch/issues/1588)) ([40672dd](https://github.com/algolia/react-instantsearch/commit/40672dd)) - - - - -## [5.2.3](https://github.com/algolia/react-instantsearch/compare/v5.2.2...v5.2.3) (2018-08-16) - - -### Bug Fixes - -* Allow object as type for Root (closes [#1446](https://github.com/algolia/react-instantsearch/issues/1446)) ([#1461](https://github.com/algolia/react-instantsearch/issues/1461)) ([7c2317b](https://github.com/algolia/react-instantsearch/commit/7c2317b)) -* **List:** render children list only when required ([#1472](https://github.com/algolia/react-instantsearch/issues/1472)) ([9eb2cbb](https://github.com/algolia/react-instantsearch/commit/9eb2cbb)), closes [#1459](https://github.com/algolia/react-instantsearch/issues/1459) - - - - -## [5.2.2](https://github.com/algolia/react-instantsearch/compare/v5.2.1...v5.2.2) (2018-07-16) - - -Publish the previous version on `stable`. - - - - -## [5.2.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0...v5.2.1) (2018-07-16) - - -### Bug Fixes - -* **GoogleMapsLoader:** inline the import to scriptjs ([#1427](https://github.com/algolia/react-instantsearch/issues/1427)) ([8019416](https://github.com/algolia/react-instantsearch/commit/8019416)) - - - - -# [5.2.0](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.2...v5.2.0) (2018-07-04) - - -### Bug Fixes - -* **translatable:** avoid create a new function on every render ([#1383](https://github.com/algolia/react-instantsearch/issues/1383)) ([1285b3b](https://github.com/algolia/react-instantsearch/commit/1285b3b)) - - -### Features - -* **core:** export translatable ([#1351](https://github.com/algolia/react-instantsearch/issues/1351)) ([6d5a89d](https://github.com/algolia/react-instantsearch/commit/6d5a89d)) -* **maps:** add connector & widget ([#1171](https://github.com/algolia/react-instantsearch/issues/1171)) ([16e288a](https://github.com/algolia/react-instantsearch/commit/16e288a)) - - - - -# [5.2.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.1...v5.2.0-beta.2) (2018-06-19) - - -### Features - -* export highlight tags from DOM / native ([#1342](https://github.com/algolia/react-instantsearch/issues/1342)) ([28a699e](https://github.com/algolia/react-instantsearch/commit/28a699e)) -* **createInstantSearch:** enable _useRequestCache ([#1346](https://github.com/algolia/react-instantsearch/issues/1346)) ([f772600](https://github.com/algolia/react-instantsearch/commit/f772600)) -* **dom:** export create class name ([#1348](https://github.com/algolia/react-instantsearch/issues/1348)) ([9017468](https://github.com/algolia/react-instantsearch/commit/9017468)) - - - - -# [5.2.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.0...v5.2.0-beta.1) (2018-06-04) - - -### Bug Fixes - -* **dom:** publish server file ([#1305](https://github.com/algolia/react-instantsearch/issues/1305)) ([bd79693](https://github.com/algolia/react-instantsearch/commit/bd79693)) - - - - -# [5.2.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.1.0...v5.2.0-beta.0) (2018-06-04) - - -This new version introduce a complete revamp of the package structure, but it should be completely transparent for the users. - -If you have any troubles with this version please open a issue on [Github](https://github.com/algolia/react-instantsearch/issues/new), thanks! - - - - -# [5.1.0](https://github.com/algolia/react-instantsearch/compare/v5.0.3...v5.1.0) (2018-05-28) - - -### Bug Fixes - -* **connectInfiniteHits:** always set a value for previous page ([#1195](https://github.com/algolia/react-instantsearch/issues/1195)) ([4c218d5](https://github.com/algolia/react-instantsearch/commit/4c218d5)) -* **indexUtils:** avoid throw an error on cleanUp multi indices ([#1265](https://github.com/algolia/react-instantsearch/issues/1265)) ([12f5ace](https://github.com/algolia/react-instantsearch/commit/12f5ace)) - - -### Features - -* **searchClient:** Add support for custom Search Clients ([#1216](https://github.com/algolia/react-instantsearch/issues/1216)) ([174cc28](https://github.com/algolia/react-instantsearch/commit/174cc28)) - - - - -## [5.0.3](https://github.com/algolia/react-instantsearch/compare/v5.0.2...v5.0.3) (2018-04-03) - - -### Bug Fixes - -* revert dependencies as devDependencies ([#1135](https://github.com/algolia/react-instantsearch/issues/1135)) ([6b627bb](https://github.com/algolia/react-instantsearch/commit/6b627bb)) - - - - -## [5.0.2](https://github.com/algolia/react-instantsearch/compare/v5.0.1...v5.0.2) (2018-04-03) - - -### Bug Fixes - -* use lodash version of unsupported Array.{fill, find} ([#1118](https://github.com/algolia/react-instantsearch/issues/1118)) ([ea7bf42](https://github.com/algolia/react-instantsearch/commit/ea7bf42)) - - - - -## [5.0.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0...v5.0.1) (2018-03-12) - - -### Bug Fixes - -* **connectInfiniteHits:** always provide an array for hits ([#1064](https://github.com/algolia/react-instantsearch/issues/1064)) ([c75e38b](https://github.com/algolia/react-instantsearch/commit/c75e38b)) - - - - -# [5.0.0](https://github.com/algolia/react-instantsearch/compare/v4.5.2...v5.0.0) (2018-03-06) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.5.2](https://github.com/algolia/react-instantsearch/compare/v4.5.1...v4.5.2) (2018-03-06) - - -### Bug Fixes - -* **connectRange:** update default refinement propTypes ([#978](https://github.com/algolia/react-instantsearch/issues/978)) ([c065fb1](https://github.com/algolia/react-instantsearch/commit/c065fb1)) -* **IndexUtils:** avoid throw an error when cleanUp multi index ([#1019](https://github.com/algolia/react-instantsearch/issues/1019)) ([865a3c3](https://github.com/algolia/react-instantsearch/commit/865a3c3)) -* **SearchBox:** avoid to bind click on reset button ([#979](https://github.com/algolia/react-instantsearch/issues/979)) ([ea3063a](https://github.com/algolia/react-instantsearch/commit/ea3063a)) - - - - -# [5.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2018-02-06) - - -Apply features & bug fixes from [v4.5.0](#450-2018-02-06) & [v4.5.1](#451-2018-02-06) on the v5. - -See their CHANGELOG for more details. - - - - -## [4.5.1](https://github.com/algolia/react-instantsearch/compare/v4.5.0...v4.5.1) (2018-02-06) - - -### Bug Fixes - -* **StarRating:** move to 1 based instead of 0 ([#949](https://github.com/algolia/react-instantsearch/issues/949)) ([eb0152d](https://github.com/algolia/react-instantsearch/commit/eb0152d)) - - - - -# [4.5.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v4.5.0) (2018-02-06) - - -### Bug Fixes - -* **connectRange:** use the same behaviour for currentRefinement in getMetadata ([#923](https://github.com/algolia/react-instantsearch/issues/923)) ([08917b6](https://github.com/algolia/react-instantsearch/commit/08917b6)) -* **connectToggle:** use currentRefinement in metadata instead of the label ([#909](https://github.com/algolia/react-instantsearch/issues/909)) ([89cae2b](https://github.com/algolia/react-instantsearch/commit/89cae2b)) -* **StarRatings:** always show the stars below ([#929](https://github.com/algolia/react-instantsearch/issues/929)) ([22bf93a](https://github.com/algolia/react-instantsearch/commit/22bf93a)) - - -### Features - -* **connectStateResults:** expose isSearchStalled ([#933](https://github.com/algolia/react-instantsearch/issues/933)) ([f45ba27](https://github.com/algolia/react-instantsearch/commit/f45ba27)) - - - -# [5.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v5.0.0-beta.0) (2018-01-30) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.4.2](https://github.com/algolia/react-instantsearch/compare/v4.4.1...v4.4.2) (2018-01-24) - - -### Bug Fixes - -* **currentRefinements:** give access to id and index from transformItems for deduplication ([#830](https://github.com/algolia/react-instantsearch/issues/830)) ([316b8f5](https://github.com/algolia/react-instantsearch/commit/316b8f5)) -* pass maxFacetHits to SFFV ([#863](https://github.com/algolia/react-instantsearch/issues/863)) ([de23a46](https://github.com/algolia/react-instantsearch/commit/de23a46)) - - - - -## [4.4.1](https://github.com/algolia/react-instantsearch/compare/v4.4.0...v4.4.1) (2018-01-09) - - -### Bug Fixes - -* **SearchBox**: clear SearchBox without search as you type ([#802](https://github.com/algolia/react-instantsearch/issues/802)) ([c49b2b6](https://github.com/algolia/react-instantsearch/commit/c49b2b6)) -* **connectRange:** check if facet exist before access ([#797](https://github.com/algolia/react-instantsearch/issues/797)) ([6520760](https://github.com/algolia/react-instantsearch/commit/6520760)) -* **stories:** avoid to use linear-background it breaks Argos every time ([#804](https://github.com/algolia/react-instantsearch/issues/804)) ([0beded7](https://github.com/algolia/react-instantsearch/commit/0beded7)) -* **stories:** limit hits per page on Index ([#806](https://github.com/algolia/react-instantsearch/issues/806)) ([6eb14d3](https://github.com/algolia/react-instantsearch/commit/6eb14d3)) - - -### Features - -* **Index:** allow custom root ([#792](https://github.com/algolia/react-instantsearch/issues/792)) ([d793b0a](https://github.com/algolia/react-instantsearch/commit/d793b0a)) - - - - -# [4.4.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0...v4.4.0) (2018-01-03) - - -### Bug Fixes - -* **createInstantSearch:** remove the client from the Snapshot ([#749](https://github.com/algolia/react-instantsearch/issues/749)) ([700d8f4](https://github.com/algolia/react-instantsearch/commit/700d8f4)) -* refresh cache memory leak example ([#784](https://github.com/algolia/react-instantsearch/issues/784)) ([cf228ac](https://github.com/algolia/react-instantsearch/commit/cf228ac)) -* **stories:** rename InstantSearch to `` ([#789](https://github.com/algolia/react-instantsearch/issues/789)) ([05efda5](https://github.com/algolia/react-instantsearch/commit/05efda5)) - - -### Features - -* InstantSearch root props ([#770](https://github.com/algolia/react-instantsearch/issues/770)) ([2d458f8](https://github.com/algolia/react-instantsearch/commit/2d458f8)) - - - - -# [4.3.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0-beta.0...v4.3.0) (2017-12-20) - - -### Bug Fixes - -* reset page with multi index ([#665](https://github.com/algolia/react-instantsearch/issues/665)) ([865b7dc](https://github.com/algolia/react-instantsearch/commit/865b7dc)) -* track all index in the manager ([#660](https://github.com/algolia/react-instantsearch/issues/660)) ([793502b](https://github.com/algolia/react-instantsearch/commit/793502b)) - - -### Features - -* **SearchBox:** provide a loading indicator ([#544](https://github.com/algolia/react-instantsearch/issues/544)) ([189659e](https://github.com/algolia/react-instantsearch/commit/189659e)) -* **Highlight:** support array of strings ([#715](https://github.com/algolia/react-instantsearch/issues/715)) ([8e93c6a](https://github.com/algolia/react-instantsearch/commit/8e93c6a)) - - - - -# [4.3.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.2.0...v4.3.0-beta.0) (2017-11-27) - - -### Bug Fixes - -* **babelrc:** add a key for each env development, production, es ([#547](https://github.com/algolia/react-instantsearch/issues/547)) ([fa9528d](https://github.com/algolia/react-instantsearch/commit/fa9528d)) -* **localizecount:** allow localized string for count in MenuSelect ([#657](https://github.com/algolia/react-instantsearch/issues/657)) ([67ebd34](https://github.com/algolia/react-instantsearch/commit/67ebd34)) -* **react-router-example:** Properly update search query when using browser navigation ([#604](https://github.com/algolia/react-instantsearch/issues/604)) ([9ee6600](https://github.com/algolia/react-instantsearch/commit/9ee6600)) - - -### Features - -* **refreshcache:** add prop refresh to InstantSearch instance ([#619](https://github.com/algolia/react-instantsearch/issues/619)) ([19f6de0](https://github.com/algolia/react-instantsearch/commit/19f6de0)) - - - - -# [4.2.0](https://github.com/algolia/react-instantsearch/compare/v4.1.3...v4.2.0) (2017-11-02) - - -### Bug Fixes - -* **connectRange:** handle boundaries on first call ([9f14dc0](https://github.com/algolia/react-instantsearch/commit/9f14dc0)) -* **connectRange:** use refine instead of cleanUp in metadata ([#526](https://github.com/algolia/react-instantsearch/issues/526)) ([1861235](https://github.com/algolia/react-instantsearch/commit/1861235)) -* **hierarchicaMenu:** allow sorting and using limit ([fe178ed](https://github.com/algolia/react-instantsearch/commit/fe178ed)), closes [#92](https://github.com/algolia/react-instantsearch/issues/92) -* **InfiniteHits:** add disabled style to the LoadMore button ([#477](https://github.com/algolia/react-instantsearch/issues/477)) ([faba1ad](https://github.com/algolia/react-instantsearch/commit/faba1ad)) -* **Range:** handle float, allow reset and respect boundaries ([75969b8](https://github.com/algolia/react-instantsearch/commit/75969b8)) -* **RangeInput:** fix compatibility with React 16 & Panel ([3f218db](https://github.com/algolia/react-instantsearch/commit/3f218db)) -* **searchbox:** add maxlength 512 ([#542](https://github.com/algolia/react-instantsearch/issues/542)) ([5bd4033](https://github.com/algolia/react-instantsearch/commit/5bd4033)), closes [#510](https://github.com/algolia/react-instantsearch/issues/510) - - -### Features - -* **MenuSelect:** add component and connector ([cc6e0d7](https://github.com/algolia/react-instantsearch/commit/cc6e0d7)) - - - - -## [4.1.3](https://github.com/algolia/react-instantsearch/compare/v4.1.2...v4.1.3) (2017-10-09) - - -### Bug Fixes - -* **List:** remove React16 warning ([#442](https://github.com/algolia/react-instantsearch/issues/442)) ([8d6cf18](https://github.com/algolia/react-instantsearch/commit/8d6cf18)) - - -### Features - -* **connectStateResults:** add component props ([#434](https://github.com/algolia/react-instantsearch/issues/434)) ([c629b97](https://github.com/algolia/react-instantsearch/commit/c629b97)) - - - - -## [4.1.2](https://github.com/algolia/react-instantsearch/compare/v4.1.1...v4.1.2) (2017-09-26) - - -### Features - -* **Conditional:** add connectStateResults connector ([#357](https://github.com/algolia/react-instantsearch/issues/357)) ([462df5f](https://github.com/algolia/react-instantsearch/commit/462df5f)) - - - - -## [4.1.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0...v4.1.1) (2017-09-13) - - -### Bug Fixes - -* **MultiIndex:** reset page to 1 when share widgets refine (#312) ([c85a7bf](https://github.com/algolia/react-instantsearch/commit/c85a7bf)) -* **MultiIndex:** Trigger new search when `` props are updated (#318) ([bb11965](https://github.com/algolia/react-instantsearch/commit/bb11965)) - - - - -# [4.1.0](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.5...v4.1.0) (2017-08-28) - - -### Bug Fixes - -* **Highlighting:** revert breaking change (#245) ([045ee06](https://github.com/algolia/react-instantsearch/commit/045ee06)) -* **List:** adds support for any type of renderable element (#266) ([d848bb6](https://github.com/algolia/react-instantsearch/commit/d848bb6)) -* **Pagination:** fixed the offset ([3c0fff2](https://github.com/algolia/react-instantsearch/commit/3c0fff2)) -* **PoweredBy:** aria-* tags are not camelcased (#261) ([dc4a5bb](https://github.com/algolia/react-instantsearch/commit/dc4a5bb)) - - - - -# [4.1.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2017-08-08) - - -### Bug Fixes - -* **SSR:** clean SP before rendering agan (#238) ([e765886](https://github.com/algolia/react-instantsearch/commit/e765886)) - - -### Features - -* **Breadcrumb:** add a new widget & connector (#228) ([7f8f3ae](https://github.com/algolia/react-instantsearch/commit/7f8f3ae)) - - - - -# [4.1.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2017-08-03) - - -### Bug Fixes - -* **deps:** Update dependency lint-staged to version ^4.0.0 (#201) ([6867a1b](https://github.com/algolia/react-instantsearch/commit/6867a1b)) -* **nextjs/ssr:** parse `params.asPath` (#189) ([ae17da0](https://github.com/algolia/react-instantsearch/commit/ae17da0)) -* **PoweredBy:** add a label to the Algolia logo (#216) ([cd235bd](https://github.com/algolia/react-instantsearch/commit/cd235bd)) -* **react:** remove typo around `"" 2` (#220) ([f73eb04](https://github.com/algolia/react-instantsearch/commit/f73eb04)) -* **ScrollTo:** scroll to only if change triggered by the widget observed (#202) ([2d76022](https://github.com/algolia/react-instantsearch/commit/2d76022)) -* **theme:** format the count of items appearing in a refinement (#217) ([2225c24](https://github.com/algolia/react-instantsearch/commit/2225c24)) - - - - -# [4.1.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2017-07-25) - - -### Bug Fixes - -* **error:** reset error when receiving results of a query (not when sending it) (#179) ([bb12c29](https://github.com/algolia/react-instantsearch/commit/bb12c29)) -* **highlight:** wrong parsing between client and server (#183) ([2daae70](https://github.com/algolia/react-instantsearch/commit/2daae70)) -* **poweredBy:** SSR compatibility (#181) ([ec0fa8a](https://github.com/algolia/react-instantsearch/commit/ec0fa8a)) - - -### BREAKING CHANGES - -* **highlight:** We remove the timestamp present in our highlight preTag and postTag. If you were using regex to parse the -highlighting results then you'll need to adapt it as now it's only "ais-highlight". - - - - -# [4.1.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2017-07-20) - - -### Bug Fixes - -* **error:** reset error if next query is successful (#175) ([ff50a07](https://github.com/algolia/react-instantsearch/commit/ff50a07)) - - - - -# [4.1.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2017-07-12) - - - - -# [4.1.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.0.7...v4.1.0-beta.0) (2017-07-10) - - -### Bug Fixes - -* **argos:** address flakyness (#152) ([84ef8f1](https://github.com/algolia/react-instantsearch/commit/84ef8f1)) - - -### Features - -* **server-side rendering:** Add API features for server-side rendering ([86b14d1](https://github.com/algolia/react-instantsearch/commit/86b14d1)) - - - - -## [4.0.7](https://github.com/algolia/react-instantsearch/compare/v4.0.6...v4.0.7) (2017-07-06) - - -### Bug Fixes - -* **results:** revert commit that ensure hits are returned only if right indices (#149) ([df9aa25](https://github.com/algolia/react-instantsearch/commit/df9aa25)) - - - - -## [4.0.6](https://github.com/algolia/react-instantsearch/compare/v4.0.5...v4.0.6) (2017-06-29) - - -### Bug Fixes - -* **store:** delay call to listener to prevent infinite loops (#143) ([0945958](https://github.com/algolia/react-instantsearch/commit/0945958)) - - - - -## [4.0.5](https://github.com/algolia/react-instantsearch/compare/v4.0.4...v4.0.5) (2017-06-26) - - -### Bug Fixes - -* **MultiIndex:** ensure getResults return only hits matching index in the context (#136) ([124ffe6](https://github.com/algolia/react-instantsearch/commit/124ffe6)) -* **MultiIndex:** handle if namespace isn't in search state (#139) ([1aab324](https://github.com/algolia/react-instantsearch/commit/1aab324)) -* **storybook:** process CSS through autoprefixer (#138) ([62cf512](https://github.com/algolia/react-instantsearch/commit/62cf512)) - - - - -## [4.0.4](https://github.com/algolia/react-instantsearch/compare/v4.0.3...v4.0.4) (2017-06-19) - - -### Bug Fixes - -* **MultiIndex:** handle switch between mono and multi index (#132) ([e161921](https://github.com/algolia/react-instantsearch/commit/e161921)) - - - - -## [4.0.3](https://github.com/algolia/react-instantsearch/compare/v4.0.2...v4.0.3) (2017-06-14) - - -### Bug Fixes - -* **SFFV:** search status we're not inside search state (#125) ([5f3e670](https://github.com/algolia/react-instantsearch/commit/5f3e670)) - - - - -## [4.0.2](https://github.com/algolia/react-instantsearch/compare/v4.0.1...v4.0.2) (2017-05-30) - - - - -## [4.0.1](https://github.com/algolia/react-instantsearch/compare/v4.0.0...v4.0.1) (2017-05-17) - - -### Bug Fixes - -* **state:** nested attributes for faceting were not handled ([11bd122](https://github.com/algolia/react-instantsearch/commit/11bd122)) - - - - -# [4.0.0](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.6...v4.0.0) (2017-05-15) - -### Features and migration guide - -You can find all the details of the release and the migration guide from v3 to v4 here: https://discourse.algolia.com/t/react-instantsearch-v4/1329. - - - -# [4.0.0-beta.6](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.5...v4.0.0-beta.6) (2017-05-04) - - - - -# [4.0.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.4...v4.0.0-beta.5) (2017-05-02) - - -### Bug Fixes - -* **connectAutoComplete:** allow usage with hits from a single index (#75) ([8b3b358](https://github.com/algolia/react-instantsearch/commit/8b3b358)), closes [#74](https://github.com/algolia/react-instantsearch/issues/74) -* **InstantSearch:** update algoliaClient when it change (#70) ([9e97dbd](https://github.com/algolia/react-instantsearch/commit/9e97dbd)) - - - - -# [4.0.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.3...v4.0.0-beta.4) (2017-04-25) - - -### Bug Fixes - -* **MultIndex:** no need to nest hits, if those are from main index. (#56) ([86e0bd7](https://github.com/algolia/react-instantsearch/commit/86e0bd7)) - - -### Features - -* **MultiIndex:** remove the need for virtual hits when using connectAutoComplete (#45) ([7549019](https://github.com/algolia/react-instantsearch/commit/7549019)) - - - - -# [4.0.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.2...v4.0.0-beta.3) (2017-04-21) - - -### Bug Fixes - -* replace usage of Object.values (#47) ([4c79e3e](https://github.com/algolia/react-instantsearch/commit/4c79e3e)) - - - - -# [4.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2017-04-18) - - -### Bug Fixes - -* **InstantSearch:** dont fire request/onsearchStateChange when unmounting (#26) ([9a1487a](https://github.com/algolia/react-instantsearch/commit/9a1487a)) -* **MultiIndex:** derived helper were using main index specifics params (#36) ([991fea6](https://github.com/algolia/react-instantsearch/commit/991fea6)) -* **MultiIndex:** revert breaking change if no multiple index (#32) ([44f7de0](https://github.com/algolia/react-instantsearch/commit/44f7de0)) -* **util:** remove empty key was removing non object key (#29) ([9f795c7](https://github.com/algolia/react-instantsearch/commit/9f795c7)) - - -### Features - -* **Highlighter:** allow rendering to custom tag (#11) ([52a1212](https://github.com/algolia/react-instantsearch/commit/52a1212)) -* **SearchBox:** add default width and height to buttons. (#34) ([bcabf9b](https://github.com/algolia/react-instantsearch/commit/bcabf9b)) - - - - -# [4.0.0-beta.1](https://github.com/algolia/instantsearch.js/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2017-04-03) - - -### Bug Fixes - -* **SFFV:** fix wrong query behaviour with slow network (#2086) ([c251e8f](https://github.com/algolia/instantsearch.js/commit/c251e8f)), closes [#2086](https://github.com/algolia/instantsearch.js/issues/2086) - - - - -# [4.0.0-beta.0](https://github.com/algolia/instantsearch.js/compare/v3.3.0...v4.0.0-beta.0) (2017-03-28) - - -### Features - -* **multi-index:** ease multi index and auto complete ([09a4e1d](https://github.com/algolia/instantsearch.js/commit/09a4e1d)) - - -### BREAKING CHANGES - -* multi-index: * Reseting the pagination should be done at each connector level inside the "refine" function when returning the search state. -* The current page now appears inside the search state when a widget is used -* Query values are part of the items prop of the connectCurrentRefinements connector. Behaviour is unchanged, query will be filtered if clearsQuery prop is false. -* Add the index name to all the current refinements items. (not used by our widgets yet, but available if needed). - - - - -# [3.3.0](https://github.com/algolia/instantsearch.js/compare/v3.2.2-beta0...v3.3.0) (2017-03-22) - - -### Bug Fixes - -* **example:** Fix access to props in react-router example ([1417d6f](https://github.com/algolia/instantsearch.js/commit/1417d6f)) - - - - -## [3.2.2-beta0](https://github.com/algolia/instantsearch.js/compare/v3.2.1...v3.2.2-beta0) (2017-03-20) - - -### Bug Fixes - -* **InfiniteHits:** provide translation key for `Load More` (#2048) ([6130bf2](https://github.com/algolia/instantsearch.js/commit/6130bf2)) -* **SearchBox:** better mobile behaviour by default ([ea968b3](https://github.com/algolia/instantsearch.js/commit/ea968b3)) -* **example:** link to instantsearch/react (#2007) ([5e674cd](https://github.com/algolia/instantsearch.js/commit/5e674cd)) -* **recipes:** react router v4 ([de673bf](https://github.com/algolia/instantsearch.js/commit/de673bf)) - - -### Features - -* **SearchBox:** add role=search to the form (#2046) ([d1e90f3](https://github.com/algolia/instantsearch.js/commit/d1e90f3)) -* **SearchBox:** allow custom reset and submit components (#1991) ([cd303d7](https://github.com/algolia/instantsearch.js/commit/cd303d7)) -* **searchBox:** add event handling ([e267ab6](https://github.com/algolia/instantsearch.js/commit/e267ab6)), closes [#2017](https://github.com/algolia/instantsearch.js/issues/2017) - - - - -## [3.2.1](https://github.com/algolia/instantsearch.js/compare/v3.2.0...v3.2.1) (2017-02-22) - - -### Bug Fixes - -* **umd:** Add connectors to UMD build (#1988) ([23ac5e6](https://github.com/algolia/instantsearch.js/commit/23ac5e6)), closes [#1987](https://github.com/algolia/instantsearch.js/issues/1987) - - - - -# [3.2.0](https://github.com/algolia/instantsearch.js/compare/v3.1.0...v3.2.0) (2017-02-15) - - -### Bug Fixes - -* **Configure:** use props a unique source of truth (#1967) ([9d53d86](https://github.com/algolia/instantsearch.js/commit/9d53d86)) -* **SearchBox:** Safari can only have with xlinkHref (#1970) ([7ab00bd](https://github.com/algolia/instantsearch.js/commit/7ab00bd)), closes [#1968](https://github.com/algolia/instantsearch.js/issues/1968) - - -### Features - -* **MultiRange:** add an all range (#1959) ([a3dc950](https://github.com/algolia/instantsearch.js/commit/a3dc950)) - - -### BREAKING CHANGES - -* MultiRange: - MultiRange/connectMultiRange: will add a "All" range to allow unselection of range without the usage of CurrentRefinements. This range can be either filtered or ramove via CSS if not needed. The label can be changed by using our translations system. - - - - -# [3.1.0](https://github.com/algolia/instantsearch.js/compare/v3.0.0...v3.1.0) (2017-02-08) - - -### Bug Fixes - -* **Configure:** call onSearchStateChange when props are updated (#1953) ([7e151db](https://github.com/algolia/instantsearch.js/commit/7e151db)), closes [#1950](https://github.com/algolia/instantsearch.js/issues/1950) -* **Configure:** trigger onSearchStateChange with the right data ([11e5af8](https://github.com/algolia/instantsearch.js/commit/11e5af8)) -* **createConnector:** updates with latest props on state change (#1951) ([cd3a82c](https://github.com/algolia/instantsearch.js/commit/cd3a82c)) - - -### Features - -* **ClearAll:** add withQuery to also clear the search query (#1958) ([c0e695b](https://github.com/algolia/instantsearch.js/commit/c0e695b)) - - - - -# [3.0.0](https://github.com/algolia/instantsearch.js/compare/v2.2.5...v3.0.0) (2017-02-06) - - -### Bug Fixes - -* ***List:** disable shortcuts in *List SearchBoxes (#1921) ([51a76ae](https://github.com/algolia/instantsearch.js/commit/51a76ae)), closes [#1920](https://github.com/algolia/instantsearch.js/issues/1920) -* **Configure:** add configure parameters in search state (#1935) ([0971330](https://github.com/algolia/instantsearch.js/commit/0971330)), closes [#1863](https://github.com/algolia/instantsearch.js/issues/1863) -* **Hits:** limit the hitComponent to be only a function (#1912) ([b3c9578](https://github.com/algolia/instantsearch.js/commit/b3c9578)) -* **Pagination:** fix and indicate when pagination is disabled ([5f20199](https://github.com/algolia/instantsearch.js/commit/5f20199)), closes [#1938](https://github.com/algolia/instantsearch.js/issues/1938) -* **StarRating:** usage with filters (#1933) ([667e9d5](https://github.com/algolia/instantsearch.js/commit/667e9d5)) -* **withSearchBox:** keep displaying searchBox when no items found (#1930) ([30de4cd](https://github.com/algolia/instantsearch.js/commit/30de4cd)) - - -### Features - -* **MultiRange:** indicate if a range has no refinements (#1926) ([80b6450](https://github.com/algolia/instantsearch.js/commit/80b6450)) -* **panel:** add a panel widget (#1889) ([594e1a1](https://github.com/algolia/instantsearch.js/commit/594e1a1)) -* **starRating:** indicate when any refinement has no effect ([c547ae5](https://github.com/algolia/instantsearch.js/commit/c547ae5)) -* **widgets:** default design for disabled states (#1929) ([31f010b](https://github.com/algolia/instantsearch.js/commit/31f010b)) - -### Migration guide - -The migration to V3.0.0 should be safe and you should do it. - -There are two breaking changes that you will need to handle in your codebase: -- Anytime you are using a connector, when there are no more items in it or no more hits, we will still call your Component. Thus you will have to handle cases like dealing with empty arrays and decide if you want to unmount or hide the widget. -- Anytime you are using a widget, when there are no more items in it or no more hits, we will still display the widget. You can then decide to hide it with CSS. - - -## [2.2.5](https://github.com/algolia/instantsearch.js/compare/v2.2.4...v2.2.5) (2017-01-23) - - -### Bug Fixes - -* **currentRefinements:** make removing a toggle refinement work ([8995e64](https://github.com/algolia/instantsearch.js/commit/8995e64)) - - - - -## [2.2.4](https://github.com/algolia/instantsearch.js/compare/v2.2.3...v2.2.4) (2017-01-20) - - -### Bug Fixes - -* **publish:** publish react-instantsearch/dist instead of root (#1884) ([64414e0](https://github.com/algolia/instantsearch.js/commit/64414e0)) - - - - -## [2.2.3](https://github.com/algolia/instantsearch.js/compare/v2.2.2...v2.2.3) (2017-01-20) - - -### Bug Fixes - -* **SFFV:** translations for searchbox were not applied (#1879) ([e9b4ee1](https://github.com/algolia/instantsearch.js/commit/e9b4ee1)) - - - - -## [2.2.2](https://github.com/algolia/instantsearch.js/compare/v2.2.1...v2.2.2) (2017-01-18) - - -### Bug Fixes - -* **react-router:** search was triggered two many times (#1840) ([25e9db5](https://github.com/algolia/instantsearch.js/commit/25e9db5)) -* **SFFV:** empty query triggered a new SFFV (#1875) ([6c8259a](https://github.com/algolia/instantsearch.js/commit/6c8259a)) - - - - -## [2.2.1](https://github.com/algolia/instantsearch.js/compare/v2.2.0...v2.2.1) (2017-01-18) - - -### Bug Fixes - -* **createInstantsearch:** fix missing props (#1867) ([8d319b5](https://github.com/algolia/instantsearch.js/commit/8d319b5)), closes [#1867](https://github.com/algolia/instantsearch.js/issues/1867) - - - - -# [2.2.0](https://github.com/algolia/instantsearch.js/compare/v2.1.0...v2.2.0) (2017-01-17) - - -### Bug Fixes - -* **clear:** clearing wasn't working with too+ same type facets selected (#1820) ([a9a2364](https://github.com/algolia/instantsearch.js/commit/a9a2364)) -* **connectSearchBox:** handle `defaultRefinement` (#1829) ([7a730e2](https://github.com/algolia/instantsearch.js/commit/7a730e2)), closes [#1826](https://github.com/algolia/instantsearch.js/issues/1826) -* **Instantsearch:** Update all props on InstantSearch (#1828) ([2ed9b49](https://github.com/algolia/instantsearch.js/commit/2ed9b49)) -* **InstantSearch:** add specific `react-instantsearch ${version}` agent (#1844) ([a1113bc](https://github.com/algolia/instantsearch.js/commit/a1113bc)) -* **SFFV:** correct propTypes and add missing default values (#1845) ([a4c1b31](https://github.com/algolia/instantsearch.js/commit/a4c1b31)) -* **test:** add missing Snippet and Highliter snapshot ([4accce5](https://github.com/algolia/instantsearch.js/commit/4accce5)) -* **widgets:** replace setImmediate use with Promise use when update is needed (#1811) ([17e2497](https://github.com/algolia/instantsearch.js/commit/17e2497)) - - -### Features - -* **Menu, connectMenu:** add search for facet values (#1822) ([a6c513e](https://github.com/algolia/instantsearch.js/commit/a6c513e)) -* **snippet:** add a snippet widget to be able to highlight snippet results (#1797) ([2aecc40](https://github.com/algolia/instantsearch.js/commit/2aecc40)) -* **widgets:** add transformItems to be able to sort and filter (#1809) ([ba539f0](https://github.com/algolia/instantsearch.js/commit/ba539f0)) - - - - -# [2.1.0](https://github.com/algolia/instantsearch.js/compare/v2.0.1...v2.1.0) (2017-01-04) - - -### Bug Fixes - -* **createInstantSearchManager:** drop outdated response (#1765) ([76c5312](https://github.com/algolia/instantsearch.js/commit/76c5312)) -* **highlight:** highlight should work even if the attribute is missing (#1791) ([5b79b15](https://github.com/algolia/instantsearch.js/commit/5b79b15)), closes [#1790](https://github.com/algolia/instantsearch.js/issues/1790) -* **InfiniteHits:** better classname to loadmore btn (#1789) ([ad2ded3](https://github.com/algolia/instantsearch.js/commit/ad2ded3)) -* **starRatings:** click on selected range doesn't unselect it (#1766) ([beacc72](https://github.com/algolia/instantsearch.js/commit/beacc72)) -* **website:** broken demo links (#1802) ([0abe2f5](https://github.com/algolia/instantsearch.js/commit/0abe2f5)) -* **widgets:** add 300px width for the default SearchBox (#1803) ([bf5d791](https://github.com/algolia/instantsearch.js/commit/bf5d791)) - - -### Features - -* **InfiniteHits:** Add class to load more button (#1787) ([416febd](https://github.com/algolia/instantsearch.js/commit/416febd)) -* **RefinementList, connectRefinementList:** allow to search for facet values ([e086a81](https://github.com/algolia/instantsearch.js/commit/e086a81)) - - - - -## [2.0.1](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.1) (2016-12-15) - - -### Bug Fixes - -* **connectRange:** when unfinite numbers are passed throw ([75bec0d](https://github.com/algolia/instantsearch.js/commit/75bec0d)) -* **react-native:** use View as a container for react-native (#1729) ([5b76f75](https://github.com/algolia/instantsearch.js/commit/5b76f75)), closes [#1730](https://github.com/algolia/instantsearch.js/issues/1730) -* **SearchBox:** autocomplete was not disabled by default (#1742) ([bc76618](https://github.com/algolia/instantsearch.js/commit/bc76618)) -* **starRating:** call createURL with the right interface (min/max) (#1747) ([f9ab9b6](https://github.com/algolia/instantsearch.js/commit/f9ab9b6)) - - - - -## [2.0.0](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.0) (2016-12-08) - -First release of `react-instantsearch` diff --git a/packages/react-instantsearch-hooks-web/README.md b/packages/react-instantsearch-hooks-web/README.md deleted file mode 100644 index 350d545458..0000000000 --- a/packages/react-instantsearch-hooks-web/README.md +++ /dev/null @@ -1,124 +0,0 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [react-instantsearch-hooks-web](#react-instantsearch-hooks-web) - - [Why](#why) - - [Installation](#installation) - - [Getting started](#getting-started) - - [Documentation](#documentation) - - [Playground](#playground) - - [Contributing](#contributing) - - [License](#license) - - - -# react-instantsearch-hooks-web - -React InstantSearch Hooks Web is an open-source React library that lets you create an instant search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family: - -**React InstantSearch** | [InstantSearch.js][instantsearch.js-github] | [Angular InstantSearch][instantsearch-angular-github] | [Vue InstantSearch][instantsearch-vue-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github] - -## Why - -You should be using React InstantSearch Hooks Web if you want to: - -* Design search experiences with best practices -* Customize your components at will -* Follow React principles - -Note: If you are working with React Native, or otherwise do not use the DOM, check out `react-instantsearch-hooks` instead. - -## Installation - -React InstantSearch is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs. - -```sh -yarn add algoliasearch react-instantsearch-hooks-web -# or -npm install algoliasearch react-instantsearch-hooks-web -``` - -## Getting started - -Using React InstantSearch Hooks Web is as simple as adding these components to your app: - -```javascript -import React from 'react'; -import ReactDOM from 'react-dom'; -import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch, SearchBox, Hits } from 'react-instantsearch-hooks-web'; - -const searchClient = algoliasearch( - 'latency', - '6be0576ff61c053d5f9a3225e2a90f76' -); - -const App = () => ( - - - - -); -``` - -

    - - Edit on CodeSandbox - -

    - -To learn more about the library, follow the [getting started guide][doc-getting-started]. - -## Documentation - -The documentation is available on [algolia.com/doc][doc]. - -## Playground - -You can get to know React InstantSearch on [this playground][doc-playground]. - -Start by [adding components][doc-getting-started] and tweaking the display. Once you get more familiar with the library, you can learn more advanced concepts in [our guides][doc-guides]. - -## Contributing - -We welcome all contributors, from casual to regular 💙 - -- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport]. -- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest]. -- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it. -- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore]. - -To start contributing to code, you need to: - -1. [Fork the project](https://help.github.com/articles/fork-a-repo/) -1. [Clone the repository](https://help.github.com/articles/cloning-a-repository/) -1. Install the dependencies: `yarn` - -Please read [our contribution process](https://github.com/algolia/instantsearch.js/blob/master/CONTRIBUTING.md) to learn more. - -## License - -React InstantSearch Hooks is [MIT licensed](../../LICENSE). - - - -[doc]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/ -[doc-getting-started]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/react-hooks/ -[doc-guides]: https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react-hooks/ -[doc-playground]: https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/default-theme -[algolia-website]: https://www.algolia.com/ -[instantsearch.js-github]: https://github.com/algolia/instantsearch.js -[instantsearch-android-github]: https://github.com/algolia/instantsearch-android -[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios -[instantsearch-vue-github]: https://github.com/algolia/vue-instantsearch -[instantsearch-angular-github]: https://github.com/algolia/angular-instantsearch -[contributing-bugreport]: https://github.com/algolia/instantsearch.js/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-featurerequest]: https://github.com/algolia/instantsearch.js/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch+Hooks&title=Feature%20request%3A%20 -[contributing-newissue]: https://github.com/algolia/instantsearch.js/issues/new?labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-label-easy]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-bug]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-chore]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 diff --git a/packages/react-instantsearch-hooks-web/package.json b/packages/react-instantsearch-hooks-web/package.json deleted file mode 100644 index 799b1be978..0000000000 --- a/packages/react-instantsearch-hooks-web/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "react-instantsearch-hooks-web", - "version": "6.47.3", - "description": "⚡ Lightning-fast search for React, by Algolia", - "source": "src/index.ts", - "types": "dist/es/index.d.ts", - "main": "dist/cjs/index.js", - "module": "dist/es/index.js", - "type": "module", - "exports": { - ".": { - "import": "./dist/es/index.js", - "require": "./dist/cjs/index.js" - } - }, - "sideEffects": false, - "license": "MIT", - "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/", - "repository": { - "type": "git", - "url": "https://github.com/algolia/instantsearch.js" - }, - "author": { - "name": "Algolia, Inc.", - "url": "https://www.algolia.com" - }, - "keywords": [ - "algolia", - "components", - "fast", - "instantsearch", - "react", - "search" - ], - "files": [ - "README.md", - "dist" - ], - "scripts": { - "clean": "rm -rf dist", - "watch": "yarn build:cjs --watch", - "build": "yarn build:cjs && yarn build:es && yarn build:umd && yarn build:types", - "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet && ../../scripts/prepare-cjs.sh", - "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", - "build:umd": "BABEL_ENV=rollup rollup -c rollup.config.js", - "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es", - "test:exports": "node ./test/module/is-es-module.mjs && node ./test/module/is-cjs-module.cjs" - }, - "dependencies": { - "@babel/runtime": "^7.1.2", - "instantsearch.js": "4.56.8", - "react-instantsearch-hooks": "6.47.3" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 5", - "react": ">= 16.8.0 < 19", - "react-dom": ">= 16.8.0 < 19" - } -} diff --git a/packages/react-instantsearch-hooks-web/rollup.config.js b/packages/react-instantsearch-hooks-web/rollup.config.js deleted file mode 100644 index 368803df37..0000000000 --- a/packages/react-instantsearch-hooks-web/rollup.config.js +++ /dev/null @@ -1,80 +0,0 @@ -import babel from 'rollup-plugin-babel'; -import commonjs from 'rollup-plugin-commonjs'; -import filesize from 'rollup-plugin-filesize'; -import globals from 'rollup-plugin-node-globals'; -import resolve from 'rollup-plugin-node-resolve'; -import replace from 'rollup-plugin-replace'; -import { uglify } from 'rollup-plugin-uglify'; - -const clear = (x) => x.filter(Boolean); - -const version = process.env.VERSION || 'UNRELEASED'; -const algolia = '© Algolia, inc.'; -const link = 'https://github.com/algolia/instantsearch.js'; -const createBanner = (name) => - `/*! React InstantSearch${name} ${version} | ${algolia} | ${link} */`; - -const plugins = [ - babel({ - exclude: /node_modules|algoliasearch-helper/, - extensions: ['.js', '.ts', '.tsx'], - rootMode: 'upward', - runtimeHelpers: true, - }), - resolve({ - browser: true, - preferBuiltins: false, - extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], - }), - commonjs({ - namedExports: { - '../../node_modules/use-sync-external-store/shim/index.js': [ - 'useSyncExternalStore', - ], - }, - }), - globals(), - replace({ - 'process.env.NODE_ENV': JSON.stringify('production'), - }), - filesize({ - showMinifiedSize: false, - showGzippedSize: true, - }), -]; - -const createConfiguration = ({ name, minify = false } = {}) => ({ - input: 'src/index.ts', - external: ['react'], - output: { - file: `dist/umd/ReactInstantSearch${name}${minify ? '.min' : ''}.js`, - name: `ReactInstantSearch${name}`, - format: 'umd', - globals: { - react: 'React', - }, - banner: createBanner(name), - sourcemap: true, - }, - plugins: plugins.concat( - clear([ - minify && - uglify({ - output: { - preamble: createBanner(name), - }, - }), - ]) - ), -}); - -export default [ - createConfiguration({ - name: 'HooksDOM', - }), - - createConfiguration({ - name: 'HooksDOM', - minify: true, - }), -]; diff --git a/packages/react-instantsearch-hooks-web/src/index.ts b/packages/react-instantsearch-hooks-web/src/index.ts deleted file mode 100644 index cf8fff457d..0000000000 --- a/packages/react-instantsearch-hooks-web/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from 'react-instantsearch-hooks'; -export * from './widgets'; diff --git a/packages/react-instantsearch-hooks-web/test/module/is-cjs-module.cjs b/packages/react-instantsearch-hooks-web/test/module/is-cjs-module.cjs deleted file mode 100644 index 9668df8fb9..0000000000 --- a/packages/react-instantsearch-hooks-web/test/module/is-cjs-module.cjs +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable no-console */ - -const assert = require('assert'); - -const ReactInstantSearchHooksWeb = require('react-instantsearch-hooks-web'); - -assert.ok(ReactInstantSearchHooksWeb); - -console.log('react-instantsearch-hooks-web is valid CJS'); diff --git a/packages/react-instantsearch-hooks-web/test/module/is-es-module.mjs b/packages/react-instantsearch-hooks-web/test/module/is-es-module.mjs deleted file mode 100644 index d65ecf88f6..0000000000 --- a/packages/react-instantsearch-hooks-web/test/module/is-es-module.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-disable no-console */ -import assert from 'assert'; - -import * as ReactInstantSearchHooksWeb from 'react-instantsearch-hooks-web'; - -assert.ok(ReactInstantSearchHooksWeb); - -console.log('react-instantsearch-hooks-web is valid ESM'); diff --git a/packages/react-instantsearch-hooks/CHANGELOG.md b/packages/react-instantsearch-hooks/CHANGELOG.md deleted file mode 100644 index 1036d686f4..0000000000 --- a/packages/react-instantsearch-hooks/CHANGELOG.md +++ /dev/null @@ -1,2060 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [6.47.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.47.2...react-instantsearch-hooks@6.47.3) (2023-07-27) - - -### Bug Fixes - -* add a future warning when the package name changes ([#5778](https://github.com/algolia/instantsearch.js/issues/5778)) ([3d22ee4](https://github.com/algolia/instantsearch.js/commit/3d22ee45e1f03a443323a371621262f1fe45e664)) -* **useInstantSearch:** deprecate `use` function ([#5781](https://github.com/algolia/instantsearch.js/issues/5781)) ([ec16c6e](https://github.com/algolia/instantsearch.js/commit/ec16c6e74cc9e364aca47d64983be32bf0cce0fe)) - - - - - -## [6.47.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.47.1...react-instantsearch-hooks@6.47.2) (2023-07-25) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -## [6.47.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.47.0...react-instantsearch-hooks@6.47.1) (2023-07-19) - - -### Bug Fixes - -* **instantsearch:** keep algoliasearch-helper as external dependency during build ([#5765](https://github.com/algolia/instantsearch.js/issues/5765)) ([550fefa](https://github.com/algolia/instantsearch.js/commit/550fefa1401773f38dedc20322513ae662faa25d)) - - - - - -# [6.47.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.46.0...react-instantsearch-hooks@6.47.0) (2023-07-18) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - -# [6.46.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.45.1...react-instantsearch-hooks@6.46.0) (2023-07-10) - - -### Bug Fixes - -* **url:** base createURL on UiState instead of SearchParameters ([#5696](https://github.com/algolia/instantsearch.js/issues/5696)) ([7e2c8a2](https://github.com/algolia/instantsearch.js/commit/7e2c8a295a6fc5ba36d9482f645ef55b422d5e75)), closes [#5694](https://github.com/algolia/instantsearch.js/issues/5694) - - -### Features - -* **GeoSearch:** expose useGeoSearch() hook in RISH ([#5693](https://github.com/algolia/instantsearch.js/issues/5693)) ([b951b7b](https://github.com/algolia/instantsearch.js/commit/b951b7bcafb384d990ccf02538d9bb9e248a6bba)) - - - - - -## [6.45.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.45.0...react-instantsearch-hooks@6.45.1) (2023-07-04) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -# [6.45.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.3...react-instantsearch-hooks@6.45.0) (2023-06-20) - - -### Bug Fixes - -* **dependencies:** update helper requirement ([#5676](https://github.com/algolia/instantsearch.js/issues/5676)) ([c289120](https://github.com/algolia/instantsearch.js/commit/c2891205c1125b1203b3b3db946d57e0fc4e4687)), closes [#5658](https://github.com/algolia/instantsearch.js/issues/5658) - - -### Features - -* **algoliaAgent:** track Next.js version as Algolia agent ([#5677](https://github.com/algolia/instantsearch.js/issues/5677)) ([b205ac0](https://github.com/algolia/instantsearch.js/commit/b205ac019f79699cd608d63792b8ff5a0832aa7c)) - - - - - -## [6.44.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.2...react-instantsearch-hooks@6.44.3) (2023-06-13) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -## [6.44.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.1...react-instantsearch-hooks@6.44.2) (2023-06-05) - - -### Bug Fixes - -* **hooks:** generate version for algoliaAgent in build ([#5655](https://github.com/algolia/instantsearch.js/issues/5655)) ([4d92ac4](https://github.com/algolia/instantsearch.js/commit/4d92ac4769e19e81fb55481aedc71121bef981cb)) - - - - - -## [6.44.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.44.0...react-instantsearch-hooks@6.44.1) (2023-05-30) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -# [6.44.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.43.0...react-instantsearch-hooks@6.44.0) (2023-05-16) - - -### Bug Fixes - -* **this:** ensure all functions are able to be destructured ([#5611](https://github.com/algolia/instantsearch.js/issues/5611)) ([a8b5c1e](https://github.com/algolia/instantsearch.js/commit/a8b5c1e5bbd6afac39fce523f7d7c2ec02f51153)), closes [#5589](https://github.com/algolia/instantsearch.js/issues/5589) - - -### Features - -* **instantsearch:** make root indexName optional ([#5590](https://github.com/algolia/instantsearch.js/issues/5590)) ([80f309e](https://github.com/algolia/instantsearch.js/commit/80f309ed69b61534ca118b60c9c88691e0148fca)) - - - - - -# [6.43.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.42.2...react-instantsearch-hooks@6.43.0) (2023-04-24) - - -### Bug Fixes - -* **lifecycle:** prevent extra network requests when unmounting multiple widgets ([#5602](https://github.com/algolia/instantsearch.js/issues/5602)) ([11458ee](https://github.com/algolia/instantsearch.js/commit/11458eee7e7f0f3e9c5f368584a16f58646b1cdd)) - - -### Features - - -* **insights:** add insights option to InstantSearch ([#5488](https://github.com/algolia/instantsearch.js/issues/5488)) ([9031573](https://github.com/algolia/instantsearch.js/commit/9031573807fa6803dcfae9f33d61b8f111f68423)) ([#5578](https://github.com/algolia/instantsearch.js/issues/5578)) ([8fb517f](https://github.com/algolia/instantsearch.js/commit/8fb517f15381ecb32ea00cf4b01a0fd5e70e1d17)) ([#5545](https://github.com/algolia/instantsearch.js/issues/5545)) ([99a0972](https://github.com/algolia/instantsearch.js/commit/99a0972663b8f3284cac3b5621571ced7a33908f)) ([#5493](https://github.com/algolia/instantsearch.js/issues/5493)) ([cff723f](https://github.com/algolia/instantsearch.js/commit/cff723fc95a90ebb2ed14c46c51ab05764835a47)) -* **insights:** always pass Algolia credentials locally ([#5554](https://github.com/algolia/instantsearch.js/issues/5554)) ([654ab81](https://github.com/algolia/instantsearch.js/commit/654ab81e1669354c249710b6756610fba35d54b4)) ([#5558](https://github.com/algolia/instantsearch.js/issues/5558)) ([82144c0](https://github.com/algolia/instantsearch.js/commit/82144c0a0b18e6b47d6f508e5c670a9de274c121)) ([#5529](https://github.com/algolia/instantsearch.js/issues/5529)) ([8537f8f](https://github.com/algolia/instantsearch.js/commit/8537f8f7a10bcaf053ff62180c082e077b1b052d)) -* **insights:** annotate events with algoliaSource ([#5580](https://github.com/algolia/instantsearch.js/issues/5580)) ([c419307](https://github.com/algolia/instantsearch.js/commit/c419307a5f7fe46d5032c9437a17c8e3dad57fe5)) -* **insights:** automatically load search-insights if not passed ([#5484](https://github.com/algolia/instantsearch.js/issues/5484)) ([a85797b](https://github.com/algolia/instantsearch.js/commit/a85797b503edc94e001c5bfb3b754db6cb556943)) -* **insights:** enable default click events on hits and infinite hits ([#5522](https://github.com/algolia/instantsearch.js/issues/5522)) ([271bd12](https://github.com/algolia/instantsearch.js/commit/271bd12e34bc55656976bb53c90282193083eb86)) ([#5527](https://github.com/algolia/instantsearch.js/issues/5527)) ([0e55821](https://github.com/algolia/instantsearch.js/commit/0e558213c807cd17d592fadec052f3d1fc692e6c)) -* **insights:** prevent potential errors ([#5487](https://github.com/algolia/instantsearch.js/issues/5487)) ([33fe510](https://github.com/algolia/instantsearch.js/commit/33fe510307e4b382a5ba1153a0eaf160420acd11)) ([#5606](https://github.com/algolia/instantsearch.js/issues/5606)) ([bdd9290](https://github.com/algolia/instantsearch.js/commit/bdd92901b59ae4e5d7311eadfbf53ed656bbaf4a)) ([#5512](https://github.com/algolia/instantsearch.js/issues/5512)) ([85dfbc9](https://github.com/algolia/instantsearch.js/commit/85dfbc9ebd722fbe6a7e1bd056950fdbcc16d8d9)) -* **metadata:** register metadata around middleware ([#5492](https://github.com/algolia/instantsearch.js/issues/5492)) ([3e72ec8](https://github.com/algolia/instantsearch.js/commit/3e72ec82894a05a071328a4802d2f764233fe005)) - - - - -## [6.42.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.42.1...react-instantsearch-hooks@6.42.2) (2023-04-11) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -## [6.42.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.42.0...react-instantsearch-hooks@6.42.1) (2023-03-28) - -### Features - -* **InstantSearch**: warn when an unstable search client is detected ([#5563](https://github.com/algolia/instantsearch.js/issues/5563)) ([0fcf716](https://github.com/algolia/instantsearch/commit/0fcf7162ce77246133c0d4a6ff7ea975ba17cc4c)) - - - -# [6.42.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.41.0...react-instantsearch-hooks@6.42.0) (2023-03-21) - - -### Features - -* **current-refinements:** provide indexId of refinements in transformItems ([#5546](https://github.com/algolia/instantsearch.js/issues/5546)) ([89781db](https://github.com/algolia/instantsearch.js/commit/89781db6cb1d2b8ebbc116e9bcd8a10f646e7baf)) - - - - - -# [6.41.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.40.1...react-instantsearch-hooks@6.41.0) (2023-03-07) - - -### Features - -* **index:** introduce setIndexUiState ([#5504](https://github.com/algolia/instantsearch.js/issues/5504)) ([c199feb](https://github.com/algolia/instantsearch.js/commit/c199febbc3381df574afbb2504edd7373b32904a)) -* **react:** export `InstantSearchApi` type ([#5518](https://github.com/algolia/instantsearch.js/issues/5518)) ([27b478f](https://github.com/algolia/instantsearch.js/commit/27b478f8f20c4e8835914cceabbdce57ff5d4650)) - - -## Bug Fixes - -* **DynamicWidgets**: prevent non-stable fallbackComponent ([#5532](https://github.com/algolia/instantsearch.js/issues/5532)) ([0625c90](https://github.com/algolia/instantsearch.js/commit/0625c90346e32b926d6ce276a4e0b13d4fa4bf6c)) - - -## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.40.1...react-instantsearch-hooks@6.40.2) (2023-02-28) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.40.0...react-instantsearch-hooks@6.40.1) (2023-02-21) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -# [6.40.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.3...react-instantsearch-hooks@6.40.0) (2023-02-14) - - -### Features - -* Warn when not using the dedicated Next.js router ([#5432](https://github.com/algolia/instantsearch.js/issues/5432)) ([39b5859](https://github.com/algolia/instantsearch.js/commit/39b5859ba78a5e8472a80e357a35ba900c963b61)) - - -### Bug Fixes - -* Prevent issue where instantsearch instance got created twice in ssr ([#5432](https://github.com/algolia/instantsearch.js/issues/5432)) ([39b5859](https://github.com/algolia/instantsearch.js/commit/39b5859ba78a5e8472a80e357a35ba900c963b61)) - - - -## [6.39.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.2...react-instantsearch-hooks@6.39.3) (2023-02-07) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -## [6.39.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.1...react-instantsearch-hooks@6.39.2) (2023-01-30) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.39.0...react-instantsearch-hooks@6.39.1) (2023-01-26) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -# [6.39.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.38.3...react-instantsearch-hooks@6.39.0) (2023-01-25) - - -### Features - -* **react-instantsearch-hooks-web:** Add stats widget and ui component ([#5427](https://github.com/algolia/instantsearch.js/issues/5427)) ([d07cf0d](https://github.com/algolia/instantsearch.js/commit/d07cf0d0310bf4e49d4a4c2142b3783d9bcda79d)) -* **react-instantsearch-hooks:** Add useStats hook ([#5425](https://github.com/algolia/instantsearch.js/issues/5425)) ([772c918](https://github.com/algolia/instantsearch.js/commit/772c918f47aec183af3f1aa78c65505f70dd0088)) -* **rendering:** always render with current state ([#5429](https://github.com/algolia/instantsearch.js/issues/5429)) ([920e951](https://github.com/algolia/instantsearch.js/commit/920e951f03aada0e6a1ce16bc389a82a2f00b202)) - - - - - -## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.38.2...react-instantsearch-hooks@6.38.3) (2023-01-09) - - -### Bug Fixes - -* **dependencies:** update helper requirement for minor PP vulnerability ([#5417](https://github.com/algolia/instantsearch.js/issues/5417)) ([254ef00](https://github.com/algolia/instantsearch.js/commit/254ef00439a9af48be15f22b4fd9902899610226)) - - - - - -## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks@6.38.1...react-instantsearch-hooks@6.38.2) (2023-01-03) - -**Note:** Version bump only for package react-instantsearch-hooks - - - - - -## [6.38.1](https://github.com/algolia/react-instantsearch/compare/v6.38.0...v6.38.1) (2022-11-08) - - -### Bug Fixes - -* **hooks:** avoid effect in useStableValue ([#3670](https://github.com/algolia/react-instantsearch/issues/3670)) ([d1f53ae](https://github.com/algolia/react-instantsearch/commit/d1f53ae815b75f13c18fd245e0403d57e7ae391c)) - - - -# [6.38.0](https://github.com/algolia/react-instantsearch/compare/v6.37.0...v6.38.0) (2022-10-25) - - -### Features - -* **getServerState:** allow users to inject renderToString ([#3658](https://github.com/algolia/react-instantsearch/issues/3658)) ([9c10449](https://github.com/algolia/react-instantsearch/commit/9c104497b9b32337f288d70a2582c41cafb13cd6)), closes [#3633](https://github.com/algolia/react-instantsearch/issues/3633) [#3618](https://github.com/algolia/react-instantsearch/issues/3618) [vercel/next.js#40067](https://github.com/vercel/next.js/issues/40067) - -* **PoweredBy:** update component logo ([#3661](https://github.com/algolia/react-instantsearch/issues/3661)) ([69c1b2a](https://github.com/algolia/react-instantsearch/commit/69c1b2acef64d972dfa6c6beb8967032119ad2d5)) - - - -# [6.37.0](https://github.com/algolia/react-instantsearch/compare/v6.36.0...v6.37.0) (2022-10-18) - - -### Features - -* **core:** support react 18 strict mode ([#3653](https://github.com/algolia/react-instantsearch/issues/3653)) ([9174806](https://github.com/algolia/react-instantsearch/commit/9174806a7997a45893a24d149027119f4a0709c3)) - - - -# [6.36.0](https://github.com/algolia/react-instantsearch/compare/v6.35.0...v6.36.0) (2022-10-04) - - -### Features - -* **HierarchicalMenu:** add css class for link of selected menu item ([#3646](https://github.com/algolia/react-instantsearch/issues/3646)) ([980ad70](https://github.com/algolia/react-instantsearch/commit/980ad70c75e970c35c11a10a534dbe3996d6c54c)) -* **useInstantSearch:** expose status & error ([#3645](https://github.com/algolia/react-instantsearch/issues/3645)) ([f436d31](https://github.com/algolia/react-instantsearch/commit/f436d31184f3f75b33a1fdaa19c665e77948df28)) - - - -# [6.35.0](https://github.com/algolia/react-instantsearch/compare/v6.34.0...v6.35.0) (2022-09-29) - - -### Features - -* **hooks-web:** introduce Translations API ([#3638](https://github.com/algolia/react-instantsearch/issues/3638)) ([63b506f](https://github.com/algolia/react-instantsearch/commit/63b506f9dbad284f45ac17210e17c4a2a8f099b6)) - -### Fixes -* **hooks-web:** when searchAsYouType=false, pressing the reset button searches (previously only reset the query) ([#3642](https://github.com/algolia/react-instantsearch/issues/3642)) ([f969deb](https://github.com/algolia/react-instantsearch/commit/f969deb05fd4f53aaa251ff88b52db2224ce0786)) - -# [6.34.0](https://github.com/algolia/react-instantsearch/compare/v6.33.0...v6.34.0) (2022-09-27) - - -### Features - -* **SearchBox:** expose formRef ([#3565](https://github.com/algolia/react-instantsearch/issues/3565)) ([1c2f46d](https://github.com/algolia/react-instantsearch/commit/1c2f46da2d1cf705cfd3946c52aef4ca9ec943d7)) - - - -# [6.33.0](https://github.com/algolia/react-instantsearch/compare/v6.32.1...v6.33.0) (2022-09-15) - - -### Bug Fixes - -* **react-native:** mark as compatible with react 18 ([#3614](https://github.com/algolia/react-instantsearch/issues/3614)) ([2a191a8](https://github.com/algolia/react-instantsearch/commit/2a191a84751127de5a3eb34b59b460a1d1bfe594)) -* **rish:** hide reset button when search is stalled in `SearchBox` ([#3617](https://github.com/algolia/react-instantsearch/issues/3617)) ([93ee9d0](https://github.com/algolia/react-instantsearch/commit/93ee9d0212893cef4842c86b7c78f285aa136be8)) - - -### Features - -* **dependencies:** update instantsearch and helper ([#3622](https://github.com/algolia/react-instantsearch/issues/3622)) ([6773ab1](https://github.com/algolia/react-instantsearch/commit/6773ab169cd74dfe1133e255daade4d57e99d9a4)) - - - -## [6.32.1](https://github.com/algolia/react-instantsearch/compare/v6.32.0...v6.32.1) (2022-08-26) - - -### Bug Fixes - -* **useInstantSearch:** prevent `results` from being `null` when first search is stalled ([#3597](https://github.com/algolia/react-instantsearch/issues/3597)) ([6f71d78](https://github.com/algolia/react-instantsearch/commit/6f71d78868fde55a3f9c4460edc337a1e99df4f9)) - - - -# [6.32.0](https://github.com/algolia/react-instantsearch/compare/v6.31.1...v6.32.0) (2022-08-22) - - -### Features - -* **SearchBox:** introduce `autoFocus` prop ([#3599](https://github.com/algolia/react-instantsearch/issues/3599)) ([99121b9](https://github.com/algolia/react-instantsearch/commit/99121b952fd002cb6dae52af41f08beed8f6c3e2)) - - - -## [6.31.1](https://github.com/algolia/react-instantsearch/compare/v6.31.0...v6.31.1) (2022-08-08) - - -### Bug Fixes - -* **hooks:** prevent widget cleanup on `` unmount ([#3590](https://github.com/algolia/react-instantsearch/issues/3590)) ([d94899d](https://github.com/algolia/react-instantsearch/commit/d94899d1264134f0cb1ca2d266a660f1fb2a588c)) - - - -# [6.31.0](https://github.com/algolia/react-instantsearch/compare/v6.30.3...v6.31.0) (2022-08-03) - - -### Features - -* **hooks:** add `searchAsYouType` option to `` ([#3585](https://github.com/algolia/react-instantsearch/issues/3585)) ([c610385](https://github.com/algolia/react-instantsearch/commit/c610385cb9688b23b3e041e31b9edd60392b341d)) - - - -## [6.30.3](https://github.com/algolia/react-instantsearch/compare/v6.30.2...v6.30.3) (2022-08-01) - - -### Bug Fixes - -* **hooks:** replace `labelText` CSS class with `label` in `` to align with InstantSearch's CSS specifications ([#3583](https://github.com/algolia/react-instantsearch/issues/3583)) ([3e030ae](https://github.com/algolia/react-instantsearch/commit/3e030aedb9f285ff449eb82589bc6fea60b160cb)) - - - -## [6.30.2](https://github.com/algolia/react-instantsearch/compare/v6.30.1...v6.30.2) (2022-07-18) - - -### Bug Fixes - -* **hooks:** type of DynamicWidgets props ([#3566](https://github.com/algolia/react-instantsearch/issues/3566)) ([612c98b](https://github.com/algolia/react-instantsearch/commit/612c98b5a77fb9037185c4b5efda8c07663dbd1a)), closes [#3563](https://github.com/algolia/react-instantsearch/issues/3563) -* **hooks:** use single instance in ([#3561](https://github.com/algolia/react-instantsearch/issues/3561)) ([4c358be](https://github.com/algolia/react-instantsearch/commit/4c358bebfc91451b1610f677f89c595d7a427f1f)) - - - -## [6.30.1](https://github.com/algolia/react-instantsearch/compare/v6.30.0...v6.30.1) (2022-07-12) - - -### Bug Fixes - -* **hooks:** provide state and results APIs from "render" event ([#3554](https://github.com/algolia/react-instantsearch/issues/3554)) ([67d4788](https://github.com/algolia/react-instantsearch/commit/67d4788ab09ec2a57b43d53e8093b8c11120b761)) -* **hooks**: update instantsearch.js dependency ([#3557](https://github.com/algolia/react-instantsearch/issues/3557)) - - - -# [6.30.0](https://github.com/algolia/react-instantsearch/compare/v6.29.0...v6.30.0) (2022-07-05) - - -### Features - -* **core:** update instantsearch and helper ([#3539](https://github.com/algolia/react-instantsearch/issues/3539)) ([0ac2c7a](https://github.com/algolia/react-instantsearch/commit/0ac2c7a3f2e2a827721f5b2b7c69c54560f8574f)) - - - -# [6.29.0](https://github.com/algolia/react-instantsearch/compare/v6.28.0...v6.29.0) (2022-06-21) - - -### Bug Fixes - -* **HierarchicalMenu:** show full hierarchical parent values ([#3521](https://github.com/algolia/react-instantsearch/issues/3521)) ([79c3890](https://github.com/algolia/react-instantsearch/commit/79c3890848175a4d70311e5c3929c902bb953c10)) - - -### Features - -* **core:** sort parameters for improved cache rate ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) -* **core:** support client.search for sffv ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) - - - -# [6.28.0](https://github.com/algolia/react-instantsearch/compare/v6.27.0...v6.28.0) (2022-06-15) - - -### Bug Fixes - -* **hooks-server:** import react server via an expression ([#3515](https://github.com/algolia/react-instantsearch/issues/3515)) ([91b96f7](https://github.com/algolia/react-instantsearch/commit/91b96f743b9315ed5ea781681b77fc7f5604ab6e)), closes [#3512](https://github.com/algolia/react-instantsearch/issues/3512) -* **hooks-web:** fix duplicated key in ([#3513](https://github.com/algolia/react-instantsearch/issues/3513)) ([fc94d80](https://github.com/algolia/react-instantsearch/commit/fc94d806daf139f58b234cdc0b450da2efe861ee)) -* **hooks:** mount widgets in SSR to retrieve HTML ([#3518](https://github.com/algolia/react-instantsearch/issues/3518)) ([aa5f9d8](https://github.com/algolia/react-instantsearch/commit/aa5f9d84ddb6e97d05e6ad1baf2c6caa23891281)) -* **types:** allow useInstantSearch to be generic ([#3508](https://github.com/algolia/react-instantsearch/issues/3508)) ([6807232](https://github.com/algolia/react-instantsearch/commit/68072324cf302801502a1b4c3d06703e57b55a97)), closes [algolia/instantsearch.js#5060](https://github.com/algolia/instantsearch.js/issues/5060) -* **types:** support React 18 types ([#3481](https://github.com/algolia/react-instantsearch/issues/3481)) ([74cf8cb](https://github.com/algolia/react-instantsearch/commit/74cf8cb9be8ff3d113b57a50e7083df0d1bc94f2)) - - -### Features - -* **hooks:** introduce `useInstantSearch()` ([#3494](https://github.com/algolia/react-instantsearch/issues/3494)) ([74d522c](https://github.com/algolia/react-instantsearch/commit/74d522c032326658f2a0b8f0001bd593e0085208)) -* **hooks:** support React 18 Strict Mode ([#3514](https://github.com/algolia/react-instantsearch/issues/3514)) ([eeb67c7](https://github.com/algolia/react-instantsearch/commit/eeb67c7b5dc08c696c46d9538f104eeceecef388)) - - - -# [6.27.0](https://github.com/algolia/react-instantsearch/compare/v6.26.0...v6.27.0) (2022-06-07) - - -### Bug Fixes - -* **hooks-web:** don't pass widget props to ui components ([#3501](https://github.com/algolia/react-instantsearch/issues/3501)) ([5bd53c1](https://github.com/algolia/react-instantsearch/commit/5bd53c128ddeeea87f75ae89eb8f2324d476c70e)), closes [#3499](https://github.com/algolia/react-instantsearch/issues/3499) -* **SearchBox-hooks:** correctly pass widget props ([#3499](https://github.com/algolia/react-instantsearch/issues/3499)) ([2cdf906](https://github.com/algolia/react-instantsearch/commit/2cdf90602b7c2c5895124ef64c389ce574154386)), closes [#3498](https://github.com/algolia/react-instantsearch/issues/3498) - - -### Features - -* **hooks:** migrate to `useSyncExternalStore()` ([#3489](https://github.com/algolia/react-instantsearch/issues/3489)) ([81bbdf2](https://github.com/algolia/react-instantsearch/commit/81bbdf28f2d28d8b0081cfd7d9e84c3e33038dd2)) -* **hooks:** upgrade to InstantSearch.js 4.41.0 ([#3502](https://github.com/algolia/react-instantsearch/issues/3502)) ([0b76792](https://github.com/algolia/react-instantsearch/commit/0b76792ea0c4e2ac9fe742810d70ba1aee2a3e79)) - - - -# [6.26.0](https://github.com/algolia/react-instantsearch/compare/v6.25.0...v6.26.0) (2022-05-19) - - -### Features - -* **hooks-web:** expose `sendEvent` to `hitComponent` ([#3476](https://github.com/algolia/react-instantsearch/issues/3476)) ([5cc18d1](https://github.com/algolia/react-instantsearch/commit/5cc18d19d9f22305f33d92e43fd0aca2a5cb949a)) -* **react-instantsearch-core:** allow widgets to set their `$$widgetType` ([#3472](https://github.com/algolia/react-instantsearch/issues/3472)) ([1c436e1](https://github.com/algolia/react-instantsearch/commit/1c436e1429ab4230bbfea7c6d2474d141f5c5c64)) - - - -# [6.25.0](https://github.com/algolia/react-instantsearch/compare/v6.24.3...v6.25.0) (2022-05-17) - - -### Bug Fixes - -* **hooks-highlight:** make sure highlight and snippet don't show html-escaped content ([#3471](https://github.com/algolia/react-instantsearch/issues/3471)) ([c18ddd2](https://github.com/algolia/react-instantsearch/commit/c18ddd25faca37d6bfa3d1c28f6fc22ec5fcf6d8)) -* **hooks-server:** remove faulty UMD build ([#3465](https://github.com/algolia/react-instantsearch/issues/3465)) ([c1ddfe4](https://github.com/algolia/react-instantsearch/commit/c1ddfe408b411551ac8524877a9d65ded8133c42)) - - -### Features - -* **dom-maps:** expose GeoSearchContext ([#3468](https://github.com/algolia/react-instantsearch/issues/3468)) ([a61ff96](https://github.com/algolia/react-instantsearch/commit/a61ff96222bfd4f6301cf93bf95e2fa18b263d3c)), closes [#3448](https://github.com/algolia/react-instantsearch/issues/3448) -* **hooks-server:** support import from React 18 ([#3464](https://github.com/algolia/react-instantsearch/issues/3464)) ([0a13867](https://github.com/algolia/react-instantsearch/commit/0a13867f7dd5a8a18e0957b2072bbde3b02d6490)), closes [#3453](https://github.com/algolia/react-instantsearch/issues/3453) -* **hooks:** make InstantSearch type generic ([#3466](https://github.com/algolia/react-instantsearch/issues/3466)) ([b0905b7](https://github.com/algolia/react-instantsearch/commit/b0905b73bed558c62eedb7ae701be20c2ebe25c9)) - - - -## [6.24.3](https://github.com/algolia/react-instantsearch/compare/v6.24.2...v6.24.3) (2022-05-10) - - -### Bug Fixes - -* **numericmenu:** include range values in comparison with minmax bounds ([#3461](https://github.com/algolia/react-instantsearch/issues/3461)) ([e4c2682](https://github.com/algolia/react-instantsearch/commit/e4c268261dc42a6aa43d985934b53c82f8b71089)) - - - -## [6.24.2](https://github.com/algolia/react-instantsearch/compare/v6.24.1...v6.24.2) (2022-05-05) - - -### Bug Fixes - -* **hooks:** prevent infinite loops from render state ([#3455](https://github.com/algolia/react-instantsearch/issues/3455)) ([1799fc9](https://github.com/algolia/react-instantsearch/commit/1799fc9f78a4a5aafb54df339c3e211ff9187748)) - - - -## [6.24.1](https://github.com/algolia/react-instantsearch/compare/v6.24.0...v6.24.1) (2022-05-03) - - -### Bug Fixes - -* **types:** export correct types for react-instantsearch-hooks-web ([#3454](https://github.com/algolia/react-instantsearch/issues/3454)) ([a863430](https://github.com/algolia/react-instantsearch/commit/a8634306621f7a05a2b3056a6db25ccf8d9eabf0)) - - - -# [6.24.0](https://github.com/algolia/react-instantsearch/compare/v6.23.4...v6.24.0) (2022-04-28) - - -### Features - -* **hooks:** expose DOM components ([#3450](https://github.com/algolia/react-instantsearch/issues/3450)) ([5732e3d](https://github.com/algolia/react-instantsearch/commit/5732e3de732275911f94b26ba9e2c4165bdf77e7)) -* **hooks:** remove experimental warning ([#3446](https://github.com/algolia/react-instantsearch/issues/3446)) ([84c99fe](https://github.com/algolia/react-instantsearch/commit/84c99fe91d6906a877bec620b44c61d762f0ea57)) - - - -## [6.23.4](https://github.com/algolia/react-instantsearch/compare/v6.23.3...v6.23.4) (2022-04-21) - - -### Bug Fixes - -* **hooks:** forbid importing from instantsearch.js root path ([#3437](https://github.com/algolia/react-instantsearch/issues/3437)) ([82ef9ea](https://github.com/algolia/react-instantsearch/commit/82ef9eaaec42bc54f15796b5b031a8656330a45c)) -* **packages:** correctly mark peer dependency ([#3439](https://github.com/algolia/react-instantsearch/issues/3439)) ([51e8818](https://github.com/algolia/react-instantsearch/commit/51e8818fce224819230c8bf6dea2a08d71d9be29)), closes [#3428](https://github.com/algolia/react-instantsearch/issues/3428) - - - -## [6.23.3](https://github.com/algolia/react-instantsearch/compare/v6.23.2...v6.23.3) (2022-04-05) - - -### Bug Fixes - -* **facets:** show raw value in currentRefinements ([#3420](https://github.com/algolia/react-instantsearch/issues/3420)) ([1199ce6](https://github.com/algolia/react-instantsearch/commit/1199ce6bd4152e4b54bd2ee0e1f0c9d81021c7d5)), closes [#3412](https://github.com/algolia/react-instantsearch/issues/3412) -* **multi-index:** correctly set `searching` prop in multi-index result states ([#3419](https://github.com/algolia/react-instantsearch/issues/3419)) ([7f8e97e](https://github.com/algolia/react-instantsearch/commit/7f8e97e31b3dd5e49d3febef673f41f7dfa6d45d)) - - - -## [6.23.2](https://github.com/algolia/react-instantsearch/compare/v6.23.1...v6.23.2) (2022-04-04) - - -### Bug Fixes - -* **refinements:** use escaped value for refining ([#3412](https://github.com/algolia/react-instantsearch/issues/3412)) ([f2f5f6c](https://github.com/algolia/react-instantsearch/commit/f2f5f6cbfaed48a5c494daeb8789e8deebc64293)) -* support React 18 as peer dependency ([#3411](https://github.com/algolia/react-instantsearch/issues/3411)) ([38eb5a6](https://github.com/algolia/react-instantsearch/commit/38eb5a6a8fe92cb763a25f452bea78b189a6a82a)) - - -## [6.23.1](https://algolia/compare/v6.23.0...v6.23.1) (2022-03-30) - - -### Bug Fixes - -* **hooks:** compute initial search parameters from widget ([#3399](https://algolia/issues/3399)) ([b4bd933](https://algolia/commits/b4bd93358598bdc77a1aa858252e6eee23441345)) - - - -# [6.23.0](https://algolia/compare/v6.22.0...v6.23.0) (2022-03-23) - - -### Bug Fixes - -* **ssr:** perform initial multi-index search using a single request ([#3385](https://algolia/issues/3385)) ([b96809e](https://algolia/commits/b96809e5748d298350890647956cb7adbbb55650)) - - -### Features - -* **hooks:** allow additional widget properties to be passed from hooks ([#3359](https://algolia/issues/3359)) ([a047be4](https://algolia/commits/a047be45c7fce7ee28f7d6f61d2fbfa79e3ed2d0)) -* **hooks:** allow useHits and useInfiniteHit to be generic ([#3364](https://algolia/issues/3364)) ([8e66ad3](https://algolia/commits/8e66ad3ad587197c4811c60a5cab475137ca5afb)) -* **hooks:** mark initial results as "artificial" ([#3384](https://algolia/issues/3384)) ([937efdc](https://algolia/commits/937efdc71bae1d99270f8ecb5c5c9c501b3d7769)) - - - -# [6.22.0](https://github.com/algolia/react-instantsearch/compare/v6.21.1...v6.22.0) (2022-02-01) - - -### Bug Fixes - -* **hooks:** enable pause on exceptions on warning ([#3283](https://github.com/algolia/react-instantsearch/issues/3283)) ([ce3a6c3](https://github.com/algolia/react-instantsearch/commit/ce3a6c3f2700a05ae54a91278fd23fa64a97d733)) - - -### Features - -* **hooks:** introduce `useBreadcrumb()` ([#3245](https://github.com/algolia/react-instantsearch/issues/3245)) ([5bfbaaf](https://github.com/algolia/react-instantsearch/commit/5bfbaaf746e7632968ac9b30b73357bcb0b37e91)) - - - -## [6.21.1](https://github.com/algolia/react-instantsearch/compare/v6.21.0...v6.21.1) (2022-01-25) - - -### Bug Fixes - -* **hooks:** apply initial search parameters in useConnector ([#3276](https://github.com/algolia/react-instantsearch/issues/3276)) ([f85d679](https://github.com/algolia/react-instantsearch/commit/f85d6794c31eac61521fd8fc16b75673f02ed75b)) - - - -# [6.21.0](https://github.com/algolia/react-instantsearch/compare/v6.20.0...v6.21.0) (2022-01-24) - - -### Features - -* **hooks-server:** load data twice in the case of dynamic widget usage ([#3259](https://github.com/algolia/react-instantsearch/issues/3259)) ([9b4903b](https://github.com/algolia/react-instantsearch/commit/9b4903b2ea73d9d7e33729b87d9d55990e64312c)) -* **server:** load data twice in the case of dynamic widget usage ([#3268](https://github.com/algolia/react-instantsearch/issues/3268)) ([91cd085](https://github.com/algolia/react-instantsearch/commit/91cd085f9a323ed6b872f3a098f561007a72d0d2)), closes [/github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js#L14-L16](https://github.com//github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js/issues/L14-L16) - - - -# [6.20.0](https://github.com/algolia/react-instantsearch/compare/v6.19.0...v6.20.0) (2022-01-18) - - -### Bug Fixes - -* **hooks:** allow importing via require ([#3257](https://github.com/algolia/react-instantsearch/issues/3257)) ([6aa80b3](https://github.com/algolia/react-instantsearch/commit/6aa80b3647567199c3df1b90a07d708b223ce1fd)) - - -### Features - -* **hooks:** implement `useClearRefinements()` ([#3256](https://github.com/algolia/react-instantsearch/issues/3256)) ([930b83d](https://github.com/algolia/react-instantsearch/commit/930b83df4c4bbccbc3118f6ea1001f6a30a3d464)), closes [#3252](https://github.com/algolia/react-instantsearch/issues/3252) -* **hooks:** introduce `` ([#3261](https://github.com/algolia/react-instantsearch/issues/3261)) ([3527b94](https://github.com/algolia/react-instantsearch/commit/3527b9422de48a4a6b4bd752bd643e01cd5011d8)) -* **hooks:** introduce `usePoweredBy()` ([#3251](https://github.com/algolia/react-instantsearch/issues/3251)) ([a97230b](https://github.com/algolia/react-instantsearch/commit/a97230b89e3ba1df9bf2d21a6a9f9a70b45f41b8)) -* **hooks:** introduce `useToggleRefinement()` ([#3248](https://github.com/algolia/react-instantsearch/issues/3248)) ([a561c09](https://github.com/algolia/react-instantsearch/commit/a561c090a268e1c6ca1fa2d5bf72263cc47aa273)) - - - -# [6.19.0](https://github.com/algolia/react-instantsearch/compare/v6.18.0...v6.19.0) (2022-01-05) - - -### Features - -* **hooks:** bundle as es-module ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) -* **dependencies:** update to latest algoliasearch-helper ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) - - - -# [6.18.0](https://github.com/algolia/react-instantsearch/compare/v6.17.0...v6.18.0) (2021-12-16) - - -### Features - -* **dynamicWidgets:** send facets * and maxValuesPerFacet by default ([#3242](https://github.com/algolia/react-instantsearch/issues/3242)) ([c071776](https://github.com/algolia/react-instantsearch/commit/c07177670ac30dced55250774654e8b2a77b6739)) -* **hooks:** introduce `useNumericMenu()` ([#3237](https://github.com/algolia/react-instantsearch/issues/3237)) ([e3056c9](https://github.com/algolia/react-instantsearch/commit/e3056c9e2c64b5afafb7a736599a5cbf6137575b)) -* **hooks:** introduce `useInfiniteHits()` ([#3224](https://github.com/algolia/react-instantsearch/issues/3224)) ([177ec56](https://github.com/algolia/react-instantsearch/commit/177ec56af274670c2bf8599ba104b5544979bbe8)) - - - -# [6.17.0](https://github.com/algolia/react-instantsearch/compare/v6.16.0...v6.17.0) (2021-12-08) - - -### Bug Fixes - -* **hooks:** throw invariant violations in production ([#3217](https://github.com/algolia/react-instantsearch/issues/3217)) ([6d3f99c](https://github.com/algolia/react-instantsearch/commit/6d3f99ca91f470ee742ddc55e95f57b1f1801d7b)) - - -### Features - -* **hooks:** introduce `useMenu()` ([#3197](https://github.com/algolia/react-instantsearch/issues/3197)) ([15d1cc9](https://github.com/algolia/react-instantsearch/commit/15d1cc993437b111cd5a32f43ee1d2065c639ed4)) -* **hooks:** introduce `useRange()` ([#3198](https://github.com/algolia/react-instantsearch/issues/3198)) ([df1f1c8](https://github.com/algolia/react-instantsearch/commit/df1f1c8109dc684e74d3aee1bf0359f2a0e1b9f4)) -* **hooks:** introduce `` ([#3216](https://github.com/algolia/react-instantsearch/issues/3216)) ([d99aea6](https://github.com/algolia/react-instantsearch/commit/d99aea6cfe9dea86ae6b98ee3762373f4b3843f1)) -* **hooks:** introduce `useDynamicWidgets()` ([#3210](https://github.com/algolia/react-instantsearch/issues/3210)) ([29c2ea2](https://github.com/algolia/react-instantsearch/commit/29c2ea22b91a39d9eb40a044ae9aab85f2612db8)) -* **hooks:** introduce `useQueryRules()` ([#3212](https://github.com/algolia/react-instantsearch/issues/3212)) ([3ef1e1e](https://github.com/algolia/react-instantsearch/commit/3ef1e1e4116b3e58b2c2134d0c60fbb9f40c1501)) -* **hooks:** introduce SSR support ([#3221](https://github.com/algolia/react-instantsearch/issues/3221)) ([0a6b3ec](https://github.com/algolia/react-instantsearch/commit/0a6b3ec61942ad3849c6f078e21b3328679bffff)) -* **hooks:** introduce `useCurrentRefinements()` ([#3222](https://github.com/algolia/react-instantsearch/issues/3222)) ([7ebd8c3](https://github.com/algolia/react-instantsearch/commit/7ebd8c3da8c17b0bd7e0f8deab633b98fa052e7f)) - - - -# [6.16.0](https://github.com/algolia/react-instantsearch/compare/v6.15.0...v6.16.0) (2021-11-22) - - -### Bug Fixes - -* **PoweredBy:** support environments with `window` but no `location` ([#3186](https://github.com/algolia/react-instantsearch/issues/3186)) ([22ff23b](https://github.com/algolia/react-instantsearch/commit/22ff23b67554683567393114c2f53cacec44f4a6)) - - -### Features - -* **DynamicWidgets:** release as stable ([#3090](https://github.com/algolia/react-instantsearch/issues/3090)) ([a4a1d9e](https://github.com/algolia/react-instantsearch/commit/a4a1d9e032b31c611d5d73fdda3a03ad705f5c68)) -* **hooks:** introduce `usePagination()` ([#3182](https://github.com/algolia/react-instantsearch/issues/3182)) ([d8b1b86](https://github.com/algolia/react-instantsearch/commit/d8b1b867bb598e801f1350e81b4a4220a8e528d7)) -* **hooks:** introduce `useSortBy()` ([#3190](https://github.com/algolia/react-instantsearch/issues/3190)) ([5cce33b](https://github.com/algolia/react-instantsearch/commit/5cce33b48032548fed76b592ee0201e3c42fc3c4)) -* **hooks:** introduce `useHierarchicalMenu()` ([#3199](https://github.com/algolia/react-instantsearch/issues/3199)) ([b347061](https://github.com/algolia/react-instantsearch/commit/b3470611b962ef55c55576c65a6307abc54e5efd)) - - - -# [6.15.0](https://github.com/algolia/react-instantsearch/compare/v6.14.0...v6.15.0) (2021-10-27) - - -### Bug Fixes - -* **metadata:** stricter detection of user agent ([#3184](https://github.com/algolia/react-instantsearch/issues/3184)) ([994c8ae](https://github.com/algolia/react-instantsearch/commit/994c8ae055fc23a1a067d111d2f4727b1c7bf8ca)) - - -### Features - -* **hooks:** introduce `useConfigure()` ([#3181](https://github.com/algolia/react-instantsearch/issues/3181)) ([aa2eb9b](https://github.com/algolia/react-instantsearch/commit/aa2eb9baec6335f8a3523ee8b9b761a217cfc734)) - - - -# [6.14.0](https://github.com/algolia/react-instantsearch/compare/v6.13.0...v6.14.0) (2021-10-26) - - -### Features - -* **dependencies:** update algoliasearch-helper ([#3176](https://github.com/algolia/react-instantsearch/issues/3176)) ([a8708a3](https://github.com/algolia/react-instantsearch/commit/a8708a33f31632000bc827b076539b1cca7adf6f)) -* **metadata:** expose widget information ([#3145](https://github.com/algolia/react-instantsearch/issues/3145)) ([46cddf8](https://github.com/algolia/react-instantsearch/commit/46cddf8fcc0291beaa34b04da7aaaa7f2566e52e)) - - - -# [6.13.0](https://github.com/algolia/react-instantsearch/compare/v6.12.1...v6.13.0) (2021-10-19) - -This is the initial release of the experimental **React InstantSearch Hooks** package. Check out the [**Getting Started**](https://github.com/algolia/react-instantsearch/blob/e8d72cb1c7c45300ef7c273f1f163beb6dc57622/packages/react-instantsearch-hooks/README.md#getting-started) guide. - -### Bug Fixes - -- **core:** accept objects for `hitComponent` ([#3087](https://github.com/algolia/react-instantsearch/issues/3087)) ([4ae23d4](https://github.com/algolia/react-instantsearch/commit/4ae23d432a01ccbefff1fcdc865120aeca4d3efc)) - -### Features - -- **hooks:** add InstantSearch and Index components ([#3133](https://github.com/algolia/react-instantsearch/issues/3133)) ([8e3370d](https://github.com/algolia/react-instantsearch/commit/8e3370ddb7d5e42b8b9a5ff6a1e4255490de7dbe)) -- **hooks:** bootstrap Core package ([#3132](https://github.com/algolia/react-instantsearch/issues/3132)) ([d459e62](https://github.com/algolia/react-instantsearch/commit/d459e62f5cae4c98427ab302531873f5ee23d149)) -- **hooks:** display experimental warning ([#3149](https://github.com/algolia/react-instantsearch/issues/3149)) ([623577c](https://github.com/algolia/react-instantsearch/commit/623577c50cd0c04cd87f719edafdcfa04b5527b6)) -- **hooks:** export types ([#3159](https://github.com/algolia/react-instantsearch/issues/3159)) ([182348b](https://github.com/algolia/react-instantsearch/commit/182348b4a901823a7a41aee5d2b3bdc025cce48f)) -- **hooks:** expose `displayName` on Contexts ([#3168](https://github.com/algolia/react-instantsearch/issues/3168)) ([dafd3c6](https://github.com/algolia/react-instantsearch/commit/dafd3c66d05fbec09ebf907209ac25f55804e6f5)) -- **hooks:** friendly error when using Hooks with Core ([#3150](https://github.com/algolia/react-instantsearch/issues/3150)) ([d547ccf](https://github.com/algolia/react-instantsearch/commit/d547ccf7951299e2f6b1771e02fce052696ff65a)) -- **hooks:** introduce `useConnector()` ([#3137](https://github.com/algolia/react-instantsearch/issues/3137)) ([53e8afd](https://github.com/algolia/react-instantsearch/commit/53e8afd093b9950351467a16b82d528207ac34d2)) -- **hooks:** introduce `useHits()` ([#3147](https://github.com/algolia/react-instantsearch/issues/3147)) ([cc25cff](https://github.com/algolia/react-instantsearch/commit/cc25cff06e5ec216cba40fb8261372bc327001b6)) -- **hooks:** introduce `useRefinementList()` ([#3152](https://github.com/algolia/react-instantsearch/issues/3152)) ([0385cd9](https://github.com/algolia/react-instantsearch/commit/0385cd971635a8423ad687fab451d0778358389e)) -- **hooks:** introduce `useSearchBox()` ([#3146](https://github.com/algolia/react-instantsearch/issues/3146)) ([0d2c7f9](https://github.com/algolia/react-instantsearch/commit/0d2c7f9bd25b88cf725a1babd3b228ac804644c7)) -- **hooks:** trigger single network request on load ([#3167](https://github.com/algolia/react-instantsearch/issues/3167)) ([ff1ea49](https://github.com/algolia/react-instantsearch/commit/ff1ea49079a7800fd61ba99ceeb74b9f513eb99d)) -- **hooks:** type `useConnector()` return as render state ([#3169](https://github.com/algolia/react-instantsearch/issues/3169)) ([a801468](https://github.com/algolia/react-instantsearch/commit/a80146860164a092d2c90ee0aa4fcef88d5b675f)) -- **hooks:** update GitHub bug reports link ([#3157](https://github.com/algolia/react-instantsearch/issues/3157)) ([568b5c8](https://github.com/algolia/react-instantsearch/commit/568b5c83849a3927417907706656c3835163f216)) - - - -## [6.12.1](https://github.com/algolia/react-instantsearch/compare/v6.12.0...v6.12.1) (2021-08-02) - - -### Bug Fixes - -* **server side rendering:** return a value from mock currentRefinement/metadata ([#3078](https://github.com/algolia/react-instantsearch/issues/3078)) ([09f802b](https://github.com/algolia/react-instantsearch/commit/09f802b)) - - - -# [6.12.0](https://github.com/algolia/react-instantsearch/compare/v6.11.2...v6.12.0) (2021-07-06) - - -### Bug Fixes - -* **HitsPerPage:** Adds id prop to HitsPerPage, Select components ([#3072](https://github.com/algolia/react-instantsearch/issues/3072)) ([bc75d75](https://github.com/algolia/react-instantsearch/commit/bc75d75)) -* **MenuSelect:** Adds id prop to MenuSelect ([#3073](https://github.com/algolia/react-instantsearch/issues/3073)) ([fddaaef](https://github.com/algolia/react-instantsearch/commit/fddaaef)) -* **SearchBox:** Adds inputId prop to SearchBox ([#3074](https://github.com/algolia/react-instantsearch/issues/3074)) ([a05f6a4](https://github.com/algolia/react-instantsearch/commit/a05f6a4)) -* **SortBy:** Adds `id` prop to `SortBy`, `Select` components ([#3068](https://github.com/algolia/react-instantsearch/issues/3068)) ([1f2797f](https://github.com/algolia/react-instantsearch/commit/1f2797f)) - - -### Features - -* **DynamicWidgets:** add implementation ([#3056](https://github.com/algolia/react-instantsearch/issues/3056)) ([691ef87](https://github.com/algolia/react-instantsearch/commit/691ef87)) -* **facets:** add a new option "facetOrdering" to Menu, RefinementList & HierarchicalMenu ([#3067](https://github.com/algolia/react-instantsearch/issues/3067)) ([731d9ba](https://github.com/algolia/react-instantsearch/commit/731d9ba)) - - - -## [6.11.2](https://github.com/algolia/react-instantsearch/compare/v6.11.1...v6.11.2) (2021-06-28) - - -### Bug Fixes - -* **maps:** leave zoom in place if the bounds did not change ([#3050](https://github.com/algolia/react-instantsearch/issues/3050)) ([c430598](https://github.com/algolia/react-instantsearch/commit/c430598)) - - - -## [6.11.1](https://github.com/algolia/react-instantsearch/compare/v6.11.0...v6.11.1) (2021-06-09) - - -### Bug Fixes - -* **RefinementList:** prevent searchable component to refine on empty list ([#3059](https://github.com/algolia/react-instantsearch/issues/3059)) ([04f3644](https://github.com/algolia/react-instantsearch/commit/04f3644)) - - - -# [6.11.0](https://github.com/algolia/react-instantsearch/compare/v6.10.3...v6.11.0) (2021-05-04) - - -### Features - -* **connectNumericMenu:** add support for floating point values ([#3047](https://github.com/algolia/react-instantsearch/issues/3047)) ([091bf57](https://github.com/algolia/react-instantsearch/commit/091bf57)) - - - -## [6.10.3](https://github.com/algolia/react-instantsearch/compare/v6.10.2...v6.10.3) (2021-03-03) - - -### Bug Fixes - -* **RelevantSort:** Rename `SmartSort` widget to `RelevantSort` ([#3026](https://github.com/algolia/react-instantsearch/issues/3026)) ([47d11bf](https://github.com/algolia/react-instantsearch/commit/47d11bf)) - - - -## [6.10.2](https://github.com/algolia/react-instantsearch/compare/v6.10.1...v6.10.2) (2021-03-03) - - -### Bug Fixes - -* **infiniteHits:** fix stale hits issue ([#3021](https://github.com/algolia/react-instantsearch/issues/3021)) ([a9a29c7](https://github.com/algolia/react-instantsearch/commit/a9a29c7)) - - - -## [6.10.1](https://github.com/algolia/react-instantsearch/compare/v6.10.0...v6.10.1) (2021-03-02) - - -### Bug Fixes - -* **SmartSort:** make `textComponent` and `buttonTextComponent` optional ([#3014](https://github.com/algolia/react-instantsearch/issues/3014)) ([682ee6d](https://github.com/algolia/react-instantsearch/commit/682ee6d)) - - - -# [6.10.0](https://github.com/algolia/react-instantsearch/compare/v6.9.0...v6.10.0) (2021-02-23) - - -### Bug Fixes - -* **infiniteHits:** do not cache the cached hits ([#3011](https://github.com/algolia/react-instantsearch/issues/3011)) ([b56f5f7](https://github.com/algolia/react-instantsearch/commit/b56f5f7)) - - -### Features - -* **smartSort:** add widget ([#3009](https://github.com/algolia/react-instantsearch/issues/3009)) ([4cc8412](https://github.com/algolia/react-instantsearch/commit/4cc8412)), closes [#3010](https://github.com/algolia/react-instantsearch/issues/3010) - - - -# [6.9.0](https://github.com/algolia/react-instantsearch/compare/v6.8.3...v6.9.0) (2021-02-03) - - -### Features - -* **answers:** add `EXPERIMENTAL_Answers` widget ([#2996](https://github.com/algolia/react-instantsearch/issues/2996)) ([55e4191](https://github.com/algolia/react-instantsearch/commit/55e4191)), closes [#3005](https://github.com/algolia/react-instantsearch/issues/3005) - - - -## [6.8.3](https://github.com/algolia/react-instantsearch/compare/v6.8.2...v6.8.3) (2021-01-22) - - -### Bug Fixes - -* upgrade prop-types dependency to 15.6+ ([#3003](https://github.com/algolia/react-instantsearch/issues/3003)) ([fc03496](https://github.com/algolia/react-instantsearch/commit/fc03496)) - - - -## [6.8.2](https://github.com/algolia/react-instantsearch/compare/v6.8.1...v6.8.2) (2020-10-21) - - -### Bug Fixes - -* **ssr:** provide metadata default value ([0a2f34c](https://github.com/algolia/react-instantsearch/commit/0a2f34c)) - - - -## [6.8.1](https://github.com/algolia/react-instantsearch/compare/v6.8.0...v6.8.1) (2020-10-14) - - -### Bug Fixes - -* **ssr:** hydrate metadata with a value ([9249c19](https://github.com/algolia/react-instantsearch/commit/9249c19)) - - - -# [6.8.0](https://github.com/algolia/react-instantsearch/compare/v6.7.0...v6.8.0) (2020-10-14) - - -### Bug Fixes - -* **ssr:** make sure metadata is available on initial render ([#2973](https://github.com/algolia/react-instantsearch/issues/2973)) ([be43b65](https://github.com/algolia/react-instantsearch/commit/be43b65)), closes [#2972](https://github.com/algolia/react-instantsearch/issues/2972) -* add missing dependencies ([#2975](https://github.com/algolia/react-instantsearch/issues/2975)) ([22ecb3c](https://github.com/algolia/react-instantsearch/commit/22ecb3c)) -* **ConfigureRelatedItems:** support nested attributes ([#2967](https://github.com/algolia/react-instantsearch/issues/2967)) ([86dfe86](https://github.com/algolia/react-instantsearch/commit/86dfe86)) -* **ssr:** allow "params" to be optional in custom clients ([#2961](https://github.com/algolia/react-instantsearch/issues/2961)) ([c3e3d2e](https://github.com/algolia/react-instantsearch/commit/c3e3d2e)), closes [#2958](https://github.com/algolia/react-instantsearch/issues/2958) - - - -# [6.7.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.7.0) (2020-07-20) - - -### Bug Fixes - -* **core:** appending successful index search results by returning new object reference ([#2953](https://github.com/algolia/react-instantsearch/issues/2953)) ([0a711a7](https://github.com/algolia/react-instantsearch/commit/0a711a7)) -* **ssr:** remove second instance of "query" in the response "params" for SSR ([#2945](https://github.com/algolia/react-instantsearch/issues/2945)) ([bf837c5](https://github.com/algolia/react-instantsearch/commit/bf837c5)), closes [#2941](https://github.com/algolia/react-instantsearch/issues/2941) - - -### Features - -* **infinite-hits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.6.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.6.0) (2020-06-15) - - -### Features - -* **infiniteHits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.5.0](https://github.com/algolia/react-instantsearch/compare/v6.4.0...v6.5.0) (2020-05-18) - - -### Bug Fixes - -* **connectQueryRules:** fix crash when using connectQueryRules with multiple indexes ([#2903](https://github.com/algolia/react-instantsearch/issues/2903)) ([c66d612](https://github.com/algolia/react-instantsearch/commit/c66d612)) -* **core:** fix maximum call stack size exceeded ([#2926](https://github.com/algolia/react-instantsearch/issues/2926)) ([7e883df](https://github.com/algolia/react-instantsearch/commit/7e883df)) - - -### Features - -* **SearchBox:** provide input element ref ([#2913](https://github.com/algolia/react-instantsearch/issues/2913)) ([b41bff2](https://github.com/algolia/react-instantsearch/commit/b41bff2)) - - - -# [6.4.0](https://github.com/algolia/react-instantsearch/compare/v6.3.0...v6.4.0) (2020-03-18) - - -### Bug Fixes - -* **deps:** fix "too much recursion" error with circular deps ([#2899](https://github.com/algolia/react-instantsearch/issues/2899)) ([c5f27a1](https://github.com/algolia/react-instantsearch/commit/c5f27a1)) - - - -# [6.3.0](https://github.com/algolia/react-instantsearch/compare/v6.2.0...v6.3.0) (2020-01-30) - - -### Features - -* **algoliasearch:** add support for algoliasearch v4 ([#2890](https://github.com/algolia/react-instantsearch/issues/2890)) ([c6c7382](https://github.com/algolia/react-instantsearch/commit/c6c7382)) - - - -# [6.2.0](https://github.com/algolia/react-instantsearch/compare/v6.1.0...v6.2.0) (2020-01-20) - - -### Bug Fixes - -* **deps:** update dependency algoliasearch to v3.35.1 ([#2802](https://github.com/algolia/react-instantsearch/issues/2802)) ([cfb91f0](https://github.com/algolia/react-instantsearch/commit/cfb91f0)) -* **widgets:** rename `ExperimentalConfigureRelatedItems` compon… ([#2891](https://github.com/algolia/react-instantsearch/issues/2891)) ([b910df2](https://github.com/algolia/react-instantsearch/commit/b910df2)) - - -### Features - -* **insights:** add getInsightsAnonymousUserToken helper ([#2887](https://github.com/algolia/react-instantsearch/issues/2887)) ([b5fe4f7](https://github.com/algolia/react-instantsearch/commit/b5fe4f7)) -* **widgets:** introduce `ConfigureRelatedItems` as experimental ([#2880](https://github.com/algolia/react-instantsearch/issues/2880)) ([923cd43](https://github.com/algolia/react-instantsearch/commit/923cd43)) - - - -# [6.1.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0...v6.1.0) (2019-12-17) - - -### Bug Fixes - -* **connectNumericMenu:** support numeric refinement 0 ([#2882](https://github.com/algolia/react-instantsearch/issues/2882)) ([30bd9fd](https://github.com/algolia/react-instantsearch/commit/30bd9fd)) -* **deps:** update dependency next to v9.1.1 ([9d49d33](https://github.com/algolia/react-instantsearch/commit/9d49d33)) -* **helper:** rely on stable version of algoliasearch-helper ([#2871](https://github.com/algolia/react-instantsearch/issues/2871)) ([e3531a1](https://github.com/algolia/react-instantsearch/commit/e3531a1)) - - -### Features - -* **insights:** show an error when 'clickAnalytics: true' is missing. ([#2877](https://github.com/algolia/react-instantsearch/issues/2877)) ([621656a](https://github.com/algolia/react-instantsearch/commit/621656a)) -* **voice:** add additionalQueryParameters ([#2366](https://github.com/algolia/react-instantsearch/issues/2366)) ([3a45b2c](https://github.com/algolia/react-instantsearch/commit/3a45b2c)) - - - -# [6.0.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.2...v6.0.0) (2019-10-28) - - - -# [6.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.1...v6.0.0-beta.2) (2019-10-25) - - -### Bug Fixes - -* serialize cache value on hydrate ([#2862](https://github.com/algolia/react-instantsearch/issues/2862)) ([3319665](https://github.com/algolia/react-instantsearch/commit/3319665)), closes [#2828](https://github.com/algolia/react-instantsearch/issues/2828) - - - -# [6.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.1) (2019-10-18) - - -### Bug Fixes - -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **deps:** update dependency antd to v3.19.3 ([#2530](https://github.com/algolia/react-instantsearch/issues/2530)) ([73636c5](https://github.com/algolia/react-instantsearch/commit/73636c5)) -* **deps:** update dependency antd to v3.19.4 ([#2559](https://github.com/algolia/react-instantsearch/issues/2559)) ([c3e8267](https://github.com/algolia/react-instantsearch/commit/c3e8267)) -* **deps:** update dependency antd to v3.19.5 ([#2560](https://github.com/algolia/react-instantsearch/issues/2560)) ([72efd31](https://github.com/algolia/react-instantsearch/commit/72efd31)) -* **deps:** update dependency antd to v3.19.6 ([#2564](https://github.com/algolia/react-instantsearch/issues/2564)) ([654f986](https://github.com/algolia/react-instantsearch/commit/654f986)) -* **deps:** update dependency antd to v3.19.7 ([#2573](https://github.com/algolia/react-instantsearch/issues/2573)) ([7e963ad](https://github.com/algolia/react-instantsearch/commit/7e963ad)) -* **deps:** update dependency antd to v3.19.8 ([#2584](https://github.com/algolia/react-instantsearch/issues/2584)) ([34dd9b2](https://github.com/algolia/react-instantsearch/commit/34dd9b2)) -* **deps:** update dependency antd to v3.20.0 ([#2611](https://github.com/algolia/react-instantsearch/issues/2611)) ([b976c67](https://github.com/algolia/react-instantsearch/commit/b976c67)) -* **deps:** update dependency antd to v3.20.1 ([#2635](https://github.com/algolia/react-instantsearch/issues/2635)) ([792ad9c](https://github.com/algolia/react-instantsearch/commit/792ad9c)) -* **deps:** update dependency antd to v3.20.2 ([#2655](https://github.com/algolia/react-instantsearch/issues/2655)) ([301c2d8](https://github.com/algolia/react-instantsearch/commit/301c2d8)) -* **deps:** update dependency antd to v3.20.3 ([#2658](https://github.com/algolia/react-instantsearch/issues/2658)) ([d078e70](https://github.com/algolia/react-instantsearch/commit/d078e70)) -* **deps:** update dependency antd to v3.20.5 ([#2686](https://github.com/algolia/react-instantsearch/issues/2686)) ([42ef821](https://github.com/algolia/react-instantsearch/commit/42ef821)) -* **deps:** update dependency antd to v3.20.6 ([#2711](https://github.com/algolia/react-instantsearch/issues/2711)) ([927fbfe](https://github.com/algolia/react-instantsearch/commit/927fbfe)) -* **deps:** update dependency antd to v3.20.7 ([#2712](https://github.com/algolia/react-instantsearch/issues/2712)) ([1830952](https://github.com/algolia/react-instantsearch/commit/1830952)) -* **deps:** update dependency antd to v3.21.1 ([#2736](https://github.com/algolia/react-instantsearch/issues/2736)) ([39a51a6](https://github.com/algolia/react-instantsearch/commit/39a51a6)) -* **deps:** update dependency antd to v3.21.2 ([#2738](https://github.com/algolia/react-instantsearch/issues/2738)) ([a7a998a](https://github.com/algolia/react-instantsearch/commit/a7a998a)) -* **deps:** update dependency antd to v3.21.4 ([#2747](https://github.com/algolia/react-instantsearch/issues/2747)) ([60012be](https://github.com/algolia/react-instantsearch/commit/60012be)) -* **deps:** update dependency antd to v3.22.0 ([#2758](https://github.com/algolia/react-instantsearch/issues/2758)) ([9cda468](https://github.com/algolia/react-instantsearch/commit/9cda468)) -* **deps:** update dependency antd to v3.22.2 ([#2791](https://github.com/algolia/react-instantsearch/issues/2791)) ([ff1f5d9](https://github.com/algolia/react-instantsearch/commit/ff1f5d9)) -* **deps:** update dependency antd to v3.23.2 ([#2814](https://github.com/algolia/react-instantsearch/issues/2814)) ([a190410](https://github.com/algolia/react-instantsearch/commit/a190410)) -* **deps:** update dependency lodash to v4.17.13 ([c4974cf](https://github.com/algolia/react-instantsearch/commit/c4974cf)) -* **deps:** update dependency lodash to v4.17.14 ([#2647](https://github.com/algolia/react-instantsearch/issues/2647)) ([a2d2dd5](https://github.com/algolia/react-instantsearch/commit/a2d2dd5)) -* **deps:** update dependency lodash to v4.17.15 ([#2684](https://github.com/algolia/react-instantsearch/issues/2684)) ([354143f](https://github.com/algolia/react-instantsearch/commit/354143f)) -* **deps:** update dependency next to v9 ([#2638](https://github.com/algolia/react-instantsearch/issues/2638)) ([d22f61d](https://github.com/algolia/react-instantsearch/commit/d22f61d)) -* **deps:** update dependency next to v9.0.1 ([#2652](https://github.com/algolia/react-instantsearch/issues/2652)) ([2c2dab9](https://github.com/algolia/react-instantsearch/commit/2c2dab9)) -* **deps:** update dependency next to v9.0.2 ([#2662](https://github.com/algolia/react-instantsearch/issues/2662)) ([6fa4c5e](https://github.com/algolia/react-instantsearch/commit/6fa4c5e)) -* **deps:** update dependency next to v9.0.3 ([#2724](https://github.com/algolia/react-instantsearch/issues/2724)) ([f51b04b](https://github.com/algolia/react-instantsearch/commit/f51b04b)) -* **deps:** update dependency next to v9.0.4 ([#2767](https://github.com/algolia/react-instantsearch/issues/2767)) ([9af9180](https://github.com/algolia/react-instantsearch/commit/9af9180)) -* **deps:** update dependency next to v9.0.5 ([#2789](https://github.com/algolia/react-instantsearch/issues/2789)) ([0a75f41](https://github.com/algolia/react-instantsearch/commit/0a75f41)) -* **deps:** update dependency qs to v6.8.0 ([#2757](https://github.com/algolia/react-instantsearch/issues/2757)) ([8bffb87](https://github.com/algolia/react-instantsearch/commit/8bffb87)) -* **deps:** update dependency react-compound-slider to v2.1.0 ([#2610](https://github.com/algolia/react-instantsearch/issues/2610)) ([3389ee5](https://github.com/algolia/react-instantsearch/commit/3389ee5)) -* **deps:** update dependency react-compound-slider to v2.2.0 ([#2649](https://github.com/algolia/react-instantsearch/issues/2649)) ([7b81af1](https://github.com/algolia/react-instantsearch/commit/7b81af1)) -* **deps:** update dependency react-native-vector-icons to v6.5.0 ([#2520](https://github.com/algolia/react-instantsearch/issues/2520)) ([5f7f5b6](https://github.com/algolia/react-instantsearch/commit/5f7f5b6)) -* **deps:** update dependency react-native-vector-icons to v6.6.0 ([#2599](https://github.com/algolia/react-instantsearch/issues/2599)) ([b6bb199](https://github.com/algolia/react-instantsearch/commit/b6bb199)) -* **deps:** update dependency react-router-dom to v5.0.1 ([#2506](https://github.com/algolia/react-instantsearch/issues/2506)) ([d762230](https://github.com/algolia/react-instantsearch/commit/d762230)) -* **highlight:** switch to index as key ([#2691](https://github.com/algolia/react-instantsearch/issues/2691)) ([17e75d1](https://github.com/algolia/react-instantsearch/commit/17e75d1)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### chore - -* **release:** 6.0.0-beta.1 ([#2861](https://github.com/algolia/react-instantsearch/issues/2861)) ([cb56ca0](https://github.com/algolia/react-instantsearch/commit/cb56ca0)), closes [#2023](https://github.com/algolia/react-instantsearch/issues/2023) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2203](https://github.com/algolia/react-instantsearch/issues/2203) [#2432](https://github.com/algolia/react-instantsearch/issues/2432) [#2444](https://github.com/algolia/react-instantsearch/issues/2444) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2454](https://github.com/algolia/react-instantsearch/issues/2454) [#2455](https://github.com/algolia/react-instantsearch/issues/2455) [#2459](https://github.com/algolia/react-instantsearch/issues/2459) [#2458](https://github.com/algolia/react-instantsearch/issues/2458) [#2460](https://github.com/algolia/react-instantsearch/issues/2460) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2446](https://github.com/algolia/react-instantsearch/issues/2446) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2466](https://github.com/algolia/react-instantsearch/issues/2466) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2555](https://github.com/algolia/react-instantsearch/issues/2555) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2349](https://github.com/algolia/react-instantsearch/issues/2349) [#2570](https://github.com/algolia/react-instantsearch/issues/2570) [#2462](https://github.com/algolia/react-instantsearch/issues/2462) [#2600](https://github.com/algolia/react-instantsearch/issues/2600) [#2468](https://github.com/algolia/react-instantsearch/issues/2468) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2621](https://github.com/algolia/react-instantsearch/issues/2621) [#2627](https://github.com/algolia/react-instantsearch/issues/2627) [#2644](https://github.com/algolia/react-instantsearch/issues/2644) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2645](https://github.com/algolia/react-instantsearch/issues/2645) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2643](https://github.com/algolia/react-instantsearch/issues/2643) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2722](https://github.com/algolia/react-instantsearch/issues/2722) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2701](https://github.com/algolia/react-instantsearch/issues/2701) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2530](https://github.com/algolia/react-instantsearch/issues/2530) [#2559](https://github.com/algolia/react-instantsearch/issues/2559) [#2560](https://github.com/algolia/react-instantsearch/issues/2560) [#2564](https://github.com/algolia/react-instantsearch/issues/2564) [#2573](https://github.com/algolia/react-instantsearch/issues/2573) [#2584](https://github.com/algolia/react-instantsearch/issues/2584) [#2611](https://github.com/algolia/react-instantsearch/issues/2611) [#2635](https://github.com/algolia/react-instantsearch/issues/2635) [#2655](https://github.com/algolia/react-instantsearch/issues/2655) [#2658](https://github.com/algolia/react-instantsearch/issues/2658) [#2686](https://github.com/algolia/react-instantsearch/issues/2686) [#2711](https://github.com/algolia/react-instantsearch/issues/2711) [#2712](https://github.com/algolia/react-instantsearch/issues/2712) [#2736](https://github.com/algolia/react-instantsearch/issues/2736) [#2738](https://github.com/algolia/react-instantsearch/issues/2738) [#2747](https://github.com/algolia/react-instantsearch/issues/2747) [#2758](https://github.com/algolia/react-instantsearch/issues/2758) [#2647](https://github.com/algolia/react-instantsearch/issues/2647) [#2684](https://github.com/algolia/react-instantsearch/issues/2684) [#2638](https://github.com/algolia/react-instantsearch/issues/2638) [#2652](https://github.com/algolia/react-instantsearch/issues/2652) [#2662](https://github.com/algolia/react-instantsearch/issues/2662) [#2724](https://github.com/algolia/react-instantsearch/issues/2724) [#2767](https://github.com/algolia/react-instantsearch/issues/2767) [#2757](https://github.com/algolia/react-instantsearch/issues/2757) [#2610](https://github.com/algolia/react-instantsearch/issues/2610) [#2649](https://github.com/algolia/react-instantsearch/issues/2649) [#2520](https://github.com/algolia/react-instantsearch/issues/2520) [#2599](https://github.com/algolia/react-instantsearch/issues/2599) [#2506](https://github.com/algolia/react-instantsearch/issues/2506) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2688](https://github.com/algolia/react-instantsearch/issues/2688) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2535](https://github.com/algolia/react-instantsearch/issues/2535) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2834](https://github.com/algolia/react-instantsearch/issues/2834) [#2845](https://github.com/algolia/react-instantsearch/issues/2845) [#2842](https://github.com/algolia/react-instantsearch/issues/2842) [#2852](https://github.com/algolia/react-instantsearch/issues/2852) [#2853](https://github.com/algolia/react-instantsearch/issues/2853) - - -### BREAKING CHANGES - -* **release:** translation will render default value if passed undefined as value - -* chore(lodash): remove imports - -* fix(translation): allow undefined value to be passed on purpose -* **release:** no longer do we allow paths like `attribute[5].something`, or other indexed forms, only `.` is allowed as special key. - -All existing tests still pass, and we never documented you could use `lodash.get` patterns other than `.`. - -* feat(get): accept array & bracked-separated string - -moved to utils at the same time - -* fix typo - -* feedback: test for undefined behaviour - -* chore(size): update expectation - -this will go down afterwards, but for now there's some more duplication - - - -# [6.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.0) (2019-08-21) - -[Migration guide](MIGRATION.md) - -### Bug Fixes - -* **react 17 compat:** upgrade RangeInput lifecycle ([#2289](https://github.com/algolia/react-instantsearch/issues/2289)) ([110b1af](https://github.com/algolia/react-instantsearch/commit/110b1af)) -* **react 17 compat:** upgrade RangeSlider lifecycle ([#2290](https://github.com/algolia/react-instantsearch/issues/2290)) ([69a7f53](https://github.com/algolia/react-instantsearch/commit/69a7f53)) -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **core:** searchState can be non-Object object ([#2722](https://github.com/algolia/react-instantsearch/issues/2722)) ([dea493c](https://github.com/algolia/react-instantsearch/commit/dea493c)), closes [#2568](https://github.com/algolia/react-instantsearch/issues/2568) -* **createConnector:** new React life cycles ([#2357](https://github.com/algolia/react-instantsearch/issues/2357)) ([fc10640](https://github.com/algolia/react-instantsearch/commit/fc10640)) -* **createInstantSearchManager:** do not trigger search on index update ([#2552](https://github.com/algolia/react-instantsearch/issues/2552)) ([e209362](https://github.com/algolia/react-instantsearch/commit/e209362)) -* **geo:** check for undefined in isEqual ([#2643](https://github.com/algolia/react-instantsearch/issues/2643)) ([a544231](https://github.com/algolia/react-instantsearch/commit/a544231)), closes [#2467](https://github.com/algolia/react-instantsearch/issues/2467) -* **geo:** remove lifecycle compat ([#2644](https://github.com/algolia/react-instantsearch/issues/2644)) ([2b2b898](https://github.com/algolia/react-instantsearch/commit/2b2b898)), closes [#2626](https://github.com/algolia/react-instantsearch/issues/2626) -* **highlight:** switch to index as key ([#2690](https://github.com/algolia/react-instantsearch/issues/2690)) ([51de682](https://github.com/algolia/react-instantsearch/commit/51de682)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **peerDependencies:** update React ([#2626](https://github.com/algolia/react-instantsearch/issues/2626)) ([6ccad49](https://github.com/algolia/react-instantsearch/commit/6ccad49)) -* **ssr:** avoid duplicate serializing ([#2726](https://github.com/algolia/react-instantsearch/issues/2726)) ([c768b1a](https://github.com/algolia/react-instantsearch/commit/c768b1a)) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### Code Refactoring - -* **lodash:** get ([#2461](https://github.com/algolia/react-instantsearch/issues/2461)) ([527b879](https://github.com/algolia/react-instantsearch/commit/527b879)) -* **lodash:** has ([#2434](https://github.com/algolia/react-instantsearch/issues/2434)) ([75a4a15](https://github.com/algolia/react-instantsearch/commit/75a4a15)) -* **lodash:** has been fully removed - -### Features - -* **autocomplete:** add queryID & position to provided hits ([#2687](https://github.com/algolia/react-instantsearch/issues/2687)) ([e453dab](https://github.com/algolia/react-instantsearch/commit/e453dab)) -* **client:** remove algoliaClient, appId & apiKey ([#2338](https://github.com/algolia/react-instantsearch/issues/2338)) ([b84a0b5](https://github.com/algolia/react-instantsearch/commit/b84a0b5)) (use searchClient exclusively now) -* **context:** migrate to new React context ([#2178](https://github.com/algolia/react-instantsearch/issues/2178)) ([0a1abea](https://github.com/algolia/react-instantsearch/commit/0a1abea)), closes [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) -* **ssr:** update the SSR API ([#2555](https://github.com/algolia/react-instantsearch/issues/2555)) ([925bdb8](https://github.com/algolia/react-instantsearch/commit/925bdb8)), closes [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) - - -### BREAKING CHANGES - -* **searchClient:** argument is the only option now. - -Previously there were three options to pass a search client: searchClient, appId & apiKey, algoliaClient. The latter two have been removed, and now only `searchClient` is accepted. This searchClient is an instance of the `algoliasearch` module: - -```js -import algoliasearch from 'algoliasearch/lite'; - -const searchClient = algoliasearch( - 'myAppId', - 'myApiKey', - { _useRequestCache: true } -); - -// ... - -``` - -If you were relying on duplicate requests not being fired when using appId & apiKey before, you need to enable the `_useRequestCache` option now. - -* **SSR:** imports have changed - -In the server, you now directly import `findResultsState`, which now requires a `searchClient` in the second argument. - -In the App, you now use a regular `InstantSearch` component. - -* **Index & InstantSearch:** Remove `root` DOM element - -These elements now are pure containers for their children, and don't add a `div` to the DOM anymore. If you were relying on those for styling, wrap the `InstantSearch` and `Index` element with a `div` with an appropriate class. - -* **Index & InstantSearch:** Remove support for `root` prop - -Since these two arguments now no longer wrap their children in an element, they no longer accept a `root` prop. - -* **Highlight:** some paths will no longer be accepted - -We only accept paths separated with a dot or bracket now, like before. It's possible that a different type of path worked undocumented, but no longer does. - -* **algoliasearch-helper:** updating to the next major version - -This library is mostly internal, but it has had a major refactor (including removing lodash). This has no impact, unless you are dealing with it using `createConnector`. See the [migration guide](https://github.com/algolia/algoliasearch-helper-js/blob/next/documentation-src/metalsmith/content/upgrade.md) for the v3 of algoliasearch-helper for more information. - -# [5.7.0](https://github.com/algolia/react-instantsearch/compare/v5.6.0...v5.7.0) (2019-06-04) - - -### Bug Fixes - -* **highlight:** allow array as "attribute" ([#2474](https://github.com/algolia/react-instantsearch/issues/2474)) ([9dc829a](https://github.com/algolia/react-instantsearch/commit/9dc829a)), closes [#2461](https://github.com/algolia/react-instantsearch/issues/2461) -* **indexUtils:** allow index with dots in it ([#2350](https://github.com/algolia/react-instantsearch/issues/2350)) ([f91906f](https://github.com/algolia/react-instantsearch/commit/f91906f)) - - -### Features - -* **voiceSearch:** add voice search widget ([#2316](https://github.com/algolia/react-instantsearch/issues/2316)) ([0e3b124](https://github.com/algolia/react-instantsearch/commit/0e3b124)) - - - -# [5.6.0](https://github.com/algolia/react-instantsearch/compare/v5.5.0...v5.6.0) (2019-05-15) - - -### Bug Fixes - -* **connectQueryRules:** avoid to throw an error with undefined values ([#2436](https://github.com/algolia/react-instantsearch/issues/2436)) ([1e18287](https://github.com/algolia/react-instantsearch/commit/1e18287)) - - -### Features - -* **infiniteHits:** add previous button ([#2296](https://github.com/algolia/react-instantsearch/issues/2296)) ([010a69a](https://github.com/algolia/react-instantsearch/commit/010a69a)) - - - -# [5.5.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0...v5.5.0) (2019-04-23) - - -### Bug Fixes - -* **createInstantSearch:** change the User-Agent to use the new specs ([#2209](https://github.com/algolia/react-instantsearch/issues/2209)) ([642ba0b](https://github.com/algolia/react-instantsearch/commit/642ba0b)) - - -### Features - -* **DOMMaps:** expose withGoogleMaps HOC [PART-1] ([#2000](https://github.com/algolia/react-instantsearch/issues/2000)) ([2ae1dea](https://github.com/algolia/react-instantsearch/commit/2ae1dea)) -* **queryRules:** add Query Rules features ([#2286](https://github.com/algolia/react-instantsearch/issues/2286)) ([3ae9c01](https://github.com/algolia/react-instantsearch/commit/3ae9c01)) -* **insights:** add insights features ([#2215](https://github.com/algolia/react-instantsearch/pull/2215)) ([961e7a7](https://github.com/algolia/react-instantsearch/commit/961e7a7)) - - - -# [5.4.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.1...v5.4.0) (2019-02-05) - - -### Bug Fixes - -* **DOMMaps:** set React & React DOM as peer deps ([#1922](https://github.com/algolia/react-instantsearch/issues/1922)) ([2f2cefd](https://github.com/algolia/react-instantsearch/commit/2f2cefd)) - - - -# [5.4.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.0...v5.4.0-beta.1) (2019-01-10) - - -### Bug Fixes - -* **deps:** sync algoliasearch version ([#1879](https://github.com/algolia/react-instantsearch/issues/1879)) ([40f9c26](https://github.com/algolia/react-instantsearch/commit/40f9c26)) -* **maps:** use stable version for peer deps ([#1887](https://github.com/algolia/react-instantsearch/issues/1887)) ([9055167](https://github.com/algolia/react-instantsearch/commit/9055167)) - - - -# [5.4.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.3.2...v5.4.0-beta.0) (2019-01-08) - - -### Features - -* **Index:** introduce `indexId` prop ([#1833](https://github.com/algolia/react-instantsearch/issues/1833)) ([ec9e0fb](https://github.com/algolia/react-instantsearch/commit/ec9e0fb)) - - - - -## [5.3.2](https://github.com/algolia/react-instantsearch/compare/v5.3.1...v5.3.2) (2018-10-30) - - -### Bug Fixes - -* **sffv:** clamp maxFacetHits to the allowed range ([#1696](https://github.com/algolia/react-instantsearch/issues/1696)) ([83ce245](https://github.com/algolia/react-instantsearch/commit/83ce245)) - - - - -## [5.3.1](https://github.com/algolia/react-instantsearch/compare/v5.3.0...v5.3.1) (2018-09-26) - - -### Bug Fixes - -* **connector:** ensure canRefine is computed on the transformed items ([#1568](https://github.com/algolia/react-instantsearch/pull/1568)) ([c95384f](https://github.com/algolia/react-instantsearch/commit/c95384f)) -* **toggle:** ensure facet is present ([#1613](https://github.com/algolia/react-instantsearch/issues/1613)) ([e914ff6](https://github.com/algolia/react-instantsearch/commit/e914ff6)) - - - - -# [5.3.0](https://github.com/algolia/react-instantsearch/compare/v5.2.3...v5.3.0) (2018-09-24) - - -### Bug Fixes - -* **SSR:** bind getSearchParmaters to the component instance ([f34cb3d](https://github.com/algolia/react-instantsearch/commit/f34cb3d)) -* **GoogleMapsLoader:** pick google maps version ([#1540](https://github.com/algolia/react-instantsearch/issues/1540)) ([b14efcf](https://github.com/algolia/react-instantsearch/commit/b14efcf)) - - -### Features - -* **connectToggleRefinement:** implement canRefine & count ([#1588](https://github.com/algolia/react-instantsearch/issues/1588)) ([40672dd](https://github.com/algolia/react-instantsearch/commit/40672dd)) - - - - -## [5.2.3](https://github.com/algolia/react-instantsearch/compare/v5.2.2...v5.2.3) (2018-08-16) - - -### Bug Fixes - -* Allow object as type for Root (closes [#1446](https://github.com/algolia/react-instantsearch/issues/1446)) ([#1461](https://github.com/algolia/react-instantsearch/issues/1461)) ([7c2317b](https://github.com/algolia/react-instantsearch/commit/7c2317b)) -* **List:** render children list only when required ([#1472](https://github.com/algolia/react-instantsearch/issues/1472)) ([9eb2cbb](https://github.com/algolia/react-instantsearch/commit/9eb2cbb)), closes [#1459](https://github.com/algolia/react-instantsearch/issues/1459) - - - - -## [5.2.2](https://github.com/algolia/react-instantsearch/compare/v5.2.1...v5.2.2) (2018-07-16) - - -Publish the previous version on `stable`. - - - - -## [5.2.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0...v5.2.1) (2018-07-16) - - -### Bug Fixes - -* **GoogleMapsLoader:** inline the import to scriptjs ([#1427](https://github.com/algolia/react-instantsearch/issues/1427)) ([8019416](https://github.com/algolia/react-instantsearch/commit/8019416)) - - - - -# [5.2.0](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.2...v5.2.0) (2018-07-04) - - -### Bug Fixes - -* **translatable:** avoid create a new function on every render ([#1383](https://github.com/algolia/react-instantsearch/issues/1383)) ([1285b3b](https://github.com/algolia/react-instantsearch/commit/1285b3b)) - - -### Features - -* **core:** export translatable ([#1351](https://github.com/algolia/react-instantsearch/issues/1351)) ([6d5a89d](https://github.com/algolia/react-instantsearch/commit/6d5a89d)) -* **maps:** add connector & widget ([#1171](https://github.com/algolia/react-instantsearch/issues/1171)) ([16e288a](https://github.com/algolia/react-instantsearch/commit/16e288a)) - - - - -# [5.2.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.1...v5.2.0-beta.2) (2018-06-19) - - -### Features - -* export highlight tags from DOM / native ([#1342](https://github.com/algolia/react-instantsearch/issues/1342)) ([28a699e](https://github.com/algolia/react-instantsearch/commit/28a699e)) -* **createInstantSearch:** enable _useRequestCache ([#1346](https://github.com/algolia/react-instantsearch/issues/1346)) ([f772600](https://github.com/algolia/react-instantsearch/commit/f772600)) -* **dom:** export create class name ([#1348](https://github.com/algolia/react-instantsearch/issues/1348)) ([9017468](https://github.com/algolia/react-instantsearch/commit/9017468)) - - - - -# [5.2.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.0...v5.2.0-beta.1) (2018-06-04) - - -### Bug Fixes - -* **dom:** publish server file ([#1305](https://github.com/algolia/react-instantsearch/issues/1305)) ([bd79693](https://github.com/algolia/react-instantsearch/commit/bd79693)) - - - - -# [5.2.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.1.0...v5.2.0-beta.0) (2018-06-04) - - -This new version introduce a complete revamp of the package structure, but it should be completely transparent for the users. - -If you have any troubles with this version please open a issue on [Github](https://github.com/algolia/react-instantsearch/issues/new), thanks! - - - - -# [5.1.0](https://github.com/algolia/react-instantsearch/compare/v5.0.3...v5.1.0) (2018-05-28) - - -### Bug Fixes - -* **connectInfiniteHits:** always set a value for previous page ([#1195](https://github.com/algolia/react-instantsearch/issues/1195)) ([4c218d5](https://github.com/algolia/react-instantsearch/commit/4c218d5)) -* **indexUtils:** avoid throw an error on cleanUp multi indices ([#1265](https://github.com/algolia/react-instantsearch/issues/1265)) ([12f5ace](https://github.com/algolia/react-instantsearch/commit/12f5ace)) - - -### Features - -* **searchClient:** Add support for custom Search Clients ([#1216](https://github.com/algolia/react-instantsearch/issues/1216)) ([174cc28](https://github.com/algolia/react-instantsearch/commit/174cc28)) - - - - -## [5.0.3](https://github.com/algolia/react-instantsearch/compare/v5.0.2...v5.0.3) (2018-04-03) - - -### Bug Fixes - -* revert dependencies as devDependencies ([#1135](https://github.com/algolia/react-instantsearch/issues/1135)) ([6b627bb](https://github.com/algolia/react-instantsearch/commit/6b627bb)) - - - - -## [5.0.2](https://github.com/algolia/react-instantsearch/compare/v5.0.1...v5.0.2) (2018-04-03) - - -### Bug Fixes - -* use lodash version of unsupported Array.{fill, find} ([#1118](https://github.com/algolia/react-instantsearch/issues/1118)) ([ea7bf42](https://github.com/algolia/react-instantsearch/commit/ea7bf42)) - - - - -## [5.0.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0...v5.0.1) (2018-03-12) - - -### Bug Fixes - -* **connectInfiniteHits:** always provide an array for hits ([#1064](https://github.com/algolia/react-instantsearch/issues/1064)) ([c75e38b](https://github.com/algolia/react-instantsearch/commit/c75e38b)) - - - - -# [5.0.0](https://github.com/algolia/react-instantsearch/compare/v4.5.2...v5.0.0) (2018-03-06) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.5.2](https://github.com/algolia/react-instantsearch/compare/v4.5.1...v4.5.2) (2018-03-06) - - -### Bug Fixes - -* **connectRange:** update default refinement propTypes ([#978](https://github.com/algolia/react-instantsearch/issues/978)) ([c065fb1](https://github.com/algolia/react-instantsearch/commit/c065fb1)) -* **IndexUtils:** avoid throw an error when cleanUp multi index ([#1019](https://github.com/algolia/react-instantsearch/issues/1019)) ([865a3c3](https://github.com/algolia/react-instantsearch/commit/865a3c3)) -* **SearchBox:** avoid to bind click on reset button ([#979](https://github.com/algolia/react-instantsearch/issues/979)) ([ea3063a](https://github.com/algolia/react-instantsearch/commit/ea3063a)) - - - - -# [5.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2018-02-06) - - -Apply features & bug fixes from [v4.5.0](#450-2018-02-06) & [v4.5.1](#451-2018-02-06) on the v5. - -See their CHANGELOG for more details. - - - - -## [4.5.1](https://github.com/algolia/react-instantsearch/compare/v4.5.0...v4.5.1) (2018-02-06) - - -### Bug Fixes - -* **StarRating:** move to 1 based instead of 0 ([#949](https://github.com/algolia/react-instantsearch/issues/949)) ([eb0152d](https://github.com/algolia/react-instantsearch/commit/eb0152d)) - - - - -# [4.5.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v4.5.0) (2018-02-06) - - -### Bug Fixes - -* **connectRange:** use the same behaviour for currentRefinement in getMetadata ([#923](https://github.com/algolia/react-instantsearch/issues/923)) ([08917b6](https://github.com/algolia/react-instantsearch/commit/08917b6)) -* **connectToggle:** use currentRefinement in metadata instead of the label ([#909](https://github.com/algolia/react-instantsearch/issues/909)) ([89cae2b](https://github.com/algolia/react-instantsearch/commit/89cae2b)) -* **StarRatings:** always show the stars below ([#929](https://github.com/algolia/react-instantsearch/issues/929)) ([22bf93a](https://github.com/algolia/react-instantsearch/commit/22bf93a)) - - -### Features - -* **connectStateResults:** expose isSearchStalled ([#933](https://github.com/algolia/react-instantsearch/issues/933)) ([f45ba27](https://github.com/algolia/react-instantsearch/commit/f45ba27)) - - - -# [5.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v5.0.0-beta.0) (2018-01-30) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.4.2](https://github.com/algolia/react-instantsearch/compare/v4.4.1...v4.4.2) (2018-01-24) - - -### Bug Fixes - -* **currentRefinements:** give access to id and index from transformItems for deduplication ([#830](https://github.com/algolia/react-instantsearch/issues/830)) ([316b8f5](https://github.com/algolia/react-instantsearch/commit/316b8f5)) -* pass maxFacetHits to SFFV ([#863](https://github.com/algolia/react-instantsearch/issues/863)) ([de23a46](https://github.com/algolia/react-instantsearch/commit/de23a46)) - - - - -## [4.4.1](https://github.com/algolia/react-instantsearch/compare/v4.4.0...v4.4.1) (2018-01-09) - - -### Bug Fixes - -* **SearchBox**: clear SearchBox without search as you type ([#802](https://github.com/algolia/react-instantsearch/issues/802)) ([c49b2b6](https://github.com/algolia/react-instantsearch/commit/c49b2b6)) -* **connectRange:** check if facet exist before access ([#797](https://github.com/algolia/react-instantsearch/issues/797)) ([6520760](https://github.com/algolia/react-instantsearch/commit/6520760)) -* **stories:** avoid to use linear-background it breaks Argos every time ([#804](https://github.com/algolia/react-instantsearch/issues/804)) ([0beded7](https://github.com/algolia/react-instantsearch/commit/0beded7)) -* **stories:** limit hits per page on Index ([#806](https://github.com/algolia/react-instantsearch/issues/806)) ([6eb14d3](https://github.com/algolia/react-instantsearch/commit/6eb14d3)) - - -### Features - -* **Index:** allow custom root ([#792](https://github.com/algolia/react-instantsearch/issues/792)) ([d793b0a](https://github.com/algolia/react-instantsearch/commit/d793b0a)) - - - - -# [4.4.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0...v4.4.0) (2018-01-03) - - -### Bug Fixes - -* **createInstantSearch:** remove the client from the Snapshot ([#749](https://github.com/algolia/react-instantsearch/issues/749)) ([700d8f4](https://github.com/algolia/react-instantsearch/commit/700d8f4)) -* refresh cache memory leak example ([#784](https://github.com/algolia/react-instantsearch/issues/784)) ([cf228ac](https://github.com/algolia/react-instantsearch/commit/cf228ac)) -* **stories:** rename InstantSearch to `` ([#789](https://github.com/algolia/react-instantsearch/issues/789)) ([05efda5](https://github.com/algolia/react-instantsearch/commit/05efda5)) - - -### Features - -* InstantSearch root props ([#770](https://github.com/algolia/react-instantsearch/issues/770)) ([2d458f8](https://github.com/algolia/react-instantsearch/commit/2d458f8)) - - - - -# [4.3.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0-beta.0...v4.3.0) (2017-12-20) - - -### Bug Fixes - -* reset page with multi index ([#665](https://github.com/algolia/react-instantsearch/issues/665)) ([865b7dc](https://github.com/algolia/react-instantsearch/commit/865b7dc)) -* track all index in the manager ([#660](https://github.com/algolia/react-instantsearch/issues/660)) ([793502b](https://github.com/algolia/react-instantsearch/commit/793502b)) - - -### Features - -* **SearchBox:** provide a loading indicator ([#544](https://github.com/algolia/react-instantsearch/issues/544)) ([189659e](https://github.com/algolia/react-instantsearch/commit/189659e)) -* **Highlight:** support array of strings ([#715](https://github.com/algolia/react-instantsearch/issues/715)) ([8e93c6a](https://github.com/algolia/react-instantsearch/commit/8e93c6a)) - - - - -# [4.3.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.2.0...v4.3.0-beta.0) (2017-11-27) - - -### Bug Fixes - -* **babelrc:** add a key for each env development, production, es ([#547](https://github.com/algolia/react-instantsearch/issues/547)) ([fa9528d](https://github.com/algolia/react-instantsearch/commit/fa9528d)) -* **localizecount:** allow localized string for count in MenuSelect ([#657](https://github.com/algolia/react-instantsearch/issues/657)) ([67ebd34](https://github.com/algolia/react-instantsearch/commit/67ebd34)) -* **react-router-example:** Properly update search query when using browser navigation ([#604](https://github.com/algolia/react-instantsearch/issues/604)) ([9ee6600](https://github.com/algolia/react-instantsearch/commit/9ee6600)) - - -### Features - -* **refreshcache:** add prop refresh to InstantSearch instance ([#619](https://github.com/algolia/react-instantsearch/issues/619)) ([19f6de0](https://github.com/algolia/react-instantsearch/commit/19f6de0)) - - - - -# [4.2.0](https://github.com/algolia/react-instantsearch/compare/v4.1.3...v4.2.0) (2017-11-02) - - -### Bug Fixes - -* **connectRange:** handle boundaries on first call ([9f14dc0](https://github.com/algolia/react-instantsearch/commit/9f14dc0)) -* **connectRange:** use refine instead of cleanUp in metadata ([#526](https://github.com/algolia/react-instantsearch/issues/526)) ([1861235](https://github.com/algolia/react-instantsearch/commit/1861235)) -* **hierarchicaMenu:** allow sorting and using limit ([fe178ed](https://github.com/algolia/react-instantsearch/commit/fe178ed)), closes [#92](https://github.com/algolia/react-instantsearch/issues/92) -* **InfiniteHits:** add disabled style to the LoadMore button ([#477](https://github.com/algolia/react-instantsearch/issues/477)) ([faba1ad](https://github.com/algolia/react-instantsearch/commit/faba1ad)) -* **Range:** handle float, allow reset and respect boundaries ([75969b8](https://github.com/algolia/react-instantsearch/commit/75969b8)) -* **RangeInput:** fix compatibility with React 16 & Panel ([3f218db](https://github.com/algolia/react-instantsearch/commit/3f218db)) -* **searchbox:** add maxlength 512 ([#542](https://github.com/algolia/react-instantsearch/issues/542)) ([5bd4033](https://github.com/algolia/react-instantsearch/commit/5bd4033)), closes [#510](https://github.com/algolia/react-instantsearch/issues/510) - - -### Features - -* **MenuSelect:** add component and connector ([cc6e0d7](https://github.com/algolia/react-instantsearch/commit/cc6e0d7)) - - - - -## [4.1.3](https://github.com/algolia/react-instantsearch/compare/v4.1.2...v4.1.3) (2017-10-09) - - -### Bug Fixes - -* **List:** remove React16 warning ([#442](https://github.com/algolia/react-instantsearch/issues/442)) ([8d6cf18](https://github.com/algolia/react-instantsearch/commit/8d6cf18)) - - -### Features - -* **connectStateResults:** add component props ([#434](https://github.com/algolia/react-instantsearch/issues/434)) ([c629b97](https://github.com/algolia/react-instantsearch/commit/c629b97)) - - - - -## [4.1.2](https://github.com/algolia/react-instantsearch/compare/v4.1.1...v4.1.2) (2017-09-26) - - -### Features - -* **Conditional:** add connectStateResults connector ([#357](https://github.com/algolia/react-instantsearch/issues/357)) ([462df5f](https://github.com/algolia/react-instantsearch/commit/462df5f)) - - - - -## [4.1.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0...v4.1.1) (2017-09-13) - - -### Bug Fixes - -* **MultiIndex:** reset page to 1 when share widgets refine (#312) ([c85a7bf](https://github.com/algolia/react-instantsearch/commit/c85a7bf)) -* **MultiIndex:** Trigger new search when `` props are updated (#318) ([bb11965](https://github.com/algolia/react-instantsearch/commit/bb11965)) - - - - -# [4.1.0](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.5...v4.1.0) (2017-08-28) - - -### Bug Fixes - -* **Highlighting:** revert breaking change (#245) ([045ee06](https://github.com/algolia/react-instantsearch/commit/045ee06)) -* **List:** adds support for any type of renderable element (#266) ([d848bb6](https://github.com/algolia/react-instantsearch/commit/d848bb6)) -* **Pagination:** fixed the offset ([3c0fff2](https://github.com/algolia/react-instantsearch/commit/3c0fff2)) -* **PoweredBy:** aria-* tags are not camelcased (#261) ([dc4a5bb](https://github.com/algolia/react-instantsearch/commit/dc4a5bb)) - - - - -# [4.1.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2017-08-08) - - -### Bug Fixes - -* **SSR:** clean SP before rendering agan (#238) ([e765886](https://github.com/algolia/react-instantsearch/commit/e765886)) - - -### Features - -* **Breadcrumb:** add a new widget & connector (#228) ([7f8f3ae](https://github.com/algolia/react-instantsearch/commit/7f8f3ae)) - - - - -# [4.1.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2017-08-03) - - -### Bug Fixes - -* **deps:** Update dependency lint-staged to version ^4.0.0 (#201) ([6867a1b](https://github.com/algolia/react-instantsearch/commit/6867a1b)) -* **nextjs/ssr:** parse `params.asPath` (#189) ([ae17da0](https://github.com/algolia/react-instantsearch/commit/ae17da0)) -* **PoweredBy:** add a label to the Algolia logo (#216) ([cd235bd](https://github.com/algolia/react-instantsearch/commit/cd235bd)) -* **react:** remove typo around `"" 2` (#220) ([f73eb04](https://github.com/algolia/react-instantsearch/commit/f73eb04)) -* **ScrollTo:** scroll to only if change triggered by the widget observed (#202) ([2d76022](https://github.com/algolia/react-instantsearch/commit/2d76022)) -* **theme:** format the count of items appearing in a refinement (#217) ([2225c24](https://github.com/algolia/react-instantsearch/commit/2225c24)) - - - - -# [4.1.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2017-07-25) - - -### Bug Fixes - -* **error:** reset error when receiving results of a query (not when sending it) (#179) ([bb12c29](https://github.com/algolia/react-instantsearch/commit/bb12c29)) -* **highlight:** wrong parsing between client and server (#183) ([2daae70](https://github.com/algolia/react-instantsearch/commit/2daae70)) -* **poweredBy:** SSR compatibility (#181) ([ec0fa8a](https://github.com/algolia/react-instantsearch/commit/ec0fa8a)) - - -### BREAKING CHANGES - -* **highlight:** We remove the timestamp present in our highlight preTag and postTag. If you were using regex to parse the -highlighting results then you'll need to adapt it as now it's only "ais-highlight". - - - - -# [4.1.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2017-07-20) - - -### Bug Fixes - -* **error:** reset error if next query is successful (#175) ([ff50a07](https://github.com/algolia/react-instantsearch/commit/ff50a07)) - - - - -# [4.1.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2017-07-12) - - - - -# [4.1.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.0.7...v4.1.0-beta.0) (2017-07-10) - - -### Bug Fixes - -* **argos:** address flakyness (#152) ([84ef8f1](https://github.com/algolia/react-instantsearch/commit/84ef8f1)) - - -### Features - -* **server-side rendering:** Add API features for server-side rendering ([86b14d1](https://github.com/algolia/react-instantsearch/commit/86b14d1)) - - - - -## [4.0.7](https://github.com/algolia/react-instantsearch/compare/v4.0.6...v4.0.7) (2017-07-06) - - -### Bug Fixes - -* **results:** revert commit that ensure hits are returned only if right indices (#149) ([df9aa25](https://github.com/algolia/react-instantsearch/commit/df9aa25)) - - - - -## [4.0.6](https://github.com/algolia/react-instantsearch/compare/v4.0.5...v4.0.6) (2017-06-29) - - -### Bug Fixes - -* **store:** delay call to listener to prevent infinite loops (#143) ([0945958](https://github.com/algolia/react-instantsearch/commit/0945958)) - - - - -## [4.0.5](https://github.com/algolia/react-instantsearch/compare/v4.0.4...v4.0.5) (2017-06-26) - - -### Bug Fixes - -* **MultiIndex:** ensure getResults return only hits matching index in the context (#136) ([124ffe6](https://github.com/algolia/react-instantsearch/commit/124ffe6)) -* **MultiIndex:** handle if namespace isn't in search state (#139) ([1aab324](https://github.com/algolia/react-instantsearch/commit/1aab324)) -* **storybook:** process CSS through autoprefixer (#138) ([62cf512](https://github.com/algolia/react-instantsearch/commit/62cf512)) - - - - -## [4.0.4](https://github.com/algolia/react-instantsearch/compare/v4.0.3...v4.0.4) (2017-06-19) - - -### Bug Fixes - -* **MultiIndex:** handle switch between mono and multi index (#132) ([e161921](https://github.com/algolia/react-instantsearch/commit/e161921)) - - - - -## [4.0.3](https://github.com/algolia/react-instantsearch/compare/v4.0.2...v4.0.3) (2017-06-14) - - -### Bug Fixes - -* **SFFV:** search status we're not inside search state (#125) ([5f3e670](https://github.com/algolia/react-instantsearch/commit/5f3e670)) - - - - -## [4.0.2](https://github.com/algolia/react-instantsearch/compare/v4.0.1...v4.0.2) (2017-05-30) - - - - -## [4.0.1](https://github.com/algolia/react-instantsearch/compare/v4.0.0...v4.0.1) (2017-05-17) - - -### Bug Fixes - -* **state:** nested attributes for faceting were not handled ([11bd122](https://github.com/algolia/react-instantsearch/commit/11bd122)) - - - - -# [4.0.0](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.6...v4.0.0) (2017-05-15) - -### Features and migration guide - -You can find all the details of the release and the migration guide from v3 to v4 here: https://discourse.algolia.com/t/react-instantsearch-v4/1329. - - - -# [4.0.0-beta.6](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.5...v4.0.0-beta.6) (2017-05-04) - - - - -# [4.0.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.4...v4.0.0-beta.5) (2017-05-02) - - -### Bug Fixes - -* **connectAutoComplete:** allow usage with hits from a single index (#75) ([8b3b358](https://github.com/algolia/react-instantsearch/commit/8b3b358)), closes [#74](https://github.com/algolia/react-instantsearch/issues/74) -* **InstantSearch:** update algoliaClient when it change (#70) ([9e97dbd](https://github.com/algolia/react-instantsearch/commit/9e97dbd)) - - - - -# [4.0.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.3...v4.0.0-beta.4) (2017-04-25) - - -### Bug Fixes - -* **MultIndex:** no need to nest hits, if those are from main index. (#56) ([86e0bd7](https://github.com/algolia/react-instantsearch/commit/86e0bd7)) - - -### Features - -* **MultiIndex:** remove the need for virtual hits when using connectAutoComplete (#45) ([7549019](https://github.com/algolia/react-instantsearch/commit/7549019)) - - - - -# [4.0.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.2...v4.0.0-beta.3) (2017-04-21) - - -### Bug Fixes - -* replace usage of Object.values (#47) ([4c79e3e](https://github.com/algolia/react-instantsearch/commit/4c79e3e)) - - - - -# [4.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2017-04-18) - - -### Bug Fixes - -* **InstantSearch:** dont fire request/onsearchStateChange when unmounting (#26) ([9a1487a](https://github.com/algolia/react-instantsearch/commit/9a1487a)) -* **MultiIndex:** derived helper were using main index specifics params (#36) ([991fea6](https://github.com/algolia/react-instantsearch/commit/991fea6)) -* **MultiIndex:** revert breaking change if no multiple index (#32) ([44f7de0](https://github.com/algolia/react-instantsearch/commit/44f7de0)) -* **util:** remove empty key was removing non object key (#29) ([9f795c7](https://github.com/algolia/react-instantsearch/commit/9f795c7)) - - -### Features - -* **Highlighter:** allow rendering to custom tag (#11) ([52a1212](https://github.com/algolia/react-instantsearch/commit/52a1212)) -* **SearchBox:** add default width and height to buttons. (#34) ([bcabf9b](https://github.com/algolia/react-instantsearch/commit/bcabf9b)) - - - - -# [4.0.0-beta.1](https://github.com/algolia/instantsearch.js/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2017-04-03) - - -### Bug Fixes - -* **SFFV:** fix wrong query behaviour with slow network (#2086) ([c251e8f](https://github.com/algolia/instantsearch.js/commit/c251e8f)), closes [#2086](https://github.com/algolia/instantsearch.js/issues/2086) - - - - -# [4.0.0-beta.0](https://github.com/algolia/instantsearch.js/compare/v3.3.0...v4.0.0-beta.0) (2017-03-28) - - -### Features - -* **multi-index:** ease multi index and auto complete ([09a4e1d](https://github.com/algolia/instantsearch.js/commit/09a4e1d)) - - -### BREAKING CHANGES - -* multi-index: * Reseting the pagination should be done at each connector level inside the "refine" function when returning the search state. -* The current page now appears inside the search state when a widget is used -* Query values are part of the items prop of the connectCurrentRefinements connector. Behaviour is unchanged, query will be filtered if clearsQuery prop is false. -* Add the index name to all the current refinements items. (not used by our widgets yet, but available if needed). - - - - -# [3.3.0](https://github.com/algolia/instantsearch.js/compare/v3.2.2-beta0...v3.3.0) (2017-03-22) - - -### Bug Fixes - -* **example:** Fix access to props in react-router example ([1417d6f](https://github.com/algolia/instantsearch.js/commit/1417d6f)) - - - - -## [3.2.2-beta0](https://github.com/algolia/instantsearch.js/compare/v3.2.1...v3.2.2-beta0) (2017-03-20) - - -### Bug Fixes - -* **InfiniteHits:** provide translation key for `Load More` (#2048) ([6130bf2](https://github.com/algolia/instantsearch.js/commit/6130bf2)) -* **SearchBox:** better mobile behaviour by default ([ea968b3](https://github.com/algolia/instantsearch.js/commit/ea968b3)) -* **example:** link to instantsearch/react (#2007) ([5e674cd](https://github.com/algolia/instantsearch.js/commit/5e674cd)) -* **recipes:** react router v4 ([de673bf](https://github.com/algolia/instantsearch.js/commit/de673bf)) - - -### Features - -* **SearchBox:** add role=search to the form (#2046) ([d1e90f3](https://github.com/algolia/instantsearch.js/commit/d1e90f3)) -* **SearchBox:** allow custom reset and submit components (#1991) ([cd303d7](https://github.com/algolia/instantsearch.js/commit/cd303d7)) -* **searchBox:** add event handling ([e267ab6](https://github.com/algolia/instantsearch.js/commit/e267ab6)), closes [#2017](https://github.com/algolia/instantsearch.js/issues/2017) - - - - -## [3.2.1](https://github.com/algolia/instantsearch.js/compare/v3.2.0...v3.2.1) (2017-02-22) - - -### Bug Fixes - -* **umd:** Add connectors to UMD build (#1988) ([23ac5e6](https://github.com/algolia/instantsearch.js/commit/23ac5e6)), closes [#1987](https://github.com/algolia/instantsearch.js/issues/1987) - - - - -# [3.2.0](https://github.com/algolia/instantsearch.js/compare/v3.1.0...v3.2.0) (2017-02-15) - - -### Bug Fixes - -* **Configure:** use props a unique source of truth (#1967) ([9d53d86](https://github.com/algolia/instantsearch.js/commit/9d53d86)) -* **SearchBox:** Safari can only have with xlinkHref (#1970) ([7ab00bd](https://github.com/algolia/instantsearch.js/commit/7ab00bd)), closes [#1968](https://github.com/algolia/instantsearch.js/issues/1968) - - -### Features - -* **MultiRange:** add an all range (#1959) ([a3dc950](https://github.com/algolia/instantsearch.js/commit/a3dc950)) - - -### BREAKING CHANGES - -* MultiRange: - MultiRange/connectMultiRange: will add a "All" range to allow unselection of range without the usage of CurrentRefinements. This range can be either filtered or ramove via CSS if not needed. The label can be changed by using our translations system. - - - - -# [3.1.0](https://github.com/algolia/instantsearch.js/compare/v3.0.0...v3.1.0) (2017-02-08) - - -### Bug Fixes - -* **Configure:** call onSearchStateChange when props are updated (#1953) ([7e151db](https://github.com/algolia/instantsearch.js/commit/7e151db)), closes [#1950](https://github.com/algolia/instantsearch.js/issues/1950) -* **Configure:** trigger onSearchStateChange with the right data ([11e5af8](https://github.com/algolia/instantsearch.js/commit/11e5af8)) -* **createConnector:** updates with latest props on state change (#1951) ([cd3a82c](https://github.com/algolia/instantsearch.js/commit/cd3a82c)) - - -### Features - -* **ClearAll:** add withQuery to also clear the search query (#1958) ([c0e695b](https://github.com/algolia/instantsearch.js/commit/c0e695b)) - - - - -# [3.0.0](https://github.com/algolia/instantsearch.js/compare/v2.2.5...v3.0.0) (2017-02-06) - - -### Bug Fixes - -* ***List:** disable shortcuts in *List SearchBoxes (#1921) ([51a76ae](https://github.com/algolia/instantsearch.js/commit/51a76ae)), closes [#1920](https://github.com/algolia/instantsearch.js/issues/1920) -* **Configure:** add configure parameters in search state (#1935) ([0971330](https://github.com/algolia/instantsearch.js/commit/0971330)), closes [#1863](https://github.com/algolia/instantsearch.js/issues/1863) -* **Hits:** limit the hitComponent to be only a function (#1912) ([b3c9578](https://github.com/algolia/instantsearch.js/commit/b3c9578)) -* **Pagination:** fix and indicate when pagination is disabled ([5f20199](https://github.com/algolia/instantsearch.js/commit/5f20199)), closes [#1938](https://github.com/algolia/instantsearch.js/issues/1938) -* **StarRating:** usage with filters (#1933) ([667e9d5](https://github.com/algolia/instantsearch.js/commit/667e9d5)) -* **withSearchBox:** keep displaying searchBox when no items found (#1930) ([30de4cd](https://github.com/algolia/instantsearch.js/commit/30de4cd)) - - -### Features - -* **MultiRange:** indicate if a range has no refinements (#1926) ([80b6450](https://github.com/algolia/instantsearch.js/commit/80b6450)) -* **panel:** add a panel widget (#1889) ([594e1a1](https://github.com/algolia/instantsearch.js/commit/594e1a1)) -* **starRating:** indicate when any refinement has no effect ([c547ae5](https://github.com/algolia/instantsearch.js/commit/c547ae5)) -* **widgets:** default design for disabled states (#1929) ([31f010b](https://github.com/algolia/instantsearch.js/commit/31f010b)) - -### Migration guide - -The migration to V3.0.0 should be safe and you should do it. - -There are two breaking changes that you will need to handle in your codebase: -- Anytime you are using a connector, when there are no more items in it or no more hits, we will still call your Component. Thus you will have to handle cases like dealing with empty arrays and decide if you want to unmount or hide the widget. -- Anytime you are using a widget, when there are no more items in it or no more hits, we will still display the widget. You can then decide to hide it with CSS. - - -## [2.2.5](https://github.com/algolia/instantsearch.js/compare/v2.2.4...v2.2.5) (2017-01-23) - - -### Bug Fixes - -* **currentRefinements:** make removing a toggle refinement work ([8995e64](https://github.com/algolia/instantsearch.js/commit/8995e64)) - - - - -## [2.2.4](https://github.com/algolia/instantsearch.js/compare/v2.2.3...v2.2.4) (2017-01-20) - - -### Bug Fixes - -* **publish:** publish react-instantsearch/dist instead of root (#1884) ([64414e0](https://github.com/algolia/instantsearch.js/commit/64414e0)) - - - - -## [2.2.3](https://github.com/algolia/instantsearch.js/compare/v2.2.2...v2.2.3) (2017-01-20) - - -### Bug Fixes - -* **SFFV:** translations for searchbox were not applied (#1879) ([e9b4ee1](https://github.com/algolia/instantsearch.js/commit/e9b4ee1)) - - - - -## [2.2.2](https://github.com/algolia/instantsearch.js/compare/v2.2.1...v2.2.2) (2017-01-18) - - -### Bug Fixes - -* **react-router:** search was triggered two many times (#1840) ([25e9db5](https://github.com/algolia/instantsearch.js/commit/25e9db5)) -* **SFFV:** empty query triggered a new SFFV (#1875) ([6c8259a](https://github.com/algolia/instantsearch.js/commit/6c8259a)) - - - - -## [2.2.1](https://github.com/algolia/instantsearch.js/compare/v2.2.0...v2.2.1) (2017-01-18) - - -### Bug Fixes - -* **createInstantsearch:** fix missing props (#1867) ([8d319b5](https://github.com/algolia/instantsearch.js/commit/8d319b5)), closes [#1867](https://github.com/algolia/instantsearch.js/issues/1867) - - - - -# [2.2.0](https://github.com/algolia/instantsearch.js/compare/v2.1.0...v2.2.0) (2017-01-17) - - -### Bug Fixes - -* **clear:** clearing wasn't working with too+ same type facets selected (#1820) ([a9a2364](https://github.com/algolia/instantsearch.js/commit/a9a2364)) -* **connectSearchBox:** handle `defaultRefinement` (#1829) ([7a730e2](https://github.com/algolia/instantsearch.js/commit/7a730e2)), closes [#1826](https://github.com/algolia/instantsearch.js/issues/1826) -* **Instantsearch:** Update all props on InstantSearch (#1828) ([2ed9b49](https://github.com/algolia/instantsearch.js/commit/2ed9b49)) -* **InstantSearch:** add specific `react-instantsearch ${version}` agent (#1844) ([a1113bc](https://github.com/algolia/instantsearch.js/commit/a1113bc)) -* **SFFV:** correct propTypes and add missing default values (#1845) ([a4c1b31](https://github.com/algolia/instantsearch.js/commit/a4c1b31)) -* **test:** add missing Snippet and Highliter snapshot ([4accce5](https://github.com/algolia/instantsearch.js/commit/4accce5)) -* **widgets:** replace setImmediate use with Promise use when update is needed (#1811) ([17e2497](https://github.com/algolia/instantsearch.js/commit/17e2497)) - - -### Features - -* **Menu, connectMenu:** add search for facet values (#1822) ([a6c513e](https://github.com/algolia/instantsearch.js/commit/a6c513e)) -* **snippet:** add a snippet widget to be able to highlight snippet results (#1797) ([2aecc40](https://github.com/algolia/instantsearch.js/commit/2aecc40)) -* **widgets:** add transformItems to be able to sort and filter (#1809) ([ba539f0](https://github.com/algolia/instantsearch.js/commit/ba539f0)) - - - - -# [2.1.0](https://github.com/algolia/instantsearch.js/compare/v2.0.1...v2.1.0) (2017-01-04) - - -### Bug Fixes - -* **createInstantSearchManager:** drop outdated response (#1765) ([76c5312](https://github.com/algolia/instantsearch.js/commit/76c5312)) -* **highlight:** highlight should work even if the attribute is missing (#1791) ([5b79b15](https://github.com/algolia/instantsearch.js/commit/5b79b15)), closes [#1790](https://github.com/algolia/instantsearch.js/issues/1790) -* **InfiniteHits:** better classname to loadmore btn (#1789) ([ad2ded3](https://github.com/algolia/instantsearch.js/commit/ad2ded3)) -* **starRatings:** click on selected range doesn't unselect it (#1766) ([beacc72](https://github.com/algolia/instantsearch.js/commit/beacc72)) -* **website:** broken demo links (#1802) ([0abe2f5](https://github.com/algolia/instantsearch.js/commit/0abe2f5)) -* **widgets:** add 300px width for the default SearchBox (#1803) ([bf5d791](https://github.com/algolia/instantsearch.js/commit/bf5d791)) - - -### Features - -* **InfiniteHits:** Add class to load more button (#1787) ([416febd](https://github.com/algolia/instantsearch.js/commit/416febd)) -* **RefinementList, connectRefinementList:** allow to search for facet values ([e086a81](https://github.com/algolia/instantsearch.js/commit/e086a81)) - - - - -## [2.0.1](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.1) (2016-12-15) - - -### Bug Fixes - -* **connectRange:** when unfinite numbers are passed throw ([75bec0d](https://github.com/algolia/instantsearch.js/commit/75bec0d)) -* **react-native:** use View as a container for react-native (#1729) ([5b76f75](https://github.com/algolia/instantsearch.js/commit/5b76f75)), closes [#1730](https://github.com/algolia/instantsearch.js/issues/1730) -* **SearchBox:** autocomplete was not disabled by default (#1742) ([bc76618](https://github.com/algolia/instantsearch.js/commit/bc76618)) -* **starRating:** call createURL with the right interface (min/max) (#1747) ([f9ab9b6](https://github.com/algolia/instantsearch.js/commit/f9ab9b6)) - - - - -## [2.0.0](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.0) (2016-12-08) - -First release of `react-instantsearch` diff --git a/packages/react-instantsearch-hooks/README.md b/packages/react-instantsearch-hooks/README.md deleted file mode 100644 index b962037f90..0000000000 --- a/packages/react-instantsearch-hooks/README.md +++ /dev/null @@ -1,81 +0,0 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [react-instantsearch-hooks](#react-instantsearch-hooks) - - [Installation](#installation) - - [Getting started](#getting-started) - - [API reference](#api-reference) - - [Documentation](#documentation) - - [Contributing](#contributing) - - [License](#license) - - - -# react-instantsearch-hooks - -React InstantSearch Hooks is an open-source UI library for React that lets you quickly build a search interface in your front-end application. - -InstantSearch’s goal is to help you implement awesome search experiences as smoothly as possible by providing a [complete search ecosystem](https://algolia.com/doc/guides/getting-started/how-algolia-works/#the-full-ecosystem). InstantSearch tackles an important part of this vast goal by providing front-end primitives that you can assemble into unique search interfaces. - -

    - - Edit on CodeSandbox - -

    - -> Note: `react-instantsearch-hooks` exports renderless components and hooks which can be used for both web and React Native. If you are using React in a web project, we recommend using the package `react-instantsearch-hooks-web` instead, as it includes complete components that render to the DOM. - -## Installation - -React InstantSearch Hooks is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs. - -```sh -yarn add algoliasearch react-instantsearch-hooks -# or -npm install algoliasearch react-instantsearch-hooks -``` - -## Getting started - -React InstantSearch Hooks is a headless React library that lets you create an instant search results experience using Algolia’s search API. - -Check out our [**Getting Started guide**](https://algolia.com/doc/guides/building-search-ui/getting-started/react-hooks/) to start implementing a full-featured search experience with React InstantSearch Hooks. - -## API reference - -Check out the [**API reference**](https://www.algolia.com/doc/api-reference/widgets/react-hooks/). - -## Documentation - -The documentation is available on [algolia.com/doc](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/). - -## Contributing - -We welcome all contributors, from casual to regular 💙 - -- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport]. -- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest]. -- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it. -- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore]. - -To start contributing to code, you need to: - -1. [Fork the project](https://help.github.com/articles/fork-a-repo/) -1. [Clone the repository](https://help.github.com/articles/cloning-a-repository/) -1. Install the dependencies: `yarn` - -Please read [our contribution process](https://github.com/algolia/instantsearch.js/blob/master/CONTRIBUTING.md) to learn more. - -## License - -React InstantSearch Hooks is [MIT licensed](../../LICENSE). - - - -[contributing-bugreport]: https://github.com/algolia/instantsearch.js/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-featurerequest]: https://github.com/algolia/instantsearch.js/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch+Hooks&title=Feature%20request%3A%20 -[contributing-newissue]: https://github.com/algolia/instantsearch.js/issues/new?labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-label-easy]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-bug]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-chore]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 diff --git a/packages/react-instantsearch-hooks/global.d.ts b/packages/react-instantsearch-hooks/global.d.ts deleted file mode 100644 index b867229b43..0000000000 --- a/packages/react-instantsearch-hooks/global.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare const __DEV__: boolean; diff --git a/packages/react-instantsearch-hooks/package.json b/packages/react-instantsearch-hooks/package.json deleted file mode 100644 index ce75facbd5..0000000000 --- a/packages/react-instantsearch-hooks/package.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "react-instantsearch-hooks", - "version": "6.47.3", - "description": "⚡ Lightning-fast search for React, by Algolia", - "source": "src/index.ts", - "types": "dist/es/index.d.ts", - "main": "dist/cjs/index.js", - "module": "dist/es/index.js", - "type": "module", - "exports": { - ".": { - "import": "./dist/es/index.js", - "require": "./dist/cjs/index.js" - } - }, - "sideEffects": false, - "license": "MIT", - "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/", - "repository": { - "type": "git", - "url": "https://github.com/algolia/instantsearch.js" - }, - "author": { - "name": "Algolia, Inc.", - "url": "https://www.algolia.com" - }, - "keywords": [ - "algolia", - "components", - "fast", - "instantsearch", - "react", - "search" - ], - "files": [ - "README.md", - "dist" - ], - "scripts": { - "clean": "rm -rf dist", - "watch": "yarn build:cjs --watch", - "build": "yarn build:cjs && yarn build:es && yarn build:umd && yarn build:types", - "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet && ../../scripts/prepare-cjs.sh", - "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", - "build:umd": "BABEL_ENV=rollup rollup -c rollup.config.js", - "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es", - "test:exports": "node ./test/module/is-es-module.mjs && node ./test/module/is-cjs-module.cjs", - "version": "./scripts/version.cjs" - }, - "dependencies": { - "@babel/runtime": "^7.1.2", - "algoliasearch-helper": "3.14.0", - "instantsearch.js": "4.56.8", - "use-sync-external-store": "^1.0.0" - }, - "devDependencies": { - "@types/use-sync-external-store": "0.0.3" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 5", - "react": ">= 16.8.0 < 19" - } -} diff --git a/packages/react-instantsearch-hooks/rollup.config.js b/packages/react-instantsearch-hooks/rollup.config.js deleted file mode 100644 index 0f2347d61a..0000000000 --- a/packages/react-instantsearch-hooks/rollup.config.js +++ /dev/null @@ -1,80 +0,0 @@ -import babel from 'rollup-plugin-babel'; -import commonjs from 'rollup-plugin-commonjs'; -import filesize from 'rollup-plugin-filesize'; -import globals from 'rollup-plugin-node-globals'; -import resolve from 'rollup-plugin-node-resolve'; -import replace from 'rollup-plugin-replace'; -import { uglify } from 'rollup-plugin-uglify'; - -const clear = (x) => x.filter(Boolean); - -const version = process.env.VERSION || 'UNRELEASED'; -const algolia = '© Algolia, inc.'; -const link = 'https://github.com/algolia/instantsearch.js'; -const createBanner = (name) => - `/*! React InstantSearch${name} ${version} | ${algolia} | ${link} */`; - -const plugins = [ - babel({ - exclude: /node_modules|algoliasearch-helper/, - extensions: ['.js', '.ts', '.tsx'], - rootMode: 'upward', - runtimeHelpers: true, - }), - resolve({ - browser: true, - preferBuiltins: false, - extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], - }), - commonjs({ - namedExports: { - '../../node_modules/use-sync-external-store/shim/index.js': [ - 'useSyncExternalStore', - ], - }, - }), - globals(), - replace({ - 'process.env.NODE_ENV': JSON.stringify('production'), - }), - filesize({ - showMinifiedSize: false, - showGzippedSize: true, - }), -]; - -const createConfiguration = ({ name, minify = false } = {}) => ({ - input: 'src/index.ts', - external: ['react'], - output: { - file: `dist/umd/ReactInstantSearch${name}${minify ? '.min' : ''}.js`, - name: `ReactInstantSearch${name}`, - format: 'umd', - globals: { - react: 'React', - }, - banner: createBanner(name), - sourcemap: true, - }, - plugins: plugins.concat( - clear([ - minify && - uglify({ - output: { - preamble: createBanner(name), - }, - }), - ]) - ), -}); - -export default [ - createConfiguration({ - name: 'Hooks', - }), - - createConfiguration({ - name: 'Hooks', - minify: true, - }), -]; diff --git a/packages/react-instantsearch-hooks/src/__tests__/index.test.ts b/packages/react-instantsearch-hooks/src/__tests__/index.test.ts deleted file mode 100644 index 74ba2f792b..0000000000 --- a/packages/react-instantsearch-hooks/src/__tests__/index.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -describe('warnings', () => { - it('warns on import after release', () => { - jest.useFakeTimers('modern').setSystemTime(new Date('2023-08-09')); - - expect(() => require('../index')).toWarnDev( - '[InstantSearch] The package `react-instantsearch-hooks` is replaced by `react-instantsearch-core`. The API is the same, but the package name has changed. Please update your dependencies and follow the migration guide: https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/' - ); - }); - - it('does not warn on import before release', () => { - jest.useFakeTimers('modern').setSystemTime(new Date('2023-08-08')); - - expect(() => require('../index')).not.toWarnDev(); - }); -}); diff --git a/packages/react-instantsearch-hooks/src/index.ts b/packages/react-instantsearch-hooks/src/index.ts deleted file mode 100644 index 0eed78859d..0000000000 --- a/packages/react-instantsearch-hooks/src/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { warn } from './lib/warn'; - -warn( - Date.now() < new Date('2023-08-09').getTime(), - 'The package `react-instantsearch-hooks` is replaced by `react-instantsearch-core`. The API is the same, but the package name has changed. Please update your dependencies and follow the migration guide: https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/' -); - -export { default as version } from './version'; -export * from './components/Configure'; -export * from './components/DynamicWidgets'; -export * from './components/Index'; -export * from './components/InstantSearch'; -export * from './components/InstantSearchServerContext'; -export * from './components/InstantSearchSSRProvider'; -export * from './connectors/useBreadcrumb'; -export * from './connectors/useClearRefinements'; -export * from './connectors/useConfigure'; -export * from './connectors/useCurrentRefinements'; -export * from './connectors/useDynamicWidgets'; -export * from './connectors/useGeoSearch'; -export * from './connectors/useHierarchicalMenu'; -export * from './connectors/useHits'; -export * from './connectors/useHitsPerPage'; -export * from './connectors/useInfiniteHits'; -export * from './connectors/useMenu'; -export * from './connectors/useNumericMenu'; -export * from './connectors/usePagination'; -export * from './connectors/usePoweredBy'; -export * from './connectors/useQueryRules'; -export * from './connectors/useRange'; -export * from './connectors/useRefinementList'; -export * from './connectors/useSearchBox'; -export * from './connectors/useSortBy'; -export * from './connectors/useStats'; -export * from './connectors/useToggleRefinement'; -export * from './hooks/useConnector'; -export * from './hooks/useInstantSearch'; diff --git a/packages/react-instantsearch-hooks/test/module/is-cjs-module.cjs b/packages/react-instantsearch-hooks/test/module/is-cjs-module.cjs deleted file mode 100644 index f659152499..0000000000 --- a/packages/react-instantsearch-hooks/test/module/is-cjs-module.cjs +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable no-console */ - -const assert = require('assert'); - -const ReactInstantSearchHooks = require('react-instantsearch-hooks'); - -assert.ok(ReactInstantSearchHooks); - -console.log('react-instantsearch-hooks is valid CJS'); diff --git a/packages/react-instantsearch-hooks/test/module/is-es-module.mjs b/packages/react-instantsearch-hooks/test/module/is-es-module.mjs deleted file mode 100644 index 98a477ee7e..0000000000 --- a/packages/react-instantsearch-hooks/test/module/is-es-module.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-disable no-console */ -import assert from 'assert'; - -import * as ReactInstantSearchHooks from 'react-instantsearch-hooks'; - -assert.ok(ReactInstantSearchHooks); - -console.log('react-instantsearch-hooks is valid ESM'); diff --git a/packages/react-instantsearch-hooks/tsconfig.declaration.json b/packages/react-instantsearch-hooks/tsconfig.declaration.json deleted file mode 100644 index 1e0c6449f8..0000000000 --- a/packages/react-instantsearch-hooks/tsconfig.declaration.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.declaration" -} diff --git a/packages/react-instantsearch-native/CHANGELOG.md b/packages/react-instantsearch-native/CHANGELOG.md deleted file mode 100644 index 71406a6aea..0000000000 --- a/packages/react-instantsearch-native/CHANGELOG.md +++ /dev/null @@ -1,1855 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [6.40.4](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.40.3...react-instantsearch-native@6.40.4) (2023-07-25) - -**Note:** Version bump only for package react-instantsearch-native - - - - - -## [6.40.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.40.2...react-instantsearch-native@6.40.3) (2023-07-19) - -**Note:** Version bump only for package react-instantsearch-native - - - - - -## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.40.1...react-instantsearch-native@6.40.2) (2023-07-18) - -**Note:** Version bump only for package react-instantsearch-native - - - - - -## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.40.0...react-instantsearch-native@6.40.1) (2023-06-20) - -**Note:** Version bump only for package react-instantsearch-native - - - - - -## [6.39.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.39.1...react-instantsearch-native@6.39.2) (2023-05-16) - -**Note:** Version bump only for package react-instantsearch-native - - - - - -## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.38.3...react-instantsearch-native@6.39.1) (2023-01-26) - -**Note:** Version bump only for package react-instantsearch-native - - - - - - -## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.38.2...react-instantsearch-native@6.38.3) (2023-01-09) - -**Note:** Version bump only for package react-instantsearch-native - - - - - -## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-native@6.38.1...react-instantsearch-native@6.38.2) (2023-01-03) - -**Note:** Version bump only for package react-instantsearch-native - - - - - -## [6.38.1](https://github.com/algolia/react-instantsearch/compare/v6.38.0...v6.38.1) (2022-11-08) - - -### Bug Fixes - -* **hooks:** avoid effect in useStableValue ([#3670](https://github.com/algolia/react-instantsearch/issues/3670)) ([d1f53ae](https://github.com/algolia/react-instantsearch/commit/d1f53ae815b75f13c18fd245e0403d57e7ae391c)) - - - -# [6.38.0](https://github.com/algolia/react-instantsearch/compare/v6.37.0...v6.38.0) (2022-10-25) - - -### Features - -* **getServerState:** allow users to inject renderToString ([#3658](https://github.com/algolia/react-instantsearch/issues/3658)) ([9c10449](https://github.com/algolia/react-instantsearch/commit/9c104497b9b32337f288d70a2582c41cafb13cd6)), closes [#3633](https://github.com/algolia/react-instantsearch/issues/3633) [#3618](https://github.com/algolia/react-instantsearch/issues/3618) [vercel/next.js#40067](https://github.com/vercel/next.js/issues/40067) - -* **PoweredBy:** update component logo ([#3661](https://github.com/algolia/react-instantsearch/issues/3661)) ([69c1b2a](https://github.com/algolia/react-instantsearch/commit/69c1b2acef64d972dfa6c6beb8967032119ad2d5)) - - - -# [6.37.0](https://github.com/algolia/react-instantsearch/compare/v6.36.0...v6.37.0) (2022-10-18) - - -### Features - -* **core:** support react 18 strict mode ([#3653](https://github.com/algolia/react-instantsearch/issues/3653)) ([9174806](https://github.com/algolia/react-instantsearch/commit/9174806a7997a45893a24d149027119f4a0709c3)) - - - -# [6.36.0](https://github.com/algolia/react-instantsearch/compare/v6.35.0...v6.36.0) (2022-10-04) - - -### Features - -* **HierarchicalMenu:** add css class for link of selected menu item ([#3646](https://github.com/algolia/react-instantsearch/issues/3646)) ([980ad70](https://github.com/algolia/react-instantsearch/commit/980ad70c75e970c35c11a10a534dbe3996d6c54c)) -* **useInstantSearch:** expose status & error ([#3645](https://github.com/algolia/react-instantsearch/issues/3645)) ([f436d31](https://github.com/algolia/react-instantsearch/commit/f436d31184f3f75b33a1fdaa19c665e77948df28)) - - - -# [6.35.0](https://github.com/algolia/react-instantsearch/compare/v6.34.0...v6.35.0) (2022-09-29) - - -### Features - -* **hooks-web:** introduce Translations API ([#3638](https://github.com/algolia/react-instantsearch/issues/3638)) ([63b506f](https://github.com/algolia/react-instantsearch/commit/63b506f9dbad284f45ac17210e17c4a2a8f099b6)) - -### Fixes -* **hooks-web:** when searchAsYouType=false, pressing the reset button searches (previously only reset the query) ([#3642](https://github.com/algolia/react-instantsearch/issues/3642)) ([f969deb](https://github.com/algolia/react-instantsearch/commit/f969deb05fd4f53aaa251ff88b52db2224ce0786)) - -# [6.34.0](https://github.com/algolia/react-instantsearch/compare/v6.33.0...v6.34.0) (2022-09-27) - - -### Features - -* **SearchBox:** expose formRef ([#3565](https://github.com/algolia/react-instantsearch/issues/3565)) ([1c2f46d](https://github.com/algolia/react-instantsearch/commit/1c2f46da2d1cf705cfd3946c52aef4ca9ec943d7)) - - - -# [6.33.0](https://github.com/algolia/react-instantsearch/compare/v6.32.1...v6.33.0) (2022-09-15) - - -### Bug Fixes - -* **react-native:** mark as compatible with react 18 ([#3614](https://github.com/algolia/react-instantsearch/issues/3614)) ([2a191a8](https://github.com/algolia/react-instantsearch/commit/2a191a84751127de5a3eb34b59b460a1d1bfe594)) -* **rish:** hide reset button when search is stalled in `SearchBox` ([#3617](https://github.com/algolia/react-instantsearch/issues/3617)) ([93ee9d0](https://github.com/algolia/react-instantsearch/commit/93ee9d0212893cef4842c86b7c78f285aa136be8)) - - -### Features - -* **dependencies:** update instantsearch and helper ([#3622](https://github.com/algolia/react-instantsearch/issues/3622)) ([6773ab1](https://github.com/algolia/react-instantsearch/commit/6773ab169cd74dfe1133e255daade4d57e99d9a4)) - - - -## [6.32.1](https://github.com/algolia/react-instantsearch/compare/v6.32.0...v6.32.1) (2022-08-26) - - -### Bug Fixes - -* **useInstantSearch:** prevent `results` from being `null` when first search is stalled ([#3597](https://github.com/algolia/react-instantsearch/issues/3597)) ([6f71d78](https://github.com/algolia/react-instantsearch/commit/6f71d78868fde55a3f9c4460edc337a1e99df4f9)) - - - -# [6.32.0](https://github.com/algolia/react-instantsearch/compare/v6.31.1...v6.32.0) (2022-08-22) - - -### Features - -* **SearchBox:** introduce `autoFocus` prop ([#3599](https://github.com/algolia/react-instantsearch/issues/3599)) ([99121b9](https://github.com/algolia/react-instantsearch/commit/99121b952fd002cb6dae52af41f08beed8f6c3e2)) - - - -## [6.31.1](https://github.com/algolia/react-instantsearch/compare/v6.31.0...v6.31.1) (2022-08-08) - - -### Bug Fixes - -* **hooks:** prevent widget cleanup on `` unmount ([#3590](https://github.com/algolia/react-instantsearch/issues/3590)) ([d94899d](https://github.com/algolia/react-instantsearch/commit/d94899d1264134f0cb1ca2d266a660f1fb2a588c)) - - - -# [6.31.0](https://github.com/algolia/react-instantsearch/compare/v6.30.3...v6.31.0) (2022-08-03) - - -### Features - -* **hooks:** add `searchAsYouType` option to `` ([#3585](https://github.com/algolia/react-instantsearch/issues/3585)) ([c610385](https://github.com/algolia/react-instantsearch/commit/c610385cb9688b23b3e041e31b9edd60392b341d)) - - - -## [6.30.3](https://github.com/algolia/react-instantsearch/compare/v6.30.2...v6.30.3) (2022-08-01) - - -### Bug Fixes - -* **hooks:** replace `labelText` CSS class with `label` in `` to align with InstantSearch's CSS specifications ([#3583](https://github.com/algolia/react-instantsearch/issues/3583)) ([3e030ae](https://github.com/algolia/react-instantsearch/commit/3e030aedb9f285ff449eb82589bc6fea60b160cb)) - - - -## [6.30.2](https://github.com/algolia/react-instantsearch/compare/v6.30.1...v6.30.2) (2022-07-18) - - -### Bug Fixes - -* **hooks:** type of DynamicWidgets props ([#3566](https://github.com/algolia/react-instantsearch/issues/3566)) ([612c98b](https://github.com/algolia/react-instantsearch/commit/612c98b5a77fb9037185c4b5efda8c07663dbd1a)), closes [#3563](https://github.com/algolia/react-instantsearch/issues/3563) -* **hooks:** use single instance in ([#3561](https://github.com/algolia/react-instantsearch/issues/3561)) ([4c358be](https://github.com/algolia/react-instantsearch/commit/4c358bebfc91451b1610f677f89c595d7a427f1f)) - - - -## [6.30.1](https://github.com/algolia/react-instantsearch/compare/v6.30.0...v6.30.1) (2022-07-12) - - -### Bug Fixes - -* **hooks:** provide state and results APIs from "render" event ([#3554](https://github.com/algolia/react-instantsearch/issues/3554)) ([67d4788](https://github.com/algolia/react-instantsearch/commit/67d4788ab09ec2a57b43d53e8093b8c11120b761)) -* **hooks**: update instantsearch.js dependency ([#3557](https://github.com/algolia/react-instantsearch/issues/3557)) - - - -# [6.30.0](https://github.com/algolia/react-instantsearch/compare/v6.29.0...v6.30.0) (2022-07-05) - - -### Features - -* **core:** update instantsearch and helper ([#3539](https://github.com/algolia/react-instantsearch/issues/3539)) ([0ac2c7a](https://github.com/algolia/react-instantsearch/commit/0ac2c7a3f2e2a827721f5b2b7c69c54560f8574f)) - - - -# [6.29.0](https://github.com/algolia/react-instantsearch/compare/v6.28.0...v6.29.0) (2022-06-21) - - -### Bug Fixes - -* **HierarchicalMenu:** show full hierarchical parent values ([#3521](https://github.com/algolia/react-instantsearch/issues/3521)) ([79c3890](https://github.com/algolia/react-instantsearch/commit/79c3890848175a4d70311e5c3929c902bb953c10)) - - -### Features - -* **core:** sort parameters for improved cache rate ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) -* **core:** support client.search for sffv ([#3528](https://github.com/algolia/react-instantsearch/issues/3528)) ([8320d99](https://github.com/algolia/react-instantsearch/commit/8320d995385e27f271134b014bab6ffa955b3986)) - - - -# [6.28.0](https://github.com/algolia/react-instantsearch/compare/v6.27.0...v6.28.0) (2022-06-15) - - -### Bug Fixes - -* **hooks-server:** import react server via an expression ([#3515](https://github.com/algolia/react-instantsearch/issues/3515)) ([91b96f7](https://github.com/algolia/react-instantsearch/commit/91b96f743b9315ed5ea781681b77fc7f5604ab6e)), closes [#3512](https://github.com/algolia/react-instantsearch/issues/3512) -* **hooks-web:** fix duplicated key in ([#3513](https://github.com/algolia/react-instantsearch/issues/3513)) ([fc94d80](https://github.com/algolia/react-instantsearch/commit/fc94d806daf139f58b234cdc0b450da2efe861ee)) -* **hooks:** mount widgets in SSR to retrieve HTML ([#3518](https://github.com/algolia/react-instantsearch/issues/3518)) ([aa5f9d8](https://github.com/algolia/react-instantsearch/commit/aa5f9d84ddb6e97d05e6ad1baf2c6caa23891281)) -* **types:** allow useInstantSearch to be generic ([#3508](https://github.com/algolia/react-instantsearch/issues/3508)) ([6807232](https://github.com/algolia/react-instantsearch/commit/68072324cf302801502a1b4c3d06703e57b55a97)), closes [algolia/instantsearch.js#5060](https://github.com/algolia/instantsearch.js/issues/5060) -* **types:** support React 18 types ([#3481](https://github.com/algolia/react-instantsearch/issues/3481)) ([74cf8cb](https://github.com/algolia/react-instantsearch/commit/74cf8cb9be8ff3d113b57a50e7083df0d1bc94f2)) - - -### Features - -* **hooks:** introduce `useInstantSearch()` ([#3494](https://github.com/algolia/react-instantsearch/issues/3494)) ([74d522c](https://github.com/algolia/react-instantsearch/commit/74d522c032326658f2a0b8f0001bd593e0085208)) -* **hooks:** support React 18 Strict Mode ([#3514](https://github.com/algolia/react-instantsearch/issues/3514)) ([eeb67c7](https://github.com/algolia/react-instantsearch/commit/eeb67c7b5dc08c696c46d9538f104eeceecef388)) - - - -# [6.27.0](https://github.com/algolia/react-instantsearch/compare/v6.26.0...v6.27.0) (2022-06-07) - - -### Bug Fixes - -* **hooks-web:** don't pass widget props to ui components ([#3501](https://github.com/algolia/react-instantsearch/issues/3501)) ([5bd53c1](https://github.com/algolia/react-instantsearch/commit/5bd53c128ddeeea87f75ae89eb8f2324d476c70e)), closes [#3499](https://github.com/algolia/react-instantsearch/issues/3499) -* **SearchBox-hooks:** correctly pass widget props ([#3499](https://github.com/algolia/react-instantsearch/issues/3499)) ([2cdf906](https://github.com/algolia/react-instantsearch/commit/2cdf90602b7c2c5895124ef64c389ce574154386)), closes [#3498](https://github.com/algolia/react-instantsearch/issues/3498) - - -### Features - -* **hooks:** migrate to `useSyncExternalStore()` ([#3489](https://github.com/algolia/react-instantsearch/issues/3489)) ([81bbdf2](https://github.com/algolia/react-instantsearch/commit/81bbdf28f2d28d8b0081cfd7d9e84c3e33038dd2)) -* **hooks:** upgrade to InstantSearch.js 4.41.0 ([#3502](https://github.com/algolia/react-instantsearch/issues/3502)) ([0b76792](https://github.com/algolia/react-instantsearch/commit/0b76792ea0c4e2ac9fe742810d70ba1aee2a3e79)) - - - -# [6.26.0](https://github.com/algolia/react-instantsearch/compare/v6.25.0...v6.26.0) (2022-05-19) - - -### Features - -* **hooks-web:** expose `sendEvent` to `hitComponent` ([#3476](https://github.com/algolia/react-instantsearch/issues/3476)) ([5cc18d1](https://github.com/algolia/react-instantsearch/commit/5cc18d19d9f22305f33d92e43fd0aca2a5cb949a)) -* **react-instantsearch-core:** allow widgets to set their `$$widgetType` ([#3472](https://github.com/algolia/react-instantsearch/issues/3472)) ([1c436e1](https://github.com/algolia/react-instantsearch/commit/1c436e1429ab4230bbfea7c6d2474d141f5c5c64)) - - - -# [6.25.0](https://github.com/algolia/react-instantsearch/compare/v6.24.3...v6.25.0) (2022-05-17) - - -### Bug Fixes - -* **hooks-highlight:** make sure highlight and snippet don't show html-escaped content ([#3471](https://github.com/algolia/react-instantsearch/issues/3471)) ([c18ddd2](https://github.com/algolia/react-instantsearch/commit/c18ddd25faca37d6bfa3d1c28f6fc22ec5fcf6d8)) -* **hooks-server:** remove faulty UMD build ([#3465](https://github.com/algolia/react-instantsearch/issues/3465)) ([c1ddfe4](https://github.com/algolia/react-instantsearch/commit/c1ddfe408b411551ac8524877a9d65ded8133c42)) - - -### Features - -* **dom-maps:** expose GeoSearchContext ([#3468](https://github.com/algolia/react-instantsearch/issues/3468)) ([a61ff96](https://github.com/algolia/react-instantsearch/commit/a61ff96222bfd4f6301cf93bf95e2fa18b263d3c)), closes [#3448](https://github.com/algolia/react-instantsearch/issues/3448) -* **hooks-server:** support import from React 18 ([#3464](https://github.com/algolia/react-instantsearch/issues/3464)) ([0a13867](https://github.com/algolia/react-instantsearch/commit/0a13867f7dd5a8a18e0957b2072bbde3b02d6490)), closes [#3453](https://github.com/algolia/react-instantsearch/issues/3453) -* **hooks:** make InstantSearch type generic ([#3466](https://github.com/algolia/react-instantsearch/issues/3466)) ([b0905b7](https://github.com/algolia/react-instantsearch/commit/b0905b73bed558c62eedb7ae701be20c2ebe25c9)) - - - -## [6.24.3](https://github.com/algolia/react-instantsearch/compare/v6.24.2...v6.24.3) (2022-05-10) - - -### Bug Fixes - -* **numericmenu:** include range values in comparison with minmax bounds ([#3461](https://github.com/algolia/react-instantsearch/issues/3461)) ([e4c2682](https://github.com/algolia/react-instantsearch/commit/e4c268261dc42a6aa43d985934b53c82f8b71089)) - - - -## [6.24.2](https://github.com/algolia/react-instantsearch/compare/v6.24.1...v6.24.2) (2022-05-05) - - -### Bug Fixes - -* **hooks:** prevent infinite loops from render state ([#3455](https://github.com/algolia/react-instantsearch/issues/3455)) ([1799fc9](https://github.com/algolia/react-instantsearch/commit/1799fc9f78a4a5aafb54df339c3e211ff9187748)) - - - -## [6.24.1](https://github.com/algolia/react-instantsearch/compare/v6.24.0...v6.24.1) (2022-05-03) - - -### Bug Fixes - -* **types:** export correct types for react-instantsearch-hooks-web ([#3454](https://github.com/algolia/react-instantsearch/issues/3454)) ([a863430](https://github.com/algolia/react-instantsearch/commit/a8634306621f7a05a2b3056a6db25ccf8d9eabf0)) - - - -# [6.24.0](https://github.com/algolia/react-instantsearch/compare/v6.23.4...v6.24.0) (2022-04-28) - - -### Features - -* **hooks:** expose DOM components ([#3450](https://github.com/algolia/react-instantsearch/issues/3450)) ([5732e3d](https://github.com/algolia/react-instantsearch/commit/5732e3de732275911f94b26ba9e2c4165bdf77e7)) -* **hooks:** remove experimental warning ([#3446](https://github.com/algolia/react-instantsearch/issues/3446)) ([84c99fe](https://github.com/algolia/react-instantsearch/commit/84c99fe91d6906a877bec620b44c61d762f0ea57)) - - - -## [6.23.4](https://github.com/algolia/react-instantsearch/compare/v6.23.3...v6.23.4) (2022-04-21) - - -### Bug Fixes - -* **hooks:** forbid importing from instantsearch.js root path ([#3437](https://github.com/algolia/react-instantsearch/issues/3437)) ([82ef9ea](https://github.com/algolia/react-instantsearch/commit/82ef9eaaec42bc54f15796b5b031a8656330a45c)) -* **packages:** correctly mark peer dependency ([#3439](https://github.com/algolia/react-instantsearch/issues/3439)) ([51e8818](https://github.com/algolia/react-instantsearch/commit/51e8818fce224819230c8bf6dea2a08d71d9be29)), closes [#3428](https://github.com/algolia/react-instantsearch/issues/3428) - - - -## [6.23.3](https://github.com/algolia/react-instantsearch/compare/v6.23.2...v6.23.3) (2022-04-05) - - -### Bug Fixes - -* **facets:** show raw value in currentRefinements ([#3420](https://github.com/algolia/react-instantsearch/issues/3420)) ([1199ce6](https://github.com/algolia/react-instantsearch/commit/1199ce6bd4152e4b54bd2ee0e1f0c9d81021c7d5)), closes [#3412](https://github.com/algolia/react-instantsearch/issues/3412) -* **multi-index:** correctly set `searching` prop in multi-index result states ([#3419](https://github.com/algolia/react-instantsearch/issues/3419)) ([7f8e97e](https://github.com/algolia/react-instantsearch/commit/7f8e97e31b3dd5e49d3febef673f41f7dfa6d45d)) - - - -## [6.23.2](https://github.com/algolia/react-instantsearch/compare/v6.23.1...v6.23.2) (2022-04-04) - - -### Bug Fixes - -* **refinements:** use escaped value for refining ([#3412](https://github.com/algolia/react-instantsearch/issues/3412)) ([f2f5f6c](https://github.com/algolia/react-instantsearch/commit/f2f5f6cbfaed48a5c494daeb8789e8deebc64293)) -* support React 18 as peer dependency ([#3411](https://github.com/algolia/react-instantsearch/issues/3411)) ([38eb5a6](https://github.com/algolia/react-instantsearch/commit/38eb5a6a8fe92cb763a25f452bea78b189a6a82a)) - - -## [6.23.1](https://algolia/compare/v6.23.0...v6.23.1) (2022-03-30) - - -### Bug Fixes - -* **hooks:** compute initial search parameters from widget ([#3399](https://algolia/issues/3399)) ([b4bd933](https://algolia/commits/b4bd93358598bdc77a1aa858252e6eee23441345)) - - - -# [6.23.0](https://algolia/compare/v6.22.0...v6.23.0) (2022-03-23) - - -### Bug Fixes - -* **ssr:** perform initial multi-index search using a single request ([#3385](https://algolia/issues/3385)) ([b96809e](https://algolia/commits/b96809e5748d298350890647956cb7adbbb55650)) - - -### Features - -* **hooks:** allow additional widget properties to be passed from hooks ([#3359](https://algolia/issues/3359)) ([a047be4](https://algolia/commits/a047be45c7fce7ee28f7d6f61d2fbfa79e3ed2d0)) -* **hooks:** allow useHits and useInfiniteHit to be generic ([#3364](https://algolia/issues/3364)) ([8e66ad3](https://algolia/commits/8e66ad3ad587197c4811c60a5cab475137ca5afb)) -* **hooks:** mark initial results as "artificial" ([#3384](https://algolia/issues/3384)) ([937efdc](https://algolia/commits/937efdc71bae1d99270f8ecb5c5c9c501b3d7769)) - - - -# [6.22.0](https://github.com/algolia/react-instantsearch/compare/v6.21.1...v6.22.0) (2022-02-01) - - -### Bug Fixes - -* **hooks:** enable pause on exceptions on warning ([#3283](https://github.com/algolia/react-instantsearch/issues/3283)) ([ce3a6c3](https://github.com/algolia/react-instantsearch/commit/ce3a6c3f2700a05ae54a91278fd23fa64a97d733)) - - -### Features - -* **hooks:** introduce `useBreadcrumb()` ([#3245](https://github.com/algolia/react-instantsearch/issues/3245)) ([5bfbaaf](https://github.com/algolia/react-instantsearch/commit/5bfbaaf746e7632968ac9b30b73357bcb0b37e91)) - - - -## [6.21.1](https://github.com/algolia/react-instantsearch/compare/v6.21.0...v6.21.1) (2022-01-25) - - -### Bug Fixes - -* **hooks:** apply initial search parameters in useConnector ([#3276](https://github.com/algolia/react-instantsearch/issues/3276)) ([f85d679](https://github.com/algolia/react-instantsearch/commit/f85d6794c31eac61521fd8fc16b75673f02ed75b)) - - - -# [6.21.0](https://github.com/algolia/react-instantsearch/compare/v6.20.0...v6.21.0) (2022-01-24) - - -### Features - -* **hooks-server:** load data twice in the case of dynamic widget usage ([#3259](https://github.com/algolia/react-instantsearch/issues/3259)) ([9b4903b](https://github.com/algolia/react-instantsearch/commit/9b4903b2ea73d9d7e33729b87d9d55990e64312c)) -* **server:** load data twice in the case of dynamic widget usage ([#3268](https://github.com/algolia/react-instantsearch/issues/3268)) ([91cd085](https://github.com/algolia/react-instantsearch/commit/91cd085f9a323ed6b872f3a098f561007a72d0d2)), closes [/github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js#L14-L16](https://github.com//github.com/algolia/react-instantsearch/blob/d459e62f5cae4c98427ab302531873f5ee23d149/packages/react-instantsearch-core/src/core/indexUtils.js/issues/L14-L16) - - - -# [6.20.0](https://github.com/algolia/react-instantsearch/compare/v6.19.0...v6.20.0) (2022-01-18) - - -### Bug Fixes - -* **hooks:** allow importing via require ([#3257](https://github.com/algolia/react-instantsearch/issues/3257)) ([6aa80b3](https://github.com/algolia/react-instantsearch/commit/6aa80b3647567199c3df1b90a07d708b223ce1fd)) - - -### Features - -* **hooks:** implement `useClearRefinements()` ([#3256](https://github.com/algolia/react-instantsearch/issues/3256)) ([930b83d](https://github.com/algolia/react-instantsearch/commit/930b83df4c4bbccbc3118f6ea1001f6a30a3d464)), closes [#3252](https://github.com/algolia/react-instantsearch/issues/3252) -* **hooks:** introduce `` ([#3261](https://github.com/algolia/react-instantsearch/issues/3261)) ([3527b94](https://github.com/algolia/react-instantsearch/commit/3527b9422de48a4a6b4bd752bd643e01cd5011d8)) -* **hooks:** introduce `usePoweredBy()` ([#3251](https://github.com/algolia/react-instantsearch/issues/3251)) ([a97230b](https://github.com/algolia/react-instantsearch/commit/a97230b89e3ba1df9bf2d21a6a9f9a70b45f41b8)) -* **hooks:** introduce `useToggleRefinement()` ([#3248](https://github.com/algolia/react-instantsearch/issues/3248)) ([a561c09](https://github.com/algolia/react-instantsearch/commit/a561c090a268e1c6ca1fa2d5bf72263cc47aa273)) - - - -# [6.19.0](https://github.com/algolia/react-instantsearch/compare/v6.18.0...v6.19.0) (2022-01-05) - - -### Features - -* **hooks:** bundle as es-module ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) -* **dependencies:** update to latest algoliasearch-helper ([#3232](https://github.com/algolia/react-instantsearch/issues/3232)) ([ae4df8a](https://github.com/algolia/react-instantsearch/commit/ae4df8a7dec396e5ea15a4ab2243cd05e05d3ebc)) - - - -# [6.18.0](https://github.com/algolia/react-instantsearch/compare/v6.17.0...v6.18.0) (2021-12-16) - - -### Features - -* **dynamicWidgets:** send facets * and maxValuesPerFacet by default ([#3242](https://github.com/algolia/react-instantsearch/issues/3242)) ([c071776](https://github.com/algolia/react-instantsearch/commit/c07177670ac30dced55250774654e8b2a77b6739)) -* **hooks:** introduce `useNumericMenu()` ([#3237](https://github.com/algolia/react-instantsearch/issues/3237)) ([e3056c9](https://github.com/algolia/react-instantsearch/commit/e3056c9e2c64b5afafb7a736599a5cbf6137575b)) -* **hooks:** introduce `useInfiniteHits()` ([#3224](https://github.com/algolia/react-instantsearch/issues/3224)) ([177ec56](https://github.com/algolia/react-instantsearch/commit/177ec56af274670c2bf8599ba104b5544979bbe8)) - - - -# [6.17.0](https://github.com/algolia/react-instantsearch/compare/v6.16.0...v6.17.0) (2021-12-08) - - -### Bug Fixes - -* **hooks:** throw invariant violations in production ([#3217](https://github.com/algolia/react-instantsearch/issues/3217)) ([6d3f99c](https://github.com/algolia/react-instantsearch/commit/6d3f99ca91f470ee742ddc55e95f57b1f1801d7b)) - - -### Features - -* **hooks:** introduce `useMenu()` ([#3197](https://github.com/algolia/react-instantsearch/issues/3197)) ([15d1cc9](https://github.com/algolia/react-instantsearch/commit/15d1cc993437b111cd5a32f43ee1d2065c639ed4)) -* **hooks:** introduce `useRange()` ([#3198](https://github.com/algolia/react-instantsearch/issues/3198)) ([df1f1c8](https://github.com/algolia/react-instantsearch/commit/df1f1c8109dc684e74d3aee1bf0359f2a0e1b9f4)) -* **hooks:** introduce `` ([#3216](https://github.com/algolia/react-instantsearch/issues/3216)) ([d99aea6](https://github.com/algolia/react-instantsearch/commit/d99aea6cfe9dea86ae6b98ee3762373f4b3843f1)) -* **hooks:** introduce `useDynamicWidgets()` ([#3210](https://github.com/algolia/react-instantsearch/issues/3210)) ([29c2ea2](https://github.com/algolia/react-instantsearch/commit/29c2ea22b91a39d9eb40a044ae9aab85f2612db8)) -* **hooks:** introduce `useQueryRules()` ([#3212](https://github.com/algolia/react-instantsearch/issues/3212)) ([3ef1e1e](https://github.com/algolia/react-instantsearch/commit/3ef1e1e4116b3e58b2c2134d0c60fbb9f40c1501)) -* **hooks:** introduce SSR support ([#3221](https://github.com/algolia/react-instantsearch/issues/3221)) ([0a6b3ec](https://github.com/algolia/react-instantsearch/commit/0a6b3ec61942ad3849c6f078e21b3328679bffff)) -* **hooks:** introduce `useCurrentRefinements()` ([#3222](https://github.com/algolia/react-instantsearch/issues/3222)) ([7ebd8c3](https://github.com/algolia/react-instantsearch/commit/7ebd8c3da8c17b0bd7e0f8deab633b98fa052e7f)) - - - -# [6.16.0](https://github.com/algolia/react-instantsearch/compare/v6.15.0...v6.16.0) (2021-11-22) - - -### Bug Fixes - -* **PoweredBy:** support environments with `window` but no `location` ([#3186](https://github.com/algolia/react-instantsearch/issues/3186)) ([22ff23b](https://github.com/algolia/react-instantsearch/commit/22ff23b67554683567393114c2f53cacec44f4a6)) - - -### Features - -* **DynamicWidgets:** release as stable ([#3090](https://github.com/algolia/react-instantsearch/issues/3090)) ([a4a1d9e](https://github.com/algolia/react-instantsearch/commit/a4a1d9e032b31c611d5d73fdda3a03ad705f5c68)) -* **hooks:** introduce `usePagination()` ([#3182](https://github.com/algolia/react-instantsearch/issues/3182)) ([d8b1b86](https://github.com/algolia/react-instantsearch/commit/d8b1b867bb598e801f1350e81b4a4220a8e528d7)) -* **hooks:** introduce `useSortBy()` ([#3190](https://github.com/algolia/react-instantsearch/issues/3190)) ([5cce33b](https://github.com/algolia/react-instantsearch/commit/5cce33b48032548fed76b592ee0201e3c42fc3c4)) -* **hooks:** introduce `useHierarchicalMenu()` ([#3199](https://github.com/algolia/react-instantsearch/issues/3199)) ([b347061](https://github.com/algolia/react-instantsearch/commit/b3470611b962ef55c55576c65a6307abc54e5efd)) - - - -# [6.15.0](https://github.com/algolia/react-instantsearch/compare/v6.14.0...v6.15.0) (2021-10-27) - - -### Bug Fixes - -* **metadata:** stricter detection of user agent ([#3184](https://github.com/algolia/react-instantsearch/issues/3184)) ([994c8ae](https://github.com/algolia/react-instantsearch/commit/994c8ae055fc23a1a067d111d2f4727b1c7bf8ca)) - - -### Features - -* **hooks:** introduce `useConfigure()` ([#3181](https://github.com/algolia/react-instantsearch/issues/3181)) ([aa2eb9b](https://github.com/algolia/react-instantsearch/commit/aa2eb9baec6335f8a3523ee8b9b761a217cfc734)) - - - -# [6.14.0](https://github.com/algolia/react-instantsearch/compare/v6.13.0...v6.14.0) (2021-10-26) - - -### Features - -* **dependencies:** update algoliasearch-helper ([#3176](https://github.com/algolia/react-instantsearch/issues/3176)) ([a8708a3](https://github.com/algolia/react-instantsearch/commit/a8708a33f31632000bc827b076539b1cca7adf6f)) -* **metadata:** expose widget information ([#3145](https://github.com/algolia/react-instantsearch/issues/3145)) ([46cddf8](https://github.com/algolia/react-instantsearch/commit/46cddf8fcc0291beaa34b04da7aaaa7f2566e52e)) - - - -# [6.13.0](https://github.com/algolia/react-instantsearch/compare/v6.12.1...v6.13.0) (2021-10-19) - -This is the initial release of the experimental **React InstantSearch Hooks** package. Check out the [**Getting Started**](https://github.com/algolia/react-instantsearch/blob/e8d72cb1c7c45300ef7c273f1f163beb6dc57622/packages/react-instantsearch-hooks/README.md#getting-started) guide. - -### Bug Fixes - -- **core:** accept objects for `hitComponent` ([#3087](https://github.com/algolia/react-instantsearch/issues/3087)) ([4ae23d4](https://github.com/algolia/react-instantsearch/commit/4ae23d432a01ccbefff1fcdc865120aeca4d3efc)) - -### Features - -- **hooks:** add InstantSearch and Index components ([#3133](https://github.com/algolia/react-instantsearch/issues/3133)) ([8e3370d](https://github.com/algolia/react-instantsearch/commit/8e3370ddb7d5e42b8b9a5ff6a1e4255490de7dbe)) -- **hooks:** bootstrap Core package ([#3132](https://github.com/algolia/react-instantsearch/issues/3132)) ([d459e62](https://github.com/algolia/react-instantsearch/commit/d459e62f5cae4c98427ab302531873f5ee23d149)) -- **hooks:** display experimental warning ([#3149](https://github.com/algolia/react-instantsearch/issues/3149)) ([623577c](https://github.com/algolia/react-instantsearch/commit/623577c50cd0c04cd87f719edafdcfa04b5527b6)) -- **hooks:** export types ([#3159](https://github.com/algolia/react-instantsearch/issues/3159)) ([182348b](https://github.com/algolia/react-instantsearch/commit/182348b4a901823a7a41aee5d2b3bdc025cce48f)) -- **hooks:** expose `displayName` on Contexts ([#3168](https://github.com/algolia/react-instantsearch/issues/3168)) ([dafd3c6](https://github.com/algolia/react-instantsearch/commit/dafd3c66d05fbec09ebf907209ac25f55804e6f5)) -- **hooks:** friendly error when using Hooks with Core ([#3150](https://github.com/algolia/react-instantsearch/issues/3150)) ([d547ccf](https://github.com/algolia/react-instantsearch/commit/d547ccf7951299e2f6b1771e02fce052696ff65a)) -- **hooks:** introduce `useConnector()` ([#3137](https://github.com/algolia/react-instantsearch/issues/3137)) ([53e8afd](https://github.com/algolia/react-instantsearch/commit/53e8afd093b9950351467a16b82d528207ac34d2)) -- **hooks:** introduce `useHits()` ([#3147](https://github.com/algolia/react-instantsearch/issues/3147)) ([cc25cff](https://github.com/algolia/react-instantsearch/commit/cc25cff06e5ec216cba40fb8261372bc327001b6)) -- **hooks:** introduce `useRefinementList()` ([#3152](https://github.com/algolia/react-instantsearch/issues/3152)) ([0385cd9](https://github.com/algolia/react-instantsearch/commit/0385cd971635a8423ad687fab451d0778358389e)) -- **hooks:** introduce `useSearchBox()` ([#3146](https://github.com/algolia/react-instantsearch/issues/3146)) ([0d2c7f9](https://github.com/algolia/react-instantsearch/commit/0d2c7f9bd25b88cf725a1babd3b228ac804644c7)) -- **hooks:** trigger single network request on load ([#3167](https://github.com/algolia/react-instantsearch/issues/3167)) ([ff1ea49](https://github.com/algolia/react-instantsearch/commit/ff1ea49079a7800fd61ba99ceeb74b9f513eb99d)) -- **hooks:** type `useConnector()` return as render state ([#3169](https://github.com/algolia/react-instantsearch/issues/3169)) ([a801468](https://github.com/algolia/react-instantsearch/commit/a80146860164a092d2c90ee0aa4fcef88d5b675f)) -- **hooks:** update GitHub bug reports link ([#3157](https://github.com/algolia/react-instantsearch/issues/3157)) ([568b5c8](https://github.com/algolia/react-instantsearch/commit/568b5c83849a3927417907706656c3835163f216)) - - - -## [6.12.1](https://github.com/algolia/react-instantsearch/compare/v6.12.0...v6.12.1) (2021-08-02) - - -### Bug Fixes - -* **server side rendering:** return a value from mock currentRefinement/metadata ([#3078](https://github.com/algolia/react-instantsearch/issues/3078)) ([09f802b](https://github.com/algolia/react-instantsearch/commit/09f802b)) - - - -# [6.12.0](https://github.com/algolia/react-instantsearch/compare/v6.11.2...v6.12.0) (2021-07-06) - - -### Bug Fixes - -* **HitsPerPage:** Adds id prop to HitsPerPage, Select components ([#3072](https://github.com/algolia/react-instantsearch/issues/3072)) ([bc75d75](https://github.com/algolia/react-instantsearch/commit/bc75d75)) -* **MenuSelect:** Adds id prop to MenuSelect ([#3073](https://github.com/algolia/react-instantsearch/issues/3073)) ([fddaaef](https://github.com/algolia/react-instantsearch/commit/fddaaef)) -* **SearchBox:** Adds inputId prop to SearchBox ([#3074](https://github.com/algolia/react-instantsearch/issues/3074)) ([a05f6a4](https://github.com/algolia/react-instantsearch/commit/a05f6a4)) -* **SortBy:** Adds `id` prop to `SortBy`, `Select` components ([#3068](https://github.com/algolia/react-instantsearch/issues/3068)) ([1f2797f](https://github.com/algolia/react-instantsearch/commit/1f2797f)) - - -### Features - -* **DynamicWidgets:** add implementation ([#3056](https://github.com/algolia/react-instantsearch/issues/3056)) ([691ef87](https://github.com/algolia/react-instantsearch/commit/691ef87)) -* **facets:** add a new option "facetOrdering" to Menu, RefinementList & HierarchicalMenu ([#3067](https://github.com/algolia/react-instantsearch/issues/3067)) ([731d9ba](https://github.com/algolia/react-instantsearch/commit/731d9ba)) - - - -## [6.11.2](https://github.com/algolia/react-instantsearch/compare/v6.11.1...v6.11.2) (2021-06-28) - - -### Bug Fixes - -* **maps:** leave zoom in place if the bounds did not change ([#3050](https://github.com/algolia/react-instantsearch/issues/3050)) ([c430598](https://github.com/algolia/react-instantsearch/commit/c430598)) - - - -## [6.11.1](https://github.com/algolia/react-instantsearch/compare/v6.11.0...v6.11.1) (2021-06-09) - - -### Bug Fixes - -* **RefinementList:** prevent searchable component to refine on empty list ([#3059](https://github.com/algolia/react-instantsearch/issues/3059)) ([04f3644](https://github.com/algolia/react-instantsearch/commit/04f3644)) - - - -# [6.11.0](https://github.com/algolia/react-instantsearch/compare/v6.10.3...v6.11.0) (2021-05-04) - - -### Features - -* **connectNumericMenu:** add support for floating point values ([#3047](https://github.com/algolia/react-instantsearch/issues/3047)) ([091bf57](https://github.com/algolia/react-instantsearch/commit/091bf57)) - - - -## [6.10.3](https://github.com/algolia/react-instantsearch/compare/v6.10.2...v6.10.3) (2021-03-03) - - -### Bug Fixes - -* **RelevantSort:** Rename `SmartSort` widget to `RelevantSort` ([#3026](https://github.com/algolia/react-instantsearch/issues/3026)) ([47d11bf](https://github.com/algolia/react-instantsearch/commit/47d11bf)) - - - -## [6.10.2](https://github.com/algolia/react-instantsearch/compare/v6.10.1...v6.10.2) (2021-03-03) - - -### Bug Fixes - -* **infiniteHits:** fix stale hits issue ([#3021](https://github.com/algolia/react-instantsearch/issues/3021)) ([a9a29c7](https://github.com/algolia/react-instantsearch/commit/a9a29c7)) - - - -## [6.10.1](https://github.com/algolia/react-instantsearch/compare/v6.10.0...v6.10.1) (2021-03-02) - - -### Bug Fixes - -* **SmartSort:** make `textComponent` and `buttonTextComponent` optional ([#3014](https://github.com/algolia/react-instantsearch/issues/3014)) ([682ee6d](https://github.com/algolia/react-instantsearch/commit/682ee6d)) - - - -# [6.10.0](https://github.com/algolia/react-instantsearch/compare/v6.9.0...v6.10.0) (2021-02-23) - - -### Bug Fixes - -* **infiniteHits:** do not cache the cached hits ([#3011](https://github.com/algolia/react-instantsearch/issues/3011)) ([b56f5f7](https://github.com/algolia/react-instantsearch/commit/b56f5f7)) - - -### Features - -* **smartSort:** add widget ([#3009](https://github.com/algolia/react-instantsearch/issues/3009)) ([4cc8412](https://github.com/algolia/react-instantsearch/commit/4cc8412)), closes [#3010](https://github.com/algolia/react-instantsearch/issues/3010) - - - -# [6.9.0](https://github.com/algolia/react-instantsearch/compare/v6.8.3...v6.9.0) (2021-02-03) - - -### Features - -* **answers:** add `EXPERIMENTAL_Answers` widget ([#2996](https://github.com/algolia/react-instantsearch/issues/2996)) ([55e4191](https://github.com/algolia/react-instantsearch/commit/55e4191)), closes [#3005](https://github.com/algolia/react-instantsearch/issues/3005) - - - -## [6.8.3](https://github.com/algolia/react-instantsearch/compare/v6.8.2...v6.8.3) (2021-01-22) - - -### Bug Fixes - -* upgrade prop-types dependency to 15.6+ ([#3003](https://github.com/algolia/react-instantsearch/issues/3003)) ([fc03496](https://github.com/algolia/react-instantsearch/commit/fc03496)) - - - -## [6.8.2](https://github.com/algolia/react-instantsearch/compare/v6.8.1...v6.8.2) (2020-10-21) - - -### Bug Fixes - -* **ssr:** provide metadata default value ([0a2f34c](https://github.com/algolia/react-instantsearch/commit/0a2f34c)) - - - -## [6.8.1](https://github.com/algolia/react-instantsearch/compare/v6.8.0...v6.8.1) (2020-10-14) - - -### Bug Fixes - -* **ssr:** hydrate metadata with a value ([9249c19](https://github.com/algolia/react-instantsearch/commit/9249c19)) - - - -# [6.8.0](https://github.com/algolia/react-instantsearch/compare/v6.7.0...v6.8.0) (2020-10-14) - - -### Bug Fixes - -* **ssr:** make sure metadata is available on initial render ([#2973](https://github.com/algolia/react-instantsearch/issues/2973)) ([be43b65](https://github.com/algolia/react-instantsearch/commit/be43b65)), closes [#2972](https://github.com/algolia/react-instantsearch/issues/2972) -* add missing dependencies ([#2975](https://github.com/algolia/react-instantsearch/issues/2975)) ([22ecb3c](https://github.com/algolia/react-instantsearch/commit/22ecb3c)) -* **ConfigureRelatedItems:** support nested attributes ([#2967](https://github.com/algolia/react-instantsearch/issues/2967)) ([86dfe86](https://github.com/algolia/react-instantsearch/commit/86dfe86)) -* **ssr:** allow "params" to be optional in custom clients ([#2961](https://github.com/algolia/react-instantsearch/issues/2961)) ([c3e3d2e](https://github.com/algolia/react-instantsearch/commit/c3e3d2e)), closes [#2958](https://github.com/algolia/react-instantsearch/issues/2958) - - - -# [6.7.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.7.0) (2020-07-20) - - -### Bug Fixes - -* **core:** appending successful index search results by returning new object reference ([#2953](https://github.com/algolia/react-instantsearch/issues/2953)) ([0a711a7](https://github.com/algolia/react-instantsearch/commit/0a711a7)) -* **ssr:** remove second instance of "query" in the response "params" for SSR ([#2945](https://github.com/algolia/react-instantsearch/issues/2945)) ([bf837c5](https://github.com/algolia/react-instantsearch/commit/bf837c5)), closes [#2941](https://github.com/algolia/react-instantsearch/issues/2941) - - -### Features - -* **infinite-hits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.6.0](https://github.com/algolia/react-instantsearch/compare/v6.5.0...v6.6.0) (2020-06-15) - - -### Features - -* **infiniteHits:** support cache ([#2921](https://github.com/algolia/react-instantsearch/issues/2921)) ([7b26adc](https://github.com/algolia/react-instantsearch/commit/7b26adc)) - - - -# [6.5.0](https://github.com/algolia/react-instantsearch/compare/v6.4.0...v6.5.0) (2020-05-18) - - -### Bug Fixes - -* **connectQueryRules:** fix crash when using connectQueryRules with multiple indexes ([#2903](https://github.com/algolia/react-instantsearch/issues/2903)) ([c66d612](https://github.com/algolia/react-instantsearch/commit/c66d612)) -* **core:** fix maximum call stack size exceeded ([#2926](https://github.com/algolia/react-instantsearch/issues/2926)) ([7e883df](https://github.com/algolia/react-instantsearch/commit/7e883df)) - - -### Features - -* **SearchBox:** provide input element ref ([#2913](https://github.com/algolia/react-instantsearch/issues/2913)) ([b41bff2](https://github.com/algolia/react-instantsearch/commit/b41bff2)) - - - -# [6.4.0](https://github.com/algolia/react-instantsearch/compare/v6.3.0...v6.4.0) (2020-03-18) - - -### Bug Fixes - -* **deps:** fix "too much recursion" error with circular deps ([#2899](https://github.com/algolia/react-instantsearch/issues/2899)) ([c5f27a1](https://github.com/algolia/react-instantsearch/commit/c5f27a1)) - - - -# [6.3.0](https://github.com/algolia/react-instantsearch/compare/v6.2.0...v6.3.0) (2020-01-30) - - -### Features - -* **algoliasearch:** add support for algoliasearch v4 ([#2890](https://github.com/algolia/react-instantsearch/issues/2890)) ([c6c7382](https://github.com/algolia/react-instantsearch/commit/c6c7382)) - - - -# [6.2.0](https://github.com/algolia/react-instantsearch/compare/v6.1.0...v6.2.0) (2020-01-20) - - -### Bug Fixes - -* **deps:** update dependency algoliasearch to v3.35.1 ([#2802](https://github.com/algolia/react-instantsearch/issues/2802)) ([cfb91f0](https://github.com/algolia/react-instantsearch/commit/cfb91f0)) -* **widgets:** rename `ExperimentalConfigureRelatedItems` compon… ([#2891](https://github.com/algolia/react-instantsearch/issues/2891)) ([b910df2](https://github.com/algolia/react-instantsearch/commit/b910df2)) - - -### Features - -* **insights:** add getInsightsAnonymousUserToken helper ([#2887](https://github.com/algolia/react-instantsearch/issues/2887)) ([b5fe4f7](https://github.com/algolia/react-instantsearch/commit/b5fe4f7)) -* **widgets:** introduce `ConfigureRelatedItems` as experimental ([#2880](https://github.com/algolia/react-instantsearch/issues/2880)) ([923cd43](https://github.com/algolia/react-instantsearch/commit/923cd43)) - - - -# [6.1.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0...v6.1.0) (2019-12-17) - - -### Bug Fixes - -* **connectNumericMenu:** support numeric refinement 0 ([#2882](https://github.com/algolia/react-instantsearch/issues/2882)) ([30bd9fd](https://github.com/algolia/react-instantsearch/commit/30bd9fd)) -* **deps:** update dependency next to v9.1.1 ([9d49d33](https://github.com/algolia/react-instantsearch/commit/9d49d33)) -* **helper:** rely on stable version of algoliasearch-helper ([#2871](https://github.com/algolia/react-instantsearch/issues/2871)) ([e3531a1](https://github.com/algolia/react-instantsearch/commit/e3531a1)) - - -### Features - -* **insights:** show an error when 'clickAnalytics: true' is missing. ([#2877](https://github.com/algolia/react-instantsearch/issues/2877)) ([621656a](https://github.com/algolia/react-instantsearch/commit/621656a)) -* **voice:** add additionalQueryParameters ([#2366](https://github.com/algolia/react-instantsearch/issues/2366)) ([3a45b2c](https://github.com/algolia/react-instantsearch/commit/3a45b2c)) - - - -# [6.0.0](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.2...v6.0.0) (2019-10-28) - - - -# [6.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v6.0.0-beta.1...v6.0.0-beta.2) (2019-10-25) - - -### Bug Fixes - -* serialize cache value on hydrate ([#2862](https://github.com/algolia/react-instantsearch/issues/2862)) ([3319665](https://github.com/algolia/react-instantsearch/commit/3319665)), closes [#2828](https://github.com/algolia/react-instantsearch/issues/2828) - - - -# [6.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.1) (2019-10-18) - - -### Bug Fixes - -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **deps:** update dependency antd to v3.19.3 ([#2530](https://github.com/algolia/react-instantsearch/issues/2530)) ([73636c5](https://github.com/algolia/react-instantsearch/commit/73636c5)) -* **deps:** update dependency antd to v3.19.4 ([#2559](https://github.com/algolia/react-instantsearch/issues/2559)) ([c3e8267](https://github.com/algolia/react-instantsearch/commit/c3e8267)) -* **deps:** update dependency antd to v3.19.5 ([#2560](https://github.com/algolia/react-instantsearch/issues/2560)) ([72efd31](https://github.com/algolia/react-instantsearch/commit/72efd31)) -* **deps:** update dependency antd to v3.19.6 ([#2564](https://github.com/algolia/react-instantsearch/issues/2564)) ([654f986](https://github.com/algolia/react-instantsearch/commit/654f986)) -* **deps:** update dependency antd to v3.19.7 ([#2573](https://github.com/algolia/react-instantsearch/issues/2573)) ([7e963ad](https://github.com/algolia/react-instantsearch/commit/7e963ad)) -* **deps:** update dependency antd to v3.19.8 ([#2584](https://github.com/algolia/react-instantsearch/issues/2584)) ([34dd9b2](https://github.com/algolia/react-instantsearch/commit/34dd9b2)) -* **deps:** update dependency antd to v3.20.0 ([#2611](https://github.com/algolia/react-instantsearch/issues/2611)) ([b976c67](https://github.com/algolia/react-instantsearch/commit/b976c67)) -* **deps:** update dependency antd to v3.20.1 ([#2635](https://github.com/algolia/react-instantsearch/issues/2635)) ([792ad9c](https://github.com/algolia/react-instantsearch/commit/792ad9c)) -* **deps:** update dependency antd to v3.20.2 ([#2655](https://github.com/algolia/react-instantsearch/issues/2655)) ([301c2d8](https://github.com/algolia/react-instantsearch/commit/301c2d8)) -* **deps:** update dependency antd to v3.20.3 ([#2658](https://github.com/algolia/react-instantsearch/issues/2658)) ([d078e70](https://github.com/algolia/react-instantsearch/commit/d078e70)) -* **deps:** update dependency antd to v3.20.5 ([#2686](https://github.com/algolia/react-instantsearch/issues/2686)) ([42ef821](https://github.com/algolia/react-instantsearch/commit/42ef821)) -* **deps:** update dependency antd to v3.20.6 ([#2711](https://github.com/algolia/react-instantsearch/issues/2711)) ([927fbfe](https://github.com/algolia/react-instantsearch/commit/927fbfe)) -* **deps:** update dependency antd to v3.20.7 ([#2712](https://github.com/algolia/react-instantsearch/issues/2712)) ([1830952](https://github.com/algolia/react-instantsearch/commit/1830952)) -* **deps:** update dependency antd to v3.21.1 ([#2736](https://github.com/algolia/react-instantsearch/issues/2736)) ([39a51a6](https://github.com/algolia/react-instantsearch/commit/39a51a6)) -* **deps:** update dependency antd to v3.21.2 ([#2738](https://github.com/algolia/react-instantsearch/issues/2738)) ([a7a998a](https://github.com/algolia/react-instantsearch/commit/a7a998a)) -* **deps:** update dependency antd to v3.21.4 ([#2747](https://github.com/algolia/react-instantsearch/issues/2747)) ([60012be](https://github.com/algolia/react-instantsearch/commit/60012be)) -* **deps:** update dependency antd to v3.22.0 ([#2758](https://github.com/algolia/react-instantsearch/issues/2758)) ([9cda468](https://github.com/algolia/react-instantsearch/commit/9cda468)) -* **deps:** update dependency antd to v3.22.2 ([#2791](https://github.com/algolia/react-instantsearch/issues/2791)) ([ff1f5d9](https://github.com/algolia/react-instantsearch/commit/ff1f5d9)) -* **deps:** update dependency antd to v3.23.2 ([#2814](https://github.com/algolia/react-instantsearch/issues/2814)) ([a190410](https://github.com/algolia/react-instantsearch/commit/a190410)) -* **deps:** update dependency lodash to v4.17.13 ([c4974cf](https://github.com/algolia/react-instantsearch/commit/c4974cf)) -* **deps:** update dependency lodash to v4.17.14 ([#2647](https://github.com/algolia/react-instantsearch/issues/2647)) ([a2d2dd5](https://github.com/algolia/react-instantsearch/commit/a2d2dd5)) -* **deps:** update dependency lodash to v4.17.15 ([#2684](https://github.com/algolia/react-instantsearch/issues/2684)) ([354143f](https://github.com/algolia/react-instantsearch/commit/354143f)) -* **deps:** update dependency next to v9 ([#2638](https://github.com/algolia/react-instantsearch/issues/2638)) ([d22f61d](https://github.com/algolia/react-instantsearch/commit/d22f61d)) -* **deps:** update dependency next to v9.0.1 ([#2652](https://github.com/algolia/react-instantsearch/issues/2652)) ([2c2dab9](https://github.com/algolia/react-instantsearch/commit/2c2dab9)) -* **deps:** update dependency next to v9.0.2 ([#2662](https://github.com/algolia/react-instantsearch/issues/2662)) ([6fa4c5e](https://github.com/algolia/react-instantsearch/commit/6fa4c5e)) -* **deps:** update dependency next to v9.0.3 ([#2724](https://github.com/algolia/react-instantsearch/issues/2724)) ([f51b04b](https://github.com/algolia/react-instantsearch/commit/f51b04b)) -* **deps:** update dependency next to v9.0.4 ([#2767](https://github.com/algolia/react-instantsearch/issues/2767)) ([9af9180](https://github.com/algolia/react-instantsearch/commit/9af9180)) -* **deps:** update dependency next to v9.0.5 ([#2789](https://github.com/algolia/react-instantsearch/issues/2789)) ([0a75f41](https://github.com/algolia/react-instantsearch/commit/0a75f41)) -* **deps:** update dependency qs to v6.8.0 ([#2757](https://github.com/algolia/react-instantsearch/issues/2757)) ([8bffb87](https://github.com/algolia/react-instantsearch/commit/8bffb87)) -* **deps:** update dependency react-compound-slider to v2.1.0 ([#2610](https://github.com/algolia/react-instantsearch/issues/2610)) ([3389ee5](https://github.com/algolia/react-instantsearch/commit/3389ee5)) -* **deps:** update dependency react-compound-slider to v2.2.0 ([#2649](https://github.com/algolia/react-instantsearch/issues/2649)) ([7b81af1](https://github.com/algolia/react-instantsearch/commit/7b81af1)) -* **deps:** update dependency react-native-vector-icons to v6.5.0 ([#2520](https://github.com/algolia/react-instantsearch/issues/2520)) ([5f7f5b6](https://github.com/algolia/react-instantsearch/commit/5f7f5b6)) -* **deps:** update dependency react-native-vector-icons to v6.6.0 ([#2599](https://github.com/algolia/react-instantsearch/issues/2599)) ([b6bb199](https://github.com/algolia/react-instantsearch/commit/b6bb199)) -* **deps:** update dependency react-router-dom to v5.0.1 ([#2506](https://github.com/algolia/react-instantsearch/issues/2506)) ([d762230](https://github.com/algolia/react-instantsearch/commit/d762230)) -* **highlight:** switch to index as key ([#2691](https://github.com/algolia/react-instantsearch/issues/2691)) ([17e75d1](https://github.com/algolia/react-instantsearch/commit/17e75d1)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### chore - -* **release:** 6.0.0-beta.1 ([#2861](https://github.com/algolia/react-instantsearch/issues/2861)) ([cb56ca0](https://github.com/algolia/react-instantsearch/commit/cb56ca0)), closes [#2023](https://github.com/algolia/react-instantsearch/issues/2023) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2178](https://github.com/algolia/react-instantsearch/issues/2178) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2203](https://github.com/algolia/react-instantsearch/issues/2203) [#2432](https://github.com/algolia/react-instantsearch/issues/2432) [#2444](https://github.com/algolia/react-instantsearch/issues/2444) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2454](https://github.com/algolia/react-instantsearch/issues/2454) [#2455](https://github.com/algolia/react-instantsearch/issues/2455) [#2459](https://github.com/algolia/react-instantsearch/issues/2459) [#2458](https://github.com/algolia/react-instantsearch/issues/2458) [#2460](https://github.com/algolia/react-instantsearch/issues/2460) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2446](https://github.com/algolia/react-instantsearch/issues/2446) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2466](https://github.com/algolia/react-instantsearch/issues/2466) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2442](https://github.com/algolia/react-instantsearch/issues/2442) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2555](https://github.com/algolia/react-instantsearch/issues/2555) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2349](https://github.com/algolia/react-instantsearch/issues/2349) [#2570](https://github.com/algolia/react-instantsearch/issues/2570) [#2462](https://github.com/algolia/react-instantsearch/issues/2462) [#2600](https://github.com/algolia/react-instantsearch/issues/2600) [#2468](https://github.com/algolia/react-instantsearch/issues/2468) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2621](https://github.com/algolia/react-instantsearch/issues/2621) [#2627](https://github.com/algolia/react-instantsearch/issues/2627) [#2644](https://github.com/algolia/react-instantsearch/issues/2644) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2645](https://github.com/algolia/react-instantsearch/issues/2645) [#2339](https://github.com/algolia/react-instantsearch/issues/2339) [#2643](https://github.com/algolia/react-instantsearch/issues/2643) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2722](https://github.com/algolia/react-instantsearch/issues/2722) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2379](https://github.com/algolia/react-instantsearch/issues/2379) [#2289](https://github.com/algolia/react-instantsearch/issues/2289) [#2290](https://github.com/algolia/react-instantsearch/issues/2290) [#2304](https://github.com/algolia/react-instantsearch/issues/2304) [#2307](https://github.com/algolia/react-instantsearch/issues/2307) [#2314](https://github.com/algolia/react-instantsearch/issues/2314) [#2288](https://github.com/algolia/react-instantsearch/issues/2288) [#2305](https://github.com/algolia/react-instantsearch/issues/2305) [#2701](https://github.com/algolia/react-instantsearch/issues/2701) [#2568](https://github.com/algolia/react-instantsearch/issues/2568) [#2357](https://github.com/algolia/react-instantsearch/issues/2357) [#2552](https://github.com/algolia/react-instantsearch/issues/2552) [#2530](https://github.com/algolia/react-instantsearch/issues/2530) [#2559](https://github.com/algolia/react-instantsearch/issues/2559) [#2560](https://github.com/algolia/react-instantsearch/issues/2560) [#2564](https://github.com/algolia/react-instantsearch/issues/2564) [#2573](https://github.com/algolia/react-instantsearch/issues/2573) [#2584](https://github.com/algolia/react-instantsearch/issues/2584) [#2611](https://github.com/algolia/react-instantsearch/issues/2611) [#2635](https://github.com/algolia/react-instantsearch/issues/2635) [#2655](https://github.com/algolia/react-instantsearch/issues/2655) [#2658](https://github.com/algolia/react-instantsearch/issues/2658) [#2686](https://github.com/algolia/react-instantsearch/issues/2686) [#2711](https://github.com/algolia/react-instantsearch/issues/2711) [#2712](https://github.com/algolia/react-instantsearch/issues/2712) [#2736](https://github.com/algolia/react-instantsearch/issues/2736) [#2738](https://github.com/algolia/react-instantsearch/issues/2738) [#2747](https://github.com/algolia/react-instantsearch/issues/2747) [#2758](https://github.com/algolia/react-instantsearch/issues/2758) [#2647](https://github.com/algolia/react-instantsearch/issues/2647) [#2684](https://github.com/algolia/react-instantsearch/issues/2684) [#2638](https://github.com/algolia/react-instantsearch/issues/2638) [#2652](https://github.com/algolia/react-instantsearch/issues/2652) [#2662](https://github.com/algolia/react-instantsearch/issues/2662) [#2724](https://github.com/algolia/react-instantsearch/issues/2724) [#2767](https://github.com/algolia/react-instantsearch/issues/2767) [#2757](https://github.com/algolia/react-instantsearch/issues/2757) [#2610](https://github.com/algolia/react-instantsearch/issues/2610) [#2649](https://github.com/algolia/react-instantsearch/issues/2649) [#2520](https://github.com/algolia/react-instantsearch/issues/2520) [#2599](https://github.com/algolia/react-instantsearch/issues/2599) [#2506](https://github.com/algolia/react-instantsearch/issues/2506) [#2467](https://github.com/algolia/react-instantsearch/issues/2467) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2690](https://github.com/algolia/react-instantsearch/issues/2690) [#2688](https://github.com/algolia/react-instantsearch/issues/2688) [#2626](https://github.com/algolia/react-instantsearch/issues/2626) [#2726](https://github.com/algolia/react-instantsearch/issues/2726) [#2535](https://github.com/algolia/react-instantsearch/issues/2535) [#2461](https://github.com/algolia/react-instantsearch/issues/2461) [#2434](https://github.com/algolia/react-instantsearch/issues/2434) [#2687](https://github.com/algolia/react-instantsearch/issues/2687) [#2338](https://github.com/algolia/react-instantsearch/issues/2338) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) [#2834](https://github.com/algolia/react-instantsearch/issues/2834) [#2845](https://github.com/algolia/react-instantsearch/issues/2845) [#2842](https://github.com/algolia/react-instantsearch/issues/2842) [#2852](https://github.com/algolia/react-instantsearch/issues/2852) [#2853](https://github.com/algolia/react-instantsearch/issues/2853) - - -### BREAKING CHANGES - -* **release:** translation will render default value if passed undefined as value - -* chore(lodash): remove imports - -* fix(translation): allow undefined value to be passed on purpose -* **release:** no longer do we allow paths like `attribute[5].something`, or other indexed forms, only `.` is allowed as special key. - -All existing tests still pass, and we never documented you could use `lodash.get` patterns other than `.`. - -* feat(get): accept array & bracked-separated string - -moved to utils at the same time - -* fix typo - -* feedback: test for undefined behaviour - -* chore(size): update expectation - -this will go down afterwards, but for now there's some more duplication - - - -# [6.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.7.0...v6.0.0-beta.0) (2019-08-21) - -[Migration guide](MIGRATION.md) - -### Bug Fixes - -* **react 17 compat:** upgrade RangeInput lifecycle ([#2289](https://github.com/algolia/react-instantsearch/issues/2289)) ([110b1af](https://github.com/algolia/react-instantsearch/commit/110b1af)) -* **react 17 compat:** upgrade RangeSlider lifecycle ([#2290](https://github.com/algolia/react-instantsearch/issues/2290)) ([69a7f53](https://github.com/algolia/react-instantsearch/commit/69a7f53)) -* **connectToggleRefinement:** cast currentRefinement to boolean ([#2701](https://github.com/algolia/react-instantsearch/issues/2701)) ([db934fd](https://github.com/algolia/react-instantsearch/commit/db934fd)) -* **core:** searchState can be non-Object object ([#2722](https://github.com/algolia/react-instantsearch/issues/2722)) ([dea493c](https://github.com/algolia/react-instantsearch/commit/dea493c)), closes [#2568](https://github.com/algolia/react-instantsearch/issues/2568) -* **createConnector:** new React life cycles ([#2357](https://github.com/algolia/react-instantsearch/issues/2357)) ([fc10640](https://github.com/algolia/react-instantsearch/commit/fc10640)) -* **createInstantSearchManager:** do not trigger search on index update ([#2552](https://github.com/algolia/react-instantsearch/issues/2552)) ([e209362](https://github.com/algolia/react-instantsearch/commit/e209362)) -* **geo:** check for undefined in isEqual ([#2643](https://github.com/algolia/react-instantsearch/issues/2643)) ([a544231](https://github.com/algolia/react-instantsearch/commit/a544231)), closes [#2467](https://github.com/algolia/react-instantsearch/issues/2467) -* **geo:** remove lifecycle compat ([#2644](https://github.com/algolia/react-instantsearch/issues/2644)) ([2b2b898](https://github.com/algolia/react-instantsearch/commit/2b2b898)), closes [#2626](https://github.com/algolia/react-instantsearch/issues/2626) -* **highlight:** switch to index as key ([#2690](https://github.com/algolia/react-instantsearch/issues/2690)) ([51de682](https://github.com/algolia/react-instantsearch/commit/51de682)), closes [#2688](https://github.com/algolia/react-instantsearch/issues/2688) -* **peerDependencies:** update React ([#2626](https://github.com/algolia/react-instantsearch/issues/2626)) ([6ccad49](https://github.com/algolia/react-instantsearch/commit/6ccad49)) -* **ssr:** avoid duplicate serializing ([#2726](https://github.com/algolia/react-instantsearch/issues/2726)) ([c768b1a](https://github.com/algolia/react-instantsearch/commit/c768b1a)) -* **voiceSearch:** fix incorrect status on stop ([#2535](https://github.com/algolia/react-instantsearch/issues/2535)) ([824dc22](https://github.com/algolia/react-instantsearch/commit/824dc22)) - - -### Code Refactoring - -* **lodash:** get ([#2461](https://github.com/algolia/react-instantsearch/issues/2461)) ([527b879](https://github.com/algolia/react-instantsearch/commit/527b879)) -* **lodash:** has ([#2434](https://github.com/algolia/react-instantsearch/issues/2434)) ([75a4a15](https://github.com/algolia/react-instantsearch/commit/75a4a15)) -* **lodash:** has been fully removed - -### Features - -* **autocomplete:** add queryID & position to provided hits ([#2687](https://github.com/algolia/react-instantsearch/issues/2687)) ([e453dab](https://github.com/algolia/react-instantsearch/commit/e453dab)) -* **client:** remove algoliaClient, appId & apiKey ([#2338](https://github.com/algolia/react-instantsearch/issues/2338)) ([b84a0b5](https://github.com/algolia/react-instantsearch/commit/b84a0b5)) (use searchClient exclusively now) -* **context:** migrate to new React context ([#2178](https://github.com/algolia/react-instantsearch/issues/2178)) ([0a1abea](https://github.com/algolia/react-instantsearch/commit/0a1abea)), closes [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2189](https://github.com/algolia/react-instantsearch/issues/2189) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) [#2179](https://github.com/algolia/react-instantsearch/issues/2179) [#2180](https://github.com/algolia/react-instantsearch/issues/2180) [#2181](https://github.com/algolia/react-instantsearch/issues/2181) [#2185](https://github.com/algolia/react-instantsearch/issues/2185) [#2192](https://github.com/algolia/react-instantsearch/issues/2192) [#2190](https://github.com/algolia/react-instantsearch/issues/2190) -* **ssr:** update the SSR API ([#2555](https://github.com/algolia/react-instantsearch/issues/2555)) ([925bdb8](https://github.com/algolia/react-instantsearch/commit/925bdb8)), closes [#2536](https://github.com/algolia/react-instantsearch/issues/2536) [#2537](https://github.com/algolia/react-instantsearch/issues/2537) - - -### BREAKING CHANGES - -* **searchClient:** argument is the only option now. - -Previously there were three options to pass a search client: searchClient, appId & apiKey, algoliaClient. The latter two have been removed, and now only `searchClient` is accepted. This searchClient is an instance of the `algoliasearch` module: - -```js -import algoliasearch from 'algoliasearch/lite'; - -const searchClient = algoliasearch( - 'myAppId', - 'myApiKey', - { _useRequestCache: true } -); - -// ... - -``` - -If you were relying on duplicate requests not being fired when using appId & apiKey before, you need to enable the `_useRequestCache` option now. - -* **SSR:** imports have changed - -In the server, you now directly import `findResultsState`, which now requires a `searchClient` in the second argument. - -In the App, you now use a regular `InstantSearch` component. - -* **Index & InstantSearch:** Remove `root` DOM element - -These elements now are pure containers for their children, and don't add a `div` to the DOM anymore. If you were relying on those for styling, wrap the `InstantSearch` and `Index` element with a `div` with an appropriate class. - -* **Index & InstantSearch:** Remove support for `root` prop - -Since these two arguments now no longer wrap their children in an element, they no longer accept a `root` prop. - -* **Highlight:** some paths will no longer be accepted - -We only accept paths separated with a dot or bracket now, like before. It's possible that a different type of path worked undocumented, but no longer does. - -* **algoliasearch-helper:** updating to the next major version - -This library is mostly internal, but it has had a major refactor (including removing lodash). This has no impact, unless you are dealing with it using `createConnector`. See the [migration guide](https://github.com/algolia/algoliasearch-helper-js/blob/next/documentation-src/metalsmith/content/upgrade.md) for the v3 of algoliasearch-helper for more information. - -# [5.7.0](https://github.com/algolia/react-instantsearch/compare/v5.6.0...v5.7.0) (2019-06-04) - - -### Bug Fixes - -* **highlight:** allow array as "attribute" ([#2474](https://github.com/algolia/react-instantsearch/issues/2474)) ([9dc829a](https://github.com/algolia/react-instantsearch/commit/9dc829a)), closes [#2461](https://github.com/algolia/react-instantsearch/issues/2461) -* **indexUtils:** allow index with dots in it ([#2350](https://github.com/algolia/react-instantsearch/issues/2350)) ([f91906f](https://github.com/algolia/react-instantsearch/commit/f91906f)) - - -### Features - -* **voiceSearch:** add voice search widget ([#2316](https://github.com/algolia/react-instantsearch/issues/2316)) ([0e3b124](https://github.com/algolia/react-instantsearch/commit/0e3b124)) - - - -# [5.6.0](https://github.com/algolia/react-instantsearch/compare/v5.5.0...v5.6.0) (2019-05-15) - - -### Bug Fixes - -* **connectQueryRules:** avoid to throw an error with undefined values ([#2436](https://github.com/algolia/react-instantsearch/issues/2436)) ([1e18287](https://github.com/algolia/react-instantsearch/commit/1e18287)) - - -### Features - -* **infiniteHits:** add previous button ([#2296](https://github.com/algolia/react-instantsearch/issues/2296)) ([010a69a](https://github.com/algolia/react-instantsearch/commit/010a69a)) - - - -# [5.5.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0...v5.5.0) (2019-04-23) - - -### Bug Fixes - -* **createInstantSearch:** change the User-Agent to use the new specs ([#2209](https://github.com/algolia/react-instantsearch/issues/2209)) ([642ba0b](https://github.com/algolia/react-instantsearch/commit/642ba0b)) - - -### Features - -* **DOMMaps:** expose withGoogleMaps HOC [PART-1] ([#2000](https://github.com/algolia/react-instantsearch/issues/2000)) ([2ae1dea](https://github.com/algolia/react-instantsearch/commit/2ae1dea)) -* **queryRules:** add Query Rules features ([#2286](https://github.com/algolia/react-instantsearch/issues/2286)) ([3ae9c01](https://github.com/algolia/react-instantsearch/commit/3ae9c01)) -* **insights:** add insights features ([#2215](https://github.com/algolia/react-instantsearch/pull/2215)) ([961e7a7](https://github.com/algolia/react-instantsearch/commit/961e7a7)) - - - -# [5.4.0](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.1...v5.4.0) (2019-02-05) - - -### Bug Fixes - -* **DOMMaps:** set React & React DOM as peer deps ([#1922](https://github.com/algolia/react-instantsearch/issues/1922)) ([2f2cefd](https://github.com/algolia/react-instantsearch/commit/2f2cefd)) - - - -# [5.4.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.4.0-beta.0...v5.4.0-beta.1) (2019-01-10) - - -### Bug Fixes - -* **deps:** sync algoliasearch version ([#1879](https://github.com/algolia/react-instantsearch/issues/1879)) ([40f9c26](https://github.com/algolia/react-instantsearch/commit/40f9c26)) -* **maps:** use stable version for peer deps ([#1887](https://github.com/algolia/react-instantsearch/issues/1887)) ([9055167](https://github.com/algolia/react-instantsearch/commit/9055167)) - - - -# [5.4.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.3.2...v5.4.0-beta.0) (2019-01-08) - - -### Features - -* **Index:** introduce `indexId` prop ([#1833](https://github.com/algolia/react-instantsearch/issues/1833)) ([ec9e0fb](https://github.com/algolia/react-instantsearch/commit/ec9e0fb)) - - - - -## [5.3.2](https://github.com/algolia/react-instantsearch/compare/v5.3.1...v5.3.2) (2018-10-30) - - -### Bug Fixes - -* **sffv:** clamp maxFacetHits to the allowed range ([#1696](https://github.com/algolia/react-instantsearch/issues/1696)) ([83ce245](https://github.com/algolia/react-instantsearch/commit/83ce245)) - - - - -## [5.3.1](https://github.com/algolia/react-instantsearch/compare/v5.3.0...v5.3.1) (2018-09-26) - - -### Bug Fixes - -* **connector:** ensure canRefine is computed on the transformed items ([#1568](https://github.com/algolia/react-instantsearch/pull/1568)) ([c95384f](https://github.com/algolia/react-instantsearch/commit/c95384f)) -* **toggle:** ensure facet is present ([#1613](https://github.com/algolia/react-instantsearch/issues/1613)) ([e914ff6](https://github.com/algolia/react-instantsearch/commit/e914ff6)) - - - - -# [5.3.0](https://github.com/algolia/react-instantsearch/compare/v5.2.3...v5.3.0) (2018-09-24) - - -### Bug Fixes - -* **SSR:** bind getSearchParmaters to the component instance ([f34cb3d](https://github.com/algolia/react-instantsearch/commit/f34cb3d)) -* **GoogleMapsLoader:** pick google maps version ([#1540](https://github.com/algolia/react-instantsearch/issues/1540)) ([b14efcf](https://github.com/algolia/react-instantsearch/commit/b14efcf)) - - -### Features - -* **connectToggleRefinement:** implement canRefine & count ([#1588](https://github.com/algolia/react-instantsearch/issues/1588)) ([40672dd](https://github.com/algolia/react-instantsearch/commit/40672dd)) - - - - -## [5.2.3](https://github.com/algolia/react-instantsearch/compare/v5.2.2...v5.2.3) (2018-08-16) - - -### Bug Fixes - -* Allow object as type for Root (closes [#1446](https://github.com/algolia/react-instantsearch/issues/1446)) ([#1461](https://github.com/algolia/react-instantsearch/issues/1461)) ([7c2317b](https://github.com/algolia/react-instantsearch/commit/7c2317b)) -* **List:** render children list only when required ([#1472](https://github.com/algolia/react-instantsearch/issues/1472)) ([9eb2cbb](https://github.com/algolia/react-instantsearch/commit/9eb2cbb)), closes [#1459](https://github.com/algolia/react-instantsearch/issues/1459) - - - - -## [5.2.2](https://github.com/algolia/react-instantsearch/compare/v5.2.1...v5.2.2) (2018-07-16) - - -Publish the previous version on `stable`. - - - - -## [5.2.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0...v5.2.1) (2018-07-16) - - -### Bug Fixes - -* **GoogleMapsLoader:** inline the import to scriptjs ([#1427](https://github.com/algolia/react-instantsearch/issues/1427)) ([8019416](https://github.com/algolia/react-instantsearch/commit/8019416)) - - - - -# [5.2.0](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.2...v5.2.0) (2018-07-04) - - -### Bug Fixes - -* **translatable:** avoid create a new function on every render ([#1383](https://github.com/algolia/react-instantsearch/issues/1383)) ([1285b3b](https://github.com/algolia/react-instantsearch/commit/1285b3b)) - - -### Features - -* **core:** export translatable ([#1351](https://github.com/algolia/react-instantsearch/issues/1351)) ([6d5a89d](https://github.com/algolia/react-instantsearch/commit/6d5a89d)) -* **maps:** add connector & widget ([#1171](https://github.com/algolia/react-instantsearch/issues/1171)) ([16e288a](https://github.com/algolia/react-instantsearch/commit/16e288a)) - - - - -# [5.2.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.1...v5.2.0-beta.2) (2018-06-19) - - -### Features - -* export highlight tags from DOM / native ([#1342](https://github.com/algolia/react-instantsearch/issues/1342)) ([28a699e](https://github.com/algolia/react-instantsearch/commit/28a699e)) -* **createInstantSearch:** enable _useRequestCache ([#1346](https://github.com/algolia/react-instantsearch/issues/1346)) ([f772600](https://github.com/algolia/react-instantsearch/commit/f772600)) -* **dom:** export create class name ([#1348](https://github.com/algolia/react-instantsearch/issues/1348)) ([9017468](https://github.com/algolia/react-instantsearch/commit/9017468)) - - - - -# [5.2.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.2.0-beta.0...v5.2.0-beta.1) (2018-06-04) - - -### Bug Fixes - -* **dom:** publish server file ([#1305](https://github.com/algolia/react-instantsearch/issues/1305)) ([bd79693](https://github.com/algolia/react-instantsearch/commit/bd79693)) - - - - -# [5.2.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v5.1.0...v5.2.0-beta.0) (2018-06-04) - - -This new version introduce a complete revamp of the package structure, but it should be completely transparent for the users. - -If you have any troubles with this version please open a issue on [Github](https://github.com/algolia/react-instantsearch/issues/new), thanks! - - - - -# [5.1.0](https://github.com/algolia/react-instantsearch/compare/v5.0.3...v5.1.0) (2018-05-28) - - -### Bug Fixes - -* **connectInfiniteHits:** always set a value for previous page ([#1195](https://github.com/algolia/react-instantsearch/issues/1195)) ([4c218d5](https://github.com/algolia/react-instantsearch/commit/4c218d5)) -* **indexUtils:** avoid throw an error on cleanUp multi indices ([#1265](https://github.com/algolia/react-instantsearch/issues/1265)) ([12f5ace](https://github.com/algolia/react-instantsearch/commit/12f5ace)) - - -### Features - -* **searchClient:** Add support for custom Search Clients ([#1216](https://github.com/algolia/react-instantsearch/issues/1216)) ([174cc28](https://github.com/algolia/react-instantsearch/commit/174cc28)) - - - - -## [5.0.3](https://github.com/algolia/react-instantsearch/compare/v5.0.2...v5.0.3) (2018-04-03) - - -### Bug Fixes - -* revert dependencies as devDependencies ([#1135](https://github.com/algolia/react-instantsearch/issues/1135)) ([6b627bb](https://github.com/algolia/react-instantsearch/commit/6b627bb)) - - - - -## [5.0.2](https://github.com/algolia/react-instantsearch/compare/v5.0.1...v5.0.2) (2018-04-03) - - -### Bug Fixes - -* use lodash version of unsupported Array.{fill, find} ([#1118](https://github.com/algolia/react-instantsearch/issues/1118)) ([ea7bf42](https://github.com/algolia/react-instantsearch/commit/ea7bf42)) - - - - -## [5.0.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0...v5.0.1) (2018-03-12) - - -### Bug Fixes - -* **connectInfiniteHits:** always provide an array for hits ([#1064](https://github.com/algolia/react-instantsearch/issues/1064)) ([c75e38b](https://github.com/algolia/react-instantsearch/commit/c75e38b)) - - - - -# [5.0.0](https://github.com/algolia/react-instantsearch/compare/v4.5.2...v5.0.0) (2018-03-06) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.5.2](https://github.com/algolia/react-instantsearch/compare/v4.5.1...v4.5.2) (2018-03-06) - - -### Bug Fixes - -* **connectRange:** update default refinement propTypes ([#978](https://github.com/algolia/react-instantsearch/issues/978)) ([c065fb1](https://github.com/algolia/react-instantsearch/commit/c065fb1)) -* **IndexUtils:** avoid throw an error when cleanUp multi index ([#1019](https://github.com/algolia/react-instantsearch/issues/1019)) ([865a3c3](https://github.com/algolia/react-instantsearch/commit/865a3c3)) -* **SearchBox:** avoid to bind click on reset button ([#979](https://github.com/algolia/react-instantsearch/issues/979)) ([ea3063a](https://github.com/algolia/react-instantsearch/commit/ea3063a)) - - - - -# [5.0.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2018-02-06) - - -Apply features & bug fixes from [v4.5.0](#450-2018-02-06) & [v4.5.1](#451-2018-02-06) on the v5. - -See their CHANGELOG for more details. - - - - -## [4.5.1](https://github.com/algolia/react-instantsearch/compare/v4.5.0...v4.5.1) (2018-02-06) - - -### Bug Fixes - -* **StarRating:** move to 1 based instead of 0 ([#949](https://github.com/algolia/react-instantsearch/issues/949)) ([eb0152d](https://github.com/algolia/react-instantsearch/commit/eb0152d)) - - - - -# [4.5.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v4.5.0) (2018-02-06) - - -### Bug Fixes - -* **connectRange:** use the same behaviour for currentRefinement in getMetadata ([#923](https://github.com/algolia/react-instantsearch/issues/923)) ([08917b6](https://github.com/algolia/react-instantsearch/commit/08917b6)) -* **connectToggle:** use currentRefinement in metadata instead of the label ([#909](https://github.com/algolia/react-instantsearch/issues/909)) ([89cae2b](https://github.com/algolia/react-instantsearch/commit/89cae2b)) -* **StarRatings:** always show the stars below ([#929](https://github.com/algolia/react-instantsearch/issues/929)) ([22bf93a](https://github.com/algolia/react-instantsearch/commit/22bf93a)) - - -### Features - -* **connectStateResults:** expose isSearchStalled ([#933](https://github.com/algolia/react-instantsearch/issues/933)) ([f45ba27](https://github.com/algolia/react-instantsearch/commit/f45ba27)) - - - -# [5.0.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.4.2...v5.0.0-beta.0) (2018-01-30) - - -This new version introduce a complete revamp of the naming and the HTML output of most widgets. The goal of this release is to provide improved semantics to our users. - -This release also introduces a new CSS naming convention which will be reused across all InstantSearch libraries. This will enable the possibility to develop cross-libraries CSS themes easily. - -You can find all the informations for the migration [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/#upgrade-from-v4-to-v5). - - - - -## [4.4.2](https://github.com/algolia/react-instantsearch/compare/v4.4.1...v4.4.2) (2018-01-24) - - -### Bug Fixes - -* **currentRefinements:** give access to id and index from transformItems for deduplication ([#830](https://github.com/algolia/react-instantsearch/issues/830)) ([316b8f5](https://github.com/algolia/react-instantsearch/commit/316b8f5)) -* pass maxFacetHits to SFFV ([#863](https://github.com/algolia/react-instantsearch/issues/863)) ([de23a46](https://github.com/algolia/react-instantsearch/commit/de23a46)) - - - - -## [4.4.1](https://github.com/algolia/react-instantsearch/compare/v4.4.0...v4.4.1) (2018-01-09) - - -### Bug Fixes - -* **SearchBox**: clear SearchBox without search as you type ([#802](https://github.com/algolia/react-instantsearch/issues/802)) ([c49b2b6](https://github.com/algolia/react-instantsearch/commit/c49b2b6)) -* **connectRange:** check if facet exist before access ([#797](https://github.com/algolia/react-instantsearch/issues/797)) ([6520760](https://github.com/algolia/react-instantsearch/commit/6520760)) -* **stories:** avoid to use linear-background it breaks Argos every time ([#804](https://github.com/algolia/react-instantsearch/issues/804)) ([0beded7](https://github.com/algolia/react-instantsearch/commit/0beded7)) -* **stories:** limit hits per page on Index ([#806](https://github.com/algolia/react-instantsearch/issues/806)) ([6eb14d3](https://github.com/algolia/react-instantsearch/commit/6eb14d3)) - - -### Features - -* **Index:** allow custom root ([#792](https://github.com/algolia/react-instantsearch/issues/792)) ([d793b0a](https://github.com/algolia/react-instantsearch/commit/d793b0a)) - - - - -# [4.4.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0...v4.4.0) (2018-01-03) - - -### Bug Fixes - -* **createInstantSearch:** remove the client from the Snapshot ([#749](https://github.com/algolia/react-instantsearch/issues/749)) ([700d8f4](https://github.com/algolia/react-instantsearch/commit/700d8f4)) -* refresh cache memory leak example ([#784](https://github.com/algolia/react-instantsearch/issues/784)) ([cf228ac](https://github.com/algolia/react-instantsearch/commit/cf228ac)) -* **stories:** rename InstantSearch to `` ([#789](https://github.com/algolia/react-instantsearch/issues/789)) ([05efda5](https://github.com/algolia/react-instantsearch/commit/05efda5)) - - -### Features - -* InstantSearch root props ([#770](https://github.com/algolia/react-instantsearch/issues/770)) ([2d458f8](https://github.com/algolia/react-instantsearch/commit/2d458f8)) - - - - -# [4.3.0](https://github.com/algolia/react-instantsearch/compare/v4.3.0-beta.0...v4.3.0) (2017-12-20) - - -### Bug Fixes - -* reset page with multi index ([#665](https://github.com/algolia/react-instantsearch/issues/665)) ([865b7dc](https://github.com/algolia/react-instantsearch/commit/865b7dc)) -* track all index in the manager ([#660](https://github.com/algolia/react-instantsearch/issues/660)) ([793502b](https://github.com/algolia/react-instantsearch/commit/793502b)) - - -### Features - -* **SearchBox:** provide a loading indicator ([#544](https://github.com/algolia/react-instantsearch/issues/544)) ([189659e](https://github.com/algolia/react-instantsearch/commit/189659e)) -* **Highlight:** support array of strings ([#715](https://github.com/algolia/react-instantsearch/issues/715)) ([8e93c6a](https://github.com/algolia/react-instantsearch/commit/8e93c6a)) - - - - -# [4.3.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.2.0...v4.3.0-beta.0) (2017-11-27) - - -### Bug Fixes - -* **babelrc:** add a key for each env development, production, es ([#547](https://github.com/algolia/react-instantsearch/issues/547)) ([fa9528d](https://github.com/algolia/react-instantsearch/commit/fa9528d)) -* **localizecount:** allow localized string for count in MenuSelect ([#657](https://github.com/algolia/react-instantsearch/issues/657)) ([67ebd34](https://github.com/algolia/react-instantsearch/commit/67ebd34)) -* **react-router-example:** Properly update search query when using browser navigation ([#604](https://github.com/algolia/react-instantsearch/issues/604)) ([9ee6600](https://github.com/algolia/react-instantsearch/commit/9ee6600)) - - -### Features - -* **refreshcache:** add prop refresh to InstantSearch instance ([#619](https://github.com/algolia/react-instantsearch/issues/619)) ([19f6de0](https://github.com/algolia/react-instantsearch/commit/19f6de0)) - - - - -# [4.2.0](https://github.com/algolia/react-instantsearch/compare/v4.1.3...v4.2.0) (2017-11-02) - - -### Bug Fixes - -* **connectRange:** handle boundaries on first call ([9f14dc0](https://github.com/algolia/react-instantsearch/commit/9f14dc0)) -* **connectRange:** use refine instead of cleanUp in metadata ([#526](https://github.com/algolia/react-instantsearch/issues/526)) ([1861235](https://github.com/algolia/react-instantsearch/commit/1861235)) -* **hierarchicaMenu:** allow sorting and using limit ([fe178ed](https://github.com/algolia/react-instantsearch/commit/fe178ed)), closes [#92](https://github.com/algolia/react-instantsearch/issues/92) -* **InfiniteHits:** add disabled style to the LoadMore button ([#477](https://github.com/algolia/react-instantsearch/issues/477)) ([faba1ad](https://github.com/algolia/react-instantsearch/commit/faba1ad)) -* **Range:** handle float, allow reset and respect boundaries ([75969b8](https://github.com/algolia/react-instantsearch/commit/75969b8)) -* **RangeInput:** fix compatibility with React 16 & Panel ([3f218db](https://github.com/algolia/react-instantsearch/commit/3f218db)) -* **searchbox:** add maxlength 512 ([#542](https://github.com/algolia/react-instantsearch/issues/542)) ([5bd4033](https://github.com/algolia/react-instantsearch/commit/5bd4033)), closes [#510](https://github.com/algolia/react-instantsearch/issues/510) - - -### Features - -* **MenuSelect:** add component and connector ([cc6e0d7](https://github.com/algolia/react-instantsearch/commit/cc6e0d7)) - - - - -## [4.1.3](https://github.com/algolia/react-instantsearch/compare/v4.1.2...v4.1.3) (2017-10-09) - - -### Bug Fixes - -* **List:** remove React16 warning ([#442](https://github.com/algolia/react-instantsearch/issues/442)) ([8d6cf18](https://github.com/algolia/react-instantsearch/commit/8d6cf18)) - - -### Features - -* **connectStateResults:** add component props ([#434](https://github.com/algolia/react-instantsearch/issues/434)) ([c629b97](https://github.com/algolia/react-instantsearch/commit/c629b97)) - - - - -## [4.1.2](https://github.com/algolia/react-instantsearch/compare/v4.1.1...v4.1.2) (2017-09-26) - - -### Features - -* **Conditional:** add connectStateResults connector ([#357](https://github.com/algolia/react-instantsearch/issues/357)) ([462df5f](https://github.com/algolia/react-instantsearch/commit/462df5f)) - - - - -## [4.1.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0...v4.1.1) (2017-09-13) - - -### Bug Fixes - -* **MultiIndex:** reset page to 1 when share widgets refine (#312) ([c85a7bf](https://github.com/algolia/react-instantsearch/commit/c85a7bf)) -* **MultiIndex:** Trigger new search when `` props are updated (#318) ([bb11965](https://github.com/algolia/react-instantsearch/commit/bb11965)) - - - - -# [4.1.0](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.5...v4.1.0) (2017-08-28) - - -### Bug Fixes - -* **Highlighting:** revert breaking change (#245) ([045ee06](https://github.com/algolia/react-instantsearch/commit/045ee06)) -* **List:** adds support for any type of renderable element (#266) ([d848bb6](https://github.com/algolia/react-instantsearch/commit/d848bb6)) -* **Pagination:** fixed the offset ([3c0fff2](https://github.com/algolia/react-instantsearch/commit/3c0fff2)) -* **PoweredBy:** aria-* tags are not camelcased (#261) ([dc4a5bb](https://github.com/algolia/react-instantsearch/commit/dc4a5bb)) - - - - -# [4.1.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2017-08-08) - - -### Bug Fixes - -* **SSR:** clean SP before rendering agan (#238) ([e765886](https://github.com/algolia/react-instantsearch/commit/e765886)) - - -### Features - -* **Breadcrumb:** add a new widget & connector (#228) ([7f8f3ae](https://github.com/algolia/react-instantsearch/commit/7f8f3ae)) - - - - -# [4.1.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2017-08-03) - - -### Bug Fixes - -* **deps:** Update dependency lint-staged to version ^4.0.0 (#201) ([6867a1b](https://github.com/algolia/react-instantsearch/commit/6867a1b)) -* **nextjs/ssr:** parse `params.asPath` (#189) ([ae17da0](https://github.com/algolia/react-instantsearch/commit/ae17da0)) -* **PoweredBy:** add a label to the Algolia logo (#216) ([cd235bd](https://github.com/algolia/react-instantsearch/commit/cd235bd)) -* **react:** remove typo around `"" 2` (#220) ([f73eb04](https://github.com/algolia/react-instantsearch/commit/f73eb04)) -* **ScrollTo:** scroll to only if change triggered by the widget observed (#202) ([2d76022](https://github.com/algolia/react-instantsearch/commit/2d76022)) -* **theme:** format the count of items appearing in a refinement (#217) ([2225c24](https://github.com/algolia/react-instantsearch/commit/2225c24)) - - - - -# [4.1.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2017-07-25) - - -### Bug Fixes - -* **error:** reset error when receiving results of a query (not when sending it) (#179) ([bb12c29](https://github.com/algolia/react-instantsearch/commit/bb12c29)) -* **highlight:** wrong parsing between client and server (#183) ([2daae70](https://github.com/algolia/react-instantsearch/commit/2daae70)) -* **poweredBy:** SSR compatibility (#181) ([ec0fa8a](https://github.com/algolia/react-instantsearch/commit/ec0fa8a)) - - -### BREAKING CHANGES - -* **highlight:** We remove the timestamp present in our highlight preTag and postTag. If you were using regex to parse the -highlighting results then you'll need to adapt it as now it's only "ais-highlight". - - - - -# [4.1.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2017-07-20) - - -### Bug Fixes - -* **error:** reset error if next query is successful (#175) ([ff50a07](https://github.com/algolia/react-instantsearch/commit/ff50a07)) - - - - -# [4.1.0-beta.1](https://github.com/algolia/react-instantsearch/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2017-07-12) - - - - -# [4.1.0-beta.0](https://github.com/algolia/react-instantsearch/compare/v4.0.7...v4.1.0-beta.0) (2017-07-10) - - -### Bug Fixes - -* **argos:** address flakyness (#152) ([84ef8f1](https://github.com/algolia/react-instantsearch/commit/84ef8f1)) - - -### Features - -* **server-side rendering:** Add API features for server-side rendering ([86b14d1](https://github.com/algolia/react-instantsearch/commit/86b14d1)) - - - - -## [4.0.7](https://github.com/algolia/react-instantsearch/compare/v4.0.6...v4.0.7) (2017-07-06) - - -### Bug Fixes - -* **results:** revert commit that ensure hits are returned only if right indices (#149) ([df9aa25](https://github.com/algolia/react-instantsearch/commit/df9aa25)) - - - - -## [4.0.6](https://github.com/algolia/react-instantsearch/compare/v4.0.5...v4.0.6) (2017-06-29) - - -### Bug Fixes - -* **store:** delay call to listener to prevent infinite loops (#143) ([0945958](https://github.com/algolia/react-instantsearch/commit/0945958)) - - - - -## [4.0.5](https://github.com/algolia/react-instantsearch/compare/v4.0.4...v4.0.5) (2017-06-26) - - -### Bug Fixes - -* **MultiIndex:** ensure getResults return only hits matching index in the context (#136) ([124ffe6](https://github.com/algolia/react-instantsearch/commit/124ffe6)) -* **MultiIndex:** handle if namespace isn't in search state (#139) ([1aab324](https://github.com/algolia/react-instantsearch/commit/1aab324)) -* **storybook:** process CSS through autoprefixer (#138) ([62cf512](https://github.com/algolia/react-instantsearch/commit/62cf512)) - - - - -## [4.0.4](https://github.com/algolia/react-instantsearch/compare/v4.0.3...v4.0.4) (2017-06-19) - - -### Bug Fixes - -* **MultiIndex:** handle switch between mono and multi index (#132) ([e161921](https://github.com/algolia/react-instantsearch/commit/e161921)) - - - - -## [4.0.3](https://github.com/algolia/react-instantsearch/compare/v4.0.2...v4.0.3) (2017-06-14) - - -### Bug Fixes - -* **SFFV:** search status we're not inside search state (#125) ([5f3e670](https://github.com/algolia/react-instantsearch/commit/5f3e670)) - - - - -## [4.0.2](https://github.com/algolia/react-instantsearch/compare/v4.0.1...v4.0.2) (2017-05-30) - - - - -## [4.0.1](https://github.com/algolia/react-instantsearch/compare/v4.0.0...v4.0.1) (2017-05-17) - - -### Bug Fixes - -* **state:** nested attributes for faceting were not handled ([11bd122](https://github.com/algolia/react-instantsearch/commit/11bd122)) - - - - -# [4.0.0](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.6...v4.0.0) (2017-05-15) - -### Features and migration guide - -You can find all the details of the release and the migration guide from v3 to v4 here: https://discourse.algolia.com/t/react-instantsearch-v4/1329. - - - -# [4.0.0-beta.6](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.5...v4.0.0-beta.6) (2017-05-04) - - - - -# [4.0.0-beta.5](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.4...v4.0.0-beta.5) (2017-05-02) - - -### Bug Fixes - -* **connectAutoComplete:** allow usage with hits from a single index (#75) ([8b3b358](https://github.com/algolia/react-instantsearch/commit/8b3b358)), closes [#74](https://github.com/algolia/react-instantsearch/issues/74) -* **InstantSearch:** update algoliaClient when it change (#70) ([9e97dbd](https://github.com/algolia/react-instantsearch/commit/9e97dbd)) - - - - -# [4.0.0-beta.4](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.3...v4.0.0-beta.4) (2017-04-25) - - -### Bug Fixes - -* **MultIndex:** no need to nest hits, if those are from main index. (#56) ([86e0bd7](https://github.com/algolia/react-instantsearch/commit/86e0bd7)) - - -### Features - -* **MultiIndex:** remove the need for virtual hits when using connectAutoComplete (#45) ([7549019](https://github.com/algolia/react-instantsearch/commit/7549019)) - - - - -# [4.0.0-beta.3](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.2...v4.0.0-beta.3) (2017-04-21) - - -### Bug Fixes - -* replace usage of Object.values (#47) ([4c79e3e](https://github.com/algolia/react-instantsearch/commit/4c79e3e)) - - - - -# [4.0.0-beta.2](https://github.com/algolia/react-instantsearch/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2017-04-18) - - -### Bug Fixes - -* **InstantSearch:** dont fire request/onsearchStateChange when unmounting (#26) ([9a1487a](https://github.com/algolia/react-instantsearch/commit/9a1487a)) -* **MultiIndex:** derived helper were using main index specifics params (#36) ([991fea6](https://github.com/algolia/react-instantsearch/commit/991fea6)) -* **MultiIndex:** revert breaking change if no multiple index (#32) ([44f7de0](https://github.com/algolia/react-instantsearch/commit/44f7de0)) -* **util:** remove empty key was removing non object key (#29) ([9f795c7](https://github.com/algolia/react-instantsearch/commit/9f795c7)) - - -### Features - -* **Highlighter:** allow rendering to custom tag (#11) ([52a1212](https://github.com/algolia/react-instantsearch/commit/52a1212)) -* **SearchBox:** add default width and height to buttons. (#34) ([bcabf9b](https://github.com/algolia/react-instantsearch/commit/bcabf9b)) - - - - -# [4.0.0-beta.1](https://github.com/algolia/instantsearch.js/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2017-04-03) - - -### Bug Fixes - -* **SFFV:** fix wrong query behaviour with slow network (#2086) ([c251e8f](https://github.com/algolia/instantsearch.js/commit/c251e8f)), closes [#2086](https://github.com/algolia/instantsearch.js/issues/2086) - - - - -# [4.0.0-beta.0](https://github.com/algolia/instantsearch.js/compare/v3.3.0...v4.0.0-beta.0) (2017-03-28) - - -### Features - -* **multi-index:** ease multi index and auto complete ([09a4e1d](https://github.com/algolia/instantsearch.js/commit/09a4e1d)) - - -### BREAKING CHANGES - -* multi-index: * Reseting the pagination should be done at each connector level inside the "refine" function when returning the search state. -* The current page now appears inside the search state when a widget is used -* Query values are part of the items prop of the connectCurrentRefinements connector. Behaviour is unchanged, query will be filtered if clearsQuery prop is false. -* Add the index name to all the current refinements items. (not used by our widgets yet, but available if needed). - - - - -# [3.3.0](https://github.com/algolia/instantsearch.js/compare/v3.2.2-beta0...v3.3.0) (2017-03-22) - - -### Bug Fixes - -* **example:** Fix access to props in react-router example ([1417d6f](https://github.com/algolia/instantsearch.js/commit/1417d6f)) - - - - -## [3.2.2-beta0](https://github.com/algolia/instantsearch.js/compare/v3.2.1...v3.2.2-beta0) (2017-03-20) - - -### Bug Fixes - -* **InfiniteHits:** provide translation key for `Load More` (#2048) ([6130bf2](https://github.com/algolia/instantsearch.js/commit/6130bf2)) -* **SearchBox:** better mobile behaviour by default ([ea968b3](https://github.com/algolia/instantsearch.js/commit/ea968b3)) -* **example:** link to instantsearch/react (#2007) ([5e674cd](https://github.com/algolia/instantsearch.js/commit/5e674cd)) -* **recipes:** react router v4 ([de673bf](https://github.com/algolia/instantsearch.js/commit/de673bf)) - - -### Features - -* **SearchBox:** add role=search to the form (#2046) ([d1e90f3](https://github.com/algolia/instantsearch.js/commit/d1e90f3)) -* **SearchBox:** allow custom reset and submit components (#1991) ([cd303d7](https://github.com/algolia/instantsearch.js/commit/cd303d7)) -* **searchBox:** add event handling ([e267ab6](https://github.com/algolia/instantsearch.js/commit/e267ab6)), closes [#2017](https://github.com/algolia/instantsearch.js/issues/2017) - - - - -## [3.2.1](https://github.com/algolia/instantsearch.js/compare/v3.2.0...v3.2.1) (2017-02-22) - - -### Bug Fixes - -* **umd:** Add connectors to UMD build (#1988) ([23ac5e6](https://github.com/algolia/instantsearch.js/commit/23ac5e6)), closes [#1987](https://github.com/algolia/instantsearch.js/issues/1987) - - - - -# [3.2.0](https://github.com/algolia/instantsearch.js/compare/v3.1.0...v3.2.0) (2017-02-15) - - -### Bug Fixes - -* **Configure:** use props a unique source of truth (#1967) ([9d53d86](https://github.com/algolia/instantsearch.js/commit/9d53d86)) -* **SearchBox:** Safari can only have with xlinkHref (#1970) ([7ab00bd](https://github.com/algolia/instantsearch.js/commit/7ab00bd)), closes [#1968](https://github.com/algolia/instantsearch.js/issues/1968) - - -### Features - -* **MultiRange:** add an all range (#1959) ([a3dc950](https://github.com/algolia/instantsearch.js/commit/a3dc950)) - - -### BREAKING CHANGES - -* MultiRange: - MultiRange/connectMultiRange: will add a "All" range to allow unselection of range without the usage of CurrentRefinements. This range can be either filtered or ramove via CSS if not needed. The label can be changed by using our translations system. - - - - -# [3.1.0](https://github.com/algolia/instantsearch.js/compare/v3.0.0...v3.1.0) (2017-02-08) - - -### Bug Fixes - -* **Configure:** call onSearchStateChange when props are updated (#1953) ([7e151db](https://github.com/algolia/instantsearch.js/commit/7e151db)), closes [#1950](https://github.com/algolia/instantsearch.js/issues/1950) -* **Configure:** trigger onSearchStateChange with the right data ([11e5af8](https://github.com/algolia/instantsearch.js/commit/11e5af8)) -* **createConnector:** updates with latest props on state change (#1951) ([cd3a82c](https://github.com/algolia/instantsearch.js/commit/cd3a82c)) - - -### Features - -* **ClearAll:** add withQuery to also clear the search query (#1958) ([c0e695b](https://github.com/algolia/instantsearch.js/commit/c0e695b)) - - - - -# [3.0.0](https://github.com/algolia/instantsearch.js/compare/v2.2.5...v3.0.0) (2017-02-06) - - -### Bug Fixes - -* ***List:** disable shortcuts in *List SearchBoxes (#1921) ([51a76ae](https://github.com/algolia/instantsearch.js/commit/51a76ae)), closes [#1920](https://github.com/algolia/instantsearch.js/issues/1920) -* **Configure:** add configure parameters in search state (#1935) ([0971330](https://github.com/algolia/instantsearch.js/commit/0971330)), closes [#1863](https://github.com/algolia/instantsearch.js/issues/1863) -* **Hits:** limit the hitComponent to be only a function (#1912) ([b3c9578](https://github.com/algolia/instantsearch.js/commit/b3c9578)) -* **Pagination:** fix and indicate when pagination is disabled ([5f20199](https://github.com/algolia/instantsearch.js/commit/5f20199)), closes [#1938](https://github.com/algolia/instantsearch.js/issues/1938) -* **StarRating:** usage with filters (#1933) ([667e9d5](https://github.com/algolia/instantsearch.js/commit/667e9d5)) -* **withSearchBox:** keep displaying searchBox when no items found (#1930) ([30de4cd](https://github.com/algolia/instantsearch.js/commit/30de4cd)) - - -### Features - -* **MultiRange:** indicate if a range has no refinements (#1926) ([80b6450](https://github.com/algolia/instantsearch.js/commit/80b6450)) -* **panel:** add a panel widget (#1889) ([594e1a1](https://github.com/algolia/instantsearch.js/commit/594e1a1)) -* **starRating:** indicate when any refinement has no effect ([c547ae5](https://github.com/algolia/instantsearch.js/commit/c547ae5)) -* **widgets:** default design for disabled states (#1929) ([31f010b](https://github.com/algolia/instantsearch.js/commit/31f010b)) - -### Migration guide - -The migration to V3.0.0 should be safe and you should do it. - -There are two breaking changes that you will need to handle in your codebase: -- Anytime you are using a connector, when there are no more items in it or no more hits, we will still call your Component. Thus you will have to handle cases like dealing with empty arrays and decide if you want to unmount or hide the widget. -- Anytime you are using a widget, when there are no more items in it or no more hits, we will still display the widget. You can then decide to hide it with CSS. - - -## [2.2.5](https://github.com/algolia/instantsearch.js/compare/v2.2.4...v2.2.5) (2017-01-23) - - -### Bug Fixes - -* **currentRefinements:** make removing a toggle refinement work ([8995e64](https://github.com/algolia/instantsearch.js/commit/8995e64)) - - - - -## [2.2.4](https://github.com/algolia/instantsearch.js/compare/v2.2.3...v2.2.4) (2017-01-20) - - -### Bug Fixes - -* **publish:** publish react-instantsearch/dist instead of root (#1884) ([64414e0](https://github.com/algolia/instantsearch.js/commit/64414e0)) - - - - -## [2.2.3](https://github.com/algolia/instantsearch.js/compare/v2.2.2...v2.2.3) (2017-01-20) - - -### Bug Fixes - -* **SFFV:** translations for searchbox were not applied (#1879) ([e9b4ee1](https://github.com/algolia/instantsearch.js/commit/e9b4ee1)) - - - - -## [2.2.2](https://github.com/algolia/instantsearch.js/compare/v2.2.1...v2.2.2) (2017-01-18) - - -### Bug Fixes - -* **react-router:** search was triggered two many times (#1840) ([25e9db5](https://github.com/algolia/instantsearch.js/commit/25e9db5)) -* **SFFV:** empty query triggered a new SFFV (#1875) ([6c8259a](https://github.com/algolia/instantsearch.js/commit/6c8259a)) - - - - -## [2.2.1](https://github.com/algolia/instantsearch.js/compare/v2.2.0...v2.2.1) (2017-01-18) - - -### Bug Fixes - -* **createInstantsearch:** fix missing props (#1867) ([8d319b5](https://github.com/algolia/instantsearch.js/commit/8d319b5)), closes [#1867](https://github.com/algolia/instantsearch.js/issues/1867) - - - - -# [2.2.0](https://github.com/algolia/instantsearch.js/compare/v2.1.0...v2.2.0) (2017-01-17) - - -### Bug Fixes - -* **clear:** clearing wasn't working with too+ same type facets selected (#1820) ([a9a2364](https://github.com/algolia/instantsearch.js/commit/a9a2364)) -* **connectSearchBox:** handle `defaultRefinement` (#1829) ([7a730e2](https://github.com/algolia/instantsearch.js/commit/7a730e2)), closes [#1826](https://github.com/algolia/instantsearch.js/issues/1826) -* **Instantsearch:** Update all props on InstantSearch (#1828) ([2ed9b49](https://github.com/algolia/instantsearch.js/commit/2ed9b49)) -* **InstantSearch:** add specific `react-instantsearch ${version}` agent (#1844) ([a1113bc](https://github.com/algolia/instantsearch.js/commit/a1113bc)) -* **SFFV:** correct propTypes and add missing default values (#1845) ([a4c1b31](https://github.com/algolia/instantsearch.js/commit/a4c1b31)) -* **test:** add missing Snippet and Highliter snapshot ([4accce5](https://github.com/algolia/instantsearch.js/commit/4accce5)) -* **widgets:** replace setImmediate use with Promise use when update is needed (#1811) ([17e2497](https://github.com/algolia/instantsearch.js/commit/17e2497)) - - -### Features - -* **Menu, connectMenu:** add search for facet values (#1822) ([a6c513e](https://github.com/algolia/instantsearch.js/commit/a6c513e)) -* **snippet:** add a snippet widget to be able to highlight snippet results (#1797) ([2aecc40](https://github.com/algolia/instantsearch.js/commit/2aecc40)) -* **widgets:** add transformItems to be able to sort and filter (#1809) ([ba539f0](https://github.com/algolia/instantsearch.js/commit/ba539f0)) - - - - -# [2.1.0](https://github.com/algolia/instantsearch.js/compare/v2.0.1...v2.1.0) (2017-01-04) - - -### Bug Fixes - -* **createInstantSearchManager:** drop outdated response (#1765) ([76c5312](https://github.com/algolia/instantsearch.js/commit/76c5312)) -* **highlight:** highlight should work even if the attribute is missing (#1791) ([5b79b15](https://github.com/algolia/instantsearch.js/commit/5b79b15)), closes [#1790](https://github.com/algolia/instantsearch.js/issues/1790) -* **InfiniteHits:** better classname to loadmore btn (#1789) ([ad2ded3](https://github.com/algolia/instantsearch.js/commit/ad2ded3)) -* **starRatings:** click on selected range doesn't unselect it (#1766) ([beacc72](https://github.com/algolia/instantsearch.js/commit/beacc72)) -* **website:** broken demo links (#1802) ([0abe2f5](https://github.com/algolia/instantsearch.js/commit/0abe2f5)) -* **widgets:** add 300px width for the default SearchBox (#1803) ([bf5d791](https://github.com/algolia/instantsearch.js/commit/bf5d791)) - - -### Features - -* **InfiniteHits:** Add class to load more button (#1787) ([416febd](https://github.com/algolia/instantsearch.js/commit/416febd)) -* **RefinementList, connectRefinementList:** allow to search for facet values ([e086a81](https://github.com/algolia/instantsearch.js/commit/e086a81)) - - - - -## [2.0.1](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.1) (2016-12-15) - - -### Bug Fixes - -* **connectRange:** when unfinite numbers are passed throw ([75bec0d](https://github.com/algolia/instantsearch.js/commit/75bec0d)) -* **react-native:** use View as a container for react-native (#1729) ([5b76f75](https://github.com/algolia/instantsearch.js/commit/5b76f75)), closes [#1730](https://github.com/algolia/instantsearch.js/issues/1730) -* **SearchBox:** autocomplete was not disabled by default (#1742) ([bc76618](https://github.com/algolia/instantsearch.js/commit/bc76618)) -* **starRating:** call createURL with the right interface (min/max) (#1747) ([f9ab9b6](https://github.com/algolia/instantsearch.js/commit/f9ab9b6)) - - - - -## [2.0.0](https://github.com/algolia/instantsearch.js/compare/v2.0.0...v2.0.0) (2016-12-08) - -First release of `react-instantsearch` diff --git a/packages/react-instantsearch-native/README.md b/packages/react-instantsearch-native/README.md deleted file mode 100644 index 3f84a43a88..0000000000 --- a/packages/react-instantsearch-native/README.md +++ /dev/null @@ -1,13 +0,0 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [react-instantsearch-native](#react-instantsearch-native) - - - -# react-instantsearch-native - -This is the [React Native](https://facebook.github.io/react-native) version of Algolia's `instantsearch` library. - -Go to the [React InstantSearch website](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) or the [React InstantSearch GitHub repository](https://github.com/algolia/instantsearch.js) for more information. diff --git a/packages/react-instantsearch-native/package.json b/packages/react-instantsearch-native/package.json deleted file mode 100644 index 6aaa435828..0000000000 --- a/packages/react-instantsearch-native/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "react-instantsearch-native", - "version": "6.40.4", - "description": "⚡ Lightning-fast search for React Native, by Algolia", - "main": "dist/cjs/index.js", - "module": "dist/es/index.js", - "sideEffects": false, - "license": "MIT", - "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/", - "repository": { - "type": "git", - "url": "https://github.com/algolia/instantsearch.js" - }, - "author": { - "name": "Algolia, Inc.", - "url": "https://www.algolia.com" - }, - "keywords": [ - "algolia", - "components", - "fast", - "instantsearch", - "react", - "react-native", - "search" - ], - "files": [ - "README.md", - "dist" - ], - "scripts": { - "clean": "rm -rf dist", - "watch": "yarn build:cjs --watch", - "build": "yarn build:cjs && yarn build:es", - "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", - "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet" - }, - "dependencies": { - "@babel/runtime": "^7.1.2", - "algoliasearch": ">= 3.27.1 < 5", - "react-instantsearch-core": "6.40.4" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 5", - "react": ">= 16.3.0 < 19", - "react-native": ">= 0.54.0" - } -} diff --git a/packages/react-instantsearch-native/src/index.ts b/packages/react-instantsearch-native/src/index.ts deleted file mode 100644 index 88eee85db4..0000000000 --- a/packages/react-instantsearch-native/src/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Core -export { createConnector } from 'react-instantsearch-core'; -export { HIGHLIGHT_TAGS } from 'react-instantsearch-core'; -export { translatable } from 'react-instantsearch-core'; - -// Widgets -export { Configure } from 'react-instantsearch-core'; -export { ExperimentalConfigureRelatedItems } from 'react-instantsearch-core'; -export { - DynamicWidgets, - ExperimentalDynamicWidgets, -} from 'react-instantsearch-core'; -export { QueryRuleContext } from 'react-instantsearch-core'; -export { Index } from 'react-instantsearch-core'; -export { InstantSearch } from 'react-instantsearch-core'; - -// Connectors -export { connectAutoComplete } from 'react-instantsearch-core'; -export { connectBreadcrumb } from 'react-instantsearch-core'; -export { connectConfigure } from 'react-instantsearch-core'; -export { EXPERIMENTAL_connectConfigureRelatedItems } from 'react-instantsearch-core'; -export { - EXPERIMENTAL_connectDynamicWidgets, - connectDynamicWidgets, -} from 'react-instantsearch-core'; -export { connectCurrentRefinements } from 'react-instantsearch-core'; -export { connectGeoSearch } from 'react-instantsearch-core'; -export { connectHierarchicalMenu } from 'react-instantsearch-core'; -export { connectHighlight } from 'react-instantsearch-core'; -export { connectHitInsights } from 'react-instantsearch-core'; -export { connectHits } from 'react-instantsearch-core'; -export { connectHitsPerPage } from 'react-instantsearch-core'; -export { connectInfiniteHits } from 'react-instantsearch-core'; -export { connectMenu } from 'react-instantsearch-core'; -export { connectNumericMenu } from 'react-instantsearch-core'; -export { connectPagination } from 'react-instantsearch-core'; -export { connectPoweredBy } from 'react-instantsearch-core'; -export { connectQueryRules } from 'react-instantsearch-core'; -export { connectRange } from 'react-instantsearch-core'; -export { connectRefinementList } from 'react-instantsearch-core'; -export { connectScrollTo } from 'react-instantsearch-core'; -export { connectSearchBox } from 'react-instantsearch-core'; -export { connectRelevantSort } from 'react-instantsearch-core'; -export { connectSortBy } from 'react-instantsearch-core'; -export { connectStateResults } from 'react-instantsearch-core'; -export { connectStats } from 'react-instantsearch-core'; -export { connectToggleRefinement } from 'react-instantsearch-core'; diff --git a/packages/react-instantsearch-hooks-router-nextjs/CHANGELOG.md b/packages/react-instantsearch-router-nextjs/CHANGELOG.md similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/CHANGELOG.md rename to packages/react-instantsearch-router-nextjs/CHANGELOG.md diff --git a/packages/react-instantsearch-hooks-router-nextjs/README.md b/packages/react-instantsearch-router-nextjs/README.md similarity index 82% rename from packages/react-instantsearch-hooks-router-nextjs/README.md rename to packages/react-instantsearch-router-nextjs/README.md index c0fd3976e4..e54a96ff42 100644 --- a/packages/react-instantsearch-hooks-router-nextjs/README.md +++ b/packages/react-instantsearch-router-nextjs/README.md @@ -2,7 +2,7 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* -- [react-instantsearch-hooks-router-nextjs](#react-instantsearch-hooks-router-nextjs) +- [react-instantsearch-router-nextjs](#react-instantsearch-router-nextjs) - [Installation](#installation) - [Usage](#usage) - [API](#api) @@ -12,29 +12,29 @@ -# react-instantsearch-hooks-router-nextjs +# react-instantsearch-router-nextjs -This package is a router for [React InstantSearch Hooks](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/) that is compatible with [Next.js](https://nextjs.org/) routing. +This package is a router for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js](https://nextjs.org/) routing. > :warning: **This function cannot be used in conjunction with [`getStaticProps()`](https://nextjs.org/docs/api-reference/data-fetching/get-static-props). Use `getServerSideProps()` or client-side rendering instead.** ## Installation ```sh -yarn add react-instantsearch-hooks-router-nextjs +yarn add react-instantsearch-router-nextjs # or with npm -npm install react-instantsearch-hooks-router-nextjs +npm install react-instantsearch-router-nextjs ``` ## Usage You need to pass the Next.js router singleton that you can import from `next/router`. -If you are doing SSR with the `getServerState` and `InstantSearchSSRProvider` from `react-instantsearch-hooks-server`, you need to pass the `url` prop to `createInstantSearchRouterNext`'s `serverUrl` option : +If you are doing SSR with the `getServerState` and `InstantSearchSSRProvider` from `react-instantsearch/server`, you need to pass the `url` prop to `createInstantSearchRouterNext`'s `serverUrl` option : ```js import singletonRouter from 'next/router'; -import { createInstantSearchRouterNext } from 'react-instantsearch-hooks-router-nextjs'; +import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs'; export default function Page({ serverState, url }) { return ( @@ -55,7 +55,7 @@ If you are not doing SSR but only CSR, you can omit the `serverUrl` option: ```js import singletonRouter from 'next/router'; -import { createInstantSearchRouterNext } from 'react-instantsearch-hooks-router-nextjs'; +import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs'; export default function Page() { return ( @@ -74,7 +74,7 @@ Lastly, if you had custom routing logic in your app, you can pass it to the `cre ```js import singletonRouter from 'next/router'; -import { createInstantSearchRouterNext } from 'react-instantsearch-hooks-router-nextjs'; +import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs'; export default function Page({ serverState, url }) { return ( @@ -118,7 +118,7 @@ For troubleshooting purposes, some other options are available : ## Troubleshooting -If you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch.js/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml). +If you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch.js/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml). ## Contributing @@ -139,13 +139,13 @@ Please read [our contribution process](https://github.com/algolia/instantsearch. ## License -React InstantSearch Hooks is [MIT licensed](../../LICENSE). +React InstantSearch is [MIT licensed](../../LICENSE). -[contributing-bugreport]: https://github.com/algolia/instantsearch.js/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-featurerequest]: https://github.com/algolia/instantsearch.js/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch+Hooks&title=Feature%20request%3A%20 -[contributing-newissue]: https://github.com/algolia/instantsearch.js/issues/new?labels=triage,Library%3A%20React+InstantSearch+Hooks -[contributing-label-easy]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-bug]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 -[contributing-label-chore]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch+Hooks%22 +[contributing-bugreport]: https://github.com/algolia/instantsearch.js/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch +[contributing-featurerequest]: https://github.com/algolia/instantsearch.js/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20 +[contributing-newissue]: https://github.com/algolia/instantsearch.js/issues/new?labels=triage,Library%3A%20React+InstantSearch +[contributing-label-easy]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22 +[contributing-label-bug]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22 +[contributing-label-chore]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22 diff --git a/packages/react-instantsearch-hooks-router-nextjs/__tests__/e2e/backButton.test.ts b/packages/react-instantsearch-router-nextjs/__tests__/e2e/backButton.test.ts similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/__tests__/e2e/backButton.test.ts rename to packages/react-instantsearch-router-nextjs/__tests__/e2e/backButton.test.ts diff --git a/packages/react-instantsearch-hooks-router-nextjs/__tests__/e2e/nextLink.test.ts b/packages/react-instantsearch-router-nextjs/__tests__/e2e/nextLink.test.ts similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/__tests__/e2e/nextLink.test.ts rename to packages/react-instantsearch-router-nextjs/__tests__/e2e/nextLink.test.ts diff --git a/packages/react-instantsearch-hooks-router-nextjs/__tests__/e2e/utils.ts b/packages/react-instantsearch-router-nextjs/__tests__/e2e/utils.ts similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/__tests__/e2e/utils.ts rename to packages/react-instantsearch-router-nextjs/__tests__/e2e/utils.ts diff --git a/packages/react-instantsearch-hooks-router-nextjs/__tests__/module/is-cjs-module.cjs b/packages/react-instantsearch-router-nextjs/__tests__/module/is-cjs-module.cjs similarity index 65% rename from packages/react-instantsearch-hooks-router-nextjs/__tests__/module/is-cjs-module.cjs rename to packages/react-instantsearch-router-nextjs/__tests__/module/is-cjs-module.cjs index e81061759d..25f12ad4d2 100644 --- a/packages/react-instantsearch-hooks-router-nextjs/__tests__/module/is-cjs-module.cjs +++ b/packages/react-instantsearch-router-nextjs/__tests__/module/is-cjs-module.cjs @@ -2,8 +2,8 @@ const assert = require('assert'); -const ReactInstantSearchRouterNext = require('react-instantsearch-hooks-router-nextjs'); +const ReactInstantSearchRouterNext = require('react-instantsearch-router-nextjs'); assert.ok(ReactInstantSearchRouterNext); -console.log('react-instantsearch-hooks-router-nextjs is valid CJS'); +console.log('react-instantsearch-router-nextjs is valid CJS'); diff --git a/packages/react-instantsearch-hooks-router-nextjs/__tests__/module/is-es-module.mjs b/packages/react-instantsearch-router-nextjs/__tests__/module/is-es-module.mjs similarity index 64% rename from packages/react-instantsearch-hooks-router-nextjs/__tests__/module/is-es-module.mjs rename to packages/react-instantsearch-router-nextjs/__tests__/module/is-es-module.mjs index 45f90ca3a6..98b93a46b1 100644 --- a/packages/react-instantsearch-hooks-router-nextjs/__tests__/module/is-es-module.mjs +++ b/packages/react-instantsearch-router-nextjs/__tests__/module/is-es-module.mjs @@ -1,8 +1,8 @@ /* eslint-disable no-console */ import assert from 'assert'; -import * as ReactInstantSearchRouterNext from 'react-instantsearch-hooks-router-nextjs'; +import * as ReactInstantSearchRouterNext from 'react-instantsearch-router-nextjs'; assert.ok(ReactInstantSearchRouterNext); -console.log('react-instantsearch-hooks-router-nextjs is valid ESM'); +console.log('react-instantsearch-router-nextjs is valid ESM'); diff --git a/packages/react-instantsearch-hooks-router-nextjs/e2e.d.ts b/packages/react-instantsearch-router-nextjs/e2e.d.ts similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/e2e.d.ts rename to packages/react-instantsearch-router-nextjs/e2e.d.ts diff --git a/packages/react-instantsearch-hooks-router-nextjs/package.json b/packages/react-instantsearch-router-nextjs/package.json similarity index 86% rename from packages/react-instantsearch-hooks-router-nextjs/package.json rename to packages/react-instantsearch-router-nextjs/package.json index ebe885866c..a26515545f 100644 --- a/packages/react-instantsearch-hooks-router-nextjs/package.json +++ b/packages/react-instantsearch-router-nextjs/package.json @@ -1,7 +1,7 @@ { - "name": "react-instantsearch-hooks-router-nextjs", + "name": "react-instantsearch-router-nextjs", "version": "6.47.3", - "description": "React InstantSearch Hooks Router for Next.js", + "description": "React InstantSearch Router for Next.js", "source": "src/index.ts", "types": "dist/es/index.d.ts", "main": "dist/cjs/index.js", @@ -14,7 +14,7 @@ }, "sideEffects": false, "license": "MIT", - "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react-hooks/", + "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/", "repository": { "type": "git", "url": "https://github.com/algolia/instantsearch.js" @@ -44,12 +44,12 @@ "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es", "test:exports": "node ./__tests__/module/is-es-module.mjs && node ./__tests__/module/is-cjs-module.cjs", - "test:start-server": "yarn workspace example-react-instantsearch-hooks-next-routing-example build && yarn workspace example-react-instantsearch-hooks-next-routing-example start", + "test:start-server": "yarn workspace example-react-instantsearch-next-routing-example build && yarn workspace example-react-instantsearch-next-routing-example start", "test:e2e": "start-server-and-test test:start-server 3000 'wdio run ./wdio.conf.cjs'" }, "dependencies": { "instantsearch.js": "4.56.8", - "react-instantsearch-hooks": "6.47.3" + "react-instantsearch-core": "6.47.3" }, "devDependencies": { "@types/jasmine": "3.3.16", diff --git a/packages/react-instantsearch-hooks-router-nextjs/src/index.ts b/packages/react-instantsearch-router-nextjs/src/index.ts similarity index 98% rename from packages/react-instantsearch-hooks-router-nextjs/src/index.ts rename to packages/react-instantsearch-router-nextjs/src/index.ts index 283ffabff6..c8c3d52c59 100644 --- a/packages/react-instantsearch-hooks-router-nextjs/src/index.ts +++ b/packages/react-instantsearch-router-nextjs/src/index.ts @@ -13,7 +13,7 @@ type CreateInstantSearchRouterNextOptions = { * * @example * import singletonRouter from 'next/router'; - * import { createInstantSearchNextRouter } from 'react-instantsearch-hooks-router-nextjs'; + * import { createInstantSearchNextRouter } from 'react-instantsearch-router-nextjs'; * * const router = createInstantSearchNextRouter({ singletonRouter }); */ @@ -59,7 +59,7 @@ type CreateInstantSearchRouterNextOptions = { /** * Options passed to the underlying history router. - * See https://www.algolia.com/doc/api-reference/widgets/history-router/react-hooks/ + * See https://www.algolia.com/doc/api-reference/widgets/history-router/react/ * for the list of available options. */ routerOptions?: Partial< diff --git a/packages/react-instantsearch-hooks-router-nextjs/tsconfig.declaration.json b/packages/react-instantsearch-router-nextjs/tsconfig.declaration.json similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/tsconfig.declaration.json rename to packages/react-instantsearch-router-nextjs/tsconfig.declaration.json diff --git a/packages/react-instantsearch-hooks-router-nextjs/tsconfig.wdio.json b/packages/react-instantsearch-router-nextjs/tsconfig.wdio.json similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/tsconfig.wdio.json rename to packages/react-instantsearch-router-nextjs/tsconfig.wdio.json diff --git a/packages/react-instantsearch-hooks-router-nextjs/wdio.conf.cjs b/packages/react-instantsearch-router-nextjs/wdio.conf.cjs similarity index 100% rename from packages/react-instantsearch-hooks-router-nextjs/wdio.conf.cjs rename to packages/react-instantsearch-router-nextjs/wdio.conf.cjs diff --git a/packages/react-instantsearch/CHANGELOG.md b/packages/react-instantsearch/CHANGELOG.md index 17aed471db..2b4129ab53 100644 --- a/packages/react-instantsearch/CHANGELOG.md +++ b/packages/react-instantsearch/CHANGELOG.md @@ -3,15 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [6.40.4](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.40.3...react-instantsearch@6.40.4) (2023-07-25) +## [6.47.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.47.2...react-instantsearch-hooks-web@6.47.3) (2023-07-27) -**Note:** Version bump only for package react-instantsearch + +### Bug Fixes + +* add a future warning when the package name changes ([#5778](https://github.com/algolia/instantsearch.js/issues/5778)) ([3d22ee4](https://github.com/algolia/instantsearch.js/commit/3d22ee45e1f03a443323a371621262f1fe45e664)) + + + + + +## [6.47.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.47.1...react-instantsearch-hooks-web@6.47.2) (2023-07-25) + +**Note:** Version bump only for package react-instantsearch-hooks-web -## [6.40.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.40.2...react-instantsearch@6.40.3) (2023-07-19) +## [6.47.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.47.0...react-instantsearch-hooks-web@6.47.1) (2023-07-19) ### Bug Fixes @@ -22,58 +33,223 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.40.1...react-instantsearch@6.40.2) (2023-07-18) +# [6.47.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.46.0...react-instantsearch-hooks-web@6.47.0) (2023-07-18) + + +### Features + +* **react-instantsearch-hooks web:** rename `` translation ([#5756](https://github.com/algolia/instantsearch.js/issues/5756)) ([6c70035](https://github.com/algolia/instantsearch.js/commit/6c700350377d69a71b8ed44f70952d33ed81d085)) + + + + + +## [6.46.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.45.1...react-instantsearch-hooks-web@6.46.0) (2023-07-10) + + +### Bug Fixes + +* **url:** base createURL on UiState instead of SearchParameters ([#5696](https://github.com/algolia/instantsearch.js/issues/5696)) ([7e2c8a2](https://github.com/algolia/instantsearch.js/commit/7e2c8a295a6fc5ba36d9482f645ef55b422d5e75)), closes [#5694](https://github.com/algolia/instantsearch.js/issues/5694) + + + + + +## [6.45.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.45.0...react-instantsearch-hooks-web@6.45.1) (2023-07-04) + +**Note:** Version bump only for package react-instantsearch-hooks-web + + + + + +## [6.45.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.3...react-instantsearch-hooks-web@6.45.0) (2023-06-20) + +**Note:** Version bump only for package react-instantsearch-hooks-web -**Note:** Version bump only for package react-instantsearch +## [6.44.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.2...react-instantsearch-hooks-web@6.44.3) (2023-06-13) -## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.40.0...react-instantsearch@6.40.1) (2023-06-20) +**Note:** Version bump only for package react-instantsearch-hooks-web -**Note:** Version bump only for package react-instantsearch +## [6.44.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.1...react-instantsearch-hooks-web@6.44.2) (2023-06-05) + +**Note:** Version bump only for package react-instantsearch-hooks-web + + + + + +## [6.44.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.44.0...react-instantsearch-hooks-web@6.44.1) (2023-05-30) + + +### Bug Fixes + +* **insights:** send default click event when using auxiliary pointer button ([#5634](https://github.com/algolia/instantsearch.js/issues/5634)) ([7e4a216](https://github.com/algolia/instantsearch.js/commit/7e4a2162f87596a384b35c97efa51db9bb6f8973)) + + + + + +## [6.43.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.43.0...react-instantsearch-hooks-web@6.43.1) (2023-05-16) + + +### Bug Fixes + +* **rangeinput:** allow input of numbers with precision ([#5541](https://github.com/algolia/instantsearch.js/issues/5541)) ([fb48951](https://github.com/algolia/instantsearch.js/commit/fb489513a8550528f3e2867be30fb380229ad188)) +* **this:** ensure all functions are able to be destructured ([#5611](https://github.com/algolia/instantsearch.js/issues/5611)) ([a8b5c1e](https://github.com/algolia/instantsearch.js/commit/a8b5c1e5bbd6afac39fce523f7d7c2ec02f51153)), closes [#5589](https://github.com/algolia/instantsearch.js/issues/5589) + + + + + +# [6.43.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.42.2...react-instantsearch-hooks-web@6.43.0) (2023-04-24) + + +### Bug Fixes + +* **lifecycle:** prevent extra network requests when unmounting multiple widgets ([#5602](https://github.com/algolia/instantsearch.js/issues/5602)) ([11458ee](https://github.com/algolia/instantsearch.js/commit/11458eee7e7f0f3e9c5f368584a16f58646b1cdd)) + + +### Features + + +* **insights:** add insights option to InstantSearch ([#5488](https://github.com/algolia/instantsearch.js/issues/5488)) ([9031573](https://github.com/algolia/instantsearch.js/commit/9031573807fa6803dcfae9f33d61b8f111f68423)) ([#5578](https://github.com/algolia/instantsearch.js/issues/5578)) ([8fb517f](https://github.com/algolia/instantsearch.js/commit/8fb517f15381ecb32ea00cf4b01a0fd5e70e1d17)) ([#5545](https://github.com/algolia/instantsearch.js/issues/5545)) ([99a0972](https://github.com/algolia/instantsearch.js/commit/99a0972663b8f3284cac3b5621571ced7a33908f)) ([#5493](https://github.com/algolia/instantsearch.js/issues/5493)) ([cff723f](https://github.com/algolia/instantsearch.js/commit/cff723fc95a90ebb2ed14c46c51ab05764835a47)) +* **insights:** always pass Algolia credentials locally ([#5554](https://github.com/algolia/instantsearch.js/issues/5554)) ([654ab81](https://github.com/algolia/instantsearch.js/commit/654ab81e1669354c249710b6756610fba35d54b4)) ([#5558](https://github.com/algolia/instantsearch.js/issues/5558)) ([82144c0](https://github.com/algolia/instantsearch.js/commit/82144c0a0b18e6b47d6f508e5c670a9de274c121)) ([#5529](https://github.com/algolia/instantsearch.js/issues/5529)) ([8537f8f](https://github.com/algolia/instantsearch.js/commit/8537f8f7a10bcaf053ff62180c082e077b1b052d)) +* **insights:** annotate events with algoliaSource ([#5580](https://github.com/algolia/instantsearch.js/issues/5580)) ([c419307](https://github.com/algolia/instantsearch.js/commit/c419307a5f7fe46d5032c9437a17c8e3dad57fe5)) +* **insights:** automatically load search-insights if not passed ([#5484](https://github.com/algolia/instantsearch.js/issues/5484)) ([a85797b](https://github.com/algolia/instantsearch.js/commit/a85797b503edc94e001c5bfb3b754db6cb556943)) +* **insights:** enable default click events on hits and infinite hits ([#5522](https://github.com/algolia/instantsearch.js/issues/5522)) ([271bd12](https://github.com/algolia/instantsearch.js/commit/271bd12e34bc55656976bb53c90282193083eb86)) ([#5527](https://github.com/algolia/instantsearch.js/issues/5527)) ([0e55821](https://github.com/algolia/instantsearch.js/commit/0e558213c807cd17d592fadec052f3d1fc692e6c)) +* **insights:** prevent potential errors ([#5487](https://github.com/algolia/instantsearch.js/issues/5487)) ([33fe510](https://github.com/algolia/instantsearch.js/commit/33fe510307e4b382a5ba1153a0eaf160420acd11)) ([#5606](https://github.com/algolia/instantsearch.js/issues/5606)) ([bdd9290](https://github.com/algolia/instantsearch.js/commit/bdd92901b59ae4e5d7311eadfbf53ed656bbaf4a)) ([#5512](https://github.com/algolia/instantsearch.js/issues/5512)) ([85dfbc9](https://github.com/algolia/instantsearch.js/commit/85dfbc9ebd722fbe6a7e1bd056950fdbcc16d8d9)) +* **metadata:** register metadata around middleware ([#5492](https://github.com/algolia/instantsearch.js/issues/5492)) ([3e72ec8](https://github.com/algolia/instantsearch.js/commit/3e72ec82894a05a071328a4802d2f764233fe005)) -## [6.39.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.39.1...react-instantsearch@6.39.2) (2023-05-16) -**Note:** Version bump only for package react-instantsearch +## [6.42.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.42.1...react-instantsearch-hooks-web@6.42.2) (2023-04-11) +**Note:** Version bump only for package react-instantsearch-hooks-web -## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.39.0...react-instantsearch@6.39.1) (2023-03-21) -**Note:** Version bump only for package react-instantsearch +## [6.42.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.42.0...react-instantsearch-hooks-web@6.42.1) (2023-03-28) +**Note:** Version bump only for package react-instantsearch-hooks-web -## [6.39.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.38.3...react-instantsearch@6.39.0) (2023-01-26) -**Note:** Version bump only for package react-instantsearch +## [6.42.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.41.0...react-instantsearch-hooks-web@6.42.0) (2023-03-21) + + +### Bug Fixes + +* **searchbox:** add aria-hidden to svg icons ([#5547](https://github.com/algolia/instantsearch.js/issues/5547)) ([50344e3](https://github.com/algolia/instantsearch.js/commit/50344e3b14c22c886415c0e7d799aca778dc39ab)), closes [#5546](https://github.com/algolia/instantsearch.js/issues/5546) + + + + + +## [6.41.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.40.1...react-instantsearch-hooks-web@6.41.0) (2023-03-07) + +**Note:** Version bump only for package react-instantsearch-hooks-web + + + + + +## [6.40.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.40.1...react-instantsearch-hooks-web@6.40.2) (2023-02-28) + +**Note:** Version bump only for package react-instantsearch-hooks-web + + + + + +## [6.40.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.40.0...react-instantsearch-hooks-web@6.40.1) (2023-02-21) + + +### Bug Fixes + +* **breadcrumb:** guard against undefined facets ([#5482](https://github.com/algolia/instantsearch.js/issues/5482)) ([3159afe](https://github.com/algolia/instantsearch.js/commit/3159afe57472fe2b669dceb5f1ee638b658f7f52)) + + + + + +## [6.40.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.3...react-instantsearch-hooks-web@6.40.0) (2023-02-14) + +**Note:** Version bump only for package react-instantsearch-hooks-web + + + + + +## [6.39.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.2...react-instantsearch-hooks-web@6.39.3) (2023-02-07) + +**Note:** Version bump only for package react-instantsearch-hooks-web + + + + + +## [6.39.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.1...react-instantsearch-hooks-web@6.39.2) (2023-01-30) + + +### Bug Fixes + +* **infiniteHits:** read cache correctly when search is loading ([#5461](https://github.com/algolia/instantsearch.js/issues/5461)) ([bfabe00](https://github.com/algolia/instantsearch.js/commit/bfabe002a26e15e13b33200c355379f4e3c60f21)) + + + + + +## [6.39.1](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.39.0...react-instantsearch-hooks-web@6.39.1) (2023-01-26) + + +### Bug Fixes + +* **dependencies:** update typescript ([#5454](https://github.com/algolia/instantsearch.js/issues/5454)) ([0e6bb48](https://github.com/algolia/instantsearch.js/commit/0e6bb485a31cd3294436ac9902c2c2662dfcdf8b)) +* **HierarchicalMenu:** don't give --parent class if data is empty ([#5458](https://github.com/algolia/instantsearch.js/issues/5458)) ([1d1a209](https://github.com/algolia/instantsearch.js/commit/1d1a209992e86b720939607cd22e37a04e865195)), closes [/github.com/algolia/instantsearch/blob/f84c01b2f66ac279f7e33fafe5f1cd559436edef/packages/instantsearch.js/src/components/RefinementList/RefinementList.tsx#L175-L179](https://github.com//github.com/algolia/instantsearch/blob/f84c01b2f66ac279f7e33fafe5f1cd559436edef/packages/instantsearch.js/src/components/RefinementList/RefinementList.tsx/issues/L175-L179) + + + + + +# [6.39.0](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.38.3...react-instantsearch-hooks-web@6.39.0) (2023-01-25) + + +### Features + +* **react-instantsearch-hooks-web:** Add stats widget and ui component ([#5427](https://github.com/algolia/instantsearch.js/issues/5427)) ([d07cf0d](https://github.com/algolia/instantsearch.js/commit/d07cf0d0310bf4e49d4a4c2142b3783d9bcda79d)) +* **rendering:** always render with current state ([#5429](https://github.com/algolia/instantsearch.js/issues/5429)) ([920e951](https://github.com/algolia/instantsearch.js/commit/920e951f03aada0e6a1ce16bc389a82a2f00b202)) +* **rendering:** revert search state on error ([#5438](https://github.com/algolia/instantsearch.js/issues/5438)) ([732fcac](https://github.com/algolia/instantsearch.js/commit/732fcac79ea1f51b19f62d5c4bf1fdf22619fa73)) + + -## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.38.2...react-instantsearch@6.38.3) (2023-01-09) +## [6.38.3](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.38.2...react-instantsearch-hooks-web@6.38.3) (2023-01-09) -**Note:** Version bump only for package react-instantsearch +**Note:** Version bump only for package react-instantsearch-hooks-web -## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch@6.38.1...react-instantsearch@6.38.2) (2023-01-03) +## [6.38.2](https://github.com/algolia/instantsearch.js/compare/react-instantsearch-hooks-web@6.38.1...react-instantsearch-hooks-web@6.38.2) (2023-01-03) -**Note:** Version bump only for package react-instantsearch +**Note:** Version bump only for package react-instantsearch-hooks-web diff --git a/packages/react-instantsearch/README.md b/packages/react-instantsearch/README.md index ca76b9bc71..9f3749bd2b 100644 --- a/packages/react-instantsearch/README.md +++ b/packages/react-instantsearch/README.md @@ -3,11 +3,122 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [react-instantsearch](#react-instantsearch) + - [Why](#why) + - [Installation](#installation) + - [Getting started](#getting-started) + - [Documentation](#documentation) + - [Playground](#playground) + - [Contributing](#contributing) + - [License](#license) # react-instantsearch -This is the [React](https://facebook.github.io/react) version of Algolia's `instantsearch` library. +React InstantSearch is an open-source React library that lets you create an instant search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family: -Go to the [React InstantSearch website](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) or the [React InstantSearch GitHub repository](https://github.com/algolia/instantsearch.js) for more information. +**React InstantSearch** | [InstantSearch.js][instantsearch.js-github] | [Angular InstantSearch][instantsearch-angular-github] | [Vue InstantSearch][instantsearch-vue-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github] + +## Why + +You should be using React InstantSearch if you want to: + +* Design search experiences with best practices +* Customize your components at will +* Follow React principles + +Note: If you are working with React Native, or otherwise do not use the DOM, check out `react-instantsearch-core` instead. + +## Installation + +React InstantSearch is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs. + +```sh +yarn add algoliasearch react-instantsearch +# or +npm install algoliasearch react-instantsearch +``` + +## Getting started + +Using React InstantSearch is as simple as adding these components to your app: + +```javascript +import React from 'react'; +import ReactDOM from 'react-dom'; +import algoliasearch from 'algoliasearch/lite'; +import { InstantSearch, SearchBox, Hits } from 'react-instantsearch'; + +const searchClient = algoliasearch( + 'latency', + '6be0576ff61c053d5f9a3225e2a90f76' +); + +const App = () => ( + + + + +); +``` + +

    + + Edit on CodeSandbox + +

    + +To learn more about the library, follow the [getting started guide][doc-getting-started]. + +## Documentation + +The documentation is available on [algolia.com/doc][doc]. + +## Playground + +You can get to know React InstantSearch on [this playground][doc-playground]. + +Start by [adding components][doc-getting-started] and tweaking the display. Once you get more familiar with the library, you can learn more advanced concepts in [our guides][doc-guides]. + +## Contributing + +We welcome all contributors, from casual to regular 💙 + +- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport]. +- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest]. +- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it. +- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore]. + +To start contributing to code, you need to: + +1. [Fork the project](https://help.github.com/articles/fork-a-repo/) +1. [Clone the repository](https://help.github.com/articles/cloning-a-repository/) +1. Install the dependencies: `yarn` + +Please read [our contribution process](https://github.com/algolia/instantsearch.js/blob/master/CONTRIBUTING.md) to learn more. + +## License + +React InstantSearch is [MIT licensed](../../LICENSE). + + + +[doc]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/ +[doc-getting-started]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/react/ +[doc-guides]: https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/ +[doc-playground]: https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-theme +[algolia-website]: https://www.algolia.com/ +[instantsearch.js-github]: https://github.com/algolia/instantsearch.js +[instantsearch-android-github]: https://github.com/algolia/instantsearch-android +[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios +[instantsearch-vue-github]: https://github.com/algolia/vue-instantsearch +[instantsearch-angular-github]: https://github.com/algolia/angular-instantsearch +[contributing-bugreport]: https://github.com/algolia/instantsearch.js/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch +[contributing-featurerequest]: https://github.com/algolia/instantsearch.js/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20 +[contributing-newissue]: https://github.com/algolia/instantsearch.js/issues/new?labels=triage,Library%3A%20React+InstantSearch +[contributing-label-easy]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22 +[contributing-label-bug]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22 +[contributing-label-chore]: https://github.com/algolia/instantsearch.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22 diff --git a/packages/react-instantsearch/connectors.js b/packages/react-instantsearch/connectors.js deleted file mode 100644 index 1a737ad660..0000000000 --- a/packages/react-instantsearch/connectors.js +++ /dev/null @@ -1,29 +0,0 @@ -export { connectAutoComplete } from 'react-instantsearch-core'; -export { connectBreadcrumb } from 'react-instantsearch-core'; -export { connectConfigure } from 'react-instantsearch-core'; -export { EXPERIMENTAL_connectConfigureRelatedItems } from 'react-instantsearch-core'; -export { - connectDynamicWidgets, - EXPERIMENTAL_connectDynamicWidgets, -} from 'react-instantsearch-core'; -export { connectCurrentRefinements } from 'react-instantsearch-core'; -export { connectGeoSearch } from 'react-instantsearch-core'; -export { connectHierarchicalMenu } from 'react-instantsearch-core'; -export { connectHighlight } from 'react-instantsearch-core'; -export { connectHitInsights } from 'react-instantsearch-core'; -export { connectHits } from 'react-instantsearch-core'; -export { connectHitsPerPage } from 'react-instantsearch-core'; -export { connectInfiniteHits } from 'react-instantsearch-core'; -export { connectMenu } from 'react-instantsearch-core'; -export { connectNumericMenu } from 'react-instantsearch-core'; -export { connectPagination } from 'react-instantsearch-core'; -export { connectPoweredBy } from 'react-instantsearch-core'; -export { connectRange } from 'react-instantsearch-core'; -export { connectRefinementList } from 'react-instantsearch-core'; -export { connectScrollTo } from 'react-instantsearch-core'; -export { connectSearchBox } from 'react-instantsearch-core'; -export { connectRelevantSort } from 'react-instantsearch-core'; -export { connectSortBy } from 'react-instantsearch-core'; -export { connectStateResults } from 'react-instantsearch-core'; -export { connectStats } from 'react-instantsearch-core'; -export { connectToggleRefinement } from 'react-instantsearch-core'; diff --git a/packages/react-instantsearch/dom.js b/packages/react-instantsearch/dom.js deleted file mode 100644 index 48dfad4799..0000000000 --- a/packages/react-instantsearch/dom.js +++ /dev/null @@ -1,34 +0,0 @@ -export { InstantSearch } from 'react-instantsearch-dom'; -export { Index } from 'react-instantsearch-dom'; -export { Breadcrumb } from 'react-instantsearch-dom'; -export { ClearRefinements } from 'react-instantsearch-dom'; -export { Configure } from 'react-instantsearch-dom'; -export { ExperimentalConfigureRelatedItems } from 'react-instantsearch-dom'; -export { - DynamicWidgets, - ExperimentalDynamicWidgets, -} from 'react-instantsearch-dom'; -export { CurrentRefinements } from 'react-instantsearch-dom'; -export { HierarchicalMenu } from 'react-instantsearch-dom'; -export { Highlight } from 'react-instantsearch-dom'; -export { Hits } from 'react-instantsearch-dom'; -export { HitsPerPage } from 'react-instantsearch-dom'; -export { InfiniteHits } from 'react-instantsearch-dom'; -export { Menu } from 'react-instantsearch-dom'; -export { MenuSelect } from 'react-instantsearch-dom'; -export { NumericMenu } from 'react-instantsearch-dom'; -export { Pagination } from 'react-instantsearch-dom'; -export { Panel } from 'react-instantsearch-dom'; -export { PoweredBy } from 'react-instantsearch-dom'; -export { RangeInput } from 'react-instantsearch-dom'; -export { RangeSlider } from 'react-instantsearch-dom'; -export { RatingMenu } from 'react-instantsearch-dom'; -export { RefinementList } from 'react-instantsearch-dom'; -export { ScrollTo } from 'react-instantsearch-dom'; -export { SearchBox } from 'react-instantsearch-dom'; -export { RelevantSort } from 'react-instantsearch-dom'; -export { Snippet } from 'react-instantsearch-dom'; -export { SortBy } from 'react-instantsearch-dom'; -export { Stats } from 'react-instantsearch-dom'; -export { ToggleRefinement } from 'react-instantsearch-dom'; -export { getInsightsAnonymousUserToken } from 'react-instantsearch-dom'; diff --git a/packages/react-instantsearch-hooks-web/global.d.ts b/packages/react-instantsearch/global.d.ts similarity index 100% rename from packages/react-instantsearch-hooks-web/global.d.ts rename to packages/react-instantsearch/global.d.ts diff --git a/packages/react-instantsearch/index.js b/packages/react-instantsearch/index.js deleted file mode 100644 index 5315621351..0000000000 --- a/packages/react-instantsearch/index.js +++ /dev/null @@ -1 +0,0 @@ -export { createConnector } from 'react-instantsearch-core'; diff --git a/packages/react-instantsearch/native.js b/packages/react-instantsearch/native.js deleted file mode 100644 index 16e8957cbf..0000000000 --- a/packages/react-instantsearch/native.js +++ /dev/null @@ -1,8 +0,0 @@ -export { InstantSearch } from 'react-instantsearch-native'; -export { Index } from 'react-instantsearch-native'; -export { Configure } from 'react-instantsearch-native'; -export { ExperimentalConfigureRelatedItems } from 'react-instantsearch-native'; -export { - DynamicWidgets, - ExperimentalDynamicWidgets, -} from 'react-instantsearch-native'; diff --git a/packages/react-instantsearch/package.json b/packages/react-instantsearch/package.json index 35969f6ce7..5606dfb86b 100644 --- a/packages/react-instantsearch/package.json +++ b/packages/react-instantsearch/package.json @@ -1,23 +1,24 @@ { "name": "react-instantsearch", - "version": "6.40.4", - "description": "⚡ Lightning-fast search for React and React Native apps, by Algolia", - "main": "index.js", + "version": "6.47.3", + "description": "⚡ Lightning-fast search for React, by Algolia", + "source": "src/index.ts", + "types": "dist/es/index.d.ts", + "main": "dist/cjs/index.js", "module": "dist/es/index.js", - "files": [ - "connectors.js", - "dom.js", - "index.js", - "native.js", - "server.js", - "dist" - ], + "type": "module", + "exports": { + ".": { + "import": "./dist/es/index.js", + "require": "./dist/cjs/index.js" + } + }, "sideEffects": false, "license": "MIT", "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/", "repository": { "type": "git", - "url": "https://github.com/algolia/instantsearch.js" + "url": "https://github.com/algolia/instantsearch" }, "author": { "name": "Algolia, Inc.", @@ -29,27 +30,30 @@ "fast", "instantsearch", "react", - "react-dom", - "react-native", "search" ], + "files": [ + "README.md", + "dist" + ], "scripts": { "clean": "rm -rf dist", - "build": "yarn build:cjs && yarn build:es && yarn build:umd", - "build:cjs": "BABEL_ENV=cjs babel connectors.js dom.js index.js native.js server.js --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist --quiet", - "build:es": "BABEL_ENV=es babel connectors.js dom.js index.js native.js server.js --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --quiet", + "watch": "yarn build:cjs --watch", + "build": "yarn build:cjs && yarn build:es && yarn build:umd && yarn build:types", + "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet && ../../scripts/prepare-cjs.sh", + "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", "build:umd": "BABEL_ENV=rollup rollup -c rollup.config.js", - "preparePackageFolder": "cp package.json dist && cp ../../README.md dist", - "prepublishOnly": "yarn run preparePackageFolder" + "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es", + "test:exports": "node ./test/module/is-es-module.mjs && node ./test/module/is-cjs-module.cjs" }, "dependencies": { "@babel/runtime": "^7.1.2", - "react-instantsearch-core": "6.40.4", - "react-instantsearch-dom": "6.40.4", - "react-instantsearch-native": "6.40.4" + "instantsearch.js": "4.56.8", + "react-instantsearch-core": "6.47.3" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 5", - "react": ">= 16.3.0 < 19" + "react": ">= 16.8.0 < 19", + "react-dom": ">= 16.8.0 < 19" } } diff --git a/packages/react-instantsearch/rollup.config.js b/packages/react-instantsearch/rollup.config.js index d3781b2cf6..603f61ddb6 100644 --- a/packages/react-instantsearch/rollup.config.js +++ b/packages/react-instantsearch/rollup.config.js @@ -11,8 +11,8 @@ const clear = (x) => x.filter(Boolean); const version = process.env.VERSION || 'UNRELEASED'; const algolia = '© Algolia, inc.'; const link = 'https://github.com/algolia/instantsearch.js'; -const createBanner = () => - `/*! React InstantSearch ${version} | ${algolia} | ${link} */`; +const createBanner = (name) => + `/*! React InstantSearch${name} ${version} | ${algolia} | ${link} */`; const plugins = [ babel({ @@ -26,7 +26,13 @@ const plugins = [ preferBuiltins: false, extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], }), - commonjs(), + commonjs({ + namedExports: { + '../../node_modules/use-sync-external-store/shim/index.js': [ + 'useSyncExternalStore', + ], + }, + }), globals(), replace({ 'process.env.NODE_ENV': JSON.stringify('production'), @@ -37,18 +43,17 @@ const plugins = [ }), ]; -const createConfiguration = ({ input, name, minify = false } = {}) => ({ - input, - external: ['react', 'react-dom'], +const createConfiguration = ({ name, minify = false } = {}) => ({ + input: 'src/index.ts', + external: ['react'], output: { - file: `dist/umd/${name}${minify ? '.min' : ''}.js`, - name: `ReactInstantSearch.${name}`, + file: `dist/umd/ReactInstantSearch${name}${minify ? '.min' : ''}.js`, + name: `ReactInstantSearch${name}`, format: 'umd', globals: { react: 'React', - 'react-dom': 'ReactDOM', }, - banner: createBanner(), + banner: createBanner(name), sourcemap: true, }, plugins: plugins.concat( @@ -56,7 +61,7 @@ const createConfiguration = ({ input, name, minify = false } = {}) => ({ minify && uglify({ output: { - preamble: createBanner(), + preamble: createBanner(name), }, }), ]) @@ -64,36 +69,12 @@ const createConfiguration = ({ input, name, minify = false } = {}) => ({ }); export default [ - // Core createConfiguration({ - input: 'index.js', - name: 'Core', - }), - createConfiguration({ - input: 'index.js', - name: 'Core', - minify: true, + name: '', }), - // DOM - createConfiguration({ - input: 'dom.js', - name: 'Dom', - }), - createConfiguration({ - input: 'dom.js', - name: 'Dom', - minify: true, - }), - - // Connectors - createConfiguration({ - input: 'connectors.js', - name: 'Connectors', - }), createConfiguration({ - input: 'connectors.js', - name: 'Connectors', + name: '', minify: true, }), ]; diff --git a/packages/react-instantsearch/server.js b/packages/react-instantsearch/server.js deleted file mode 100644 index d8fb0d1a46..0000000000 --- a/packages/react-instantsearch/server.js +++ /dev/null @@ -1 +0,0 @@ -export { createInstantSearch } from 'react-instantsearch-dom/server'; diff --git a/packages/react-instantsearch-hooks-web/src/__tests__/common-connectors.test.tsx b/packages/react-instantsearch/src/__tests__/common-connectors.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/__tests__/common-connectors.test.tsx rename to packages/react-instantsearch/src/__tests__/common-connectors.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/__tests__/common-shared.test.tsx b/packages/react-instantsearch/src/__tests__/common-shared.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/__tests__/common-shared.test.tsx rename to packages/react-instantsearch/src/__tests__/common-shared.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/__tests__/common-widgets.test.tsx b/packages/react-instantsearch/src/__tests__/common-widgets.test.tsx similarity index 98% rename from packages/react-instantsearch-hooks-web/src/__tests__/common-widgets.test.tsx rename to packages/react-instantsearch/src/__tests__/common-widgets.test.tsx index c07ee43528..03cf526293 100644 --- a/packages/react-instantsearch-hooks-web/src/__tests__/common-widgets.test.tsx +++ b/packages/react-instantsearch/src/__tests__/common-widgets.test.tsx @@ -209,7 +209,7 @@ const testSetups: TestSetupsMap = { require('../../../instantsearch.js/package.json').version })`, `react-instantsearch (${ - require('../../../react-instantsearch-hooks/package.json').version + require('../../../react-instantsearch-core/package.json').version })`, `react (${require('react').version})`, ], diff --git a/packages/react-instantsearch/src/index.ts b/packages/react-instantsearch/src/index.ts new file mode 100644 index 0000000000..4cf3bff55c --- /dev/null +++ b/packages/react-instantsearch/src/index.ts @@ -0,0 +1,2 @@ +export * from 'react-instantsearch-core'; +export * from './widgets'; diff --git a/packages/react-instantsearch-hooks-web/src/types/PartialKeys.ts b/packages/react-instantsearch/src/types/PartialKeys.ts similarity index 100% rename from packages/react-instantsearch-hooks-web/src/types/PartialKeys.ts rename to packages/react-instantsearch/src/types/PartialKeys.ts diff --git a/packages/react-instantsearch-hooks-web/src/types/Translatable.ts b/packages/react-instantsearch/src/types/Translatable.ts similarity index 100% rename from packages/react-instantsearch-hooks-web/src/types/Translatable.ts rename to packages/react-instantsearch/src/types/Translatable.ts diff --git a/packages/react-instantsearch-hooks-web/src/types/index.ts b/packages/react-instantsearch/src/types/index.ts similarity index 100% rename from packages/react-instantsearch-hooks-web/src/types/index.ts rename to packages/react-instantsearch/src/types/index.ts diff --git a/packages/react-instantsearch-hooks-web/src/ui/Breadcrumb.tsx b/packages/react-instantsearch/src/ui/Breadcrumb.tsx similarity index 98% rename from packages/react-instantsearch-hooks-web/src/ui/Breadcrumb.tsx rename to packages/react-instantsearch/src/ui/Breadcrumb.tsx index 868ca0b5d3..6b16e8cacd 100644 --- a/packages/react-instantsearch-hooks-web/src/ui/Breadcrumb.tsx +++ b/packages/react-instantsearch/src/ui/Breadcrumb.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { cx } from './lib/cx'; import { isModifierClick } from './lib/isModifierClick'; -import type { useBreadcrumb } from 'react-instantsearch-hooks'; +import type { useBreadcrumb } from 'react-instantsearch-core'; export type BreadcrumbTranslations = { /** diff --git a/packages/react-instantsearch-hooks-web/src/ui/ClearRefinements.tsx b/packages/react-instantsearch/src/ui/ClearRefinements.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/ClearRefinements.tsx rename to packages/react-instantsearch/src/ui/ClearRefinements.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/CurrentRefinements.tsx b/packages/react-instantsearch/src/ui/CurrentRefinements.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/CurrentRefinements.tsx rename to packages/react-instantsearch/src/ui/CurrentRefinements.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/HierarchicalMenu.tsx b/packages/react-instantsearch/src/ui/HierarchicalMenu.tsx similarity index 98% rename from packages/react-instantsearch-hooks-web/src/ui/HierarchicalMenu.tsx rename to packages/react-instantsearch/src/ui/HierarchicalMenu.tsx index 688b1fa33e..e6175e767e 100644 --- a/packages/react-instantsearch-hooks-web/src/ui/HierarchicalMenu.tsx +++ b/packages/react-instantsearch/src/ui/HierarchicalMenu.tsx @@ -5,7 +5,7 @@ import { isModifierClick } from './lib/isModifierClick'; import { ShowMoreButton } from './ShowMoreButton'; import type { ShowMoreButtonTranslations } from './ShowMoreButton'; -import type { useHierarchicalMenu } from 'react-instantsearch-hooks'; +import type { useHierarchicalMenu } from 'react-instantsearch-core'; type HierarchicalMenuClassNames = { /** diff --git a/packages/react-instantsearch-hooks-web/src/ui/Highlight.tsx b/packages/react-instantsearch/src/ui/Highlight.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/Highlight.tsx rename to packages/react-instantsearch/src/ui/Highlight.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/Hits.tsx b/packages/react-instantsearch/src/ui/Hits.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/Hits.tsx rename to packages/react-instantsearch/src/ui/Hits.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/HitsPerPage.tsx b/packages/react-instantsearch/src/ui/HitsPerPage.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/HitsPerPage.tsx rename to packages/react-instantsearch/src/ui/HitsPerPage.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/InfiniteHits.tsx b/packages/react-instantsearch/src/ui/InfiniteHits.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/InfiniteHits.tsx rename to packages/react-instantsearch/src/ui/InfiniteHits.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/InternalHighlight.tsx b/packages/react-instantsearch/src/ui/InternalHighlight.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/InternalHighlight.tsx rename to packages/react-instantsearch/src/ui/InternalHighlight.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/Menu.tsx b/packages/react-instantsearch/src/ui/Menu.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/Menu.tsx rename to packages/react-instantsearch/src/ui/Menu.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/Pagination.tsx b/packages/react-instantsearch/src/ui/Pagination.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/Pagination.tsx rename to packages/react-instantsearch/src/ui/Pagination.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/PoweredBy.tsx b/packages/react-instantsearch/src/ui/PoweredBy.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/PoweredBy.tsx rename to packages/react-instantsearch/src/ui/PoweredBy.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/RangeInput.tsx b/packages/react-instantsearch/src/ui/RangeInput.tsx similarity index 98% rename from packages/react-instantsearch-hooks-web/src/ui/RangeInput.tsx rename to packages/react-instantsearch/src/ui/RangeInput.tsx index d05adf8cad..a029dcbd15 100644 --- a/packages/react-instantsearch-hooks-web/src/ui/RangeInput.tsx +++ b/packages/react-instantsearch/src/ui/RangeInput.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { cx } from './lib/cx'; -import type { useRange } from 'react-instantsearch-hooks'; +import type { useRange } from 'react-instantsearch-core'; type RangeRenderState = ReturnType; diff --git a/packages/react-instantsearch-hooks-web/src/ui/RefinementList.tsx b/packages/react-instantsearch/src/ui/RefinementList.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/RefinementList.tsx rename to packages/react-instantsearch/src/ui/RefinementList.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/SearchBox.tsx b/packages/react-instantsearch/src/ui/SearchBox.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/SearchBox.tsx rename to packages/react-instantsearch/src/ui/SearchBox.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/ShowMoreButton.tsx b/packages/react-instantsearch/src/ui/ShowMoreButton.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/ShowMoreButton.tsx rename to packages/react-instantsearch/src/ui/ShowMoreButton.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/Snippet.tsx b/packages/react-instantsearch/src/ui/Snippet.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/Snippet.tsx rename to packages/react-instantsearch/src/ui/Snippet.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/SortBy.tsx b/packages/react-instantsearch/src/ui/SortBy.tsx similarity index 95% rename from packages/react-instantsearch-hooks-web/src/ui/SortBy.tsx rename to packages/react-instantsearch/src/ui/SortBy.tsx index 7b050e225b..0821c2caf2 100644 --- a/packages/react-instantsearch-hooks-web/src/ui/SortBy.tsx +++ b/packages/react-instantsearch/src/ui/SortBy.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { cx } from './lib/cx'; -import type { UseSortByProps } from 'react-instantsearch-hooks'; +import type { UseSortByProps } from 'react-instantsearch-core'; export type SortByProps = Omit, 'onChange'> & Pick & diff --git a/packages/react-instantsearch-hooks-web/src/ui/Stats.tsx b/packages/react-instantsearch/src/ui/Stats.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/Stats.tsx rename to packages/react-instantsearch/src/ui/Stats.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/ToggleRefinement.tsx b/packages/react-instantsearch/src/ui/ToggleRefinement.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/ToggleRefinement.tsx rename to packages/react-instantsearch/src/ui/ToggleRefinement.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/Breadcrumb.test.tsx b/packages/react-instantsearch/src/ui/__tests__/Breadcrumb.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/Breadcrumb.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/Breadcrumb.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/ClearRefinements.test.tsx b/packages/react-instantsearch/src/ui/__tests__/ClearRefinements.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/ClearRefinements.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/ClearRefinements.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/CurrentRefinements.test.tsx b/packages/react-instantsearch/src/ui/__tests__/CurrentRefinements.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/CurrentRefinements.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/CurrentRefinements.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/HierarchicalMenu.test.tsx b/packages/react-instantsearch/src/ui/__tests__/HierarchicalMenu.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/HierarchicalMenu.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/HierarchicalMenu.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/Hits.test.tsx b/packages/react-instantsearch/src/ui/__tests__/Hits.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/Hits.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/Hits.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/HitsPerPage.test.tsx b/packages/react-instantsearch/src/ui/__tests__/HitsPerPage.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/HitsPerPage.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/HitsPerPage.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/InfiniteHits.test.tsx b/packages/react-instantsearch/src/ui/__tests__/InfiniteHits.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/InfiniteHits.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/InfiniteHits.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/InternalHighlight.test.tsx b/packages/react-instantsearch/src/ui/__tests__/InternalHighlight.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/InternalHighlight.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/InternalHighlight.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/Menu.test.tsx b/packages/react-instantsearch/src/ui/__tests__/Menu.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/Menu.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/Menu.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/Pagination.test.tsx b/packages/react-instantsearch/src/ui/__tests__/Pagination.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/Pagination.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/Pagination.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/PoweredBy.test.tsx b/packages/react-instantsearch/src/ui/__tests__/PoweredBy.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/PoweredBy.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/PoweredBy.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/RangeInput.test.tsx b/packages/react-instantsearch/src/ui/__tests__/RangeInput.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/RangeInput.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/RangeInput.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/RefinementList.test.tsx b/packages/react-instantsearch/src/ui/__tests__/RefinementList.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/RefinementList.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/RefinementList.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/SearchBox.test.tsx b/packages/react-instantsearch/src/ui/__tests__/SearchBox.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/SearchBox.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/SearchBox.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/ShowMoreButton.test.tsx b/packages/react-instantsearch/src/ui/__tests__/ShowMoreButton.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/ShowMoreButton.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/ShowMoreButton.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/SortBy.test.tsx b/packages/react-instantsearch/src/ui/__tests__/SortBy.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/SortBy.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/SortBy.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/Stats.test.tsx b/packages/react-instantsearch/src/ui/__tests__/Stats.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/Stats.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/Stats.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/__tests__/ToggleRefinement.test.tsx b/packages/react-instantsearch/src/ui/__tests__/ToggleRefinement.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/__tests__/ToggleRefinement.test.tsx rename to packages/react-instantsearch/src/ui/__tests__/ToggleRefinement.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/lib/__tests__/isModifierClick.test.tsx b/packages/react-instantsearch/src/ui/lib/__tests__/isModifierClick.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/lib/__tests__/isModifierClick.test.tsx rename to packages/react-instantsearch/src/ui/lib/__tests__/isModifierClick.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/ui/lib/cx.ts b/packages/react-instantsearch/src/ui/lib/cx.ts similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/lib/cx.ts rename to packages/react-instantsearch/src/ui/lib/cx.ts diff --git a/packages/react-instantsearch-hooks-web/src/ui/lib/isModifierClick.ts b/packages/react-instantsearch/src/ui/lib/isModifierClick.ts similarity index 100% rename from packages/react-instantsearch-hooks-web/src/ui/lib/isModifierClick.ts rename to packages/react-instantsearch/src/ui/lib/isModifierClick.ts diff --git a/packages/react-instantsearch-hooks-web/src/widgets/Breadcrumb.tsx b/packages/react-instantsearch/src/widgets/Breadcrumb.tsx similarity index 88% rename from packages/react-instantsearch-hooks-web/src/widgets/Breadcrumb.tsx rename to packages/react-instantsearch/src/widgets/Breadcrumb.tsx index 254e896f77..c185419dc8 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/Breadcrumb.tsx +++ b/packages/react-instantsearch/src/widgets/Breadcrumb.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useBreadcrumb } from 'react-instantsearch-hooks'; +import { useBreadcrumb } from 'react-instantsearch-core'; import { Breadcrumb as BreadcrumbUiComponent } from '../ui/Breadcrumb'; import type { BreadcrumbProps as BreadcrumbUiProps } from '../ui/Breadcrumb'; -import type { UseBreadcrumbProps } from 'react-instantsearch-hooks'; +import type { UseBreadcrumbProps } from 'react-instantsearch-core'; type UiProps = Pick< BreadcrumbUiProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/ClearRefinements.tsx b/packages/react-instantsearch/src/widgets/ClearRefinements.tsx similarity index 93% rename from packages/react-instantsearch-hooks-web/src/widgets/ClearRefinements.tsx rename to packages/react-instantsearch/src/widgets/ClearRefinements.tsx index d38f0fce84..b8bff3a83d 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/ClearRefinements.tsx +++ b/packages/react-instantsearch/src/widgets/ClearRefinements.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useClearRefinements } from 'react-instantsearch-hooks'; +import { useClearRefinements } from 'react-instantsearch-core'; import { ClearRefinements as ClearRefinementsUiComponent } from '../ui/ClearRefinements'; import type { ClearRefinementsProps as ClearRefinementsUiComponentProps } from '../ui/ClearRefinements'; -import type { UseClearRefinementsProps } from 'react-instantsearch-hooks'; +import type { UseClearRefinementsProps } from 'react-instantsearch-core'; type UiProps = Pick< ClearRefinementsUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/CurrentRefinements.tsx b/packages/react-instantsearch/src/widgets/CurrentRefinements.tsx similarity index 92% rename from packages/react-instantsearch-hooks-web/src/widgets/CurrentRefinements.tsx rename to packages/react-instantsearch/src/widgets/CurrentRefinements.tsx index 51e31575ae..0397378a8d 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/CurrentRefinements.tsx +++ b/packages/react-instantsearch/src/widgets/CurrentRefinements.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useCurrentRefinements } from 'react-instantsearch-hooks'; +import { useCurrentRefinements } from 'react-instantsearch-core'; import { CurrentRefinements as CurrentRefinementsUiComponent } from '../ui/CurrentRefinements'; import type { CurrentRefinementsProps as CurrentRefinementsUiComponentProps } from '../ui/CurrentRefinements'; -import type { UseCurrentRefinementsProps } from 'react-instantsearch-hooks'; +import type { UseCurrentRefinementsProps } from 'react-instantsearch-core'; type UiProps = Pick< CurrentRefinementsUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/HierarchicalMenu.tsx b/packages/react-instantsearch/src/widgets/HierarchicalMenu.tsx similarity index 95% rename from packages/react-instantsearch-hooks-web/src/widgets/HierarchicalMenu.tsx rename to packages/react-instantsearch/src/widgets/HierarchicalMenu.tsx index dc7b4eab6e..e1ae2a3136 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/HierarchicalMenu.tsx +++ b/packages/react-instantsearch/src/widgets/HierarchicalMenu.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useHierarchicalMenu } from 'react-instantsearch-hooks'; +import { useHierarchicalMenu } from 'react-instantsearch-core'; import { HierarchicalMenu as HierarchicalMenuUiComponent } from '../ui/HierarchicalMenu'; import type { HierarchicalMenuProps as HierarchicalMenuUiComponentProps } from '../ui/HierarchicalMenu'; -import type { UseHierarchicalMenuProps } from 'react-instantsearch-hooks'; +import type { UseHierarchicalMenuProps } from 'react-instantsearch-core'; type UiProps = Pick< HierarchicalMenuUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/Highlight.tsx b/packages/react-instantsearch/src/widgets/Highlight.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/widgets/Highlight.tsx rename to packages/react-instantsearch/src/widgets/Highlight.tsx diff --git a/packages/react-instantsearch-hooks-web/src/widgets/Hits.tsx b/packages/react-instantsearch/src/widgets/Hits.tsx similarity index 87% rename from packages/react-instantsearch-hooks-web/src/widgets/Hits.tsx rename to packages/react-instantsearch/src/widgets/Hits.tsx index f46d670c9f..5a99c67b67 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/Hits.tsx +++ b/packages/react-instantsearch/src/widgets/Hits.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { useHits } from 'react-instantsearch-hooks'; +import { useHits } from 'react-instantsearch-core'; import { Hits as HitsUiComponent } from '../ui/Hits'; import type { HitsProps as HitsUiComponentProps } from '../ui/Hits'; import type { Hit, BaseHit } from 'instantsearch.js'; -import type { UseHitsProps } from 'react-instantsearch-hooks'; +import type { UseHitsProps } from 'react-instantsearch-core'; type UiProps = Pick< HitsUiComponentProps>, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/HitsPerPage.tsx b/packages/react-instantsearch/src/widgets/HitsPerPage.tsx similarity index 87% rename from packages/react-instantsearch-hooks-web/src/widgets/HitsPerPage.tsx rename to packages/react-instantsearch/src/widgets/HitsPerPage.tsx index 87ea1483cd..09df156c20 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/HitsPerPage.tsx +++ b/packages/react-instantsearch/src/widgets/HitsPerPage.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useHitsPerPage } from 'react-instantsearch-hooks'; +import { useHitsPerPage } from 'react-instantsearch-core'; import { HitsPerPage as HitsPerPageUiComponent } from '../ui/HitsPerPage'; import type { HitsPerPageProps as HitsPerPageUiComponentProps } from '../ui/HitsPerPage'; -import type { UseHitsPerPageProps } from 'react-instantsearch-hooks'; +import type { UseHitsPerPageProps } from 'react-instantsearch-core'; type UiProps = Pick< HitsPerPageUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/InfiniteHits.tsx b/packages/react-instantsearch/src/widgets/InfiniteHits.tsx similarity index 93% rename from packages/react-instantsearch-hooks-web/src/widgets/InfiniteHits.tsx rename to packages/react-instantsearch/src/widgets/InfiniteHits.tsx index 28552ab968..09a72a167e 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/InfiniteHits.tsx +++ b/packages/react-instantsearch/src/widgets/InfiniteHits.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { useInfiniteHits } from 'react-instantsearch-hooks'; +import { useInfiniteHits } from 'react-instantsearch-core'; import { InfiniteHits as InfiniteHitsUiComponent } from '../ui/InfiniteHits'; import type { InfiniteHitsProps as InfiniteHitsUiComponentProps } from '../ui/InfiniteHits'; import type { BaseHit, Hit } from 'instantsearch.js'; -import type { UseInfiniteHitsProps } from 'react-instantsearch-hooks'; +import type { UseInfiniteHitsProps } from 'react-instantsearch-core'; type UiProps = Pick< InfiniteHitsUiComponentProps>, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/Menu.tsx b/packages/react-instantsearch/src/widgets/Menu.tsx similarity index 91% rename from packages/react-instantsearch-hooks-web/src/widgets/Menu.tsx rename to packages/react-instantsearch/src/widgets/Menu.tsx index b0fa3e5aa1..70ddb15732 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/Menu.tsx +++ b/packages/react-instantsearch/src/widgets/Menu.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useMenu } from 'react-instantsearch-hooks'; +import { useMenu } from 'react-instantsearch-core'; import { Menu as MenuUiComponent } from '../ui/Menu'; import type { MenuProps as MenuUiComponentProps } from '../ui/Menu'; -import type { UseMenuProps } from 'react-instantsearch-hooks'; +import type { UseMenuProps } from 'react-instantsearch-core'; type UiProps = Pick< MenuUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/Pagination.tsx b/packages/react-instantsearch/src/widgets/Pagination.tsx similarity index 93% rename from packages/react-instantsearch-hooks-web/src/widgets/Pagination.tsx rename to packages/react-instantsearch/src/widgets/Pagination.tsx index 69ada2fb2d..4c2a929c77 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/Pagination.tsx +++ b/packages/react-instantsearch/src/widgets/Pagination.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { usePagination } from 'react-instantsearch-hooks'; +import { usePagination } from 'react-instantsearch-core'; import { Pagination as PaginationUiComponent } from '../ui/Pagination'; import type { PaginationProps as PaginationUiComponentProps } from '../ui/Pagination'; -import type { UsePaginationProps } from 'react-instantsearch-hooks'; +import type { UsePaginationProps } from 'react-instantsearch-core'; type UiProps = Pick< PaginationUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/PoweredBy.tsx b/packages/react-instantsearch/src/widgets/PoweredBy.tsx similarity index 89% rename from packages/react-instantsearch-hooks-web/src/widgets/PoweredBy.tsx rename to packages/react-instantsearch/src/widgets/PoweredBy.tsx index 8ee62d5b49..544ba2e9d2 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/PoweredBy.tsx +++ b/packages/react-instantsearch/src/widgets/PoweredBy.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { usePoweredBy } from 'react-instantsearch-hooks'; +import { usePoweredBy } from 'react-instantsearch-core'; import { PoweredBy as PoweredByUiComponent } from '../ui/PoweredBy'; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/RangeInput.tsx b/packages/react-instantsearch/src/widgets/RangeInput.tsx similarity index 89% rename from packages/react-instantsearch-hooks-web/src/widgets/RangeInput.tsx rename to packages/react-instantsearch/src/widgets/RangeInput.tsx index 71c34e8c52..8e1e8e2bb5 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/RangeInput.tsx +++ b/packages/react-instantsearch/src/widgets/RangeInput.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useRange } from 'react-instantsearch-hooks'; +import { useRange } from 'react-instantsearch-core'; import { RangeInput as RangeInputUiComponent } from '../ui/RangeInput'; import type { RangeInputProps as RangeInputUiProps } from '../ui/RangeInput'; -import type { UseRangeProps } from 'react-instantsearch-hooks'; +import type { UseRangeProps } from 'react-instantsearch-core'; type UiProps = Pick< RangeInputUiProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/RefinementList.tsx b/packages/react-instantsearch/src/widgets/RefinementList.tsx similarity index 98% rename from packages/react-instantsearch-hooks-web/src/widgets/RefinementList.tsx rename to packages/react-instantsearch/src/widgets/RefinementList.tsx index 06d2a75e86..bcec125340 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/RefinementList.tsx +++ b/packages/react-instantsearch/src/widgets/RefinementList.tsx @@ -1,5 +1,5 @@ import React, { useRef, useState } from 'react'; -import { useRefinementList } from 'react-instantsearch-hooks'; +import { useRefinementList } from 'react-instantsearch-core'; import { RefinementList as RefinementListUiComponent } from '../ui/RefinementList'; import { SearchBox as SearchBoxUiComponent } from '../ui/SearchBox'; @@ -8,7 +8,7 @@ import type { RefinementListProps as RefinementListUiComponentProps } from '../u import type { SearchBoxTranslations } from '../ui/SearchBox'; import type { RefinementListItem } from 'instantsearch.js/es/connectors/refinement-list/connectRefinementList'; import type { RefinementListWidgetParams } from 'instantsearch.js/es/widgets/refinement-list/refinement-list'; -import type { UseRefinementListProps } from 'react-instantsearch-hooks'; +import type { UseRefinementListProps } from 'react-instantsearch-core'; type UiProps = Pick< RefinementListUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/SearchBox.tsx b/packages/react-instantsearch/src/widgets/SearchBox.tsx similarity index 94% rename from packages/react-instantsearch-hooks-web/src/widgets/SearchBox.tsx rename to packages/react-instantsearch/src/widgets/SearchBox.tsx index bdf70fe44a..c5e950a953 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/SearchBox.tsx +++ b/packages/react-instantsearch/src/widgets/SearchBox.tsx @@ -1,10 +1,10 @@ import React, { useRef, useState } from 'react'; -import { useSearchBox } from 'react-instantsearch-hooks'; +import { useSearchBox } from 'react-instantsearch-core'; import { SearchBox as SearchBoxUiComponent } from '../ui/SearchBox'; import type { SearchBoxProps as SearchBoxUiComponentProps } from '../ui/SearchBox'; -import type { UseSearchBoxProps } from 'react-instantsearch-hooks'; +import type { UseSearchBoxProps } from 'react-instantsearch-core'; type UiProps = Pick< SearchBoxUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/Snippet.tsx b/packages/react-instantsearch/src/widgets/Snippet.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/widgets/Snippet.tsx rename to packages/react-instantsearch/src/widgets/Snippet.tsx diff --git a/packages/react-instantsearch-hooks-web/src/widgets/SortBy.tsx b/packages/react-instantsearch/src/widgets/SortBy.tsx similarity index 85% rename from packages/react-instantsearch-hooks-web/src/widgets/SortBy.tsx rename to packages/react-instantsearch/src/widgets/SortBy.tsx index aa2648a129..75c0fc3d41 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/SortBy.tsx +++ b/packages/react-instantsearch/src/widgets/SortBy.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { useSortBy } from 'react-instantsearch-hooks'; +import { useSortBy } from 'react-instantsearch-core'; import { SortBy as SortByUiComponent } from '../ui/SortBy'; import type { SortByProps as SortByUiComponentProps } from '../ui/SortBy'; -import type { UseSortByProps } from 'react-instantsearch-hooks'; +import type { UseSortByProps } from 'react-instantsearch-core'; type UiProps = Pick; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/Stats.tsx b/packages/react-instantsearch/src/widgets/Stats.tsx similarity index 91% rename from packages/react-instantsearch-hooks-web/src/widgets/Stats.tsx rename to packages/react-instantsearch/src/widgets/Stats.tsx index caa8619e2e..384a83fdd6 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/Stats.tsx +++ b/packages/react-instantsearch/src/widgets/Stats.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useStats } from 'react-instantsearch-hooks'; +import { useStats } from 'react-instantsearch-core'; import { Stats as StatsUiComponent } from '../ui/Stats'; @@ -7,7 +7,7 @@ import type { StatsProps as StatsUiComponentProps, StatsTranslationOptions, } from '../ui/Stats'; -import type { UseStatsProps } from 'react-instantsearch-hooks'; +import type { UseStatsProps } from 'react-instantsearch-core'; type UiProps = Pick< StatsUiComponentProps, diff --git a/packages/react-instantsearch-hooks-web/src/widgets/ToggleRefinement.tsx b/packages/react-instantsearch/src/widgets/ToggleRefinement.tsx similarity index 93% rename from packages/react-instantsearch-hooks-web/src/widgets/ToggleRefinement.tsx rename to packages/react-instantsearch/src/widgets/ToggleRefinement.tsx index 7ad0d8c242..33898121c1 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/ToggleRefinement.tsx +++ b/packages/react-instantsearch/src/widgets/ToggleRefinement.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { useToggleRefinement } from 'react-instantsearch-hooks'; +import { useToggleRefinement } from 'react-instantsearch-core'; import { ToggleRefinement as ToggleRefinementUiComponent } from '../ui/ToggleRefinement'; import type { PartialKeys } from '../types'; import type { ToggleRefinementProps as ToggleRefinementUiComponentProps } from '../ui/ToggleRefinement'; -import type { UseToggleRefinementProps } from 'react-instantsearch-hooks'; +import type { UseToggleRefinementProps } from 'react-instantsearch-core'; type UiProps = Pick; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Breadcrumb.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Breadcrumb.test.tsx similarity index 86% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/Breadcrumb.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/Breadcrumb.test.tsx index fdea8fe02f..54d0ac7a54 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Breadcrumb.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/Breadcrumb.test.tsx @@ -7,14 +7,14 @@ import { createSearchClient, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import React from 'react'; -import { useHierarchicalMenu } from 'react-instantsearch-hooks'; +import { useHierarchicalMenu } from 'react-instantsearch-core'; import { Breadcrumb } from '../Breadcrumb'; -import type { UseHierarchicalMenuProps } from 'react-instantsearch-hooks'; +import type { UseHierarchicalMenuProps } from 'react-instantsearch-core'; describe('Breadcrumb', () => { const hierarchicalFacets = { @@ -46,7 +46,7 @@ describe('Breadcrumb', () => { test('forwards custom class names and `div` props to the root element', () => { const searchClient = createMockedSearchClient(); const { container } = render( - + { title="Some custom title" attributes={hierarchicalAttributes} /> - + ); const root = container.firstChild; @@ -65,13 +65,13 @@ describe('Breadcrumb', () => { test('renders with translations', async () => { const searchClient = createMockedSearchClient(); const { getByRole } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/ClearRefinements.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/ClearRefinements.test.tsx similarity index 93% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/ClearRefinements.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/ClearRefinements.test.tsx index 0483a464eb..5fa9421c0a 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/ClearRefinements.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/ClearRefinements.test.tsx @@ -3,27 +3,27 @@ */ import { createSearchClient } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { useCurrentRefinements, useRefinementList, -} from 'react-instantsearch-hooks'; +} from 'react-instantsearch-core'; import { ClearRefinements } from '../ClearRefinements'; import type { UseRefinementListProps, UseCurrentRefinementsProps, -} from 'react-instantsearch-hooks'; +} from 'react-instantsearch-core'; describe('ClearRefinements', () => { test('renders with default props', async () => { const searchClient = createSearchClient({}); const { container } = render( - { > - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -58,9 +58,9 @@ describe('ClearRefinements', () => { test('renders with a disabled button when there are no refinements', async () => { const searchClient = createSearchClient({}); const { container } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -88,7 +88,7 @@ describe('ClearRefinements', () => { test('clears all refinements', async () => { const searchClient = createSearchClient({}); const { container, queryAllByRole } = render( - { - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -160,7 +160,7 @@ describe('ClearRefinements', () => { test('inclusively restricts what refinements to clear', async () => { const searchClient = createSearchClient({}); const { container, queryAllByRole } = render( - { - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -254,7 +254,7 @@ describe('ClearRefinements', () => { test('exclusively restricts what refinements to clear', async () => { const searchClient = createSearchClient({}); const { container, queryAllByRole } = render( - { - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -348,7 +348,7 @@ describe('ClearRefinements', () => { test('restricts what refinements to clear with custom logic', async () => { const searchClient = createSearchClient({}); const { container, queryAllByRole } = render( - { items.filter((item) => item !== 'brand')} /> - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -443,13 +443,13 @@ describe('ClearRefinements', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; @@ -460,7 +460,7 @@ describe('ClearRefinements', () => { test('renders with translations', async () => { const searchClient = createSearchClient({}); const { getByRole } = render( - { > - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/CurrentRefinements.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/CurrentRefinements.test.tsx similarity index 95% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/CurrentRefinements.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/CurrentRefinements.test.tsx index 568c350957..9cd8c1355e 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/CurrentRefinements.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/CurrentRefinements.test.tsx @@ -3,20 +3,20 @@ */ import { createSearchClient } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { useRefinementList } from 'react-instantsearch-hooks'; +import { useRefinementList } from 'react-instantsearch-core'; import { CurrentRefinements } from '../CurrentRefinements'; -import type { UseRefinementListProps } from 'react-instantsearch-hooks'; +import type { UseRefinementListProps } from 'react-instantsearch-core'; describe('CurrentRefinements', () => { test('renders with default props', async () => { const { container } = render( - { - + ); await waitFor(() => { @@ -123,9 +123,9 @@ describe('CurrentRefinements', () => { test('renders with a specific set of class names when there are no refinements', async () => { const searchClient = createSearchClient({}); const { container } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -145,7 +145,7 @@ describe('CurrentRefinements', () => { test('clears a refinement', async () => { const { container, queryByText } = render( - { - + ); await waitFor(() => { @@ -304,7 +304,7 @@ describe('CurrentRefinements', () => { render(
    - { > - +
    ); @@ -334,7 +334,7 @@ describe('CurrentRefinements', () => { test('does not clear when pressing a modifier key', async () => { const searchClient = createSearchClient({}); const { container } = render( - { > - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -450,7 +450,7 @@ describe('CurrentRefinements', () => { test('inclusively restricts what refinements to display', async () => { const searchClient = createSearchClient({}); const { container, queryByText } = render( - { - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -513,7 +513,7 @@ describe('CurrentRefinements', () => { test('exclusively restricts what refinements to display', async () => { const searchClient = createSearchClient({}); const { container, queryByText } = render( - { - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -576,7 +576,7 @@ describe('CurrentRefinements', () => { test('restricts what refinements to display with custom logic', async () => { const searchClient = createSearchClient({}); const { container, queryByText } = render( - { items.filter((item) => item.attribute !== 'brand') } /> - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -642,13 +642,13 @@ describe('CurrentRefinements', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/HierarchicalMenu.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/HierarchicalMenu.test.tsx similarity index 91% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/HierarchicalMenu.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/HierarchicalMenu.test.tsx index 2152dc4617..15e33f4c8d 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/HierarchicalMenu.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/HierarchicalMenu.test.tsx @@ -7,7 +7,7 @@ import { createSearchClient, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -49,9 +49,9 @@ describe('HierarchicalMenu', () => { test('renders with props', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -164,9 +164,9 @@ describe('HierarchicalMenu', () => { test('limits the number of items to display', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -211,14 +211,14 @@ describe('HierarchicalMenu', () => { test('transforms the items', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + items.map((item) => ({ ...item, label: item.label.toUpperCase() })) } /> - + ); await waitFor(() => { @@ -238,9 +238,9 @@ describe('HierarchicalMenu', () => { test('sorts the items by ascending name', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -259,9 +259,9 @@ describe('HierarchicalMenu', () => { test('sorts the items by descending name', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -280,9 +280,9 @@ describe('HierarchicalMenu', () => { test('sorts the items by count', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -297,7 +297,7 @@ describe('HierarchicalMenu', () => { test('sorts the items by refinement state', async () => { const searchClient = createMockedSearchClient(); const { container, findByText } = render( - + { })) } /> - + ); await waitFor(() => @@ -355,12 +355,12 @@ describe('HierarchicalMenu', () => { test('sorts the items using a sorting function', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + b.name.localeCompare(a.name)} /> - + ); await waitFor(() => @@ -381,9 +381,9 @@ describe('HierarchicalMenu', () => { test('displays a "Show more" button', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -447,14 +447,14 @@ describe('HierarchicalMenu', () => { test('limits the number of items to reveal', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -482,14 +482,14 @@ describe('HierarchicalMenu', () => { test('forwards custom class names and `div` props to the root element', () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); const root = container.firstChild; @@ -500,7 +500,7 @@ describe('HierarchicalMenu', () => { test('renders with translations', async () => { const searchClient = createMockedSearchClient(); const { getByRole } = render( - + { }} showMore /> - + ); await waitFor(() => { diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Highlight.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Highlight.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/Highlight.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/Highlight.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Hits.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Hits.test.tsx similarity index 90% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/Hits.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/Hits.test.tsx index 266494a7ad..8ed42a3a43 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Hits.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/Hits.test.tsx @@ -7,7 +7,7 @@ import { createMultiSearchResponse, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import React from 'react'; @@ -19,9 +19,9 @@ import type { AlgoliaHit } from 'instantsearch.js'; describe('Hits', () => { test('renders with default props', async () => { const { container } = render( - + - + ); await waitFor(() => { @@ -63,13 +63,13 @@ describe('Hits', () => { }); const { container } = render( - + hitComponent={({ hit }) => ( {`${hit.__position} - ${hit.somethingSpecial}`} )} /> - + ); await waitFor(() => { @@ -110,13 +110,13 @@ describe('Hits', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/HitsPerPage.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/HitsPerPage.test.tsx similarity index 89% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/HitsPerPage.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/HitsPerPage.test.tsx index 2e0a98d477..3fd49d863a 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/HitsPerPage.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/HitsPerPage.test.tsx @@ -3,7 +3,7 @@ */ import { createSearchClient } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -14,7 +14,7 @@ describe('HitsPerPage', () => { test('renders with props', async () => { const searchClient = createSearchClient({}); const { container } = render( - + { { label: '30', value: 30 }, ]} /> - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -62,7 +62,7 @@ describe('HitsPerPage', () => { test('selects current value', async () => { const searchClient = createSearchClient({}); const { getByRole } = render( - { { label: '30', value: 30 }, ]} /> - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -96,7 +96,7 @@ describe('HitsPerPage', () => { test('refines on select', async () => { const searchClient = createSearchClient({}); const { getByRole } = render( - + { { label: '30', value: 30 }, ]} /> - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -125,7 +125,7 @@ describe('HitsPerPage', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + { { label: '30', value: 30 }, ]} /> - + ); const root = container.firstChild; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/InfiniteHits.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/InfiniteHits.test.tsx similarity index 92% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/InfiniteHits.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/InfiniteHits.test.tsx index 86a45bad2c..aee95a6ae1 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/InfiniteHits.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/InfiniteHits.test.tsx @@ -7,7 +7,7 @@ import { createMultiSearchResponse, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { act, render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -57,9 +57,9 @@ describe('InfiniteHits', () => { test('renders with default props', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -124,13 +124,13 @@ describe('InfiniteHits', () => { test('renders with a custom hit component', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + hitComponent={({ hit }) => ( {`${hit.__position} - ${hit.somethingSpecial}`} )} /> - + ); await waitFor(() => @@ -184,13 +184,13 @@ describe('InfiniteHits', () => { test('displays more hits when clicking the "Show More" button', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + ( {__position} )} /> - + ); await waitFor(() => @@ -237,7 +237,7 @@ describe('InfiniteHits', () => { test('displays previous hits when clicking the "Show Previous" button', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - @@ -246,7 +246,7 @@ describe('InfiniteHits', () => { {__position} )} /> - + ); await waitFor(() => @@ -295,9 +295,9 @@ describe('InfiniteHits', () => { test('hides the "Show Previous" button when `showPrevious` is `false`', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -327,9 +327,9 @@ describe('InfiniteHits', () => { test('marks the "Show Previous" button as disabled on first page', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -353,12 +353,12 @@ describe('InfiniteHits', () => { test('marks the "Show More" button as disabled on last page', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -378,13 +378,13 @@ describe('InfiniteHits', () => { test('forwards custom class names and `div` props to the root element', () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); const root = container.firstChild; @@ -395,7 +395,7 @@ describe('InfiniteHits', () => { test('renders with translations', async () => { const searchClient = createMockedSearchClient(); const { getByRole } = render( - @@ -405,7 +405,7 @@ describe('InfiniteHits', () => { showPreviousButtonText: 'Display previous', }} /> - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Menu.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Menu.test.tsx similarity index 94% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/Menu.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/Menu.test.tsx index 1321e8d82b..f9e19610c6 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Menu.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/Menu.test.tsx @@ -7,7 +7,7 @@ import { createAlgoliaSearchClient, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -66,9 +66,9 @@ describe('Menu', () => { test('renders with props', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -305,9 +305,9 @@ describe('Menu', () => { test('limits the number of items to display', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -426,7 +426,7 @@ describe('Menu', () => { test('transforms the items', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + @@ -436,7 +436,7 @@ describe('Menu', () => { })) } /> - + ); await waitFor(() => { @@ -463,9 +463,9 @@ describe('Menu', () => { test('sorts the items by ascending name', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -491,9 +491,9 @@ describe('Menu', () => { test('sorts the items by descending name', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -519,9 +519,9 @@ describe('Menu', () => { test('sorts the items by count', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -547,7 +547,7 @@ describe('Menu', () => { test('sorts the items by refinement state', async () => { const searchClient = createMockedSearchClient(); const { container, findByText } = render( - + { })) } /> - + ); await waitFor(() => { @@ -626,12 +626,12 @@ describe('Menu', () => { test('sorts the items using a sorting function', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + b.name.localeCompare(a.name)} /> - + ); await waitFor(() => { @@ -659,9 +659,9 @@ describe('Menu', () => { test('displays a "Show more" button', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -892,9 +892,9 @@ describe('Menu', () => { test('limits the number of items to reveal', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -919,14 +919,14 @@ describe('Menu', () => { test('forwards custom class names and `div` props to the root element', () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); const root = container.firstChild; @@ -937,7 +937,7 @@ describe('Menu', () => { test('renders with translations', async () => { const searchClient = createMockedSearchClient(); const { getByRole } = render( - + { }} showMore /> - + ); await waitFor(() => { diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Pagination.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Pagination.test.tsx similarity index 97% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/Pagination.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/Pagination.test.tsx index cf4b4c77a4..bc8a1ef3b6 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Pagination.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/Pagination.test.tsx @@ -7,7 +7,7 @@ import { createAlgoliaSearchClient, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -41,9 +41,9 @@ function createMockedSearchClient({ nbPages }: { nbPages?: number } = {}) { describe('Pagination', () => { test('renders with default props', async () => { const { container } = render( - + - + ); await waitFor(() => @@ -120,9 +120,9 @@ describe('Pagination', () => { test('renders with props', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -282,9 +282,9 @@ describe('Pagination', () => { test('navigates between pages', async () => { const searchClient = createMockedSearchClient(); const { container, getByText } = render( - + - + ); await waitFor(() => @@ -1219,9 +1219,9 @@ describe('Pagination', () => { test('does not navigate when pressing a modifier key', async () => { const searchClient = createMockedSearchClient(); const { getByText } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -1288,9 +1288,9 @@ describe('Pagination', () => { test('adds items around the current one', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -1473,9 +1473,9 @@ describe('Pagination', () => { }); const { container } = render( - + - + ); await waitFor(() => @@ -1609,9 +1609,9 @@ describe('Pagination', () => { test('limits the total pages to display', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -1722,9 +1722,9 @@ describe('Pagination', () => { test('hides the "First" item when `showFirst` is `false`', async () => { const { container } = render( - + - + ); await waitFor(() => @@ -1795,9 +1795,9 @@ describe('Pagination', () => { test('hides the "Previous" item when `showPrevious` is `false`', async () => { const { container } = render( - + - + ); await waitFor(() => @@ -1868,9 +1868,9 @@ describe('Pagination', () => { test('hides the "Next" item when `showNext` is `false`', async () => { const { container } = render( - + - + ); await waitFor(() => @@ -1939,9 +1939,9 @@ describe('Pagination', () => { test('hides the "Last" item when `showLast` is `false`', async () => { const { container } = render( - + - + ); await waitFor(() => @@ -2010,13 +2010,13 @@ describe('Pagination', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; @@ -2026,7 +2026,7 @@ describe('Pagination', () => { test('renders with translations', async () => { const { getByRole, findByRole, debug } = render( - { `#${currentPage}/${nbPages}`, }} /> - + ); await waitFor(() => diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/PoweredBy.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/PoweredBy.test.tsx similarity index 96% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/PoweredBy.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/PoweredBy.test.tsx index b520446142..16eb41badf 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/PoweredBy.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/PoweredBy.test.tsx @@ -2,7 +2,7 @@ * @jest-environment jsdom */ -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render } from '@testing-library/react'; import React from 'react'; @@ -11,9 +11,9 @@ import { PoweredBy } from '../PoweredBy'; describe('PoweredBy', () => { test('renders with default props', () => { const { container } = render( - + - + ); expect(container).toMatchInlineSnapshot(` @@ -49,9 +49,9 @@ describe('PoweredBy', () => { test('renders for dark themes', () => { const { container } = render( - + - + ); expect(container.firstChild).toHaveClass('ais-PoweredBy--dark'); @@ -88,7 +88,7 @@ describe('PoweredBy', () => { test('customizes the class names with the light theme', () => { const { container } = render( - + { logo: 'MyCustomPoweredByLogo', }} /> - + ); expect(container.firstChild).toHaveClass( @@ -116,7 +116,7 @@ describe('PoweredBy', () => { test('customizes the class names with the dark theme', () => { const { container } = render( - + { logo: 'MyCustomPoweredByLogo', }} /> - + ); expect(container.firstChild).toHaveClass( @@ -145,13 +145,13 @@ describe('PoweredBy', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/RangeInput.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/RangeInput.test.tsx similarity index 89% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/RangeInput.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/RangeInput.test.tsx index 1c4f9846d8..76aa12521c 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/RangeInput.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/RangeInput.test.tsx @@ -7,7 +7,7 @@ import { createSearchClient, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -44,9 +44,9 @@ describe('RangeInput', () => { test('renders with default props', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -107,7 +107,7 @@ describe('RangeInput', () => { test('renders with initial refinements', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - { }} > - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -134,9 +134,9 @@ describe('RangeInput', () => { test('renders with precision', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -151,9 +151,9 @@ describe('RangeInput', () => { test('refines on submit', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -187,14 +187,14 @@ describe('RangeInput', () => { test('forwards custom class names and `div` props to the root element', () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); const root = container.firstChild; @@ -205,7 +205,7 @@ describe('RangeInput', () => { test('renders with translations', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + { submitButtonText: 'Send', }} /> - + ); await waitFor(() => { diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/RefinementList.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/RefinementList.test.tsx similarity index 95% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/RefinementList.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/RefinementList.test.tsx index 98dc06f5be..d67fb3267d 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/RefinementList.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/RefinementList.test.tsx @@ -8,7 +8,7 @@ import { createSFFVResponse, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -119,9 +119,9 @@ describe('RefinementList', () => { test('renders with props', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -403,9 +403,9 @@ describe('RefinementList', () => { test('enables conjunctive faceting', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -439,9 +439,9 @@ describe('RefinementList', () => { test('limits the number of items to display', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -582,7 +582,7 @@ describe('RefinementList', () => { test('transforms the items', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + @@ -592,7 +592,7 @@ describe('RefinementList', () => { })) } /> - + ); await waitFor(() => { @@ -619,9 +619,9 @@ describe('RefinementList', () => { test('sorts the items by ascending name', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -647,9 +647,9 @@ describe('RefinementList', () => { test('sorts the items by descending name', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -675,9 +675,9 @@ describe('RefinementList', () => { test('sorts the items by count', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -703,9 +703,9 @@ describe('RefinementList', () => { test('sorts the items by refinement state', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -759,12 +759,12 @@ describe('RefinementList', () => { test('sorts the items using a sorting function', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + a.name.length - b.name.length} /> - + ); await waitFor(() => { @@ -792,13 +792,13 @@ describe('RefinementList', () => { test('displays a search box', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); const searchInput = container.querySelector( @@ -881,13 +881,13 @@ describe('RefinementList', () => { ), }); const { container } = render( - + - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); @@ -1027,9 +1027,9 @@ describe('RefinementList', () => { test('displays a "Show more" button', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -1305,13 +1305,13 @@ describe('RefinementList', () => { test('limits the number of items to reveal', async () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => @@ -1344,14 +1344,14 @@ describe('RefinementList', () => { test('forwards custom class names and `div` props to the root element', () => { const searchClient = createMockedSearchClient(); const { container } = render( - + - + ); const root = container.firstChild; @@ -1376,7 +1376,7 @@ describe('RefinementList', () => { ), }); const { container, getByRole } = render( - + { }} searchable /> - + ); await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/SearchBox.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/SearchBox.test.tsx similarity index 91% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/SearchBox.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/SearchBox.test.tsx index 9c845cd302..0bc0569d65 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/SearchBox.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/SearchBox.test.tsx @@ -2,7 +2,7 @@ * @jest-environment jsdom */ -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { act, render, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -14,9 +14,9 @@ import type { InstantSearch, UiState } from 'instantsearch.js'; describe('SearchBox', () => { test('renders with default props', async () => { const { container } = render( - + - + ); await waitFor(() => { @@ -126,9 +126,9 @@ describe('SearchBox', () => { test('forwards placeholder prop', async () => { const { container } = render( - + - + ); await waitFor(() => { @@ -141,9 +141,9 @@ describe('SearchBox', () => { test('forwards `autoFocus` prop', async () => { const { container } = render( - + - + ); await waitFor(() => { @@ -155,7 +155,7 @@ describe('SearchBox', () => { let lastUiState: UiState = {}; const { container } = render( - { }} > - + ); const input = container.querySelector( @@ -194,7 +194,7 @@ describe('SearchBox', () => { let lastUiState: UiState = {}; const { container } = render( - { }} > - + ); const resetButton = container.querySelector( @@ -232,13 +232,13 @@ describe('SearchBox', () => { let localSetUiState: InstantSearch['setUiState']; const { container } = render( - { localSetUiState = setUiState; }} > - + ); const input = container.querySelector( '.ais-SearchBox-input' @@ -262,13 +262,13 @@ describe('SearchBox', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; @@ -280,13 +280,13 @@ describe('SearchBox', () => { let lastUiState: UiState = {}; const { container } = render( - { lastUiState = uiState; }} > - + ); const input = container.querySelector( @@ -305,13 +305,13 @@ describe('SearchBox', () => { let lastUiState: UiState = {}; const { container } = render( - { lastUiState = uiState; }} > - + ); const input = container.querySelector( @@ -329,13 +329,13 @@ describe('SearchBox', () => { let lastUiState: UiState = {}; const { container } = render( - { lastUiState = uiState; }} > - + ); const input = container.querySelector( @@ -354,14 +354,14 @@ describe('SearchBox', () => { test('renders with translations', () => { const { getByRole } = render( - + - + ); expect(getByRole('button', { name: 'Submit' })).toBeInTheDocument(); diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Snippet.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Snippet.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/Snippet.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/Snippet.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/SortBy.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/SortBy.test.tsx similarity index 92% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/SortBy.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/SortBy.test.tsx index 98c1db36ce..db72cc07c1 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/SortBy.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/SortBy.test.tsx @@ -3,7 +3,7 @@ */ import { createSearchClient } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -13,7 +13,7 @@ import { SortBy } from '../SortBy'; describe('SortBy', () => { test('renders with props', () => { const { container } = render( - + { { label: 'Price (desc)', value: 'instant_search_price_desc' }, ]} /> - + ); expect(document.querySelector('.ais-SortBy-select')).toHaveValue( @@ -62,7 +62,7 @@ describe('SortBy', () => { test('transform the passed items', () => { const { container } = render( - + items.map((item) => ({ @@ -76,7 +76,7 @@ describe('SortBy', () => { { label: 'Price (desc)', value: 'instant_search_price_desc' }, ]} /> - + ); expect(container).toMatchInlineSnapshot(` @@ -115,7 +115,7 @@ describe('SortBy', () => { const client = createSearchClient({}); const { getByRole } = render( - @@ -126,7 +126,7 @@ describe('SortBy', () => { { label: 'Price (desc)', value: 'instant_search_price_desc' }, ]} /> - + ); await waitFor(() => { @@ -156,7 +156,7 @@ describe('SortBy', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + { { label: 'Price (desc)', value: 'instant_search_price_desc' }, ]} /> - + ); const root = container.firstChild; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Stats.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Stats.test.tsx similarity index 86% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/Stats.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/Stats.test.tsx index ee0f446729..a3fa98c792 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/Stats.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/Stats.test.tsx @@ -7,7 +7,7 @@ import { createMultiSearchResponse, createSingleSearchResponse, } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import React from 'react'; @@ -36,9 +36,9 @@ function createMockedSearchClient({ nbSorted }: { nbSorted?: number } = {}) { describe('Stats', () => { test('renders with default props', async () => { const { container } = render( - + - + ); await waitFor(() => { @@ -59,9 +59,9 @@ describe('Stats', () => { test('renders with proper message nbHits and processingTimeMS', async () => { const client = createMockedSearchClient(); const { container } = render( - + - + ); await waitFor(() => { @@ -86,9 +86,9 @@ describe('Stats', () => { test('renders with proper message when hits are sorted', async () => { const client = createMockedSearchClient({ nbSorted: 500 }); const { container } = render( - + - + ); await waitFor(() => { @@ -113,9 +113,9 @@ describe('Stats', () => { test('renders with proper message when nbSorted equals nbHits', async () => { const client = createMockedSearchClient({ nbSorted: 1000 }); const { container } = render( - + - + ); await waitFor(() => { @@ -140,13 +140,13 @@ describe('Stats', () => { test('renders with translations', async () => { const client = createMockedSearchClient(); const { container } = render( - + 'Nice stats', }} /> - + ); await waitFor(() => { @@ -170,13 +170,13 @@ describe('Stats', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/ToggleRefinement.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/ToggleRefinement.test.tsx similarity index 89% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/ToggleRefinement.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/ToggleRefinement.test.tsx index e58660ea64..73aef78a29 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/ToggleRefinement.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/ToggleRefinement.test.tsx @@ -3,7 +3,7 @@ */ import { createSearchClient } from '@instantsearch/mocks'; -import { InstantSearchHooksTestWrapper } from '@instantsearch/testutils'; +import { InstantSearchTestWrapper } from '@instantsearch/testutils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -13,9 +13,9 @@ import { ToggleRefinement } from '../ToggleRefinement'; describe('ToggleRefinement', () => { test('renders with props', () => { const { container } = render( - + - + ); expect(container).toMatchInlineSnapshot(` @@ -43,9 +43,9 @@ describe('ToggleRefinement', () => { test('customizes the label', () => { const { container } = render( - + - + ); expect( @@ -55,7 +55,7 @@ describe('ToggleRefinement', () => { test('renders checked when the attribute is refined', () => { const { container } = render( - { }} > - + ); expect( @@ -79,9 +79,9 @@ describe('ToggleRefinement', () => { const client = createSearchClient({}); const { container } = render( - + - + ); await waitFor(() => { @@ -134,9 +134,9 @@ describe('ToggleRefinement', () => { const client = createSearchClient({}); const { container } = render( - + - + ); await waitFor(() => { @@ -184,14 +184,14 @@ describe('ToggleRefinement', () => { test('forwards custom class names and `div` props to the root element', () => { const { container } = render( - + - + ); const root = container.firstChild; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/__utils__/all-widgets.tsx b/packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx similarity index 99% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/__utils__/all-widgets.tsx rename to packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx index 5b1192088a..84937dc069 100644 --- a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/__utils__/all-widgets.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx @@ -4,7 +4,7 @@ import { renderToString } from 'react-dom/server'; import { InstantSearch, InstantSearchServerContext, -} from 'react-instantsearch-hooks'; +} from 'react-instantsearch-core'; import * as widgets from '../..'; diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/all-components.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/all-components.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/all-components.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/all-components.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/widgets/__tests__/all-widgets.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/all-widgets.test.tsx similarity index 100% rename from packages/react-instantsearch-hooks-web/src/widgets/__tests__/all-widgets.test.tsx rename to packages/react-instantsearch/src/widgets/__tests__/all-widgets.test.tsx diff --git a/packages/react-instantsearch-hooks-web/src/widgets/index.ts b/packages/react-instantsearch/src/widgets/index.ts similarity index 100% rename from packages/react-instantsearch-hooks-web/src/widgets/index.ts rename to packages/react-instantsearch/src/widgets/index.ts diff --git a/packages/react-instantsearch/test/module/is-cjs-module.cjs b/packages/react-instantsearch/test/module/is-cjs-module.cjs new file mode 100644 index 0000000000..b9de53a706 --- /dev/null +++ b/packages/react-instantsearch/test/module/is-cjs-module.cjs @@ -0,0 +1,9 @@ +/* eslint-disable no-console */ + +const assert = require('assert'); + +const ReactInstantSearch = require('react-instantsearch'); + +assert.ok(ReactInstantSearch); + +console.log('react-instantsearch is valid CJS'); diff --git a/packages/react-instantsearch/test/module/is-es-module.mjs b/packages/react-instantsearch/test/module/is-es-module.mjs new file mode 100644 index 0000000000..17e7902299 --- /dev/null +++ b/packages/react-instantsearch/test/module/is-es-module.mjs @@ -0,0 +1,8 @@ +/* eslint-disable no-console */ +import assert from 'assert'; + +import * as ReactInstantSearch from 'react-instantsearch'; + +assert.ok(ReactInstantSearch); + +console.log('react-instantsearch is valid ESM'); diff --git a/packages/react-instantsearch-hooks-web/tsconfig.declaration.json b/packages/react-instantsearch/tsconfig.declaration.json similarity index 100% rename from packages/react-instantsearch-hooks-web/tsconfig.declaration.json rename to packages/react-instantsearch/tsconfig.declaration.json diff --git a/scripts/website/README.md b/scripts/website/README.md deleted file mode 100644 index 0d06faad92..0000000000 --- a/scripts/website/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# React InstantSearch website builder - -The examples of React InstantSearch are built using this webpack configuration, unlike the other examples for netlify which are built using their own (parcel) configuration. diff --git a/scripts/website/package.json b/scripts/website/package.json deleted file mode 100644 index 4e9dfffaa3..0000000000 --- a/scripts/website/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "example-react-instantsearch-builder", - "private": true, - "version": "1.0.1", - "license": "MIT", - "scripts": { - "website:examples": "webpack --config webpack.config.js" - }, - "dependencies": { - "babel-loader": "^8.2.2", - "copy-webpack-plugin": "^5.0.4", - "css-loader": "^3.2.0", - "file-loader": "^4.2.0", - "glob": "^7.1.4", - "html-webpack-plugin": "^3.2.0", - "null-loader": "^3.0.0", - "style-loader": "^1.0.0", - "webpack": "^4.41.5", - "webpack-cli": "^3.3.7" - } -} diff --git a/scripts/website/webpack.config.js b/scripts/website/webpack.config.js deleted file mode 100644 index 654e82e125..0000000000 --- a/scripts/website/webpack.config.js +++ /dev/null @@ -1,102 +0,0 @@ -const path = require('path'); -const glob = require('glob'); -const HTMLWebpackPlugin = require('html-webpack-plugin'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); - -const names = ['default-theme', 'e-commerce', 'media', 'tourism']; - -const outputPath = path.join(__dirname, '..', '..', 'website'); -const examples = glob.sync( - path.join('examples', 'react', `@(${names.join('|')})`), - { - cwd: path.join(__dirname, '..', '..'), - } -); - -module.exports = { - mode: 'production', - entry: Object.fromEntries( - examples.map((example) => [ - example, - path.join(__dirname, '..', '..', example, 'index.js'), - ]) - ), - output: { - filename: '[name]/index.[chunkhash].js', - publicPath: '/', - path: outputPath, - }, - resolve: { - alias: { - 'react-instantsearch-dom': path.resolve( - __dirname, - '../../packages/react-instantsearch-dom' - ), - 'react-instantsearch-dom-maps': path.resolve( - __dirname, - '../../packages/react-instantsearch-dom-maps' - ), - }, - }, - module: { - rules: [ - { - test: /\.(js|ts|tsx)$/, - exclude: /node_modules/, - use: [ - { - loader: 'babel-loader', - options: { - rootMode: 'upward', - envName: 'webpack', - }, - }, - ], - }, - { - test: /\.css$/, - use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], - }, - { - test: /\.(png|svg|jpg|gif)$/, - use: [ - { - loader: 'file-loader', - options: { - publicPath: (filename, absolutePath, context) => - `/${path.relative(context, absolutePath)}`, - context: path.join(__dirname, '..', '..'), - outputPath(_url, resourcePath, context) { - return path.relative(context, resourcePath); - }, - name: '[name].[ext]', - }, - }, - ], - }, - ], - }, - performance: { - hints: false, - }, - plugins: [ - ...examples.map( - (example) => - new HTMLWebpackPlugin({ - template: path.join(__dirname, '..', '..', example, 'index.html'), - filename: path.join(outputPath, example, 'index.html'), - chunks: [example], - }) - ), - new CopyWebpackPlugin([ - ...examples.map((example) => ({ - from: path.join(__dirname, '..', '..', example, 'assets'), - to: path.join(outputPath, example, 'assets'), - })), - { - from: path.join(__dirname, '..', '..', 'assets'), - to: 'assets/', - }, - ]), - ], -}; diff --git a/tests/common/README.md b/tests/common/README.md index eed97f5d84..dd1f04b3ab 100644 --- a/tests/common/README.md +++ b/tests/common/README.md @@ -18,8 +18,8 @@ Tests that only apply to specific flavors belong to their relevant packages, as - [Common scenarios](../../tests/common/widgets/breadcrumb) - Flavor-specific tests: - - [InstantSearch.js](../../packages/instantsearch.js/src/widgets/breadcrumb) - - [React InstantSearch Hooks](../../packages/react-instantsearch-hooks-web/src/widgets/__tests__) + - [InstantSearch.js](../../packages/instantsearch.js/src/widgets/breadcrumb/__tests__) + - [React InstantSearch](../../packages/react-instantsearch/src/widgets/__tests__) - [Vue InstantSearch](../../packages/vue-instantsearch/src/components/__tests__) > **Note** Flavor-specific tests should be the exception. They should either cover inconsistencies between flavors that should go away in a next major, or assert flavor-specific behavior or APIs. diff --git a/tests/e2e/flavors.js b/tests/e2e/flavors.js index 6c6875111c..4c33cc5800 100644 --- a/tests/e2e/flavors.js +++ b/tests/e2e/flavors.js @@ -1,3 +1,3 @@ module.exports = { - flavors: ['js', 'js-umd', 'react', 'react-hooks', 'vue'], + flavors: ['js', 'js-umd', 'react', 'vue'], }; diff --git a/tests/utils/InstantSearchHooksTestWrapper.tsx b/tests/utils/InstantSearchTestWrapper.tsx similarity index 73% rename from tests/utils/InstantSearchHooksTestWrapper.tsx rename to tests/utils/InstantSearchTestWrapper.tsx index b40c26cd9f..dc2112dc1b 100644 --- a/tests/utils/InstantSearchHooksTestWrapper.tsx +++ b/tests/utils/InstantSearchTestWrapper.tsx @@ -1,19 +1,19 @@ import { createAlgoliaSearchClient } from '@instantsearch/mocks'; import React from 'react'; -import { InstantSearch } from 'react-instantsearch-hooks'; +import { InstantSearch } from 'react-instantsearch-core'; -import type { InstantSearchProps } from 'react-instantsearch-hooks'; +import type { InstantSearchProps } from 'react-instantsearch-core'; const searchClient = createAlgoliaSearchClient({}); -type InstantSearchHooksTestWrapperProps = { +type InstantSearchTestWrapperProps = { children: React.ReactNode; } & Partial; -export function InstantSearchHooksTestWrapper({ +export function InstantSearchTestWrapper({ children, ...props -}: InstantSearchHooksTestWrapperProps) { +}: InstantSearchTestWrapperProps) { return ( {children} diff --git a/tests/utils/createInstantSearchSpy.tsx b/tests/utils/createInstantSearchSpy.tsx index f6ebacb825..5a98f5e44a 100644 --- a/tests/utils/createInstantSearchSpy.tsx +++ b/tests/utils/createInstantSearchSpy.tsx @@ -1,10 +1,10 @@ import React, { createRef } from 'react'; -import { InstantSearch } from '../../packages/react-instantsearch-hooks/src/components/InstantSearch'; -import { IndexContext } from '../../packages/react-instantsearch-hooks/src/lib/IndexContext'; -import { InstantSearchContext } from '../../packages/react-instantsearch-hooks/src/lib/InstantSearchContext'; +import { InstantSearch } from '../../packages/react-instantsearch-core/src/components/InstantSearch'; +import { IndexContext } from '../../packages/react-instantsearch-core/src/lib/IndexContext'; +import { InstantSearchContext } from '../../packages/react-instantsearch-core/src/lib/InstantSearchContext'; -import type { InstantSearchProps } from '../../packages/react-instantsearch-hooks/src'; +import type { InstantSearchProps } from '../../packages/react-instantsearch-core/src'; import type { InstantSearch as InstantSearchType } from 'instantsearch.js'; import type { IndexWidget } from 'instantsearch.js/es/widgets/index/index'; diff --git a/tests/utils/index.ts b/tests/utils/index.ts index 31832ef2c1..7b47088eba 100644 --- a/tests/utils/index.ts +++ b/tests/utils/index.ts @@ -1,7 +1,7 @@ export * from './castToJestMock'; export * from './createInstantSearchSpy'; export * from './enzyme'; -export * from './InstantSearchHooksTestWrapper'; +export * from './InstantSearchTestWrapper'; export * from './normalizeSnapshot'; export * from './runAllMicroTasks'; export * from './wait'; diff --git a/tests/utils/setupTests.ts b/tests/utils/setupTests.ts index 2eb763bbf0..55c4d32916 100644 --- a/tests/utils/setupTests.ts +++ b/tests/utils/setupTests.ts @@ -4,7 +4,7 @@ import { createSerializer } from 'enzyme-to-json'; import htmlSerializer from 'jest-serializer-html/createSerializer'; import '@testing-library/jest-dom/extend-expect'; -import { warnCache } from '../../packages/react-instantsearch-hooks/src/lib/warn'; +import { warnCache } from '../../packages/react-instantsearch-core/src/lib/warn'; import { Vue2, isVue2, diff --git a/tests/versions/index.js b/tests/versions/index.js index f801199d55..b3e3cb16a1 100755 --- a/tests/versions/index.js +++ b/tests/versions/index.js @@ -16,15 +16,7 @@ let hasError = false; react: [ 'react-instantsearch', 'react-instantsearch-core', - 'react-instantsearch-dom', - 'react-instantsearch-dom-maps', - 'react-instantsearch-native', - ], - 'react-hooks': [ - 'react-instantsearch-hooks', - 'react-instantsearch-hooks-web', - 'react-instantsearch-hooks-server', - 'react-instantsearch-hooks-router-nextjs', + 'react-instantsearch-router-nextjs', ], }; @@ -52,7 +44,7 @@ let hasError = false; { const versions = [ { - name: 'react-instantsearch-hooks', + name: 'react-instantsearch-core', versionFile: 'src/version.ts', format: 'esm', }, @@ -61,11 +53,6 @@ let hasError = false; versionFile: 'src/lib/version.ts', format: 'esm', }, - { - name: 'react-instantsearch-core', - versionFile: 'src/core/version.js', - format: 'esm', - }, { name: 'algoliasearch-helper', versionFile: 'src/version.js', diff --git a/tsconfig.json b/tsconfig.json index 7b985a61e7..5f85860b35 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,10 +19,10 @@ "**/es", "**/dist", "tests/e2e", - "examples/react-hooks/next/next-env.d.ts", + "examples/react/next/next-env.d.ts", // @TODO: we need to re-enable type checking in the React Native example // when the RN types work with React 18. - "examples/react-hooks/react-native", + "examples/react/react-native", "packages/create-instantsearch-app/src/templates/**/*" ] } diff --git a/website/_redirects b/website/_redirects index 6a111b7903..70137dc98d 100644 --- a/website/_redirects +++ b/website/_redirects @@ -2,8 +2,8 @@ /examples https://www.algolia.com/doc/guides/building-search-ui/resources/demos/js/ /examples/js/ https://www.algolia.com/doc/guides/building-search-ui/resources/demos/ /examples/react/ https://www.algolia.com/doc/guides/building-search-ui/resources/demos/react -/examples/hooks/ https://www.algolia.com/doc/guides/building-search-ui/resources/demos/react-hooks -/examples/react-hooks/ https://www.algolia.com/doc/guides/building-search-ui/resources/demos/react-hooks +/examples/hooks/ https://www.algolia.com/doc/guides/building-search-ui/resources/demos/react +/examples/react-hooks/ https://www.algolia.com/doc/guides/building-search-ui/resources/demos/react /examples/vue/ https://www.algolia.com/doc/guides/building-search-ui/resources/demos/vue /examples/:flavor/:name/search/* /examples/:flavor/:name/index.html 200 @@ -13,4 +13,5 @@ /examples/media/* /examples/js/media/:splat 301 /examples/tourism/* /examples/js/tourism/:splat 301 +/stories/react/* /stories/js/:splat 301 /stories /stories/js 301! diff --git a/yarn.lock b/yarn.lock index 6d92b9b5ef..35e0e5895d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -170,39 +170,6 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@ant-design/colors@^3.1.0": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-3.2.2.tgz#5ad43d619e911f3488ebac303d606e66a8423903" - integrity sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ== - dependencies: - tinycolor2 "^1.4.1" - -"@ant-design/create-react-context@^0.2.4": - version "0.2.5" - resolved "https://registry.yarnpkg.com/@ant-design/create-react-context/-/create-react-context-0.2.5.tgz#f5f5a9163b4772097712837397ad30e22e79f858" - integrity sha512-1rMAa4qgP2lfl/QBH9i78+Gjxtj9FTMpMyDGZsEBW5Kih72EuUo9958mV8PgpRkh4uwPSQ7vVZWXeyNZXVAFDg== - dependencies: - gud "^1.0.0" - warning "^4.0.3" - -"@ant-design/css-animation@^1.7.2": - version "1.7.3" - resolved "https://registry.yarnpkg.com/@ant-design/css-animation/-/css-animation-1.7.3.tgz#60a1c970014e86b28f940510d69e503e428f1136" - integrity sha512-LrX0OGZtW+W6iLnTAqnTaoIsRelYeuLZWsrmBJFUXDALQphPsN8cE5DCsmoSlL0QYb94BQxINiuS70Ar/8BNgA== - -"@ant-design/icons-react@~2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons-react/-/icons-react-2.0.1.tgz#17a2513571ab317aca2927e58cea25dd31e536fb" - integrity sha512-r1QfoltMuruJZqdiKcbPim3d8LNsVPB733U0gZEUSxBLuqilwsW28K2rCTWSMTjmFX7Mfpf+v/wdiFe/XCqThw== - dependencies: - "@ant-design/colors" "^3.1.0" - babel-runtime "^6.26.0" - -"@ant-design/icons@~2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-2.1.1.tgz#7b9c08dffd4f5d41db667d9dbe5e0107d0bd9a4a" - integrity sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w== - "@astrojs/compiler@^0.29.3": version "0.29.19" resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.29.19.tgz#dd8020a08c18639ce9de5b7203f4a4212d391279" @@ -335,7 +302,7 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.44", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== @@ -402,7 +369,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.16.0", "@babel/core@^7.18.2", "@babel/core@^7.4.5", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.8.0": +"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.18.2", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.7.tgz#37072f951bd4d28315445f66e0ec9f6ae0c8c35f" integrity sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw== @@ -444,7 +411,7 @@ json5 "^2.2.2" semver "^6.3.1" -"@babel/generator@^7.0.0", "@babel/generator@^7.12.11", "@babel/generator@^7.15.4", "@babel/generator@^7.18.2", "@babel/generator@^7.20.7", "@babel/generator@^7.4.0", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2", "@babel/generator@^7.9.0": +"@babel/generator@^7.15.4", "@babel/generator@^7.18.2", "@babel/generator@^7.20.7", "@babel/generator@^7.4.0", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2", "@babel/generator@^7.9.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== @@ -507,7 +474,7 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7", "@babel/helper-create-class-features-plugin@^7.4.4": +"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.7.tgz#d0e1f8d7e4ed5dac0389364d9c0c191d948ade6f" integrity sha512-LtoWbDXOaidEf50hmdDqn9g8VEzsorMexoWMQdQODbvmqYmaF23pBP5VNPAGIFHsFQCIeKokDiz3CH5Y2jlY6w== @@ -882,13 +849,6 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-proposal-optional-chaining" "^7.20.7" -"@babel/plugin-external-helpers@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.2.0.tgz#7f4cb7dee651cd380d2034847d914288467a6be4" - integrity sha512-QFmtcCShFkyAsNtdCM3lJPmRe1iB+vPZymlB4LnDIKEBj2yKQLQKtoxXxJ8ePT5fwMl4QGg303p4mB0UsSI2/g== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions@^7.15.4", "@babel/plugin-proposal-async-generator-functions@^7.2.0", "@babel/plugin-proposal-async-generator-functions@^7.20.1": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" @@ -907,15 +867,7 @@ "@babel/helper-create-class-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-proposal-class-properties@7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.4.tgz#93a6486eed86d53452ab9bab35e368e9461198ce" - integrity sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0", "@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.14.5", "@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.18.6", "@babel/plugin-proposal-class-properties@^7.4.4", "@babel/plugin-proposal-class-properties@^7.7.0": +"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0", "@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.14.5", "@babel/plugin-proposal-class-properties@^7.18.6", "@babel/plugin-proposal-class-properties@^7.7.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== @@ -932,7 +884,7 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-decorators@^7.1.0", "@babel/plugin-proposal-decorators@^7.12.9", "@babel/plugin-proposal-decorators@^7.13.15", "@babel/plugin-proposal-decorators@^7.16.4", "@babel/plugin-proposal-decorators@^7.6.0": +"@babel/plugin-proposal-decorators@^7.1.0", "@babel/plugin-proposal-decorators@^7.12.9", "@babel/plugin-proposal-decorators@^7.13.15": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.7.tgz#05d37453c2ce818f3e47bbeda9468c8de947eecc" integrity sha512-JB45hbUweYpwAGjkiM7uCyXMENH2lG+9r3G2E+ttc2PRXAoEkpfd/KW5jDg4j8RS6tLtTG1jZi9LbHZVSfs1/A== @@ -983,7 +935,7 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6", "@babel/plugin-proposal-nullish-coalescing-operator@^7.7.4": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== @@ -991,7 +943,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-numeric-separator@^7.14.5", "@babel/plugin-proposal-numeric-separator@^7.16.0", "@babel/plugin-proposal-numeric-separator@^7.18.6": +"@babel/plugin-proposal-numeric-separator@^7.14.5", "@babel/plugin-proposal-numeric-separator@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== @@ -1018,7 +970,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0", "@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.14.5", "@babel/plugin-proposal-optional-chaining@^7.16.0", "@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.7.5": +"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0", "@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.14.5", "@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== @@ -1027,7 +979,7 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-private-methods@^7.13.0", "@babel/plugin-proposal-private-methods@^7.14.5", "@babel/plugin-proposal-private-methods@^7.16.0", "@babel/plugin-proposal-private-methods@^7.18.6": +"@babel/plugin-proposal-private-methods@^7.13.0", "@babel/plugin-proposal-private-methods@^7.14.5", "@babel/plugin-proposal-private-methods@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== @@ -1235,7 +1187,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-async-to-generator@^7.0.0", "@babel/plugin-transform-async-to-generator@^7.14.5", "@babel/plugin-transform-async-to-generator@^7.18.6", "@babel/plugin-transform-async-to-generator@^7.3.4", "@babel/plugin-transform-async-to-generator@^7.4.4": +"@babel/plugin-transform-async-to-generator@^7.14.5", "@babel/plugin-transform-async-to-generator@^7.18.6", "@babel/plugin-transform-async-to-generator@^7.3.4", "@babel/plugin-transform-async-to-generator@^7.4.4": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== @@ -1311,7 +1263,7 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.14.5", "@babel/plugin-transform-flow-strip-types@^7.16.0": +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.14.5": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" integrity sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg== @@ -1365,7 +1317,7 @@ "@babel/helper-module-transforms" "^7.20.7" "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0", "@babel/plugin-transform-modules-commonjs@^7.15.4", "@babel/plugin-transform-modules-commonjs@^7.19.6", "@babel/plugin-transform-modules-commonjs@^7.2.0", "@babel/plugin-transform-modules-commonjs@^7.4.4", "@babel/plugin-transform-modules-commonjs@^7.5.0": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0", "@babel/plugin-transform-modules-commonjs@^7.15.4", "@babel/plugin-transform-modules-commonjs@^7.19.6", "@babel/plugin-transform-modules-commonjs@^7.2.0", "@babel/plugin-transform-modules-commonjs@^7.4.4": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.7.tgz#abb5f84695e74d46acf48244082f6cbf8bb23120" integrity sha512-76jqqFiFdCD+RJwEdtBHUG2/rEKQAmpejPbAKyQECEE3/y4U5CMPc9IXvipS990vgQhzq+ZRw6WJ+q4xJ/P24w== @@ -1452,21 +1404,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-react-constant-elements@^7.0.0", "@babel/plugin-transform-react-constant-elements@^7.2.0", "@babel/plugin-transform-react-constant-elements@^7.6.3": +"@babel/plugin-transform-react-constant-elements@^7.2.0": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz#3f02c784e0b711970d7d8ccc96c4359d64e27ac7" integrity sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g== dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-react-display-name@^7.0.0", "@babel/plugin-transform-react-display-name@^7.14.5", "@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.18.6": +"@babel/plugin-transform-react-display-name@^7.0.0", "@babel/plugin-transform-react-display-name@^7.14.5": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-react-jsx-development@^7.14.5", "@babel/plugin-transform-react-jsx-development@^7.18.6": +"@babel/plugin-transform-react-jsx-development@^7.14.5": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== @@ -1498,7 +1450,7 @@ "@babel/plugin-syntax-jsx" "^7.18.6" "@babel/types" "^7.20.7" -"@babel/plugin-transform-react-pure-annotations@^7.14.5", "@babel/plugin-transform-react-pure-annotations@^7.18.6": +"@babel/plugin-transform-react-pure-annotations@^7.14.5": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== @@ -1533,7 +1485,7 @@ babel-plugin-polyfill-regenerator "^0.2.2" semver "^6.3.0" -"@babel/plugin-transform-runtime@^7.0.0", "@babel/plugin-transform-runtime@^7.13.15", "@babel/plugin-transform-runtime@^7.16.4", "@babel/plugin-transform-runtime@^7.4.0": +"@babel/plugin-transform-runtime@^7.0.0", "@babel/plugin-transform-runtime@^7.13.15", "@babel/plugin-transform-runtime@^7.4.0": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== @@ -1581,7 +1533,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.0.0", "@babel/plugin-transform-typescript@^7.15.0", "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": +"@babel/plugin-transform-typescript@^7.15.0", "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz#673f49499cd810ae32a1ea5f3f8fab370987e055" integrity sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw== @@ -1805,7 +1757,7 @@ js-levenshtein "^1.1.3" semver "^5.3.0" -"@babel/preset-env@^7.1.6", "@babel/preset-env@^7.12.9", "@babel/preset-env@^7.14.1", "@babel/preset-env@^7.16.4", "@babel/preset-env@^7.4.4", "@babel/preset-env@^7.4.5", "@babel/preset-env@^7.6.3": +"@babel/preset-env@^7.1.6", "@babel/preset-env@^7.12.9", "@babel/preset-env@^7.14.1", "@babel/preset-env@^7.4.5": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== @@ -1938,18 +1890,6 @@ "@babel/plugin-transform-react-jsx-development" "^7.14.5" "@babel/plugin-transform-react-pure-annotations" "^7.14.5" -"@babel/preset-react@^7.0.0", "@babel/preset-react@^7.16.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" - integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-transform-react-display-name" "^7.18.6" - "@babel/plugin-transform-react-jsx" "^7.18.6" - "@babel/plugin-transform-react-jsx-development" "^7.18.6" - "@babel/plugin-transform-react-pure-annotations" "^7.18.6" - "@babel/preset-typescript@7.15.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.15.0.tgz#e8fca638a1a0f64f14e1119f7fe4500277840945" @@ -1959,7 +1899,7 @@ "@babel/helper-validator-option" "^7.14.5" "@babel/plugin-transform-typescript" "^7.15.0" -"@babel/preset-typescript@^7.1.0", "@babel/preset-typescript@^7.16.0", "@babel/preset-typescript@^7.3.3": +"@babel/preset-typescript@^7.1.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== @@ -1979,7 +1919,7 @@ "@babel/plugin-transform-modules-commonjs" "^7.22.5" "@babel/plugin-transform-typescript" "^7.22.5" -"@babel/register@^7.0.0", "@babel/register@^7.8.3": +"@babel/register@^7.0.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.16.7.tgz#e7b3a6015d1646677538672106bdb3a0b4a07657" integrity sha512-Ft+cuxorVxFj4RrPDs9TbJNE7ZbuJTyazUC6jLWRvBQT/qIDZPMe7MHgjlrA+11+XDLh+I0Pnx7sxPp4LRhzcA== @@ -2031,7 +1971,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.0.0-beta.46", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2", "@babel/runtime@^7.9.6": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.0.0-beta.46", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2", "@babel/runtime@^7.9.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== @@ -2056,7 +1996,7 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.13.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.18.2", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2", "@babel/traverse@^7.9.0": +"@babel/traverse@^7.0.0", "@babel/traverse@^7.11.5", "@babel/traverse@^7.13.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.18.2", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2", "@babel/traverse@^7.9.0": version "7.20.8" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.8.tgz#e3a23eb04af24f8bbe8a8ba3eef6155b77df0b08" integrity sha512-/RNkaYDeCy4MjyV70+QkSHhxbvj2JO/5Ft2Pa880qJOG8tWrqcT/wXUuCCv43yogfqPzHL77Xu101KQPf4clnQ== @@ -2088,7 +2028,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.18.4", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.9.0": +"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.18.4", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.9.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== @@ -2246,13 +2186,6 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36" integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg== -"@egjs/hammerjs@^2.0.17": - version "2.0.17" - resolved "https://registry.yarnpkg.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124" - integrity sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A== - dependencies: - "@types/hammerjs" "^2.0.36" - "@emmetio/abbreviation@^2.2.3": version "2.2.3" resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.3.tgz#2b3c0383c1a4652f677d5b56fb3f1616fe16ef10" @@ -2404,20 +2337,6 @@ resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.26.tgz#cf5a893b7c9cc779af5b54a482196325dcde1426" integrity sha512-t+xOhn4bYSAXkXamhDPUiI2Ol+QIwHRHLn/2QiCmNAGHolaVan/hMaVveSzvCYitpaJ16b4nthvcWFoJipxGlA== -"@expo/babel-preset-cli@0.2.7": - version "0.2.7" - resolved "https://registry.yarnpkg.com/@expo/babel-preset-cli/-/babel-preset-cli-0.2.7.tgz#251b60e530943d2b29a1065295001039410e9a69" - integrity sha512-NWL5S4ODDi+dRsQRSu0x8w/m4rr55ZH+5qpb2ixB8Nojlq4dEt7EV2bHB6IPxT1XbJau4/IZiG6oN6XdRDHm+w== - dependencies: - "@babel/core" "^7.4.5" - "@babel/plugin-proposal-class-properties" "^7.4.4" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.7.4" - "@babel/plugin-proposal-optional-chaining" "^7.7.5" - "@babel/plugin-transform-modules-commonjs" "^7.5.0" - "@babel/preset-env" "^7.4.4" - "@babel/preset-typescript" "^7.3.3" - typescript "3.7.3" - "@expo/bunyan@4.0.0", "@expo/bunyan@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@expo/bunyan/-/bunyan-4.0.0.tgz#be0c1de943c7987a9fbd309ea0b1acd605890c7b" @@ -2537,23 +2456,6 @@ slugify "^1.3.4" sucrase "^3.20.0" -"@expo/config@^2.5.3": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@expo/config/-/config-2.7.0.tgz#8f03b99b72ff2ee113d2b82e7e485206e3b95cb4" - integrity sha512-19FT0RLA7vRDTBF2ftwEJsmIcYrwP/PxsBZNs2W5NpgBX20DYZLp82ptjeHonP12rzYmynDhMiGdD4tXtZyBzw== - dependencies: - "@babel/register" "^7.8.3" - "@expo/babel-preset-cli" "0.2.7" - "@expo/json-file" "8.2.7" - "@types/invariant" "^2.2.30" - find-yarn-workspace-root "^1.2.1" - fs-extra "^7.0.1" - invariant "^2.2.4" - jest-message-util "^25.1.0" - resolve-from "^5.0.0" - slugify "^1.3.4" - xml2js "^0.4.23" - "@expo/config@^6.0.6": version "6.0.18" resolved "https://registry.yarnpkg.com/@expo/config/-/config-6.0.18.tgz#fb3dd2dcf319a7ced77765c43e6825de4e14dff8" @@ -2661,18 +2563,6 @@ json5 "^1.0.1" write-file-atomic "^2.3.0" -"@expo/json-file@8.2.7": - version "8.2.7" - resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.2.7.tgz#61611b0fd62c99cb4bf1e73052ea9da08c488e40" - integrity sha512-VhaVj6EI95uwK4SDKEFGmp4Zt70RB67s7+zgGK5t92e8twFz3WAvz7J5Qn/78vGL3xWrV7Mn6ZOILDCuMeXW/A== - dependencies: - "@babel/code-frame" "^7.0.0-beta.44" - fs-extra "^8.0.1" - json5 "^1.0.1" - lodash "^4.17.15" - util.promisify "^1.0.0" - write-file-atomic "^2.3.0" - "@expo/metro-config@0.3.10": version "0.3.10" resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.3.10.tgz#99e37f12b98ec425b7d739fc2aeb9cf6a61249aa" @@ -2799,13 +2689,6 @@ dependencies: cross-spawn "^7.0.3" -"@expo/vector-icons@^10.0.2": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-10.2.1.tgz#47fb2fa12d7ad601835babde6bd3ddea7f6fde89" - integrity sha512-clYQZFLeU2y23n03hXg18EEsZS5c73sJJnfderztfSAqkUXkfUtv07fwuprYwbHIvgFkw6L7R6xJOCVYtS85iQ== - dependencies: - lodash "^4.17.4" - "@expo/vector-icons@^12.0.4": version "12.0.5" resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-12.0.5.tgz#bc508ad05fb7e9a3e008704977cfec6c18aa7728" @@ -2853,17 +2736,6 @@ webpack "4.43.0" webpack-manifest-plugin "~2.2.0" -"@expo/websql@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@expo/websql/-/websql-1.0.1.tgz#fff0cf9c1baa1f70f9e1d658b7c39a420d9b10a9" - integrity sha1-//DPnBuqH3D54dZYt8OaQg2bEKk= - dependencies: - argsarray "^0.0.1" - immediate "^3.2.2" - noop-fn "^1.0.0" - pouchdb-collections "^1.0.1" - tiny-queue "^0.2.1" - "@expo/xcpretty@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@expo/xcpretty/-/xcpretty-4.1.0.tgz#63a1b54635f1e67250bfe74d58f25d2b52c5818a" @@ -2904,7 +2776,7 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== -"@hapi/joi@^15.0.1", "@hapi/joi@^15.0.3": +"@hapi/joi@^15.0.1": version "15.1.1" resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" integrity sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ== @@ -3023,12 +2895,12 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": +"@istanbuljs/schema@^0.1.2": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^24.7.1", "@jest/console@^24.9.0": +"@jest/console@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== @@ -3049,40 +2921,6 @@ jest-util "^27.4.2" slash "^3.0.0" -"@jest/core@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" - integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== - dependencies: - "@jest/console" "^24.7.1" - "@jest/reporters" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - exit "^0.1.2" - graceful-fs "^4.1.15" - jest-changed-files "^24.9.0" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-resolve-dependencies "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - jest-watcher "^24.9.0" - micromatch "^3.1.10" - p-each-series "^1.0.0" - realpath-native "^1.1.0" - rimraf "^2.5.4" - slash "^2.0.0" - strip-ansi "^5.0.0" - "@jest/core@^27.4.7": version "27.4.7" resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913" @@ -3124,16 +2962,6 @@ dependencies: "@jest/types" "^26.6.2" -"@jest/environment@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" - integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== - dependencies: - "@jest/fake-timers" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - "@jest/environment@^27.1.0", "@jest/environment@^27.4.6": version "27.4.6" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.6.tgz#1e92885d64f48c8454df35ed9779fbcf31c56d8b" @@ -3174,33 +3002,6 @@ "@jest/types" "^27.4.2" expect "^27.4.6" -"@jest/reporters@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" - integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - exit "^0.1.2" - glob "^7.1.2" - istanbul-lib-coverage "^2.0.2" - istanbul-lib-instrument "^3.0.1" - istanbul-lib-report "^2.0.4" - istanbul-lib-source-maps "^3.0.1" - istanbul-reports "^2.2.6" - jest-haste-map "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - node-notifier "^5.4.2" - slash "^2.0.0" - source-map "^0.6.0" - string-length "^2.0.0" - "@jest/reporters@^27.4.6": version "27.4.6" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.6.tgz#b53dec3a93baf9b00826abf95b932de919d6d8dd" @@ -3232,7 +3033,7 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" -"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": +"@jest/source-map@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== @@ -3269,16 +3070,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" - integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== - dependencies: - "@jest/test-result" "^24.9.0" - jest-haste-map "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - "@jest/test-sequencer@^27.4.6": version "27.4.6" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz#447339b8a3d7b5436f50934df30854e442a9d904" @@ -3341,16 +3132,6 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@jest/types@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" - integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^3.0.0" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -3426,14 +3207,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - "@jridgewell/trace-mapping@^0.3.17": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" @@ -3442,6 +3215,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@jsdoc/salty@^0.2.1": version "0.2.5" resolved "https://registry.yarnpkg.com/@jsdoc/salty/-/salty-0.2.5.tgz#1b2fa5bb8c66485b536d86eee877c263d322f692" @@ -4355,11 +4136,6 @@ resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.6.tgz#5f44823a78335355f00f1687cfc4f1dafa3eca08" integrity sha512-Te/OBDXFSodPU6jlXYPAXpmZr/AkG6DCATAxttQxqOWaq6eDFX25Db3dK0120GZrSZmv4QCe9KsZmJKDbWs4OA== -"@next/env@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.1.tgz#18266bd92de3b4aa4037b1927aa59e6f11879260" - integrity sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg== - "@next/eslint-plugin-next@12.0.7": version "12.0.7" resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.0.7.tgz#2c71bb66b8f8ff1080086342113406aa3156976f" @@ -4372,126 +4148,61 @@ resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.6.tgz#79a35349b98f2f8c038ab6261aa9cd0d121c03f9" integrity sha512-BxBr3QAAAXWgk/K7EedvzxJr2dE014mghBSA9iOEAv0bMgF+MRq4PoASjuHi15M2zfowpcRG8XQhMFtxftCleQ== -"@next/swc-android-arm-eabi@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz#b15ce8ad376102a3b8c0f3c017dde050a22bb1a3" - integrity sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ== - "@next/swc-android-arm64@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.6.tgz#ec08ea61794f8752c8ebcacbed0aafc5b9407456" integrity sha512-EboEk3ROYY7U6WA2RrMt/cXXMokUTXXfnxe2+CU+DOahvbrO8QSWhlBl9I9ZbFzJx28AGB9Yo3oQHCvph/4Lew== -"@next/swc-android-arm64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz#85d205f568a790a137cb3c3f720d961a2436ac9c" - integrity sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q== - "@next/swc-darwin-arm64@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.6.tgz#d1053805615fd0706e9b1667893a72271cd87119" integrity sha512-P0EXU12BMSdNj1F7vdkP/VrYDuCNwBExtRPDYawgSUakzi6qP0iKJpya2BuLvNzXx+XPU49GFuDC5X+SvY0mOw== -"@next/swc-darwin-arm64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz#b105457d6760a7916b27e46c97cb1a40547114ae" - integrity sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg== - "@next/swc-darwin-x64@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.6.tgz#2d1b926a22f4c5230d5b311f9c56cfdcc406afec" integrity sha512-9FptMnbgHJK3dRDzfTpexs9S2hGpzOQxSQbe8omz6Pcl7rnEp9x4uSEKY51ho85JCjL4d0tDLBcXEJZKKLzxNg== -"@next/swc-darwin-x64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz#6947b39082271378896b095b6696a7791c6e32b1" - integrity sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA== - -"@next/swc-freebsd-x64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz#2b6c36a4d84aae8b0ea0e0da9bafc696ae27085a" - integrity sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q== - "@next/swc-linux-arm-gnueabihf@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.6.tgz#c021918d2a94a17f823106a5e069335b8a19724f" integrity sha512-PvfEa1RR55dsik/IDkCKSFkk6ODNGJqPY3ysVUZqmnWMDSuqFtf7BPWHFa/53znpvVB5XaJ5Z1/6aR5CTIqxPw== -"@next/swc-linux-arm-gnueabihf@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz#6e421c44285cfedac1f4631d5de330dd60b86298" - integrity sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w== - "@next/swc-linux-arm64-gnu@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.6.tgz#ac55c07bfabde378dfa0ce2b8fc1c3b2897e81ae" integrity sha512-53QOvX1jBbC2ctnmWHyRhMajGq7QZfl974WYlwclXarVV418X7ed7o/EzGY+YVAEKzIVaAB9JFFWGXn8WWo0gQ== -"@next/swc-linux-arm64-gnu@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz#8863f08a81f422f910af126159d2cbb9552ef717" - integrity sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ== - "@next/swc-linux-arm64-musl@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.6.tgz#e429f826279894be9096be6bec13e75e3d6bd671" integrity sha512-CMWAkYqfGdQCS+uuMA1A2UhOfcUYeoqnTW7msLr2RyYAys15pD960hlDfq7QAi8BCAKk0sQ2rjsl0iqMyziohQ== -"@next/swc-linux-arm64-musl@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz#0038f07cf0b259d70ae0c80890d826dfc775d9f3" - integrity sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg== - "@next/swc-linux-x64-gnu@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.6.tgz#1f276c0784a5ca599bfa34b2fcc0b38f3a738e08" integrity sha512-AC7jE4Fxpn0s3ujngClIDTiEM/CQiB2N2vkcyWWn6734AmGT03Duq6RYtPMymFobDdAtZGFZd5nR95WjPzbZAQ== -"@next/swc-linux-x64-gnu@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz#c66468f5e8181ffb096c537f0dbfb589baa6a9c1" - integrity sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA== - "@next/swc-linux-x64-musl@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.6.tgz#1d9933dd6ba303dcfd8a2acd6ac7c27ed41e2eea" integrity sha512-c9Vjmi0EVk0Kou2qbrynskVarnFwfYIi+wKufR9Ad7/IKKuP6aEhOdZiIIdKsYWRtK2IWRF3h3YmdnEa2WLUag== -"@next/swc-linux-x64-musl@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz#c6269f3e96ac0395bc722ad97ce410ea5101d305" - integrity sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg== - "@next/swc-win32-arm64-msvc@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.6.tgz#2ef9837f12ca652b1783d72ecb86208906042f02" integrity sha512-3UTOL/5XZSKFelM7qN0it35o3Cegm6LsyuERR3/OoqEExyj3aCk7F025b54/707HTMAnjlvQK3DzLhPu/xxO4g== -"@next/swc-win32-arm64-msvc@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz#83c639ee969cee36ce247c3abd1d9df97b5ecade" - integrity sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw== - "@next/swc-win32-ia32-msvc@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.6.tgz#74003d0aa1c59dfa56cb15481a5c607cbc0027b9" integrity sha512-8ZWoj6nCq6fI1yCzKq6oK0jE6Mxlz4MrEsRyu0TwDztWQWe7rh4XXGLAa2YVPatYcHhMcUL+fQQbqd1MsgaSDA== -"@next/swc-win32-ia32-msvc@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz#52995748b92aa8ad053440301bc2c0d9fbcf27c2" - integrity sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA== - "@next/swc-win32-x64-msvc@12.1.6": version "12.1.6" resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.6.tgz#a350caf42975e7197b24b495b8d764eec7e6a36e" integrity sha512-4ZEwiRuZEicXhXqmhw3+de8Z4EpOLQj/gp+D9fFWo6ii6W1kBkNNvvEx4A90ugppu+74pT1lIJnOuz3A9oQeJA== -"@next/swc-win32-x64-msvc@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz#27d71a95247a9eaee03d47adee7e3bd594514136" - integrity sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA== - "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.2": version "2.1.8-no-fsevents.2" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz#e324c0a247a5567192dd7180647709d7e2faf94b" @@ -6477,11 +6188,6 @@ dependencies: tsm "^2.1.4" -"@ptomasroos/react-native-multi-slider@2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@ptomasroos/react-native-multi-slider/-/react-native-multi-slider-2.2.2.tgz#35a97fb8c355627c6a2ded010b360ac5728b44ad" - integrity sha512-HWyCnRD3Z3SbHK2FLWYmBBqd1B4iXipeKv1+AK0FoY/CElEDTEixHE8hN60TsqxalPrznn798LE2Q4tHuCiyaA== - "@reach/router@^1.2.1": version "1.3.4" resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.4.tgz#d2574b19370a70c80480ed91f3da840136d10f8c" @@ -6492,13 +6198,6 @@ prop-types "^15.6.1" react-lifecycles-compat "^3.0.4" -"@react-native-community/cli-debugger-ui@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-3.0.0.tgz#d01d08d1e5ddc1633d82c7d84d48fff07bd39416" - integrity sha512-m3X+iWLsK/H7/b7PpbNO33eQayR/+M26la4ZbYe1KRke5Umg4PIWsvg21O8Tw4uJcY8LA5hsP+rBi/syBkBf0g== - dependencies: - serve-static "^1.13.1" - "@react-native-community/cli-debugger-ui@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" @@ -6517,19 +6216,6 @@ hermes-profile-transformer "^0.0.6" ip "^1.1.5" -"@react-native-community/cli-platform-android@^3.0.0-alpha.1": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-3.1.4.tgz#61f964dc311623e60b0fb29c5f3732cc8a6f076f" - integrity sha512-ClSdY20F0gzWVLTqCv7vHjnUqOcuq10jd9GgHX6lGSc2GI+Ql3/aQg3tmG4uY3KXNNwAv3U8QCoYgg1WGfwiHA== - dependencies: - "@react-native-community/cli-tools" "^3.0.0" - chalk "^2.4.2" - execa "^1.0.0" - jetifier "^1.6.2" - logkitty "^0.6.0" - slash "^3.0.0" - xmldoc "^1.1.2" - "@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" @@ -6546,16 +6232,6 @@ slash "^3.0.0" xmldoc "^1.1.2" -"@react-native-community/cli-platform-ios@^3.0.0-alpha.1": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-3.2.0.tgz#c469444f5993c9e6737a4b16d78cf033e3702f00" - integrity sha512-pzEnx68H6+mHBq5jsMrr3UmAmkrLSMlC9BZ4yoUdfUXCQq6/R70zNYvH4hjUw8h2Al7Kgq53UzHUsM0ph8TSWQ== - dependencies: - "@react-native-community/cli-tools" "^3.0.0" - chalk "^2.4.2" - js-yaml "^3.13.1" - xcode "^2.0.0" - "@react-native-community/cli-platform-ios@^5.0.1-alpha.1": version "5.0.2" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" @@ -6584,16 +6260,6 @@ serve-static "^1.13.1" ws "^1.1.0" -"@react-native-community/cli-tools@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-3.0.0.tgz#fe48b80822ed7e49b8af051f9fe41e22a2a710b1" - integrity sha512-8IhQKZdf3E4CR8T7HhkPGgorot/cLkRDgneJFDSWk/wCYZAuUh4NEAdumQV7N0jLSMWX7xxiWUPi94lOBxVY9g== - dependencies: - chalk "^2.4.2" - lodash "^4.17.5" - mime "^2.4.1" - node-fetch "^2.5.0" - "@react-native-community/cli-tools@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" @@ -6606,11 +6272,6 @@ open "^6.2.0" shell-quote "1.6.1" -"@react-native-community/cli-types@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-3.0.0.tgz#488d46605cb05e88537e030f38da236eeda74652" - integrity sha512-ng6Tm537E/M42GjE4TRUxQyL8sRfClcL7bQWblOCoxPZzJ2J3bdALsjeG3vDnVCIfI/R0AeFalN9KjMt0+Z/Zg== - "@react-native-community/cli-types@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" @@ -6618,52 +6279,6 @@ dependencies: ora "^3.4.0" -"@react-native-community/cli@^3.0.0-alpha.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-3.2.1.tgz#2a466801eb6080a1f73358c5d740c53c24ed8c6f" - integrity sha512-bZ/bfZ+9r1gQSxp6t7+00DcpC6vmbVYSvzUCFM/yo5k8bhsDdcy8aocscIaXXVGG+v9Edri/Q7hH9ks7L18/Rg== - dependencies: - "@hapi/joi" "^15.0.3" - "@react-native-community/cli-debugger-ui" "^3.0.0" - "@react-native-community/cli-tools" "^3.0.0" - "@react-native-community/cli-types" "^3.0.0" - chalk "^2.4.2" - command-exists "^1.2.8" - commander "^2.19.0" - compression "^1.7.1" - connect "^3.6.5" - cosmiconfig "^5.1.0" - deepmerge "^3.2.0" - didyoumean "^1.2.1" - envinfo "^7.1.0" - errorhandler "^1.5.0" - execa "^1.0.0" - find-up "^4.1.0" - fs-extra "^7.0.1" - glob "^7.1.1" - graceful-fs "^4.1.3" - inquirer "^3.0.6" - lodash "^4.17.5" - metro "^0.56.0" - metro-config "^0.56.0" - metro-core "^0.56.0" - metro-react-native-babel-transformer "^0.56.0" - minimist "^1.2.0" - mkdirp "^0.5.1" - morgan "^1.9.0" - node-notifier "^5.2.1" - open "^6.2.0" - ora "^3.4.0" - plist "^3.0.0" - pretty-format "^25.1.0" - semver "^6.3.0" - serve-static "^1.13.1" - shell-quote "1.6.1" - strip-ansi "^5.2.0" - sudo-prompt "^9.0.0" - wcwidth "^1.0.1" - ws "^1.1.0" - "@react-native-community/cli@^5.0.1-alpha.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" @@ -6727,24 +6342,6 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== -"@react-navigation/core@^3.7.9": - version "3.7.9" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-3.7.9.tgz#3f7ba0fcb6c8d74a77a057382af198d84c7c4e3b" - integrity sha512-EknbzM8OI9A5alRxXtQRV5Awle68B+z1QAxNty5DxmlS3BNfmduWNGnim159ROyqxkuDffK9L/U/Tbd45mx+Jg== - dependencies: - hoist-non-react-statics "^3.3.2" - path-to-regexp "^1.8.0" - query-string "^6.13.6" - react-is "^16.13.0" - -"@react-navigation/native@^3.8.4": - version "3.8.4" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-3.8.4.tgz#4d77f86506364ecf18b33c7f8740afb6763d0b37" - integrity sha512-gXSVcL7bfFDyVkvyg1FiAqTCIgZub5K1X/TZqURBs2CPqDpfX1OsCtB9D33eTF14SpbfgHW866btqrrxoCACfg== - dependencies: - hoist-non-react-statics "^3.3.2" - react-native-safe-area-view "^0.14.9" - "@rushstack/eslint-patch@^1.0.8": version "1.1.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz#7f698254aadf921e48dda8c0a6b304026b8a9323" @@ -7197,33 +6794,6 @@ pretty-hrtime "^1.0.3" regenerator-runtime "^0.13.3" -"@storybook/react@5.3.9": - version "5.3.9" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-5.3.9.tgz#cf3a954b5d0430c5a3558ea5df305bb70e2af3cd" - integrity sha512-pOc6xw1c83fUnTRcCpIrtLLDKkZUhW3EkNvwYyMHrGXMRcgDETAlpoxBMHXpnbfV7qaAsE/UAVQQ1rRq5pgPBA== - dependencies: - "@babel/plugin-transform-react-constant-elements" "^7.6.3" - "@babel/preset-flow" "^7.0.0" - "@babel/preset-react" "^7.0.0" - "@storybook/addons" "5.3.9" - "@storybook/core" "5.3.9" - "@storybook/node-logger" "5.3.9" - "@svgr/webpack" "^4.0.3" - "@types/webpack-env" "^1.15.0" - babel-plugin-add-react-displayname "^0.0.5" - babel-plugin-named-asset-import "^0.3.1" - babel-plugin-react-docgen "^4.0.0" - core-js "^3.0.1" - global "^4.3.2" - lodash "^4.17.15" - mini-css-extract-plugin "^0.8.0" - prop-types "^15.7.2" - react-dev-utils "^9.0.0" - regenerator-runtime "^0.13.3" - semver "^6.0.0" - ts-dedent "^1.1.0" - webpack "^4.33.0" - "@storybook/router@5.3.9": version "5.3.9" resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.3.9.tgz#3c6e01f4dced9de8e8c5c314352fdc437f2441c2" @@ -7311,109 +6881,6 @@ ts-dedent "^1.1.0" webpack "^4.33.0" -"@svgr/babel-plugin-add-jsx-attribute@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" - integrity sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig== - -"@svgr/babel-plugin-remove-jsx-attribute@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz#297550b9a8c0c7337bea12bdfc8a80bb66f85abc" - integrity sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ== - -"@svgr/babel-plugin-remove-jsx-empty-expression@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz#c196302f3e68eab6a05e98af9ca8570bc13131c7" - integrity sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w== - -"@svgr/babel-plugin-replace-jsx-attribute-value@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz#310ec0775de808a6a2e4fd4268c245fd734c1165" - integrity sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w== - -"@svgr/babel-plugin-svg-dynamic-title@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz#2cdedd747e5b1b29ed4c241e46256aac8110dd93" - integrity sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w== - -"@svgr/babel-plugin-svg-em-dimensions@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz#9a94791c9a288108d20a9d2cc64cac820f141391" - integrity sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w== - -"@svgr/babel-plugin-transform-react-native-svg@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz#151487322843359a1ca86b21a3815fd21a88b717" - integrity sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw== - -"@svgr/babel-plugin-transform-svg-component@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz#5f1e2f886b2c85c67e76da42f0f6be1b1767b697" - integrity sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw== - -"@svgr/babel-preset@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-4.3.3.tgz#a75d8c2f202ac0e5774e6bfc165d028b39a1316c" - integrity sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A== - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^4.2.0" - "@svgr/babel-plugin-remove-jsx-attribute" "^4.2.0" - "@svgr/babel-plugin-remove-jsx-empty-expression" "^4.2.0" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^4.2.0" - "@svgr/babel-plugin-svg-dynamic-title" "^4.3.3" - "@svgr/babel-plugin-svg-em-dimensions" "^4.2.0" - "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0" - "@svgr/babel-plugin-transform-svg-component" "^4.2.0" - -"@svgr/core@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.3.3.tgz#b37b89d5b757dc66e8c74156d00c368338d24293" - integrity sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w== - dependencies: - "@svgr/plugin-jsx" "^4.3.3" - camelcase "^5.3.1" - cosmiconfig "^5.2.1" - -"@svgr/hast-util-to-babel-ast@^4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz#1d5a082f7b929ef8f1f578950238f630e14532b8" - integrity sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg== - dependencies: - "@babel/types" "^7.4.4" - -"@svgr/plugin-jsx@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz#e2ba913dbdfbe85252a34db101abc7ebd50992fa" - integrity sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w== - dependencies: - "@babel/core" "^7.4.5" - "@svgr/babel-preset" "^4.3.3" - "@svgr/hast-util-to-babel-ast" "^4.3.2" - svg-parser "^2.0.0" - -"@svgr/plugin-svgo@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz#daac0a3d872e3f55935c6588dd370336865e9e32" - integrity sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w== - dependencies: - cosmiconfig "^5.2.1" - merge-deep "^3.0.2" - svgo "^1.2.2" - -"@svgr/webpack@^4.0.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-4.3.3.tgz#13cc2423bf3dff2d494f16b17eb7eacb86895017" - integrity sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg== - dependencies: - "@babel/core" "^7.4.5" - "@babel/plugin-transform-react-constant-elements" "^7.0.0" - "@babel/preset-env" "^7.4.5" - "@babel/preset-react" "^7.0.0" - "@svgr/core" "^4.3.3" - "@svgr/plugin-jsx" "^4.3.3" - "@svgr/plugin-svgo" "^4.3.1" - loader-utils "^1.2.3" - "@swc/core-darwin-arm64@1.3.70": version "1.3.70" resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.70.tgz#056ac6899e22cb7f7be21388d4d938ca5123a72b" @@ -7480,13 +6947,6 @@ "@swc/core-win32-ia32-msvc" "1.3.70" "@swc/core-win32-x64-msvc" "1.3.70" -"@swc/helpers@0.4.11": - version "0.4.11" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de" - integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw== - dependencies: - tslib "^2.4.0" - "@swc/helpers@^0.4.12": version "0.4.14" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" @@ -7793,11 +7253,6 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== -"@types/fbemitter@^2.0.32": - version "2.0.32" - resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c" - integrity sha1-jtIE2g9U6cjq7DGx7skeJRMtCCw= - "@types/fs-extra@^9.0.13": version "9.0.13" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" @@ -7831,11 +7286,6 @@ dependencies: "@types/node" "*" -"@types/hammerjs@^2.0.36": - version "2.0.41" - resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa" - integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA== - "@types/hast@^2.0.0": version "2.3.4" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" @@ -7876,11 +7326,6 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== -"@types/invariant@^2.2.29", "@types/invariant@^2.2.30": - version "2.2.35" - resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.35.tgz#cd3ebf581a6557452735688d8daba6cf0bd5a3be" - integrity sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg== - "@types/is-function@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/is-function/-/is-function-1.0.0.tgz#1b0b819b1636c7baf0d6785d030d12edf70c3e83" @@ -7987,18 +7432,6 @@ resolved "https://registry.yarnpkg.com/@types/lockfile/-/lockfile-1.0.2.tgz#3f77e84171a2b7e3198bd5717c7547a54393baf8" integrity sha512-jD5VbvhfMhaYN4M3qPJuhMVUg3Dfc4tvPvLEAXn6GXbs/ajDFtCQahX37GIE65ipTI3I+hEvNaXS3MYAn9Ce3Q== -"@types/lodash.zipobject@^4.1.4": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/lodash.zipobject/-/lodash.zipobject-4.1.6.tgz#75e140f44ac7d7682a18d3aae8ee4594fad094d7" - integrity sha512-30khEHqHWaLgMZR35wtkg07OmHiNiDQyor0SK7oj8Sy05tg6jDjPmJybeZ64WKeFZUEgs1tdJwdT0xUl+2qUgQ== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.14.178" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" - integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw== - "@types/markdown-it@^12.2.3": version "12.2.3" resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51" @@ -8114,7 +7547,7 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== -"@types/qs@^6.5.1", "@types/qs@^6.5.3": +"@types/qs@^6.5.3": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== @@ -8159,13 +7592,6 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-slick@^0.23.4": - version "0.23.8" - resolved "https://registry.yarnpkg.com/@types/react-slick/-/react-slick-0.23.8.tgz#91654e657158da57f082c7b213e20756d3a37502" - integrity sha512-SfzSg++/3uyftVZaCgHpW+2fnJFsyJEQ/YdsuqfOWQ5lqUYV/gY/UwAnkw4qksCj5jalto/T5rKXJ8zeFldQeA== - dependencies: - "@types/react" "*" - "@types/react-syntax-highlighter@11.0.2": version "11.0.2" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz#a2e3ff657d7c47813f80ca930f3d959c31ec51e3" @@ -8410,11 +7836,6 @@ anymatch "^3.0.0" source-map "^0.6.0" -"@types/websql@^0.0.27": - version "0.0.27" - resolved "https://registry.yarnpkg.com/@types/websql/-/websql-0.0.27.tgz#621a666a7f02018e7cbb4abab956a25736c27d71" - integrity sha1-Yhpman8CAY58u0q6uVaiVzbCfXE= - "@types/write-file-atomic@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/write-file-atomic/-/write-file-atomic-4.0.0.tgz#ffcedcb1ae027e0a28ddfe218b72b3573797b5bc" @@ -8666,22 +8087,6 @@ "@typescript-eslint/types" "5.9.0" eslint-visitor-keys "^3.0.0" -"@unimodules/core@~5.1.0": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-5.1.2.tgz#44de60184c9a50cf57bc8ec1cd024810a72ecf3e" - integrity sha512-iCWEbzsNHqDfL6p8FyCGPnL2EW7vdgMJsNNSlWtM/gl8kePdqZMI7aOxTC4cdRS2xm0wzxuDBtpfJkzZsKINZg== - dependencies: - compare-versions "^3.4.0" - -"@unimodules/react-native-adapter@~5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-5.1.1.tgz#bb67a021c722772e716eaa3817084cd988642e4e" - integrity sha512-PlP6QQ2Z3ckORhS07tWcIweK+CkkxyzitJ1j1FD+N+G7G/CB99/vSfCEQ7BFVAPRO5vPrcS2QcwSDgvz06wKVA== - dependencies: - invariant "^2.2.4" - lodash "^4.5.0" - prop-types "^15.6.1" - "@vscode/emmet-helper@^2.8.4": version "2.8.4" resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.8.4.tgz#ab937e3ce79b0873c604d1ad50a9eeb7abae2937" @@ -9733,23 +9138,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== -add-dom-event-listener@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310" - integrity sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw== - dependencies: - object-assign "4.x" - add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= -add@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235" - integrity sha1-JI8Kn25aUo7yKV2+7DBTITCuIjU= - address@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -9885,7 +9278,7 @@ ajv@^8.0.1: semver "^5.1.0" tunnel-agent "^0.6.0" -algoliasearch@4, algoliasearch@4.14.3, "algoliasearch@>= 3.27.1 < 5": +algoliasearch@4, algoliasearch@4.14.3: version "4.14.3" resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.14.3.tgz#f02a77a4db17de2f676018938847494b692035e7" integrity sha512-GZTEuxzfWbP/vr7ZJfGzIl8fOsoxN916Z6FY2Egc9q2TmZ6hvq5KfAxY89pPW01oW/2HDEKA8d30f9iAH9eXYg== @@ -9966,13 +9359,6 @@ ansi-align@^3.0.0, ansi-align@^3.0.1: dependencies: string-width "^4.1.0" -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== - dependencies: - ansi-wrap "^0.1.0" - ansi-colors@^3.0.0: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -9983,13 +9369,6 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-cyan@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" - integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM= - dependencies: - ansi-wrap "0.1.0" - ansi-escape-sequences@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz#2483c8773f50dd9174dd9557e92b1718f1816097" @@ -9997,11 +9376,6 @@ ansi-escape-sequences@^4.0.0: dependencies: array-back "^3.0.1" -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - ansi-escapes@^3.0.0, ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -10023,13 +9397,6 @@ ansi-fragments@^0.2.1: slice-ansi "^2.0.0" strip-ansi "^5.0.0" -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" - ansi-html-community@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" @@ -10040,13 +9407,6 @@ ansi-html@0.0.7: resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" - integrity sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow== - dependencies: - ansi-wrap "0.1.0" - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -10057,7 +9417,7 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.0.0, ansi-regex@^4.1.0: +ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -10109,69 +9469,6 @@ ansi-to-html@^0.6.11: dependencies: entities "^1.1.1" -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= - -antd@3.23.2: - version "3.23.2" - resolved "https://registry.yarnpkg.com/antd/-/antd-3.23.2.tgz#6fcbe5b22a932913a33dce0b6b76b4a60c30a403" - integrity sha512-S62EvyxPV0IoFR650qtCjUy2E7nY3JkxTP1t71LER09DTTK4fJGw4wWhalNCjkIHR2hPHWGfu0MSHocov3F4dw== - dependencies: - "@ant-design/create-react-context" "^0.2.4" - "@ant-design/icons" "~2.1.1" - "@ant-design/icons-react" "~2.0.1" - "@types/react-slick" "^0.23.4" - array-tree-filter "^2.1.0" - babel-runtime "6.x" - classnames "~2.2.6" - copy-to-clipboard "^3.2.0" - css-animation "^1.5.0" - dom-closest "^0.2.0" - enquire.js "^2.1.6" - lodash "^4.17.13" - moment "^2.24.0" - omit.js "^1.0.2" - prop-types "^15.7.2" - raf "^3.4.1" - rc-animate "^2.8.3" - rc-calendar "~9.15.5" - rc-cascader "~0.17.4" - rc-checkbox "~2.1.6" - rc-collapse "~1.11.3" - rc-dialog "~7.5.2" - rc-drawer "~2.0.1" - rc-dropdown "~2.4.1" - rc-editor-mention "^1.1.13" - rc-form "^2.4.5" - rc-input-number "~4.5.0" - rc-mentions "~0.4.0" - rc-menu "~7.4.23" - rc-notification "~3.3.1" - rc-pagination "~1.20.5" - rc-progress "~2.5.0" - rc-rate "~2.5.0" - rc-select "~9.2.0" - rc-slider "~8.6.11" - rc-steps "~3.5.0" - rc-switch "~1.9.0" - rc-table "~6.7.0" - rc-tabs "~9.6.4" - rc-time-picker "~3.7.1" - rc-tooltip "~3.7.3" - rc-tree "~2.1.0" - rc-tree-select "~2.9.1" - rc-trigger "^2.6.2" - rc-upload "~2.7.0" - rc-util "^4.10.0" - react-lazy-load "^3.0.13" - react-lifecycles-compat "^3.0.4" - react-slick "~0.25.2" - resize-observer-polyfill "^1.5.1" - shallowequal "^1.1.0" - warning "~4.0.3" - any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -10309,11 +9606,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -argsarray@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb" - integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs= - aria-query@^4.2.2: version "4.2.2" resolved "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" @@ -10327,29 +9619,16 @@ aria-query@^5.0.0: resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== -arr-diff@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" - integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo= - dependencies: - arr-flatten "^1.0.1" - array-slice "^0.2.3" - arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= -arr-flatten@^1.0.1, arr-flatten@^1.1.0: +arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== -arr-union@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" - integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0= - arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" @@ -10455,16 +9734,6 @@ array-reduce@~0.0.0: resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= -array-slice@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" - integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU= - -array-tree-filter@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" - integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw== - array-union@^1.0.1, array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -10539,11 +9808,6 @@ arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -art@^0.10.0: - version "0.10.3" - resolved "https://registry.yarnpkg.com/art/-/art-0.10.3.tgz#b01d84a968ccce6208df55a733838c96caeeaea2" - integrity sha512-HXwbdofRTiJT6qZX/FnchtldzJjS3vkLJxQilc3Xj+ma2MXjY4UAyQ0ls1XZYVnDvVIBiFZbC6QsvtW86TD6tQ== - asap@^2.0.0, asap@~2.0.3, asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -10598,7 +9862,7 @@ ast-types@0.13.2: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== -ast-types@0.14.2, ast-types@^0.14.1, ast-types@^0.14.2: +ast-types@0.14.2, ast-types@^0.14.1: version "0.14.2" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== @@ -10708,11 +9972,6 @@ async-retry@^1.1.4: dependencies: retry "0.12.0" -async-validator@~1.11.3: - version "1.11.5" - resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-1.11.5.tgz#9d43cf49ef6bb76be5442388d19fb9a6e47597ea" - integrity sha512-XNtCsMAeAH1pdLMEg1z8/Bb3a8cdCbui9QbJATRFHHHW5kT6+NPI3zSVQUXgikTFITzsg+kYY5NTWhM2Orwt9w== - async@^1.3.0, async@^1.4.2, async@~1.5: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -10959,7 +10218,7 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@24.9.0, babel-jest@^24.7.1, babel-jest@^24.9.0: +babel-jest@24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== @@ -11106,15 +10365,6 @@ babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.7.0: cosmiconfig "^6.0.0" resolve "^1.12.0" -babel-plugin-macros@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" - integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== - dependencies: - "@babel/runtime" "^7.12.5" - cosmiconfig "^7.0.0" - resolve "^1.19.0" - babel-plugin-minify-builtins@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.5.0.tgz#31eb82ed1a0d0efdc31312f93b6e4741ce82c36b" @@ -11191,7 +10441,7 @@ babel-plugin-minify-type-constructors@^0.4.3: dependencies: babel-helper-is-void-0 "^0.4.3" -babel-plugin-module-resolver@3.2.0, babel-plugin-module-resolver@^3.2.0: +babel-plugin-module-resolver@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz#ddfa5e301e3b9aa12d852a9979f18b37881ff5a7" integrity sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA== @@ -11213,11 +10463,6 @@ babel-plugin-module-resolver@^4.1.0: reselect "^4.0.0" resolve "^1.13.1" -babel-plugin-named-asset-import@^0.3.1: - version "0.3.8" - resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz#6b7fa43c59229685368683c28bc9734f24524cc2" - integrity sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q== - babel-plugin-polyfill-corejs2@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" @@ -11273,20 +10518,6 @@ babel-plugin-polyfill-regenerator@^0.4.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.3" -babel-plugin-react-docgen@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz#7cc8e2f94e8dc057a06e953162f0810e4e72257b" - integrity sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ== - dependencies: - ast-types "^0.14.2" - lodash "^4.17.15" - react-docgen "^5.0.0" - -babel-plugin-react-native-web@^0.11.7: - version "0.11.7" - resolved "https://registry.yarnpkg.com/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.11.7.tgz#15b578c0731bd7d65d334f9c759d95e8e4a602e2" - integrity sha512-CxE7uhhqkzAFkwV2X7+Mc/UVPujQQDtja/EGxCXRJvdYRi72QTmaJYKbK1lV9qgTZuB+TDguU89coaA9Z1BNbg== - babel-plugin-react-native-web@~0.17.1: version "0.17.5" resolved "https://registry.yarnpkg.com/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.17.5.tgz#4bce51a20d21839f20506ef184bd5743a2c6d067" @@ -11336,7 +10567,7 @@ babel-plugin-transform-react-pure-class-to-function@1.0.1: dependencies: babel-helper-is-react-class "^1.0.0" -babel-plugin-transform-react-remove-prop-types@0.4.24, babel-plugin-transform-react-remove-prop-types@^0.4.24: +babel-plugin-transform-react-remove-prop-types@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== @@ -11387,15 +10618,6 @@ babel-plugin-transform-vue-jsx@^3.5.0: dependencies: esutils "^2.0.2" -babel-polyfill@6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" - integrity sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0= - dependencies: - babel-runtime "^6.22.0" - core-js "^2.4.0" - regenerator-runtime "^0.10.0" - babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -11414,28 +10636,6 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-expo@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-8.1.0.tgz#7dfdebe6dad80edd1901e0f733ca6a6cce2ad605" - integrity sha512-ZlGIo8OlO0b7S//QrqHGIIf2BY9HId5efxgBxyic5ZbKo6NHICThjSpEz4rRyQRGka7HixBq/Jyjsn4M2D/n/g== - dependencies: - "@babel/plugin-proposal-decorators" "^7.6.0" - "@babel/preset-env" "^7.6.3" - babel-plugin-module-resolver "^3.2.0" - babel-plugin-react-native-web "^0.11.7" - metro-react-native-babel-preset "^0.56.0" - -babel-preset-expo@~8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-8.1.1.tgz#7b6bd561ee598197c7f49572c85f3f45df94d57e" - integrity sha512-73VtU6+IsOPMHsXIXHJTycoAmE9hDCvWY2E14E3sVL/EhOJsH3YbWirRTiUiklfEMAEaOxBRc4C4GxqGmaFjxQ== - dependencies: - "@babel/plugin-proposal-decorators" "^7.6.0" - "@babel/preset-env" "^7.6.3" - babel-plugin-module-resolver "^3.2.0" - babel-plugin-react-native-web "^0.11.7" - metro-react-native-babel-preset "^0.56.0" - babel-preset-expo@~9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-9.0.2.tgz#018bafbe29c61491d55bf5c1b603534b54a13bf1" @@ -11448,7 +10648,7 @@ babel-preset-expo@~9.0.2: babel-plugin-react-native-web "~0.17.1" metro-react-native-babel-preset "~0.64.0" -babel-preset-fbjs@^3.1.2, babel-preset-fbjs@^3.2.0, babel-preset-fbjs@^3.3.0: +babel-preset-fbjs@^3.3.0: version "3.4.0" resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== @@ -11526,28 +10726,6 @@ babel-preset-jest@^27.4.0: babel-plugin-transform-undefined-to-void "^6.9.4" lodash "^4.17.11" -babel-preset-react-app@10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz#ed6005a20a24f2c88521809fa9aea99903751584" - integrity sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg== - dependencies: - "@babel/core" "^7.16.0" - "@babel/plugin-proposal-class-properties" "^7.16.0" - "@babel/plugin-proposal-decorators" "^7.16.4" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0" - "@babel/plugin-proposal-numeric-separator" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.16.0" - "@babel/plugin-proposal-private-methods" "^7.16.0" - "@babel/plugin-transform-flow-strip-types" "^7.16.0" - "@babel/plugin-transform-react-display-name" "^7.16.0" - "@babel/plugin-transform-runtime" "^7.16.4" - "@babel/preset-env" "^7.16.4" - "@babel/preset-react" "^7.16.0" - "@babel/preset-typescript" "^7.16.0" - "@babel/runtime" "^7.16.3" - babel-plugin-macros "^3.1.0" - babel-plugin-transform-react-remove-prop-types "^0.4.24" - babel-preset-vue@2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/babel-preset-vue/-/babel-preset-vue-2.0.2.tgz#cfadf1bd736125397481b5f8525ced0049a0c71f" @@ -11572,7 +10750,7 @@ babel-register@^6.26.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@6.x, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: +babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= @@ -11626,11 +10804,6 @@ backo2@^1.0.2: resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= -badgin@^1.1.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/badgin/-/badgin-1.2.3.tgz#994b5f519827d7d5422224825b2c8faea2bc43ad" - integrity sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw== - bail@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.4.tgz#7181b66d508aa3055d3f6c13f0a0c720641dde9b" @@ -12032,7 +11205,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browser-resolve@^1.11.0, browser-resolve@^1.11.3, browser-resolve@^1.7.0: +browser-resolve@^1.11.0, browser-resolve@^1.7.0: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== @@ -12348,24 +11521,6 @@ bytes@3.1.2, bytes@^3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -c8@^7.6.0: - version "7.12.0" - resolved "https://registry.yarnpkg.com/c8/-/c8-7.12.0.tgz#402db1c1af4af5249153535d1c84ad70c5c96b14" - integrity sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@istanbuljs/schema" "^0.1.3" - find-up "^5.0.0" - foreground-child "^2.0.0" - istanbul-lib-coverage "^3.2.0" - istanbul-lib-report "^3.0.0" - istanbul-reports "^3.1.4" - rimraf "^3.0.2" - test-exclude "^6.0.0" - v8-to-istanbul "^9.0.0" - yargs "^16.2.0" - yargs-parser "^20.2.9" - cac@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/cac/-/cac-3.0.4.tgz#6d24ceec372efe5c9b798808bc7f49b47242a4ef" @@ -12398,7 +11553,7 @@ cacache@^10.0.4: unique-filename "^1.1.0" y18n "^4.0.0" -cacache@^12.0.2, cacache@^12.0.3: +cacache@^12.0.2: version "12.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== @@ -12672,7 +11827,7 @@ camelcase@^3.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= -camelcase@^4.0.0, camelcase@^4.1.0: +camelcase@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= @@ -12702,7 +11857,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001426: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: version "1.0.30001451" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz#2e197c698fc1373d63e1406d6607ea4617c613f1" integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== @@ -12763,24 +11918,6 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -"chainsaw@>=0.0.7 <0.1": - version "0.0.9" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.0.9.tgz#11a05102d1c4c785b6d0415d336d5a3a1612913e" - integrity sha1-EaBRAtHEx4W20EFdM21aOhYSkT4= - dependencies: - traverse ">=0.3.0 <0.4" - -chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" @@ -12823,6 +11960,17 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -12930,11 +12078,6 @@ character-reference-invalid@^2.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -13078,12 +12221,12 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@2.2.6, classnames@~2.2.6: +classnames@2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== -classnames@2.x, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6: +classnames@^2.2.5: version "2.3.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== @@ -13239,15 +12382,6 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -13284,17 +12418,6 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone-deep@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" - integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY= - dependencies: - for-own "^0.1.3" - is-plain-object "^2.0.1" - kind-of "^3.0.2" - lazy-cache "^1.0.3" - shallow-clone "^0.1.2" - clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -13596,23 +12719,11 @@ compare-versions@^3.4.0: resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== -component-classes@1.x, component-classes@^1.2.5, component-classes@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691" - integrity sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE= - dependencies: - component-indexof "0.0.3" - component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= -component-indexof@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24" - integrity sha1-EdCRMSI5648yyPJa6csAL/6NPCQ= - component-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" @@ -13653,7 +12764,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1: +concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.1: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -13960,7 +13071,7 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-to-clipboard@^3.0.8, copy-to-clipboard@^3.2.0, copy-to-clipboard@^3.3.1: +copy-to-clipboard@^3.0.8, copy-to-clipboard@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== @@ -13981,24 +13092,6 @@ copy-webpack-plugin@^4.6.0: p-limit "^1.0.0" serialize-javascript "^1.4.0" -copy-webpack-plugin@^5.0.4: - version "5.1.2" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz#8a889e1dcafa6c91c6cd4be1ad158f1d3823bae2" - integrity sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ== - dependencies: - cacache "^12.0.3" - find-cache-dir "^2.1.0" - glob-parent "^3.1.0" - globby "^7.1.1" - is-glob "^4.0.1" - loader-utils "^1.2.3" - minimatch "^3.0.4" - normalize-path "^3.0.0" - p-limit "^2.2.1" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - webpack-log "^2.0.0" - copy-webpack-plugin@~6.0.3: version "6.0.4" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.4.tgz#7b7d7f7f290aa21b3411d02525859b89988a200b" @@ -14028,7 +13121,7 @@ core-js-pure@^3.0.0, core-js-pure@^3.0.1: resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.3.tgz#10e9e3b2592ecaede4283e8f3ad7020811587c02" integrity sha512-V5qQZVAr9K0xu7jXg1M7qTEwuxUgqr7dUOezGaNa7i+Xn9oXAU/d1fzqD9ObuwpVQOaorO5s70ckyi1woP9lVA== -core-js@2, core-js@^2.2.2, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.6.12, core-js@^2.6.5: +core-js@2, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.12, core-js@^2.6.5: version "2.6.12" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== @@ -14153,7 +13246,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@*, create-react-class@^15.5.1, create-react-class@^15.5.3, create-react-class@^15.6.2, create-react-class@^15.6.3, create-react-class@^15.7.0: +create-react-class@^15.7.0: version "15.7.0" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e" integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng== @@ -14216,7 +13309,7 @@ cross-spawn@^4.0.2: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@^5.0.1, cross-spawn@^5.1.0: +cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= @@ -14257,14 +13350,6 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -css-animation@1.x, css-animation@^1.3.2, css-animation@^1.5.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.6.1.tgz#162064a3b0d51f958b7ff37b3d6d4de18e17039e" - integrity sha512-/48+/BaEaHRY6kNQ2OIPzKf9A6g8WjZYjhiNDNuIVbsm5tXCGIAsHDjB4Xu1C4vXJtUWZo26O68OQkDpNBaPog== - dependencies: - babel-runtime "6.x" - component-classes "^1.2.5" - css-blank-pseudo@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" @@ -14324,7 +13409,7 @@ css-loader@^1.0.1: postcss-value-parser "^3.3.0" source-list-map "^2.0.0" -css-loader@^3.0.0, css-loader@^3.2.0, css-loader@~3.6.0: +css-loader@^3.0.0, css-loader@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== @@ -14625,11 +13710,6 @@ cyclist@~0.2.2: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= -d3-array@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" - integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== - d3-array@^2.8.0: version "2.12.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" @@ -14712,11 +13792,6 @@ de-indent@^1.0.2: resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== -debounce@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" - integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== - debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -14813,13 +13888,6 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-3.0.0.tgz#c8e4c4d401cba25550a2f0f486a2e75bc5f219a2" - integrity sha512-YX2i9XjJ7h5q/aQ/IM9PEwEnDqETAIYbggmdDB3HLTlSgo1CxPsj6pvhPG68rq6SVE0+p+6Ywsm5fTYNrYtBWw== - dependencies: - is-obj "^1.0.0" - deep-equal@^1.0.1, deep-equal@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -15021,16 +14089,6 @@ deprecated-decorator@^0.1.6: resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= -deprecated-react-native-listview@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/deprecated-react-native-listview/-/deprecated-react-native-listview-0.0.5.tgz#fc8a6dc45b0a8ba611e6014e13b38d6d763e763f" - integrity sha512-Cy7nDdd+KU+nR3tY1BSMuoZpsYC6OVSZyAiUSTDBop2lIgzCseDx7XI57x6h+NXer/8aor2yiQDQfeFcmBMwgQ== - dependencies: - create-react-class "*" - fbjs "*" - invariant "*" - react-clone-referenced-element "*" - deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -15101,11 +14159,6 @@ detect-libc@^1.0.2, detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -detect-newline@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -15153,16 +14206,6 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -didyoumean@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" - integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== - -diff-sequences@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" - integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== - diff-sequences@^27.4.0: version "27.4.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" @@ -15316,18 +14359,6 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== -dom-align@^1.7.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.2.tgz#0f8164ebd0c9c21b0c790310493cd855892acd4b" - integrity sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg== - -dom-closest@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/dom-closest/-/dom-closest-0.2.0.tgz#ebd9f91d1bf22e8d6f477876bbcd3ec90216c0cf" - integrity sha1-69n5HRvyLo1vR3h2u80+yQIWwM8= - dependencies: - dom-matches ">=1.0.1" - dom-converter@~0.1: version "0.1.4" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b" @@ -15348,16 +14379,6 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" -dom-matches@>=1.0.1: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dom-matches/-/dom-matches-2.0.0.tgz#d2728b416a87533980eb089b848d253cf23a758c" - integrity sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw= - -dom-scroll-into-view@1.x, dom-scroll-into-view@^1.2.0, dom-scroll-into-view@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz#e8f36732dd089b0201a88d7815dc3f88e6d66c7e" - integrity sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4= - dom-serializer@0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" @@ -15564,15 +14585,6 @@ dotenv@~10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -draft-js@^0.10.0, draft-js@~0.10.0: - version "0.10.5" - resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742" - integrity sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg== - dependencies: - fbjs "^0.8.15" - immutable "~3.7.4" - object-assign "^4.1.0" - dset@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" @@ -15789,11 +14801,6 @@ enhanced-resolve@^4.1.1, enhanced-resolve@^4.5.0: memory-fs "^0.5.0" tapable "^1.0.0" -enquire.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814" - integrity sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ= - enquirer@^2.3.5, enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -15852,7 +14859,7 @@ envify@^4.0.0: esprima "^4.0.0" through "~2.3.4" -envinfo@^7.1.0, envinfo@^7.7.2, envinfo@^7.7.4, envinfo@^7.8.1: +envinfo@^7.7.2, envinfo@^7.7.4, envinfo@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== @@ -16644,15 +15651,6 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-to-babel@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/estree-to-babel/-/estree-to-babel-3.2.1.tgz#82e78315275c3ca74475fdc8ac1a5103c8a75bf5" - integrity sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg== - dependencies: - "@babel/traverse" "^7.1.6" - "@babel/types" "^7.2.0" - c8 "^7.6.0" - estree-util-is-identifier-name@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.0.1.tgz#cf07867f42705892718d9d89eb2d85eaa8f0fcb5" @@ -16719,21 +15717,11 @@ eventemitter3@^2.0.3: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo= -eventemitter3@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" - integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== - eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -eventlistener@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/eventlistener/-/eventlistener-0.0.1.tgz#ed2baabb852227af2bcf889152c72c63ca532eb8" - integrity sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg= - events@^1.1.0, events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -16912,18 +15900,6 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" - integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== - dependencies: - "@jest/types" "^24.9.0" - ansi-styles "^3.2.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.9.0" - expect@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" @@ -16939,17 +15915,6 @@ expo-application@~4.0.2: resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-4.0.2.tgz#860dbd12132a56de7cf75fe7b5146b6cd97ed30e" integrity sha512-ngTaFplTkWn0X45gMC+VNXGyJfGxX4wOwKmtr17rNMVWOQUhhLlyMkTj9bAamzsuwZh35l3S/eD/N1aMWWUwMw== -expo-asset@~8.1.3: - version "8.1.7" - resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-8.1.7.tgz#32618e51f85df56f1d7dd54c71eb486ae7f7674e" - integrity sha512-g0+a+Uc+GfOI7VtZ6d0WB78qq6Lu3vKqHN3TBfcsndcx893CSmo6ZVLcrlL9evdZwlbSO+9zLrLdzEw38a/gMA== - dependencies: - blueimp-md5 "^2.10.0" - invariant "^2.2.4" - md5-file "^3.2.3" - path-browserify "^1.0.0" - url-parse "^1.4.4" - expo-asset@~8.4.6: version "8.4.6" resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-8.4.6.tgz#1c40e9badac66dbd3d2be2810711937e5b9b09bd" @@ -17033,16 +15998,6 @@ expo-constants@~13.0.2: "@expo/config" "^6.0.6" uuid "^3.3.2" -expo-constants@~9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-9.0.0.tgz#35c600079ee91d38fe4f56375caae6e90f122fdd" - integrity sha512-1kqZMM8Ez5JT3sTEx8I69fP6NYFLOJjeM6Z63dD/m2NiwvzSADiO5+BhghnWNGN1L3bxbgOjXS6EHtS7CdSfxA== - -expo-error-recovery@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/expo-error-recovery/-/expo-error-recovery-1.1.0.tgz#98b9de5400dbfd022d1631739f3bf5e7016565da" - integrity sha512-33aRfPaXdAt0df1TL26JjM5qCAoEW8RAExjgMgunPcdQcf4sWiWFm3qYL8zrO/8DM4uUq4X2FCuPLHMlOYT/aw== - expo-error-recovery@~3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/expo-error-recovery/-/expo-error-recovery-3.0.5.tgz#1802b733e998606a8fcfb0abe6682c334319ef75" @@ -17056,13 +16011,6 @@ expo-file-system@~13.1.3: "@expo/config-plugins" "^4.0.2" uuid "^3.4.0" -expo-file-system@~8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-8.1.0.tgz#d6aa66fa32c19982b94d0013f963c5ee972dfd6d" - integrity sha512-xb4roeU8CotW8t3LkmsrliNbgFpY2KB+3sW1NnujnH39pFVwCd/kfujCYzRauj8aUy/HhSq+3xGkQTpC7pSjVw== - dependencies: - uuid "^3.4.0" - expo-font@~10.0.5: version "10.0.5" resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-10.0.5.tgz#8010060ec5326b3b462f7a1ac6b232dc4d1a7317" @@ -17070,36 +16018,11 @@ expo-font@~10.0.5: dependencies: fontfaceobserver "^2.1.0" -expo-font@~8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-8.1.1.tgz#40de629d7332887bdafae4756741f6979fc0043e" - integrity sha512-z6008K7YSA7wpJ1mNyG2eSYUhEoFVjdL2uAbwaHFpsqwxDS4tcdKHoWkanIUiEnsjtHK7Uk0ywKJ8MRzmCaklw== - dependencies: - fbjs "1.0.0" - fontfaceobserver "^2.1.0" - expo-keep-awake@~10.0.2: version "10.0.2" resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-10.0.2.tgz#706bda839782bb3e8ad4cbe43bde471a56368813" integrity sha512-Ro1lgyKldbFs4mxhWM+goX9sg0S2SRR8FiJJeOvaRzf8xNhrZfWA00Zpr+/3ocCoWQ3eEL+X9UF4PXXHf0KoOg== -expo-keep-awake@~8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-8.1.0.tgz#3a1d8aa5a8395d40c7d79e1c93020ae5f848e664" - integrity sha512-RNPwWvpwsJwJS8ZI1yklKyVQ6l2NNZBCN2aSgQMRza2SABnpFFzDLHQwMo7DC+nbmrOueMvCIDr0VI3xrzGfEg== - -expo-linear-gradient@~8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/expo-linear-gradient/-/expo-linear-gradient-8.1.0.tgz#6933765cf1e76ef2928fd8495e779929699ac4c6" - integrity sha512-AIy2pOXQRcgk2XE5IgAzd1S2jTFLutiDfveNm6m3fPAk00Rw4qFe98qzte1ayNrGYLJvQ2xq/Y7C0BmBP051mg== - -expo-location@~8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-8.1.0.tgz#6a71ad9a8b78d5f016a30a02013c3b28f46d0b1b" - integrity sha512-G9JvsK1t9Z5Iybf+FCG81Jgm9Ee9leqpazxOPVabUJEWu/55Iex3yLGX04BuIA4ozAlJKBPzkhPdyqKdC7zrSw== - dependencies: - invariant "^2.2.4" - expo-modules-autolinking@0.5.5, expo-modules-autolinking@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-0.5.5.tgz#6bcc42072dcbdfca79d207b7f549f1fdb54a2b74" @@ -17119,11 +16042,6 @@ expo-modules-core@0.6.5: compare-versions "^3.4.0" invariant "^2.2.4" -expo-permissions@~8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-8.1.0.tgz#a7f2ee91ba76ce3a467e7b10adaa9ca5201b226f" - integrity sha512-QBHD+1J9+sGFnhoEGzMRchPweeEE0OJ9ehG/0l1BMRBA7qsLS9vRC1FTJ55NwjI0Kr4RTha9r6ZX1kZHT09f/w== - expo-pwa@0.0.112: version "0.0.112" resolved "https://registry.yarnpkg.com/expo-pwa/-/expo-pwa-0.0.112.tgz#4eb26a5bf1e5c8d48c371ad8543d44fe41ddfb06" @@ -17135,75 +16053,11 @@ expo-pwa@0.0.112: commander "2.20.0" update-check "1.5.3" -expo-sqlite@~8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/expo-sqlite/-/expo-sqlite-8.1.0.tgz#858eb28e1143db281de8a49c99b4a953a81d0834" - integrity sha512-ziw6dbV1/sZErDkoGjG0afokyuKQqDtUuJglbLz9rQ6zNS1ceF3AjuEyfsWPDc2LL+QEdcnQODW7VUJelIk+0Q== - dependencies: - "@expo/websql" "^1.0.1" - "@types/websql" "^0.0.27" - lodash "^4.17.15" - expo-status-bar@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-1.2.0.tgz#16e73205da563f9536f562e439081e30e318a82e" integrity sha512-pVZZ/kDCXFK79E4dCtRecs3XLC8aiwlciutSd/fFmUPJSQZ1Txia6hlKajPt0GAYft8/YnT0V3URXzWZOBniYQ== -expo-web-browser@~8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-8.1.1.tgz#93a3d4af834e7bbd1afd54f0cc588016d592c5ae" - integrity sha512-htiXVLo0mb0t8F2vFL3+QnmD97B9U0Ek9cp2BBKSCLeHaVry6RUMewT9HLLcBOJhz2Zdjwk7JmTJh6amF1h80Q== - -expo@37.0.3: - version "37.0.3" - resolved "https://registry.yarnpkg.com/expo/-/expo-37.0.3.tgz#c7dc8d2808264b74882bb2f7bc11c0ce1ce520bc" - integrity sha512-EDQiYItWKEHwmKQHew1KGbD+vpkzaSSS6S/n3YdFywv27O5mLs26Y593UtvdRu/10oot7g0DdFvcCUg7+b//ng== - dependencies: - "@babel/runtime" "^7.1.2" - "@expo/vector-icons" "^10.0.2" - "@types/fbemitter" "^2.0.32" - "@types/invariant" "^2.2.29" - "@types/lodash.zipobject" "^4.1.4" - "@types/qs" "^6.5.1" - "@unimodules/core" "~5.1.0" - "@unimodules/react-native-adapter" "~5.1.1" - babel-preset-expo "~8.1.0" - badgin "^1.1.2" - cross-spawn "^6.0.5" - expo-asset "~8.1.3" - expo-constants "~9.0.0" - expo-error-recovery "~1.1.0" - expo-file-system "~8.1.0" - expo-font "~8.1.0" - expo-keep-awake "~8.1.0" - expo-linear-gradient "~8.1.0" - expo-location "~8.1.0" - expo-permissions "~8.1.0" - expo-sqlite "~8.1.0" - expo-web-browser "~8.1.0" - fbemitter "^2.1.1" - invariant "^2.2.2" - lodash "^4.6.0" - md5-file "^3.2.3" - nullthrows "^1.1.0" - pretty-format "^23.6.0" - prop-types "^15.6.0" - qs "^6.5.0" - react-native-view-shot "3.1.2" - serialize-error "^2.1.0" - unimodules-app-loader "~1.0.0" - unimodules-barcode-scanner-interface "~5.1.0" - unimodules-camera-interface "~5.1.0" - unimodules-constants-interface "~5.1.0" - unimodules-face-detector-interface "~5.1.0" - unimodules-file-system-interface "~5.1.0" - unimodules-font-interface "~5.1.0" - unimodules-image-loader-interface "~5.1.0" - unimodules-permissions-interface "~5.1.0" - unimodules-sensors-interface "~5.1.0" - unimodules-task-manager-interface "~5.1.0" - uuid "^3.4.0" - expo@~44.0.0: version "44.0.6" resolved "https://registry.yarnpkg.com/expo/-/expo-44.0.6.tgz#5454f08abb07166e55eb55b5fc4d45b5ad416ff4" @@ -17339,13 +16193,6 @@ express@^4.14.0, express@^4.16.3, express@^4.16.4, express@^4.17.0, express@^4.1 utils-merge "1.0.1" vary "~1.1.2" -extend-shallow@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" - integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE= - dependencies: - kind-of "^1.1.0" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -17366,15 +16213,6 @@ extend@^3.0.0, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^2.0.1, external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" - integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -17433,16 +16271,6 @@ fake-tag@^1.0.0: resolved "https://registry.yarnpkg.com/fake-tag/-/fake-tag-1.0.1.tgz#1d59da482240a02bd83500ca98976530ed154b0d" integrity sha512-qmewZoBpa71mM+y6oxXYW/d1xOYQmeIvnEXAt1oCmdP0sqcogWYLepR87QL1jQVLSVMVYDq2cjY6ec/Wu8/4pg== -fancy-log@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - fast-deep-equal@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" @@ -17579,56 +16407,26 @@ fbjs-css-vars@^1.0.0: resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== -fbjs-scripts@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fbjs-scripts/-/fbjs-scripts-1.2.0.tgz#069a0c0634242d10031c6460ef1fccefcdae8b27" - integrity sha512-5krZ8T0Bf8uky0abPoCLrfa7Orxd8UH4Qq8hRUF2RZYNMu+FmEOrBc7Ib3YVONmxTXTlLAvyrrdrVmksDb2OqQ== - dependencies: - "@babel/core" "^7.0.0" - ansi-colors "^1.0.1" - babel-preset-fbjs "^3.2.0" - core-js "^2.4.1" - cross-spawn "^5.1.0" - fancy-log "^1.3.2" - object-assign "^4.0.1" - plugin-error "^0.1.2" - semver "^5.1.0" - through2 "^2.0.0" - -fbjs@*, fbjs@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" - integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== +fbjs@^0.8.4: + version "0.8.18" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.18.tgz#9835e0addb9aca2eff53295cd79ca1cfc7c9662a" + integrity sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA== dependencies: - cross-fetch "^3.1.5" - fbjs-css-vars "^1.0.0" + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" loose-envify "^1.0.0" object-assign "^4.1.0" promise "^7.1.1" setimmediate "^1.0.5" ua-parser-js "^0.7.30" -fbjs@1.0.0, fbjs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a" - integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA== +fbjs@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" + integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== dependencies: - core-js "^2.4.1" + cross-fetch "^3.1.5" fbjs-css-vars "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.18" - -fbjs@^0.8.15, fbjs@^0.8.4: - version "0.8.18" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.18.tgz#9835e0addb9aca2eff53295cd79ca1cfc7c9662a" - integrity sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA== - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" loose-envify "^1.0.0" object-assign "^4.1.0" promise "^7.1.1" @@ -17788,11 +16586,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -filter-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" - integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= - finalhandler@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" @@ -17940,7 +16733,7 @@ find-yarn-workspace-root@~2.0.0: dependencies: micromatch "^4.0.2" -findup-sync@3.0.0, findup-sync@^3.0.0: +findup-sync@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== @@ -18039,36 +16832,16 @@ fontfaceobserver@^2.1.0: resolved "https://registry.yarnpkg.com/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz#e2705d293e2c585a6531c2a722905657317a2991" integrity sha512-ReOsO2F66jUa0jmv2nlM/s1MiutJx/srhAe2+TE8dJCMi02ZZOcCTxTCQFr3Yet+uODUtnr4Mewg+tNQ+4V1Ng== -for-in@^0.1.3: - version "0.1.8" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" - integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= - -for-in@^1.0.1, for-in@^1.0.2: +for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -for-own@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" - foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= -foreground-child@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" - integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^3.0.2" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -19051,11 +17824,6 @@ gray-matter@^4.0.3: section-matter "^1.0.0" strip-bom-string "^1.0.0" -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw== - gud@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" @@ -19089,11 +17857,6 @@ hable@^3.0.0: resolved "https://registry.yarnpkg.com/hable/-/hable-3.0.0.tgz#6de089b2df946635cf8134b9e4859f1b62de255f" integrity sha512-7+G0/2/COR8pwteYFqHIVYfQpuEiO2HXwJrhCBJVgrNrl9O5eaUoJVDGXUJX+0RpGncNVTuestexjk1afj01wQ== -hammerjs@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" - integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE= - handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -19305,13 +18068,6 @@ hashids@1.1.4: resolved "https://registry.yarnpkg.com/hashids/-/hashids-1.1.4.tgz#e4ff92ad66b684a3bd6aace7c17d66618ee5fa21" integrity sha512-U/fnTE3edW0AV92ZI/BfEluMZuVcu3MDOopsN7jS+HqDYcarQo8rXQiWlsBlm0uX48/taYSdxRsfzh2HRg5Z6w== -"hashish@>=0.0.2 <0.1": - version "0.0.4" - resolved "https://registry.yarnpkg.com/hashish/-/hashish-0.0.4.tgz#6d60bc6ffaf711b6afd60e426d077988014e6554" - integrity sha1-bWC8b/r3Ebav1g5CbQd5iAFOZVQ= - dependencies: - traverse ">=0.2.4" - hast-to-hyperscript@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-10.0.1.tgz#3decd7cb4654bca8883f6fcbd4fb3695628c4296" @@ -19451,11 +18207,6 @@ header-case@^2.0.3: capital-case "^1.0.4" tslib "^2.0.3" -hermes-engine@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.2.1.tgz#25c0f1ff852512a92cb5c5cc47cf967e1e722ea2" - integrity sha512-eNHUQHuadDMJARpaqvlCZoK/Nitpj6oywq3vQ3wCwEsww5morX34mW5PmKWQTO7aU0ck0hgulxR+EVDlXygGxQ== - hermes-engine@~0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" @@ -19488,28 +18239,6 @@ highlight.js@~9.13.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== -history@4.10.1, history@^4.9.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== - dependencies: - "@babel/runtime" "^7.1.2" - loose-envify "^1.2.0" - resolve-pathname "^3.0.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - value-equal "^1.0.1" - -history@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/history/-/history-3.3.0.tgz#fcedcce8f12975371545d735461033579a6dae9c" - integrity sha1-/O3M6PEpdTcVRdc1RhAzV5ptrpw= - dependencies: - invariant "^2.2.1" - loose-envify "^1.2.0" - query-string "^4.2.2" - warning "^3.0.0" - hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -19527,12 +18256,7 @@ hogan.js@^3.0.2: mkdirp "0.3.0" nopt "1.0.10" -hoist-non-react-statics@^2.3.1: - version "2.5.5" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" - integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== - -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -20016,7 +18740,7 @@ iconv-lite@0.4.23: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -20105,7 +18829,7 @@ image-size@^1.0.0: dependencies: queue "6.0.2" -immediate@^3.2.2, immediate@^3.2.3: +immediate@^3.2.3: version "3.3.0" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== @@ -20120,21 +18844,11 @@ immer@8.0.1: resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== -immutable@^3.7.4: - version "3.8.2" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" - integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= - immutable@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== -immutable@~3.7.4: - version "3.7.6" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" - integrity sha1-E7TTyxK++hVIKib+Gy665kAHHks= - import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -20286,13 +19000,6 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== -inline-style-prefixer@^5.0.3: - version "5.1.2" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-5.1.2.tgz#e5a5a3515e25600e016b71e39138971228486c33" - integrity sha512-PYUF+94gDfhy+LsQxM0g3d6Hge4l1pAqOSOiZuHWzMvQEGsbRQ/ck2WioLqrY2ZkHyPgVUXxn+hrkF7D6QUGbA== - dependencies: - css-in-js-utils "^2.0.0" - inline-style-prefixer@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" @@ -20307,25 +19014,6 @@ inputformat-to-jstransformer@^1.2.1: dependencies: require-one "^1.0.3" -inquirer@3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347" - integrity sha1-4EqqnQW3o8ubD0B9BDdfBEcZA0c= - dependencies: - ansi-escapes "^1.1.0" - chalk "^1.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.1" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx "^4.1.0" - string-width "^2.0.0" - strip-ansi "^3.0.0" - through "^2.3.6" - inquirer@6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" @@ -20383,26 +19071,6 @@ inquirer@8.0.0: strip-ansi "^6.0.0" through "^2.3.6" -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - inquirer@^7.0.0, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -20491,7 +19159,7 @@ interpret@1.2.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== -interpret@^1.0.0, interpret@^1.4.0: +interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== @@ -20501,7 +19169,7 @@ interpret@^2.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.0.0.tgz#b783ffac0b8371503e9ab39561df223286aa5433" integrity sha512-e0/LknJ8wpMMhTiWcjivB+ESwIuvHnBSlBbmP/pSb8CQJldoj1p2qv7xGZ/+BtbTziYRFSz8OsvdbiX45LtYQA== -invariant@*, invariant@2.2.4, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -20635,7 +19303,7 @@ is-boolean-object@^1.0.1, is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.0, is-buffer@^1.1.4, is-buffer@^1.1.5, is-buffer@~1.1.1: +is-buffer@^1.1.0, is-buffer@^1.1.4, is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -21278,7 +19946,7 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: +istanbul-lib-coverage@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== @@ -21288,7 +19956,7 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: +istanbul-lib-instrument@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== @@ -21312,15 +19980,6 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" -istanbul-lib-report@^2.0.4: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== - dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" - istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -21330,17 +19989,6 @@ istanbul-lib-report@^3.0.0: make-dir "^3.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" - source-map "^0.6.1" - istanbul-lib-source-maps@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" @@ -21350,14 +19998,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.2.6: - version "2.2.7" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931" - integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg== - dependencies: - html-escaper "^2.0.0" - -istanbul-reports@^3.1.3, istanbul-reports@^3.1.4: +istanbul-reports@^3.1.3: version "3.1.5" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== @@ -21421,15 +20062,6 @@ javascript-stringify@^1.6.0: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" integrity sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ== -jest-changed-files@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" - integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== - dependencies: - "@jest/types" "^24.9.0" - execa "^1.0.0" - throat "^4.0.0" - jest-changed-files@^27.4.2: version "27.4.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" @@ -21464,25 +20096,6 @@ jest-circus@^27.4.6: stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" - integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== - dependencies: - "@jest/core" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - exit "^0.1.2" - import-local "^2.0.0" - is-ci "^2.0.0" - jest-config "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^13.3.0" - jest-cli@^27.4.7: version "27.4.7" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a" @@ -21501,29 +20114,6 @@ jest-cli@^27.4.7: prompts "^2.0.1" yargs "^16.2.0" -jest-config@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" - integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== - dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^24.9.0" - "@jest/types" "^24.9.0" - babel-jest "^24.9.0" - chalk "^2.0.1" - glob "^7.1.1" - jest-environment-jsdom "^24.9.0" - jest-environment-node "^24.9.0" - jest-get-type "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - micromatch "^3.1.10" - pretty-format "^24.9.0" - realpath-native "^1.1.0" - jest-config@^27.4.7: version "27.4.7" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972" @@ -21562,23 +20152,6 @@ jest-diff@*, jest-diff@27.4.6, jest-diff@^27.0.0, jest-diff@^27.4.6: jest-get-type "^27.4.0" pretty-format "^27.4.6" -jest-diff@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" - integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== - dependencies: - chalk "^2.0.1" - diff-sequences "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-docblock@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" - integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== - dependencies: - detect-newline "^2.1.0" - jest-docblock@^27.4.0: version "27.4.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" @@ -21586,17 +20159,6 @@ jest-docblock@^27.4.0: dependencies: detect-newline "^3.0.0" -jest-each@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" - integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== - dependencies: - "@jest/types" "^24.9.0" - chalk "^2.0.1" - jest-get-type "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - jest-each@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.6.tgz#e7e8561be61d8cc6dbf04296688747ab186c40ff" @@ -21626,18 +20188,6 @@ jest-environment-jsdom@27.1.0: jest-util "^27.1.0" jsdom "^16.6.0" -jest-environment-jsdom@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" - integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - jsdom "^11.5.1" - jest-environment-jsdom@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz#c23a394eb445b33621dfae9c09e4c8021dea7b36" @@ -21651,17 +20201,6 @@ jest-environment-jsdom@^27.4.6: jest-util "^27.4.2" jsdom "^16.6.0" -jest-environment-node@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" - integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - jest-environment-node@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.6.tgz#ee8cd4ef458a0ef09d087c8cd52ca5856df90242" @@ -21674,25 +20213,6 @@ jest-environment-node@^27.4.6: jest-mock "^27.4.6" jest-util "^27.4.2" -jest-expo@37.0.0: - version "37.0.0" - resolved "https://registry.yarnpkg.com/jest-expo/-/jest-expo-37.0.0.tgz#2d2c5ea4622585ca6539612245f3357bc91d7b70" - integrity sha512-Bww1296svavIqGMgev7taLloi87WtwgEHXVtSmNMaKeKfBaFK05qkEzRM3xE/8cnMrYNJSlqusvA7PApdvE/vg== - dependencies: - "@expo/config" "^2.5.3" - babel-jest "^24.7.1" - jest "^24.7.1" - jest-watch-select-projects "^1.0.0" - jest-watch-typeahead "^0.4.0" - json5 "^2.1.0" - lodash "^4.5.0" - react-test-renderer "~16.9.0" - -jest-get-type@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" - integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== - jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" @@ -21703,7 +20223,7 @@ jest-get-type@^27.4.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== -jest-haste-map@^24.7.1, jest-haste-map@^24.9.0: +jest-haste-map@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== @@ -21777,28 +20297,6 @@ jest-image-snapshot@2.12.0: pngjs "^3.3.3" rimraf "^2.6.2" -jest-jasmine2@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" - integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== - dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - co "^4.6.0" - expect "^24.9.0" - is-generator-fn "^2.0.0" - jest-each "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - throat "^4.0.0" - jest-jasmine2@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz#109e8bc036cb455950ae28a018f983f2abe50127" @@ -21832,14 +20330,6 @@ jest-junit@12.2.0: uuid "^8.3.2" xml "^1.0.1" -jest-leak-detector@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" - integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== - dependencies: - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - jest-leak-detector@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz#ed9bc3ce514b4c582637088d9faf58a33bd59bf4" @@ -21848,16 +20338,6 @@ jest-leak-detector@^27.4.6: jest-get-type "^27.4.0" pretty-format "^27.4.6" -jest-matcher-utils@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" - integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== - dependencies: - chalk "^2.0.1" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - jest-matcher-utils@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz#53ca7f7b58170638590e946f5363b988775509b8" @@ -21882,20 +20362,6 @@ jest-message-util@^24.9.0: slash "^2.0.0" stack-utils "^1.0.1" -jest-message-util@^25.1.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea" - integrity sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^25.5.0" - "@types/stack-utils" "^1.0.1" - chalk "^3.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - slash "^3.0.0" - stack-utils "^1.0.1" - jest-message-util@^27.4.6: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -21926,12 +20392,12 @@ jest-mock@^27.1.0, jest-mock@^27.4.6: "@jest/types" "^27.4.2" "@types/node" "*" -jest-pnp-resolver@^1.2.1, jest-pnp-resolver@^1.2.2: +jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: +jest-regex-util@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== @@ -21946,15 +20412,6 @@ jest-regex-util@^27.0.0, jest-regex-util@^27.4.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== -jest-resolve-dependencies@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" - integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== - dependencies: - "@jest/types" "^24.9.0" - jest-regex-util "^24.3.0" - jest-snapshot "^24.9.0" - jest-resolve-dependencies@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz#fc50ee56a67d2c2183063f6a500cc4042b5e2327" @@ -21964,17 +20421,6 @@ jest-resolve-dependencies@^27.4.6: jest-regex-util "^27.4.0" jest-snapshot "^27.4.6" -jest-resolve@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" - integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== - dependencies: - "@jest/types" "^24.9.0" - browser-resolve "^1.11.3" - chalk "^2.0.1" - jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" - jest-resolve@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.6.tgz#2ec3110655e86d5bfcfa992e404e22f96b0b5977" @@ -21991,31 +20437,6 @@ jest-resolve@^27.4.6: resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" - integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.4.2" - exit "^0.1.2" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-docblock "^24.3.0" - jest-haste-map "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-leak-detector "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - source-map-support "^0.5.6" - throat "^4.0.0" - jest-runner@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.6.tgz#1d390d276ec417e9b4d0d081783584cbc3e24773" @@ -22044,35 +20465,6 @@ jest-runner@^27.4.6: source-map-support "^0.5.6" throat "^6.0.1" -jest-runtime@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" - integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - chalk "^2.0.1" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - realpath-native "^1.1.0" - slash "^2.0.0" - strip-bom "^3.0.0" - yargs "^13.3.0" - jest-runtime@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.6.tgz#83ae923818e3ea04463b22f3597f017bb5a1cffa" @@ -22108,7 +20500,7 @@ jest-serializer-html@7.1.0: dependencies: diffable-html "^4.1.0" -jest-serializer@^24.4.0, jest-serializer@^24.9.0: +jest-serializer@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== @@ -22129,25 +20521,6 @@ jest-serializer@^27.4.0: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" - integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - expect "^24.9.0" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - pretty-format "^24.9.0" - semver "^6.2.0" - jest-snapshot@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.6.tgz#e2a3b4fff8bdce3033f2373b2e525d8b6871f616" @@ -22218,18 +20591,6 @@ jest-util@^27.0.0, jest-util@^27.1.0, jest-util@^27.4.2: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^24.7.0, jest-validate@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" - integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== - dependencies: - "@jest/types" "^24.9.0" - camelcase "^5.3.1" - chalk "^2.0.1" - jest-get-type "^24.9.0" - leven "^3.1.0" - pretty-format "^24.9.0" - jest-validate@^26.5.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -22263,15 +20624,6 @@ jest-vue-preprocessor@1.7.1: find-babel-config "1.2.0" vue-property-decorator "8.3.0" -jest-watch-select-projects@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/jest-watch-select-projects/-/jest-watch-select-projects-1.0.0.tgz#c876d787006b42c52f1a7dad2cfca5e929eaa283" - integrity sha512-8zO2NNeTQG5+o9BYi4n6Idp8QZzCQdirpTel5iaed9911ktDKV0VTdiu0tYvDOoAbdfH4itTM4gR6lcJ2ATc/w== - dependencies: - ansi-escapes "^3.1.0" - chalk "^2.4.1" - prompts "^2.2.1" - jest-watch-typeahead@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.3.0.tgz#f56d9ee17ea71ecbf8253fed213df3185a1584c9" @@ -22297,20 +20649,7 @@ jest-watch-typeahead@1.0.0: string-length "^5.0.1" strip-ansi "^7.0.1" -jest-watch-typeahead@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.4.2.tgz#e5be959698a7fa2302229a5082c488c3c8780a4a" - integrity sha512-f7VpLebTdaXs81rg/oj4Vg/ObZy2QtGzAmGLNsqUS5G5KtSN68tFcIsbvNODfNyQxU78g7D8x77o3bgfBTR+2Q== - dependencies: - ansi-escapes "^4.2.1" - chalk "^2.4.1" - jest-regex-util "^24.9.0" - jest-watcher "^24.3.0" - slash "^3.0.0" - string-length "^3.1.0" - strip-ansi "^5.0.0" - -jest-watcher@^24.3.0, jest-watcher@^24.9.0: +jest-watcher@^24.3.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== @@ -22336,7 +20675,7 @@ jest-watcher@^27.0.0, jest-watcher@^27.4.6: jest-util "^27.4.2" string-length "^4.0.1" -jest-worker@^24.0.0, jest-worker@^24.6.0, jest-worker@^24.9.0: +jest-worker@^24.0.0, jest-worker@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== @@ -22371,14 +20710,6 @@ jest@27.4.7: import-local "^3.0.2" jest-cli "^27.4.7" -jest@^24.7.1: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" - integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== - dependencies: - import-local "^2.0.0" - jest-cli "^24.9.0" - jetifier@^1.6.2: version "1.6.8" resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" @@ -22773,13 +21104,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - json-stable-stringify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" @@ -22797,19 +21121,12 @@ json-stringify-safe@5.0.x, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0. resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json2mq@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" - integrity sha1-tje9O6nqvhIsg+lyBIOusQ0skEo= - dependencies: - string-convert "^0.2.0" - json3@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== -json5@2.x, json5@^2.1.0, json5@^2.1.1, json5@^2.1.2, json5@^2.1.3, json5@^2.2.0, json5@^2.2.1: +json5@2.x, json5@^2.1.1, json5@^2.1.2, json5@^2.1.3, json5@^2.2.0, json5@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== @@ -22961,18 +21278,6 @@ killable@^1.0.1: resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== -kind-of@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" - integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= - -kind-of@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" - integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= - dependencies: - is-buffer "^1.0.2" - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -23095,11 +21400,6 @@ lazy-ass@1.6.0: resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== -lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" - integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= - lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -23371,16 +21671,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -23524,7 +21814,7 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.debounce@^4.0.0, lodash.debounce@^4.0.8: +lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= @@ -23624,11 +21914,6 @@ lodash.omit@^4.0.2, lodash.omit@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= -lodash.orderby@4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.orderby/-/lodash.orderby-4.6.0.tgz#e697f04ce5d78522f54d9338b32b81a3393e4eb3" - integrity sha1-5pfwTOXXhSL1TZM4syuBozk+TrM= - lodash.padend@^4.6.1: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" @@ -23664,7 +21949,7 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~3.0.0" -lodash.throttle@^4.0.0, lodash.throttle@^4.1.1: +lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= @@ -23694,7 +21979,7 @@ lodash.zip@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= -"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.0.1, lodash@^4.15.0, lodash@^4.16.5, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.0, lodash@^4.7.0, lodash@~4.17.10, lodash@~4.17.15: +"lodash@>=3.5 <5", lodash@^4.0.1, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0, lodash@~4.17.10, lodash@~4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -23740,15 +22025,6 @@ log-update@^3.2.0: cli-cursor "^2.1.0" wrap-ansi "^5.0.0" -logkitty@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.6.1.tgz#fe29209669d261539cbd6bb998a136fc92a1a05c" - integrity sha512-cHuXN8qUZuzX/7kB6VyS7kB4xyD24e8gyHXIFNhIv+fjW3P+jEXNUhj0o/7qWJtv7UZpbnPgUqzu/AZQ8RAqxQ== - dependencies: - ansi-fragments "^0.2.1" - dayjs "^1.8.15" - yargs "^12.0.5" - logkitty@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" @@ -23778,7 +22054,7 @@ longest@^1.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -24274,13 +22550,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= - dependencies: - mimic-fn "^1.0.0" - mem@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mem/-/mem-4.0.0.tgz#6437690d9471678f6cc83659c00cbafcd6b0cdaf" @@ -24389,15 +22658,6 @@ meow@^9.0.0: type-fest "^0.18.0" yargs-parser "^20.2.3" -merge-deep@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.3.tgz#1a2b2ae926da8b2ae93a0ac15d90cd1922766003" - integrity sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA== - dependencies: - arr-union "^3.1.0" - clone-deep "^0.2.4" - kind-of "^3.0.2" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -24410,13 +22670,6 @@ merge-source-map@^1.1.0: dependencies: source-map "^0.6.1" -merge-stream@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" - integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= - dependencies: - readable-stream "^2.0.1" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -24511,24 +22764,6 @@ metro-babel-register@0.64.0: "@babel/register" "^7.0.0" escape-string-regexp "^1.0.5" -metro-babel-register@^0.56.0, metro-babel-register@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.56.4.tgz#b0c627a1cfdd1bdd768f81af79481754e833a902" - integrity sha512-Phm6hMluOWYqfykftjJ1jsTpWvbgb49AC/1taxEctxUdRCZlFgZwBleJZAhQYxJD5J+ikFkEbHDzePEXb29KVA== - dependencies: - "@babel/core" "^7.0.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/register" "^7.0.0" - core-js "^2.2.2" - escape-string-regexp "^1.0.5" - metro-babel-transformer@0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" @@ -24538,14 +22773,6 @@ metro-babel-transformer@0.64.0: metro-source-map "0.64.0" nullthrows "^1.1.1" -metro-babel-transformer@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.56.4.tgz#fe1d0dc600fcf90201a5bea4d42caea10b801057" - integrity sha512-IOi4ILgZvaX7GCGHBJp79paNVOq5QxhhbyqAdEJgDP8bHfl/OVHoVKSypfrsMSKSiBrqxhIjyc4XjkXsQtkx5g== - dependencies: - "@babel/core" "^7.0.0" - metro-source-map "^0.56.4" - metro-cache-key@0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" @@ -24560,16 +22787,6 @@ metro-cache@0.64.0: mkdirp "^0.5.1" rimraf "^2.5.4" -metro-cache@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.56.4.tgz#542f9f8a35f8fb9d5576f46fd3ab4d4f42851a7e" - integrity sha512-d1hiUSKwtRsuMxUhHVJ3tjK2BbpUlJGvTyMWohK8Wxx+0GbnWRWWFcI4vlCzlZfoK0VtZK2MJEl5t7Du1mIniQ== - dependencies: - jest-serializer "^24.4.0" - metro-core "^0.56.4" - mkdirp "^0.5.1" - rimraf "^2.5.4" - metro-config@0.64.0, metro-config@^0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" @@ -24582,18 +22799,6 @@ metro-config@0.64.0, metro-config@^0.64.0: metro-core "0.64.0" metro-runtime "0.64.0" -metro-config@^0.56.0, metro-config@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.56.4.tgz#338fd8165fba59424cec427c1a881757945e57e9" - integrity sha512-O85QDHwWdMn/8ERe13y4a6vbZL0AHyO8atTvL+9BCulLEO+FQBi1iJjr3+ViLa8cf0m5dRftDsa7P47m5euk4A== - dependencies: - cosmiconfig "^5.0.5" - jest-validate "^24.7.0" - metro "^0.56.4" - metro-cache "^0.56.4" - metro-core "^0.56.4" - pretty-format "^24.7.0" - metro-core@0.64.0, metro-core@^0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" @@ -24603,16 +22808,6 @@ metro-core@0.64.0, metro-core@^0.64.0: lodash.throttle "^4.1.1" metro-resolver "0.64.0" -metro-core@^0.56.0, metro-core@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.56.4.tgz#67cc41b3c0bf66e9c2306f50239a1080b1e82312" - integrity sha512-hMzkBdgPt5Zm9nr/1KtIT+A6H7TNiLVCEGG5OiAXj8gTRsA2yy7wAdQpwy0xbE+zi88t/pLOzXpd3ClG/YxyWg== - dependencies: - jest-haste-map "^24.7.1" - lodash.throttle "^4.1.1" - metro-resolver "^0.56.4" - wordwrap "^1.0.0" - metro-hermes-compiler@0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" @@ -24628,17 +22823,6 @@ metro-inspector-proxy@0.64.0: ws "^1.1.5" yargs "^15.3.1" -metro-inspector-proxy@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.56.4.tgz#7343ff3c5908af4fd99e96b6d646e24e99816be4" - integrity sha512-E1S3MO25mWKmcLn1UQuCDiS0hf9P2Fwq8sEAX5lBLoZbehepNH+4xJ3xXSY51JX4dozBrE8GGoKL4ll3II40LA== - dependencies: - connect "^3.6.5" - debug "^2.2.0" - rxjs "^5.4.3" - ws "^1.1.5" - yargs "^9.0.0" - metro-minify-uglify@0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" @@ -24646,13 +22830,6 @@ metro-minify-uglify@0.64.0: dependencies: uglify-es "^3.1.9" -metro-minify-uglify@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.56.4.tgz#13589dfb1d43343608aacb7f78ddfcc052daa63c" - integrity sha512-BHgj7+BKEK2pHvWHUR730bIrsZwl8DPtr49x9L0j2grPZ5/UROWXzEr8VZgIss7fl64t845uu1HXNNyuSj2EhA== - dependencies: - uglify-es "^3.1.9" - metro-react-native-babel-preset@0.64.0, metro-react-native-babel-preset@~0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" @@ -24698,47 +22875,6 @@ metro-react-native-babel-preset@0.64.0, metro-react-native-babel-preset@~0.64.0: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-preset@^0.56.0, metro-react-native-babel-preset@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.56.4.tgz#dcedc64b7ff5c0734839458e70eb0ebef6d063a8" - integrity sha512-CzbBDM9Rh6w8s1fq+ZqihAh7DDqUAcfo9pPww25+N/eJ7UK436Q7JdfxwdIPpBwLFn6o6MyYn+uwL9OEWBJarA== - dependencies: - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-export-default-from" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.0.0" - "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-assign" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-regenerator" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.0.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - "@babel/template" "^7.0.0" - react-refresh "^0.4.0" - metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transformer@^0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" @@ -24751,17 +22887,6 @@ metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transforme metro-source-map "0.64.0" nullthrows "^1.1.1" -metro-react-native-babel-transformer@^0.56.0: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.56.4.tgz#3c6e48b605c305362ee624e45ff338656e35fc1d" - integrity sha512-ng74eutuy1nyGI9+TDzzVAVfEmNPDlapV4msTQMKPi4EFqo/fBn7Ct33ME9l5E51pQBBnxt/UwcpTvd13b29kQ== - dependencies: - "@babel/core" "^7.0.0" - babel-preset-fbjs "^3.1.2" - metro-babel-transformer "^0.56.4" - metro-react-native-babel-preset "^0.56.4" - metro-source-map "^0.56.4" - metro-resolver@0.64.0, metro-resolver@^0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" @@ -24769,13 +22894,6 @@ metro-resolver@0.64.0, metro-resolver@^0.64.0: dependencies: absolute-path "^0.0.0" -metro-resolver@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.56.4.tgz#9876f57bca37fd1bfcffd733541e2ee4a89fad7f" - integrity sha512-Ug4ulVfpkKZ1Wu7mdYj9XLGuOqZTuWCqEhyx3siKTc/2eBwKZQXmiNo5d/IxWNvmwL/87Abeb724I6CMzMfjiQ== - dependencies: - absolute-path "^0.0.0" - metro-runtime@0.64.0, metro-runtime@^0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" @@ -24795,19 +22913,6 @@ metro-source-map@0.64.0: source-map "^0.5.6" vlq "^1.0.0" -metro-source-map@^0.56.0, metro-source-map@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.56.4.tgz#868ccac3f3519fe14eca358bc186f63651b2b9bc" - integrity sha512-f1P9/rpFmG3Z0Jatiw2zvLItx1TwR7mXTSDj4qLDCWeVMB3kEXAr3R0ucumTW8c6HfpJljeRBWzYFXF33fd81g== - dependencies: - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" - invariant "^2.2.4" - metro-symbolicate "^0.56.4" - ob1 "^0.56.4" - source-map "^0.5.6" - vlq "^1.0.0" - metro-symbolicate@0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" @@ -24820,17 +22925,6 @@ metro-symbolicate@0.64.0: through2 "^2.0.1" vlq "^1.0.0" -metro-symbolicate@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.56.4.tgz#53e9d40beac9049fa75a3e620ddd47d4907ff015" - integrity sha512-8mCNNn6zV5FFKCIcRgI7736Xl+owgvYuy8qanPxZN36f7utiWRYeB+PirEBPcglBk4qQvoy2lT6oPULNXZQbbQ== - dependencies: - invariant "^2.2.4" - metro-source-map "^0.56.4" - source-map "^0.5.6" - through2 "^2.0.1" - vlq "^1.0.0" - metro-transform-plugins@0.64.0: version "0.64.0" resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" @@ -24918,65 +23012,6 @@ metro@0.64.0, metro@^0.64.0: ws "^1.1.5" yargs "^15.3.1" -metro@^0.56.0, metro@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.56.4.tgz#be7e1380ee6ac3552c25ead8098eab261029e4d7" - integrity sha512-Kt3OQJQtQdts0JrKnyGdLpKHDjqYBgIfzvYrvfhmFCkKuZ8aqRlVnvpfjQ4/OBm0Fmm9NyyxbNRD9VIbj7WjnA== - dependencies: - "@babel/core" "^7.0.0" - "@babel/generator" "^7.0.0" - "@babel/parser" "^7.0.0" - "@babel/plugin-external-helpers" "^7.0.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" - absolute-path "^0.0.0" - async "^2.4.0" - babel-preset-fbjs "^3.1.2" - buffer-crc32 "^0.2.13" - chalk "^2.4.1" - concat-stream "^1.6.0" - connect "^3.6.5" - debug "^2.2.0" - denodeify "^1.2.1" - eventemitter3 "^3.0.0" - fbjs "^1.0.0" - fs-extra "^1.0.0" - graceful-fs "^4.1.3" - image-size "^0.6.0" - invariant "^2.2.4" - jest-haste-map "^24.7.1" - jest-worker "^24.6.0" - json-stable-stringify "^1.0.1" - lodash.throttle "^4.1.1" - merge-stream "^1.0.1" - metro-babel-register "^0.56.4" - metro-babel-transformer "^0.56.4" - metro-cache "^0.56.4" - metro-config "^0.56.4" - metro-core "^0.56.4" - metro-inspector-proxy "^0.56.4" - metro-minify-uglify "^0.56.4" - metro-react-native-babel-preset "^0.56.4" - metro-resolver "^0.56.4" - metro-source-map "^0.56.4" - metro-symbolicate "^0.56.4" - mime-types "2.1.11" - mkdirp "^0.5.1" - node-fetch "^2.2.0" - nullthrows "^1.1.0" - resolve "^1.5.0" - rimraf "^2.5.4" - serialize-error "^2.1.0" - source-map "^0.5.6" - temp "0.8.3" - throat "^4.1.0" - wordwrap "^1.0.0" - write-file-atomic "^1.2.0" - ws "^1.1.5" - xpipe "^1.0.5" - yargs "^9.0.0" - microevent.ts@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" @@ -25344,18 +23379,6 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-db@~1.23.0: - version "1.23.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.23.0.tgz#a31b4070adaea27d732ea333740a64d0ec9a6659" - integrity sha1-oxtAcK2uon1zLqMzdApk0OyaZlk= - -mime-types@2.1.11: - version "2.1.11" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.11.tgz#c259c471bda808a85d6cd193b430a5fae4473b3c" - integrity sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw= - dependencies: - mime-db "~1.23.0" - mime-types@^2.1.12, mime-types@^2.1.19, mime-types@^2.1.25, mime-types@^2.1.27, mime-types@^2.1.30, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -25425,14 +23448,6 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mini-create-react-context@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" - integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== - dependencies: - "@babel/runtime" "^7.12.1" - tiny-warning "^1.0.3" - mini-css-extract-plugin@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" @@ -25452,16 +23467,6 @@ mini-css-extract-plugin@^0.8.0: schema-utils "^1.0.0" webpack-sources "^1.1.0" -mini-store@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mini-store/-/mini-store-2.0.0.tgz#0843c048d6942ce55e3e78b1b67fc063022b5488" - integrity sha512-EG0CuwpQmX+XL4QVS0kxNwHW5ftSbhygu1qxQH0pipugjnPkbvkalCdQbEihMwtQY6d3MTN+MS0q+aurs+RfLQ== - dependencies: - hoist-non-react-statics "^2.3.1" - prop-types "^15.6.0" - react-lifecycles-compat "^3.0.4" - shallowequal "^1.0.2" - minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -25519,11 +23524,6 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - minimist@^1.1.0, minimist@^1.1.3: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -25685,14 +23685,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mixin-object@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" - integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= - dependencies: - for-in "^0.1.3" - is-extendable "^0.1.1" - mkdirp-infer-owner@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" @@ -25765,12 +23757,12 @@ mold-source-map@~0.4.0: convert-source-map "^1.1.0" through "~2.2.7" -moment@2.x, moment@^2.18.1, moment@^2.24.0: +moment@^2.18.1: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== -morgan@^1.7.0, morgan@^1.9.0: +morgan@^1.7.0: version "1.9.1" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" integrity sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA== @@ -25909,11 +23901,6 @@ mustache@^2.3.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.3.2.tgz#a6d4d9c3f91d13359ab889a812954f9230a3d0c5" integrity sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ== -mutationobserver-shim@^0.3.2: - version "0.3.7" - resolved "https://registry.yarnpkg.com/mutationobserver-shim/-/mutationobserver-shim-0.3.7.tgz#8bf633b0c0b0291a1107255ed32c13088a8c5bf3" - integrity sha512-oRIDTyZQU96nAiz2AQyngwx1e89iApl2hN5AOYwyxLUB47UYsU3Wv9lJWqH5y/QdiYkc5HQLi23ZNB3fELdHcQ== - mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -26037,32 +24024,6 @@ next@12.1.6: "@next/swc-win32-ia32-msvc" "12.1.6" "@next/swc-win32-x64-msvc" "12.1.6" -next@12.3.1: - version "12.3.1" - resolved "https://registry.yarnpkg.com/next/-/next-12.3.1.tgz#127b825ad2207faf869b33393ec8c75fe61e50f1" - integrity sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw== - dependencies: - "@next/env" "12.3.1" - "@swc/helpers" "0.4.11" - caniuse-lite "^1.0.30001406" - postcss "8.4.14" - styled-jsx "5.0.7" - use-sync-external-store "1.2.0" - optionalDependencies: - "@next/swc-android-arm-eabi" "12.3.1" - "@next/swc-android-arm64" "12.3.1" - "@next/swc-darwin-arm64" "12.3.1" - "@next/swc-darwin-x64" "12.3.1" - "@next/swc-freebsd-x64" "12.3.1" - "@next/swc-linux-arm-gnueabihf" "12.3.1" - "@next/swc-linux-arm64-gnu" "12.3.1" - "@next/swc-linux-arm64-musl" "12.3.1" - "@next/swc-linux-x64-gnu" "12.3.1" - "@next/swc-linux-x64-musl" "12.3.1" - "@next/swc-win32-arm64-msvc" "12.3.1" - "@next/swc-win32-ia32-msvc" "12.3.1" - "@next/swc-win32-x64-msvc" "12.3.1" - nice-try@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" @@ -26095,17 +24056,6 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== -nock@11.7.2: - version "11.7.2" - resolved "https://registry.yarnpkg.com/nock/-/nock-11.7.2.tgz#4cee4fa838dc3635c074c5b3436bcdec7f7ee213" - integrity sha512-7swr5bL1xBZ5FctyubjxEVySXOSebyqcL7Vy1bx1nS9IUqQWj81cmKjVKJLr8fHhtzI1MV8nyCdENA/cGcY1+Q== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash "^4.17.13" - mkdirp "^0.5.0" - propagate "^2.0.0" - node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" @@ -26121,7 +24071,7 @@ node-ask@^1.0.1: resolved "https://registry.yarnpkg.com/node-ask/-/node-ask-1.0.1.tgz#caaa1076cc58e0364267a0903e3eadfac158396b" integrity sha1-yqoQdsxY4DZCZ6CQPj6t+sFYOWs= -node-dir@^0.1.10, node-dir@^0.1.17: +node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= @@ -26133,15 +24083,7 @@ node-domexception@^1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" - integrity sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ= - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - -node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -26301,17 +24243,6 @@ node-loggly-bulk@^2.2.4: moment "^2.18.1" request ">=2.76.0 <3.0.0" -node-notifier@^5.2.1, node-notifier@^5.4.2: - version "5.4.5" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.5.tgz#0cbc1a2b0f658493b4025775a13ad938e96091ef" - integrity sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ== - dependencies: - growly "^1.3.0" - is-wsl "^1.1.0" - semver "^5.5.0" - shellwords "^0.1.1" - which "^1.3.0" - node-object-hash@^1.2.0: version "1.4.2" resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94" @@ -26388,11 +24319,6 @@ nomnom@~1.6.2: colors "0.5.x" underscore "~1.4.4" -noop-fn@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf" - integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78= - nopt@1.0.10, nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -26713,15 +24639,7 @@ nth-check@^2.0.0, nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -null-loader@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/null-loader/-/null-loader-3.0.0.tgz#3e2b6c663c5bda8c73a54357d8fa0708dc61b245" - integrity sha512-hf5sNLl8xdRho4UPBOOeoIwT3WhjYcMUQm0zj44EhD6UscMAz72o2udpoDFBgykucdEDGIcd6SXbc/G6zssbzw== - dependencies: - loader-utils "^1.2.3" - schema-utils "^1.0.0" - -nullthrows@1.1.1, nullthrows@^1.1.0, nullthrows@^1.1.1: +nullthrows@1.1.1, nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== @@ -26813,21 +24731,11 @@ ob1@0.64.0: resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ== -ob1@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.56.4.tgz#c4acb3baa42f4993a44b35b2da7c8ef443dcccec" - integrity sha512-URgFof9z2wotiYFsqlydXtQfGV81gvBI2ODy64xfd3vPo+AYom5PVDX4t4zn23t/O+S2IxqApSQM8uJAybmz7w== - -object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= - object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -26938,13 +24846,6 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -omit.js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-1.0.2.tgz#91a14f0eba84066dfa015bf30e474c47f30bc858" - integrity sha512-/QPc6G2NS+8d4L/cQhbk6Yit1WTB6Us2g84A7A/1+w9d/eRGHyEqC5kkQtHVoHZ5NFWGG7tUGgrhVZwgZanKrQ== - dependencies: - babel-runtime "^6.23.0" - on-finished@2.4.1, on-finished@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -27016,18 +24917,6 @@ open@^8.0.4, open@^8.3.0, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -opencollective@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1" - integrity sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE= - dependencies: - babel-polyfill "6.23.0" - chalk "1.1.3" - inquirer "3.0.6" - minimist "1.2.0" - node-fetch "1.6.3" - opn "4.0.2" - opener@1.5.2, opener@^1.5.1, opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -27038,14 +24927,6 @@ openurl@1.1.1: resolved "https://registry.yarnpkg.com/openurl/-/openurl-1.1.1.tgz#3875b4b0ef7a52c156f0db41d4609dbb0f94b387" integrity sha1-OHW0sO96UsFW8NtB1GCduw+Us4c= -opn@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" - integrity sha1-erwi5kTf9jsKltWrfyeQwPAavJU= - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -27179,16 +25060,7 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== - dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" - -os-locale@^3.0.0, os-locale@^3.1.0: +os-locale@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== @@ -27242,13 +25114,6 @@ p-defer@^1.0.0: resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= -p-each-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" - integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= - dependencies: - p-reduce "^1.0.0" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -27271,7 +25136,7 @@ p-limit@^1.0.0, p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -27350,11 +25215,6 @@ p-queue@^6.6.2: eventemitter3 "^4.0.4" p-timeout "^3.2.0" -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" - integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= - p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" @@ -27677,11 +25537,6 @@ parse-ms@^2.1.0: resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== -parse-node-version@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== - parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -27874,18 +25729,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-to-regexp@^1.7.0, path-to-regexp@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - -path-to-regexp@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" - integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== - path-to-regexp@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" @@ -27900,13 +25743,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -28069,7 +25905,7 @@ places.js@1.17.1: events "^3.0.0" insert-css "^2.0.0" -plist@^3.0.0, plist@^3.0.1, plist@^3.0.4: +plist@^3.0.1, plist@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg== @@ -28077,17 +25913,6 @@ plist@^3.0.0, plist@^3.0.1, plist@^3.0.4: base64-js "^1.5.1" xmlbuilder "^9.0.7" -plugin-error@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" - integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4= - dependencies: - ansi-cyan "^0.1.1" - ansi-red "^0.1.1" - arr-diff "^1.0.1" - arr-union "^2.0.1" - extend-shallow "^1.1.2" - pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -28933,15 +26758,6 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: indexes-of "^1.0.1" uniq "^1.0.1" -postcss@8.4.14: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - postcss@8.4.20, postcss@^8.1.10, postcss@^8.2.4, postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.16, postcss@^8.4.18, postcss@^8.4.19: version "8.4.20" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56" @@ -29009,11 +26825,6 @@ posthtml@^0.16.4, posthtml@^0.16.5: posthtml-parser "^0.10.0" posthtml-render "^3.0.0" -pouchdb-collections@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-1.0.1.tgz#fe63a17da977611abef7cb8026cb1a9553fd8359" - integrity sha1-/mOhfal3YRq+98uAJssalVP9g1k= - preact-render-to-string@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-4.1.0.tgz#70d98a8385cb86558366c558aad7311ebecb881d" @@ -29125,34 +26936,6 @@ pretty-error@^2.0.2, pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^23.6.0: - version "23.6.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" - integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw== - dependencies: - ansi-regex "^3.0.0" - ansi-styles "^3.2.0" - -pretty-format@^24.7.0, pretty-format@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" - integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== - dependencies: - "@jest/types" "^24.9.0" - ansi-regex "^4.0.0" - ansi-styles "^3.2.0" - react-is "^16.8.4" - -pretty-format@^25.1.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" - integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== - dependencies: - "@jest/types" "^25.5.0" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" - pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -29340,7 +27123,7 @@ prompts@2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" -prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.0, prompts@^2.4.2: +prompts@^2.0.1, prompts@^2.3.2, prompts@^2.4.0, prompts@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== @@ -29372,7 +27155,7 @@ prop-types@15.7.2: object-assign "^4.1.1" react-is "^16.8.1" -prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -29381,11 +27164,6 @@ prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, pr object-assign "^4.1.1" react-is "^16.13.1" -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - proper-lockfile@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" @@ -29645,7 +27423,7 @@ qrcode-terminal@0.11.0: resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz#ffc6c28a2fc0bfb47052b47e23f4f446a5fbdb9e" integrity sha1-/8bCii/Av7RwUrR+I/T0RqX7254= -qs@6.11.0, qs@^6.10.0, qs@^6.5.0, qs@^6.6.0: +qs@6.11.0, qs@^6.10.0, qs@^6.6.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -29674,7 +27452,7 @@ qs@^6.11.0: dependencies: side-channel "^1.0.4" -query-string@^4.1.0, query-string@^4.2.2: +query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= @@ -29682,16 +27460,6 @@ query-string@^4.1.0, query-string@^4.2.2: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -query-string@^6.13.6: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== - dependencies: - decode-uri-component "^0.2.0" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - querystring-es3@^0.2.0, querystring-es3@^0.2.1, querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -29736,7 +27504,7 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -raf@^3.4.0, raf@^3.4.1: +raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -29819,441 +27587,6 @@ raw-loader@^3.1.0: loader-utils "^1.1.0" schema-utils "^2.0.1" -rc-align@^2.4.0, rc-align@^2.4.1: - version "2.4.5" - resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-2.4.5.tgz#c941a586f59d1017f23a428f0b468663fb7102ab" - integrity sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw== - dependencies: - babel-runtime "^6.26.0" - dom-align "^1.7.0" - prop-types "^15.5.8" - rc-util "^4.0.4" - -rc-animate@2.x, rc-animate@^2.10.1, rc-animate@^2.3.0, rc-animate@^2.6.0, rc-animate@^2.8.2, rc-animate@^2.8.3: - version "2.11.1" - resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.11.1.tgz#2666eeb6f1f2a495a13b2af09e236712278fdb2c" - integrity sha512-1NyuCGFJG/0Y+9RKh5y/i/AalUCA51opyyS/jO2seELpgymZm2u9QV3xwODwEuzkmeQ1BDPxMLmYLcTJedPlkQ== - dependencies: - babel-runtime "6.x" - classnames "^2.2.6" - css-animation "^1.3.2" - prop-types "15.x" - raf "^3.4.0" - rc-util "^4.15.3" - react-lifecycles-compat "^3.0.4" - -rc-animate@^3.0.0-rc.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-3.1.1.tgz#defdd863f56816c222534e4dc68feddecd081386" - integrity sha512-8wg2Zg3EETy0k/9kYuis30NJNQg1D6/WSQwnCiz6SvyxQXNet/rVraRz3bPngwY6rcU2nlRvoShiYOorXyF7Sg== - dependencies: - "@ant-design/css-animation" "^1.7.2" - classnames "^2.2.6" - raf "^3.4.0" - rc-util "^4.15.3" - -rc-calendar@~9.15.5: - version "9.15.11" - resolved "https://registry.yarnpkg.com/rc-calendar/-/rc-calendar-9.15.11.tgz#ce1e5ea8e4d77435be66a8c77db12f1f0f9a345f" - integrity sha512-qv0VXfAAnysMWJigxaP6se4bJHvr17D9qsLbi8BOpdgEocsS0RkgY1IUiFaOVYKJDy/EyLC447O02sV/y5YYBg== - dependencies: - babel-runtime "6.x" - classnames "2.x" - moment "2.x" - prop-types "^15.5.8" - rc-trigger "^2.2.0" - rc-util "^4.1.1" - react-lifecycles-compat "^3.0.4" - -rc-cascader@~0.17.4: - version "0.17.5" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-0.17.5.tgz#4fde91d23b7608c420263c38eee9c0687f80f7dc" - integrity sha512-WYMVcxU0+Lj+xLr4YYH0+yXODumvNXDcVEs5i7L1mtpWwYkubPV/zbQpn+jGKFCIW/hOhjkU4J1db8/P/UKE7A== - dependencies: - array-tree-filter "^2.1.0" - prop-types "^15.5.8" - rc-trigger "^2.2.0" - rc-util "^4.0.4" - react-lifecycles-compat "^3.0.4" - shallow-equal "^1.0.0" - warning "^4.0.1" - -rc-checkbox@~2.1.6: - version "2.1.8" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-2.1.8.tgz#eedd9ef9c2f3af5b3b8e5cde5254aa89ad1a880a" - integrity sha512-6qOgh0/by0nVNASx6LZnhRTy17Etcgav+IrI7kL9V9kcDZ/g7K14JFlqrtJ3NjDq/Kyn+BPI1st1XvbkhfaJeg== - dependencies: - babel-runtime "^6.23.0" - classnames "2.x" - prop-types "15.x" - react-lifecycles-compat "^3.0.4" - -rc-collapse@~1.11.3: - version "1.11.8" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-1.11.8.tgz#66a40089d469519e9424009ab1c927e214041d80" - integrity sha512-8EhfPyScTYljkbRuIoHniSwZagD5UPpZ3CToYgoNYWC85L2qCbPYF7+OaC713FOrIkp6NbfNqXsITNxmDAmxog== - dependencies: - classnames "2.x" - css-animation "1.x" - prop-types "^15.5.6" - rc-animate "2.x" - react-is "^16.7.0" - react-lifecycles-compat "^3.0.4" - shallowequal "^1.1.0" - -rc-dialog@~7.5.2: - version "7.5.14" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-7.5.14.tgz#3e85ed8a6f3ae17cf5082b590b9c366f70ebf5dc" - integrity sha512-gmEukl2iU2K74G2g66rVH6yOCwvLbVWqmEClbRO47Iec/stFyaDXCkvJmBb5vJcAWomQwaU4yZ9muRBW4u9AfA== - dependencies: - babel-runtime "6.x" - rc-animate "2.x" - rc-util "^4.8.1" - -rc-drawer@~2.0.1: - version "2.0.9" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-2.0.9.tgz#d1d07b74416b50d22082b5886ec061be2b2b15fc" - integrity sha512-7qwEND3TLvJeyuUvZfMDkL2pHsR/XHX5HvoaBlIH9mTcFWBmMNrvYGDuGHgGsdNKZZgIBwlkvl5vhckydTUc9Q== - dependencies: - babel-runtime "6.x" - classnames "^2.2.5" - rc-util "^4.7.0" - react-lifecycles-compat "^3.0.4" - -rc-dropdown@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-2.4.1.tgz#aaef6eb3a5152cdd9982895c2a78d9b5f046cdec" - integrity sha512-p0XYn0wrOpAZ2fUGE6YJ6U8JBNc5ASijznZ6dkojdaEfQJAeZtV9KMEewhxkVlxGSbbdXe10ptjBlTEW9vEwEg== - dependencies: - babel-runtime "^6.26.0" - classnames "^2.2.6" - prop-types "^15.5.8" - rc-trigger "^2.5.1" - react-lifecycles-compat "^3.0.2" - -rc-editor-core@~0.8.3: - version "0.8.10" - resolved "https://registry.yarnpkg.com/rc-editor-core/-/rc-editor-core-0.8.10.tgz#6f215bc5df9c33ffa9f6c5b30ca73a7dabe8ab7c" - integrity sha512-T3aHpeMCIYA1sdAI7ynHHjXy5fqp83uPlD68ovZ0oClTSc3tbHmyCxXlA+Ti4YgmcpCYv7avF6a+TIbAka53kw== - dependencies: - babel-runtime "^6.26.0" - classnames "^2.2.5" - draft-js "^0.10.0" - immutable "^3.7.4" - lodash "^4.16.5" - prop-types "^15.5.8" - setimmediate "^1.0.5" - -rc-editor-mention@^1.1.13: - version "1.1.13" - resolved "https://registry.yarnpkg.com/rc-editor-mention/-/rc-editor-mention-1.1.13.tgz#9f1cab1065f86b01523840321790c2ab12ac5e8b" - integrity sha512-3AOmGir91Fi2ogfRRaXLtqlNuIwQpvla7oUnGHS1+3eo7b+fUp5IlKcagqtwUBB5oDNofoySXkLBxzWvSYNp/Q== - dependencies: - babel-runtime "^6.23.0" - classnames "^2.2.5" - dom-scroll-into-view "^1.2.0" - draft-js "~0.10.0" - immutable "~3.7.4" - prop-types "^15.5.8" - rc-animate "^2.3.0" - rc-editor-core "~0.8.3" - -rc-form@^2.4.5: - version "2.4.12" - resolved "https://registry.yarnpkg.com/rc-form/-/rc-form-2.4.12.tgz#4ee8711e90a2584baa7ac276de96bee0d9b0f5f1" - integrity sha512-sHfyWRrnjCHkeCYfYAGop2GQBUC6CKMPcJF9h/gL/vTmZB/RN6fNOGKjXrXjFbwFwKXUWBoPtIDDDmXQW9xNdw== - dependencies: - async-validator "~1.11.3" - babel-runtime "6.x" - create-react-class "^15.5.3" - dom-scroll-into-view "1.x" - hoist-non-react-statics "^3.3.0" - lodash "^4.17.4" - rc-util "^4.15.3" - react-is "^16.13.1" - warning "^4.0.3" - -rc-hammerjs@~0.6.0: - version "0.6.10" - resolved "https://registry.yarnpkg.com/rc-hammerjs/-/rc-hammerjs-0.6.10.tgz#1831a3bd8f2199700bfcc5ad6b20a35630aeb5e0" - integrity sha512-Vgh9qIudyN5CHRop4M+v+xUniQBFWXKrsJxQRVtJOi2xgRrCeI52/bkpaL5HWwUhqTK9Ayq0n7lYTItT6ld5rg== - dependencies: - babel-runtime "6.x" - hammerjs "^2.0.8" - prop-types "^15.5.9" - -rc-input-number@~4.5.0: - version "4.5.9" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-4.5.9.tgz#1cbf735e24fe23c4eb9a4301031720b95f2a3e3d" - integrity sha512-wAT4EBpLDW4+27c935k4F1JLk+gnhyGBkpzBmtkNvIHLG8yTndZSJ2bFfSYfkA6C82IxmAztXs3ffCeUd/rkbg== - dependencies: - babel-runtime "6.x" - classnames "^2.2.0" - prop-types "^15.5.7" - rc-util "^4.5.1" - rmc-feedback "^2.0.0" - -rc-mentions@~0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-0.4.2.tgz#c18ab701efb9e4b75b3851a0c0d2dd698640e246" - integrity sha512-DTZurQzacLXOfVuiHydGzqkq7cFMHXF18l2jZ9PhWUn2cqvOSY3W4osN0Pq29AOMOBpcxdZCzgc7Lb0r/bgkDw== - dependencies: - "@ant-design/create-react-context" "^0.2.4" - classnames "^2.2.6" - rc-menu "^7.4.22" - rc-trigger "^2.6.2" - rc-util "^4.6.0" - react-lifecycles-compat "^3.0.4" - -rc-menu@^7.3.0, rc-menu@^7.4.22: - version "7.5.5" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-7.5.5.tgz#78cdc817d86fc353a1430b864d3d96c7489600ca" - integrity sha512-4YJXJgrpUGEA1rMftXN7bDhrV5rPB8oBJoHqT+GVXtIWCanfQxEnM3fmhHQhatL59JoAFMZhJaNzhJIk4FUWCQ== - dependencies: - classnames "2.x" - dom-scroll-into-view "1.x" - mini-store "^2.0.0" - mutationobserver-shim "^0.3.2" - rc-animate "^2.10.1" - rc-trigger "^2.3.0" - rc-util "^4.13.0" - resize-observer-polyfill "^1.5.0" - shallowequal "^1.1.0" - -rc-menu@~7.4.23: - version "7.4.32" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-7.4.32.tgz#71409216daaa9f43d8acc4530628879740d63708" - integrity sha512-9/ySqniMdvhUhfiUFmqK3hLffYIdeR2nrDDNvXksTCc2ZYd1JmOWF16yO7iYyDPLZM33NU3Qw6EPZd21+FxJsQ== - dependencies: - classnames "2.x" - dom-scroll-into-view "1.x" - mini-store "^2.0.0" - mutationobserver-shim "^0.3.2" - rc-animate "2.x" - rc-trigger "^2.3.0" - rc-util "^4.13.0" - resize-observer-polyfill "^1.5.0" - -rc-notification@~3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-3.3.1.tgz#0baa3e70f8d40ab015ce8fa78c260c490fc7beb4" - integrity sha512-U5+f4BmBVfMSf3OHSLyRagsJ74yKwlrQAtbbL5ijoA0F2C60BufwnOcHG18tVprd7iaIjzZt1TKMmQSYSvgrig== - dependencies: - babel-runtime "6.x" - classnames "2.x" - prop-types "^15.5.8" - rc-animate "2.x" - rc-util "^4.0.4" - -rc-pagination@~1.20.5: - version "1.20.15" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-1.20.15.tgz#ccb4cd0e9bd4e47f72f29ea432c0350bf7b3d807" - integrity sha512-/Xr4/3GOa1DtL8iCYl7qRUroEMrRDhZiiuHwcVFfSiwa9LYloMlUWcOJsnr8LN6A7rLPdm3/CHStUNeYd+2pKw== - dependencies: - babel-runtime "6.x" - classnames "^2.2.6" - prop-types "^15.5.7" - react-lifecycles-compat "^3.0.4" - -rc-progress@~2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-2.5.3.tgz#00f01b95bdbe1856d3a5f82242051902e8b7a8e7" - integrity sha512-K2fa4CnqGehLZoMrdmBeZ86ONSTVcdk5FlqetbwJ3R/+42XfqhwQVOjWp2MH4P7XSQOMAGcNOy1SFfCP3415sg== - dependencies: - babel-runtime "6.x" - prop-types "^15.5.8" - -rc-rate@~2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.5.1.tgz#55fc5fd23ea9dcc72250b9a889803479f4842961" - integrity sha512-3iJkNJT8xlHklPCdeZtUZmJmRVUbr6AHRlfSsztfYTXVlHrv2TcPn3XkHsH+12j812WVB7gvilS2j3+ffjUHXg== - dependencies: - classnames "^2.2.5" - prop-types "^15.5.8" - rc-util "^4.3.0" - react-lifecycles-compat "^3.0.4" - -rc-select@~9.2.0: - version "9.2.3" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-9.2.3.tgz#64340e2d6ef64e8bc3cfc6f468ffd28625589ac2" - integrity sha512-WhswxOMWiNnkXRbxyrj0kiIvyCfo/BaRPaYbsDetSIAU2yEDwKHF798blCP5u86KLOBKBvtxWLFCkSsQw1so5w== - dependencies: - babel-runtime "^6.23.0" - classnames "2.x" - component-classes "1.x" - dom-scroll-into-view "1.x" - prop-types "^15.5.8" - raf "^3.4.0" - rc-animate "2.x" - rc-menu "^7.3.0" - rc-trigger "^2.5.4" - rc-util "^4.0.4" - react-lifecycles-compat "^3.0.2" - warning "^4.0.2" - -rc-slider@~8.6.11: - version "8.6.13" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-8.6.13.tgz#88a8150c2dda6709f3f119135de11fba80af765b" - integrity sha512-fCUe8pPn8n9pq1ARX44nN2nzJoATtna4x/PdskUrxIvZXN8ja7HuceN/hq6kokZjo3FBD2B1yMZvZh6oi68l6Q== - dependencies: - babel-runtime "6.x" - classnames "^2.2.5" - prop-types "^15.5.4" - rc-tooltip "^3.7.0" - rc-util "^4.0.4" - shallowequal "^1.0.1" - warning "^4.0.3" - -rc-steps@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-3.5.0.tgz#36b2a7f1f49907b0d90363884b18623caf9fb600" - integrity sha512-2Vkkrpa7PZbg7qPsqTNzVDov4u78cmxofjjnIHiGB9+9rqKS8oTLPzbW2uiWDr3Lk+yGwh8rbpGO1E6VAgBCOg== - dependencies: - babel-runtime "^6.23.0" - classnames "^2.2.3" - lodash "^4.17.5" - prop-types "^15.5.7" - -rc-switch@~1.9.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-1.9.2.tgz#7921c766411fe9a6426510c3429022d6ba4dfde2" - integrity sha512-qaK7mY4FLDKy99Hq3A1tf8CcqfzKtHp9LPX8WTnZ0MzdHCTneSARb1XD7Eqeu8BactasYGsi2bF9p18Q+/5JEw== - dependencies: - classnames "^2.2.1" - prop-types "^15.5.6" - react-lifecycles-compat "^3.0.4" - -rc-table@~6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-6.7.0.tgz#8aca002f84a43a2d51a4fcda0f7a51694154286d" - integrity sha512-zzu7UtEHLTzZibB1EOoeKQejH21suoxRQx3evlGGLwz5NUh2HDUHobSr12z5Kd8EPr1+y/LPzXJdX1ctFPC+hA== - dependencies: - babel-runtime "6.x" - classnames "^2.2.5" - component-classes "^1.2.6" - lodash "^4.17.5" - mini-store "^2.0.0" - prop-types "^15.5.8" - rc-util "^4.0.4" - react-lifecycles-compat "^3.0.2" - shallowequal "^1.0.2" - warning "^3.0.0" - -rc-tabs@~9.6.4: - version "9.6.7" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-9.6.7.tgz#c546115a351f5ed779ea5524143157f48ee0c015" - integrity sha512-OXbDOgaqv2MGK9QaDi6cdva6bNz3XGw+M9BHQpm1gTGmVQEGx5VcclDClH/3xobIzooxy8hrxg/s0rTlgDnC2w== - dependencies: - "@ant-design/create-react-context" "^0.2.4" - babel-runtime "6.x" - classnames "2.x" - lodash "^4.17.5" - prop-types "15.x" - raf "^3.4.1" - rc-hammerjs "~0.6.0" - rc-util "^4.0.4" - react-lifecycles-compat "^3.0.4" - resize-observer-polyfill "^1.5.1" - warning "^4.0.3" - -rc-time-picker@~3.7.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/rc-time-picker/-/rc-time-picker-3.7.3.tgz#65a8de904093250ae9c82b02a4905e0f995e23e2" - integrity sha512-Lv1Mvzp9fRXhXEnRLO4nW6GLNxUkfAZ3RsiIBsWjGjXXvMNjdr4BX/ayElHAFK0DoJqOhm7c5tjmIYpEOwcUXg== - dependencies: - classnames "2.x" - moment "2.x" - prop-types "^15.5.8" - raf "^3.4.1" - rc-trigger "^2.2.0" - react-lifecycles-compat "^3.0.4" - -rc-tooltip@^3.7.0, rc-tooltip@~3.7.3: - version "3.7.3" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.7.3.tgz#280aec6afcaa44e8dff0480fbaff9e87fc00aecc" - integrity sha512-dE2ibukxxkrde7wH9W8ozHKUO4aQnPZ6qBHtrTH9LoO836PjDdiaWO73fgPB05VfJs9FbZdmGPVEbXCeOP99Ww== - dependencies: - babel-runtime "6.x" - prop-types "^15.5.8" - rc-trigger "^2.2.2" - -rc-tree-select@~2.9.1: - version "2.9.4" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-2.9.4.tgz#6aa794e1f0e65c66c406aa0a2a0e74fd0a557b09" - integrity sha512-0HQkXAN4XbfBW20CZYh3G+V+VMrjX42XRtDCpyv6PDUm5vikC0Ob682ZBCVS97Ww2a5Hf6Ajmu0ahWEdIEpwhg== - dependencies: - classnames "^2.2.1" - dom-scroll-into-view "^1.2.1" - prop-types "^15.5.8" - raf "^3.4.0" - rc-animate "^2.8.2" - rc-tree "~2.1.0" - rc-trigger "^3.0.0" - rc-util "^4.5.0" - react-lifecycles-compat "^3.0.4" - shallowequal "^1.0.2" - warning "^4.0.1" - -rc-tree@~2.1.0: - version "2.1.4" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-2.1.4.tgz#ef759f3e799a21b43c1ecf9c794ea1c14e70b59b" - integrity sha512-Xey794Iavgs8YldFlXcZLOhfcIhlX5Oz/yfKufknBXf2AlZCOkc7aHqSM9uTF7fBPtTGPhPxNEfOqHfY7b7xng== - dependencies: - "@ant-design/create-react-context" "^0.2.4" - classnames "2.x" - prop-types "^15.5.8" - rc-animate "^2.6.0" - rc-util "^4.5.1" - react-lifecycles-compat "^3.0.4" - warning "^4.0.3" - -rc-trigger@^2.2.0, rc-trigger@^2.2.2, rc-trigger@^2.3.0, rc-trigger@^2.5.1, rc-trigger@^2.5.4, rc-trigger@^2.6.2: - version "2.6.5" - resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-2.6.5.tgz#140a857cf28bd0fa01b9aecb1e26a50a700e9885" - integrity sha512-m6Cts9hLeZWsTvWnuMm7oElhf+03GOjOLfTuU0QmdB9ZrW7jR2IpI5rpNM7i9MvAAlMAmTx5Zr7g3uu/aMvZAw== - dependencies: - babel-runtime "6.x" - classnames "^2.2.6" - prop-types "15.x" - rc-align "^2.4.0" - rc-animate "2.x" - rc-util "^4.4.0" - react-lifecycles-compat "^3.0.4" - -rc-trigger@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-3.0.0.tgz#f6d9b1da8a26b2b2d1d912a06876c1a486f5980f" - integrity sha512-hQxbbJpo23E2QnYczfq3Ec5J5tVl2mUDhkqxrEsQAqk16HfADQg+iKNWzEYXyERSncdxfnzYuaBgy764mNRzTA== - dependencies: - babel-runtime "6.x" - classnames "^2.2.6" - prop-types "15.x" - raf "^3.4.0" - rc-align "^2.4.1" - rc-animate "^3.0.0-rc.1" - rc-util "^4.15.7" - -rc-upload@~2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-2.7.0.tgz#f279b758655eb5f99ebf82a5a2648d80d88e0ff4" - integrity sha512-Oh9EJB4xE8MQUZ2D0OUST3UMIBjHjnO2IjPNW/cbPredxZz+lzbLPCZxcxRwUwu1gt0LA968UWXAgT1EvZdFfA== - dependencies: - babel-runtime "6.x" - classnames "^2.2.5" - prop-types "^15.5.7" - warning "4.x" - -rc-util@^4.0.4, rc-util@^4.1.1, rc-util@^4.10.0, rc-util@^4.13.0, rc-util@^4.15.3, rc-util@^4.15.7, rc-util@^4.3.0, rc-util@^4.4.0, rc-util@^4.5.0, rc-util@^4.5.1, rc-util@^4.6.0, rc-util@^4.7.0, rc-util@^4.8.1: - version "4.21.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.21.1.tgz#88602d0c3185020aa1053d9a1e70eac161becb05" - integrity sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg== - dependencies: - add-dom-event-listener "^1.1.0" - prop-types "^15.5.10" - react-is "^16.12.0" - react-lifecycles-compat "^3.0.4" - shallowequal "^1.1.0" - rc9@^1.2.0: version "1.2.4" resolved "https://registry.yarnpkg.com/rc9/-/rc9-1.2.4.tgz#a625f5ec63a54eebd66114eb9c6ce9a6c54dfac8" @@ -30273,24 +27606,6 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8, rc@~1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-autosuggest@9.4.3: - version "9.4.3" - resolved "https://registry.yarnpkg.com/react-autosuggest/-/react-autosuggest-9.4.3.tgz#eb46852422a48144ab9f39fb5470319222f26c7c" - integrity sha512-wFbp5QpgFQRfw9cwKvcgLR8theikOUkv8PFsuLYqI2PUgVlx186Cz8MYt5bLxculi+jxGGUUVt+h0esaBZZouw== - dependencies: - prop-types "^15.5.10" - react-autowhatever "^10.1.2" - shallow-equal "^1.0.0" - -react-autowhatever@^10.1.2: - version "10.1.2" - resolved "https://registry.yarnpkg.com/react-autowhatever/-/react-autowhatever-10.1.2.tgz#200ffc41373b2189e3f6140ac7bdb82363a79fd3" - integrity sha512-+0XgELT1LF7hHEJv5H5Zwkfb4Q1rqmMZZ5U/XJ2J+UcSPRKnG6CqEjXUJ+hYLXDHgvDqwEN5PBdxczD5rHvOuA== - dependencies: - prop-types "^15.5.8" - react-themeable "^1.1.0" - section-iterator "^2.0.0" - react-clientside-effect@^1.2.2: version "1.2.5" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz#e2c4dc3c9ee109f642fac4f5b6e9bf5bcd2219a3" @@ -30298,11 +27613,6 @@ react-clientside-effect@^1.2.2: dependencies: "@babel/runtime" "^7.12.13" -react-clone-referenced-element@*: - version "1.1.1" - resolved "https://registry.yarnpkg.com/react-clone-referenced-element/-/react-clone-referenced-element-1.1.1.tgz#8d76727dc0459788e461741e804a512d20757381" - integrity sha512-LZBPvQV8W0B5dFzXFu+D3Tpil8YHS8tO00iFsfXcTLdtpuH7XyvaHqHcoz4hd4uNPQCZ30fceh+s7mLznzMXvg== - react-color@^2.17.0: version "2.19.3" resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d" @@ -30321,16 +27631,6 @@ react-colorful@^5.1.2: resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.4.0.tgz#e05e602469f9768234f29c1bad9ec1f4e86145a2" integrity sha512-k7QJXuQGWevr/V8hoMJ1wBW9i2CVhBdDXpBf3jy/AAtzVyYtsFqEAT+y+NOGiSG1cmnGTreqm5EFLXlVaKbPLQ== -react-compound-slider@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/react-compound-slider/-/react-compound-slider-2.2.0.tgz#d79adb60d3b42fb2f2e6a88d7630795244a434a2" - integrity sha512-HFYKtbL1MZyrBrK29Y7KBRU2INnPqX0yODdcWd539sif7eLPoG2uEULloga2ju7l7zcIWzJcehO12WrieHDIiA== - dependencies: - "@babel/runtime" "^7.5.4" - d3-array "^1.2.4" - prop-types "^15.7.2" - warning "^3.0.0" - react-compound-slider@3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/react-compound-slider/-/react-compound-slider-3.4.0.tgz#0367befe1367bb7968b38d0cbf07db6192b3c57e" @@ -30401,14 +27701,6 @@ react-dev-utils@~11.0.1: strip-ansi "6.0.0" text-table "0.2.0" -react-devtools-core@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-3.6.3.tgz#977d95b684c6ad28205f0c62e1e12c5f16675814" - integrity sha512-+P+eFy/yo8Z/UH9J0DqHZuUM5+RI2wl249TNvMx3J2jpUomLQa4Zxl56GEotGfw3PIP1eI+hVf1s53FlUONStQ== - dependencies: - shell-quote "^1.6.1" - ws "^3.3.1" - react-devtools-core@^4.6.0: version "4.23.0" resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.23.0.tgz#dff9d12202a472ef62632203d6de3877dc6e58be" @@ -30417,31 +27709,6 @@ react-devtools-core@^4.6.0: shell-quote "^1.6.1" ws "^7" -react-docgen@^5.0.0: - version "5.4.3" - resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-5.4.3.tgz#7d297f73b977d0c7611402e5fc2a168acf332b26" - integrity sha512-xlLJyOlnfr8lLEEeaDZ+X2J/KJoe6Nr9AzxnkdQWush5hz2ZSu66w6iLMOScMmxoSHWpWMn+k3v5ZiyCfcWsOA== - dependencies: - "@babel/core" "^7.7.5" - "@babel/generator" "^7.12.11" - "@babel/runtime" "^7.7.6" - ast-types "^0.14.2" - commander "^2.19.0" - doctrine "^3.0.0" - estree-to-babel "^3.1.0" - neo-async "^2.6.1" - node-dir "^0.1.10" - strip-indent "^3.0.0" - -react-dom@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" - react-dom@18.1.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f" @@ -30490,10 +27757,10 @@ react-fast-compare@2.0.4: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== -react-fast-compare@3.0.1, react-fast-compare@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.0.1.tgz#884d339ce1341aad22392e7a88664c71da48600e" - integrity sha512-C5vP0J644ofZGd54P8++O7AvrqMEbrGf8Ue0eAUJLJyw168dAX2aiYyX/zcY/eSNwO0IDjsKUaLE6n83D+TnEg== +react-fast-compare@^3.0.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== react-focus-lock@^2.1.0: version "2.2.1" @@ -30542,7 +27809,17 @@ react-inspector@^4.0.0: prop-types "^15.6.1" storybook-chromatic "^2.2.2" -react-is@^16.12.0, react-is@^16.13.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0: +"react-instantsearch-core-v6@npm:react-instantsearch-core@6.40.4": + version "6.40.4" + resolved "https://registry.yarnpkg.com/react-instantsearch-core/-/react-instantsearch-core-6.40.4.tgz#74feaa94436a20df91febe64b7d8ef0f7b3e657a" + integrity sha512-sEOgRU2MKL8edO85sNHvKlZ5yq9OFw++CDsEqYpHJvbWLE/2J2N49XAUY90kior09I2kBkbgowBbov+Py1AubQ== + dependencies: + "@babel/runtime" "^7.1.2" + algoliasearch-helper "3.14.0" + prop-types "^15.6.2" + react-fast-compare "^3.0.0" + +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -30557,35 +27834,11 @@ react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-lazy-load@^3.0.13: - version "3.1.13" - resolved "https://registry.yarnpkg.com/react-lazy-load/-/react-lazy-load-3.1.13.tgz#236943f76b7084cc8458716d9632a1c9853ea5cd" - integrity sha512-eAVNUn3vhNj79Iv04NOCwy/sCLyqDEhL3j9aJKV7VJuRBDg6rCiB+BIWHuG7VXJGCgb//6nX/soR8PTyWRhFvQ== - dependencies: - eventlistener "0.0.1" - lodash.debounce "^4.0.0" - lodash.throttle "^4.0.0" - prop-types "^15.5.8" - -react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-native-animatable@^1.2.4: - version "1.3.3" - resolved "https://registry.yarnpkg.com/react-native-animatable/-/react-native-animatable-1.3.3.tgz#a13a4af8258e3bb14d0a9d839917e9bb9274ec8a" - integrity sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w== - dependencies: - prop-types "^15.7.2" - -react-native-button@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/react-native-button/-/react-native-button-2.4.0.tgz#42b487073a7ce7c54ab503f77c6ced1ab95742bf" - integrity sha512-4siaJlpOLeL9fAhX8VU3cnUfcGLe3E2zABDWSKxkF+NiYOd+AnKeYY29WXlV8hXhCFo+Ry7E+alrJ6zjZLTSfg== - dependencies: - prop-types "^15.5.10" - react-native-codegen@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" @@ -30595,119 +27848,6 @@ react-native-codegen@^0.0.6: jscodeshift "^0.11.0" nullthrows "^1.1.1" -react-native-gesture-handler@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.6.1.tgz#678e2dce250ed66e93af409759be22cd6375dd17" - integrity sha512-gQgIKhDiYf754yzhhliagLuLupvGb6ZyBdzYzr7aus3Fyi87TLOw63ers+r4kGw0h26oAWTAdHd34JnF4NeL6Q== - dependencies: - "@egjs/hammerjs" "^2.0.17" - hoist-non-react-statics "^2.3.1" - invariant "^2.2.4" - prop-types "^15.7.2" - -react-native-iphone-x-helper@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010" - integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg== - -react-native-modal-dropdown@0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/react-native-modal-dropdown/-/react-native-modal-dropdown-0.7.0.tgz#d030c4159ff026bedd5c20aa59b146495b860814" - integrity sha512-h2UrozBByQhL56XDboj/wjc/5Ny787eLQ++4ql7TecBdbLqbf+tlE62VeXKz30XVMN3iUVYUR/XmM/RIwLIXEg== - dependencies: - deprecated-react-native-listview "0.0.5" - prop-types "^15.6.0" - -react-native-reanimated@1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-1.7.0.tgz#896db2576552ac59d288a1f6c7f00afc171f240c" - integrity sha512-FQWSqP605eQVJumuK2HpR+7heF0ZI+qfy4jNguv3Xv8nPFHeIgZaRTXHCEQL2AcuSIj50zy8jGJf5l134QMQWQ== - -react-native-router-flux@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/react-native-router-flux/-/react-native-router-flux-4.2.0.tgz#71c59f6a6250e9ed10d61ade506b9b809f879db0" - integrity sha512-ab6HZNKNfYhbk/UEiwSGOY6/lhikybOIGjL8CV9bQSLn2sNT1hkAJ1tRReQQVxHlgNI7N4oWfuH1ewk9rePsRA== - dependencies: - "@babel/runtime" "^7.6.0" - add "^2.0.6" - lodash "^4.17.15" - opencollective "^1.0.3" - path-to-regexp "^2.4.0" - prop-types "^15.6.2" - react-navigation "^4.x" - react-navigation-drawer "^2.2.1" - react-navigation-stack "^1.7.3" - react-navigation-tabs "^2.5.2" - remove "^0.1.5" - -react-native-safe-area-view@^0.14.9: - version "0.14.9" - resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.14.9.tgz#90ee8383037010d9a5055a97cf97e4c1da1f0c3d" - integrity sha512-WII/ulhpVyL/qbYb7vydq7dJAfZRBcEhg4/UWt6F6nAKpLa3gAceMOxBxI914ppwSP/TdUsandFy6lkJQE0z4A== - dependencies: - hoist-non-react-statics "^2.3.1" - -react-native-screens@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-2.2.0.tgz#cc4cdf17426fdda97ad93a5e812a1899390f1978" - integrity sha512-a0VzxOWot7F9B/GQyDSssBRd3jUJazFnTQS61IiyReWB6aHlFhf3Xz10jBRoURXy1EMCDCHgenmTVTkKHpKyqQ== - dependencies: - debounce "^1.2.0" - -react-native-star-rating@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/react-native-star-rating/-/react-native-star-rating-1.1.0.tgz#57fcd023c63e947427c4dbbd1c8f4cc49e5c48ce" - integrity sha512-ocOYx+BKUvfruvXm45MBbQZtpkVO3PQieBDepB0FaLuxE3vUtDTPzHqXuBes3iCM5oRi5umrnmMUMsM0mEq5ZA== - dependencies: - prop-types "^15.5.10" - react-native-animatable "^1.2.4" - react-native-button "^2.3.0" - react-native-vector-icons "^4.5.0" - -react-native-tab-view@^2.15.2: - version "2.16.0" - resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.16.0.tgz#cae72c7084394bd328fac5fefb86cd966df37a86" - integrity sha512-ac2DmT7+l13wzIFqtbfXn4wwfgtPoKzWjjZyrK1t+T8sdemuUvD4zIt+UImg03fu3s3VD8Wh/fBrIdcqQyZJWg== - -react-native-vector-icons@6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-6.6.0.tgz#66cf004918eb05d90778d64bd42077c1800d481b" - integrity sha512-MImKVx8JEvVVBnaShMr7/yTX4Y062JZMupht1T+IEgbqBj4aQeQ1z2SH4VHWKNtWtppk4kz9gYyUiMWqx6tNSw== - dependencies: - lodash "^4.0.0" - prop-types "^15.6.2" - yargs "^13.2.2" - -react-native-vector-icons@^4.5.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-4.6.0.tgz#e4014311ffa6de397d914ffc31b7097a874cc8d5" - integrity sha512-rpfhfPiXCK2PX1nrNhdxSMrEGB/Gw/SvKoPM0G2wAkSoqynnes19K0VYI+Up7DqR1rFIpE4hP2erpT1tNx2tfg== - dependencies: - lodash "^4.0.0" - prop-types "^15.5.10" - yargs "^8.0.2" - -react-native-view-shot@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-3.1.2.tgz#8c8e84c67a4bc8b603e697dbbd59dbc9b4f84825" - integrity sha512-9u9fPtp6a52UMoZ/UCPrCjKZk8tnkI9To0Eh6yYnLKFEGkRZ7Chm6DqwDJbYJHeZrheCCopaD5oEOnRqhF4L2Q== - -react-native-web@0.11.7, react-native-web@~0.11.7: - version "0.11.7" - resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.11.7.tgz#d173d5a9b58db23b6d442c4bc4c81e9939adac23" - integrity sha512-w1KAxX2FYLS2GAi3w3BnEZg/IUu7FdgHnLmFKHplRnHMV3u1OPB2EVA7ndNdfu7ds4Rn2OZjSXoNh6F61g3gkA== - dependencies: - array-find-index "^1.0.2" - create-react-class "^15.6.2" - debounce "^1.2.0" - deep-assign "^3.0.0" - fbjs "^1.0.0" - hyphenate-style-name "^1.0.2" - inline-style-prefixer "^5.0.3" - normalize-css-color "^1.0.2" - prop-types "^15.6.0" - react-timer-mixin "^0.13.4" - react-native-web@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.17.1.tgz#90d473c89dd99b88bc9830b2a9fcdd2fc5f04902" @@ -30759,70 +27899,6 @@ react-native@0.64.3: whatwg-fetch "^3.0.0" ws "^6.1.4" -"react-native@https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz": - version "0.61.4" - resolved "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz#69f3f63c36c9df52611847a67c9d94596c1754cc" - dependencies: - "@babel/runtime" "^7.0.0" - "@react-native-community/cli" "^3.0.0-alpha.1" - "@react-native-community/cli-platform-android" "^3.0.0-alpha.1" - "@react-native-community/cli-platform-ios" "^3.0.0-alpha.1" - abort-controller "^3.0.0" - art "^0.10.0" - base64-js "^1.1.2" - connect "^3.6.5" - create-react-class "^15.6.3" - escape-string-regexp "^1.0.5" - event-target-shim "^5.0.1" - fbjs "^1.0.0" - fbjs-scripts "^1.1.0" - hermes-engine "^0.2.1" - invariant "^2.2.4" - jsc-android "^245459.0.0" - metro-babel-register "^0.56.0" - metro-react-native-babel-transformer "^0.56.0" - metro-source-map "^0.56.0" - nullthrows "^1.1.0" - pretty-format "^24.7.0" - promise "^7.1.1" - prop-types "^15.7.2" - react-devtools-core "^3.6.3" - react-refresh "^0.4.0" - regenerator-runtime "^0.13.2" - scheduler "0.15.0" - stacktrace-parser "^0.1.3" - whatwg-fetch "^3.0.0" - -react-navigation-drawer@^2.2.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/react-navigation-drawer/-/react-navigation-drawer-2.7.1.tgz#b025be11f612e8215f15f9f605851d281606fe82" - integrity sha512-W36XDl+Ts5uGNr6Ryt1xGrQ5JTeddPmvR31bNX7o6Oo7OsdPZ3b1+oMDoRClGca0d6Ggo7btIqiWUsUshm+p/g== - -react-navigation-stack@^1.7.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/react-navigation-stack/-/react-navigation-stack-1.10.3.tgz#e714e442b20427f0d2d3c18fce1f9e8cfe69be0b" - integrity sha512-1gksFi/g/Lg9sBhgLlD0OiEB5xnatHb4C0eNMA5tli9cTVlhq375XNPIqOiTyftibBmjdApAsZFj5srUCoOu/w== - dependencies: - prop-types "^15.7.2" - -react-navigation-tabs@^2.5.2: - version "2.11.1" - resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-2.11.1.tgz#dd2ccb04c540b4439aadc4bc8f5a776dfc90439f" - integrity sha512-wq2wR3awu6PKimmVOycBf+iTPA9FWThbJwcaDBQEhQiiviXQzAtT3lw3nV9oqNIg4v65tdPhL1Dg8ptTJ03NjQ== - dependencies: - hoist-non-react-statics "^3.3.2" - react-lifecycles-compat "^3.0.4" - react-native-iphone-x-helper "^1.3.0" - react-native-tab-view "^2.15.2" - -react-navigation@^4.x: - version "4.4.4" - resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.4.4.tgz#8cda2219196311db440e54998bc724523359949f" - integrity sha512-08Nzy1aKEd73496CsuzN49vLFmxPKYF5WpKGgGvkQ10clB79IRM2BtAfVl6NgPKuUM8FXq1wCsrjo/c5ftl5og== - dependencies: - "@react-navigation/core" "^3.7.9" - "@react-navigation/native" "^3.8.4" - react-popper-tooltip@^2.8.3: version "2.11.1" resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.11.1.tgz#3c4bdfd8bc10d1c2b9a162e859bab8958f5b2644" @@ -30866,48 +27942,6 @@ react-refresh@^0.9.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf" integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ== -react-router-dom@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" - integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.2.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - -react-router@3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.2.1.tgz#b9a3279962bdfbe684c8bd0482b81ef288f0f244" - integrity sha512-SXkhC0nr3G0ltzVU07IN8jYl0bB6FsrDIqlLC9dK3SITXqyTJyM7yhXlUqs89w3Nqi5OkXsfRUeHX+P874HQrg== - dependencies: - create-react-class "^15.5.1" - history "^3.0.0" - hoist-non-react-statics "^2.3.1" - invariant "^2.2.1" - loose-envify "^1.2.0" - prop-types "^15.5.6" - warning "^3.0.0" - -react-router@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" - integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - hoist-non-react-statics "^3.1.0" - loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" - path-to-regexp "^1.7.0" - prop-types "^15.6.2" - react-is "^16.6.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - react-select@^3.0.8, react-select@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.2.0.tgz#de9284700196f5f9b5277c5d850a9ce85f5c72fe" @@ -30940,17 +27974,6 @@ react-sizeme@^2.5.2, react-sizeme@^2.6.7: shallowequal "^1.1.0" throttle-debounce "^2.1.0" -react-slick@~0.25.2: - version "0.25.2" - resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.25.2.tgz#56331b67d47d8bcfe2dceb6acab1c8fd5bd1f6bc" - integrity sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw== - dependencies: - classnames "^2.2.5" - enquire.js "^2.1.6" - json2mq "^0.2.0" - lodash.debounce "^4.0.8" - resize-observer-polyfill "^1.5.0" - react-syntax-highlighter@^11.0.2: version "11.0.2" resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz#4e3f376e752b20d2f54e4c55652fd663149e4029" @@ -30962,16 +27985,6 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-test-renderer@17.0.2, react-test-renderer@^17.0.0: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" - integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== - dependencies: - object-assign "^4.1.1" - react-is "^17.0.2" - react-shallow-renderer "^16.13.1" - scheduler "^0.20.2" - react-test-renderer@18.1.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.1.0.tgz#35b75754834cf9ab517b6813db94aee0a6b545c3" @@ -30981,15 +27994,15 @@ react-test-renderer@18.1.0: react-shallow-renderer "^16.15.0" scheduler "^0.22.0" -react-test-renderer@~16.9.0: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.9.0.tgz#7ed657a374af47af88f66f33a3ef99c9610c8ae9" - integrity sha512-R62stB73qZyhrJo7wmCW9jgl/07ai+YzvouvCXIJLBkRlRqLx4j9RqcLEAfNfU3OxTGucqR2Whmn3/Aad6L3hQ== +react-test-renderer@^17.0.0: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" + integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== dependencies: object-assign "^4.1.1" - prop-types "^15.6.2" - react-is "^16.9.0" - scheduler "^0.15.0" + react-is "^17.0.2" + react-shallow-renderer "^16.13.1" + scheduler "^0.20.2" react-textarea-autosize@^7.1.0: version "7.1.2" @@ -30999,18 +28012,6 @@ react-textarea-autosize@^7.1.0: "@babel/runtime" "^7.1.2" prop-types "^15.6.0" -react-themeable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/react-themeable/-/react-themeable-1.1.0.tgz#7d4466dd9b2b5fa75058727825e9f152ba379a0e" - integrity sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4= - dependencies: - object-assign "^3.0.0" - -react-timer-mixin@^0.13.4: - version "0.13.4" - resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz#75a00c3c94c13abe29b43d63b4c65a88fc8264d3" - integrity sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q== - react-transition-group@^4.3.0: version "4.4.2" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" @@ -31029,14 +28030,6 @@ react@17.0.1: loose-envify "^1.1.0" object-assign "^4.1.1" -react@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - react@18.1.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890" @@ -31120,14 +28113,6 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -31162,15 +28147,6 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -31415,11 +28391,6 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.10.0: - version "0.10.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" - integrity sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w== - regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" @@ -31675,13 +28646,6 @@ remove-trailing-slash@^0.1.0: resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== -remove@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/remove/-/remove-0.1.5.tgz#095ffd827d65c9f41ad97d33e416a75811079955" - integrity sha1-CV/9gn1lyfQa2X0z5BanWBEHmVU= - dependencies: - seq ">= 0.3.5" - renderkid@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.1.tgz#898cabfc8bede4b7b91135a3ffd323e58c0db319" @@ -31844,7 +28808,7 @@ reselect@^4.0.0: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6" integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ== -resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: +resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== @@ -31891,11 +28855,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== - resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -31920,7 +28879,7 @@ resolve@^1.1.3, resolve@^1.1.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.3.2, resolve@^1.4.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -32129,14 +29088,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -rmc-feedback@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/rmc-feedback/-/rmc-feedback-2.0.0.tgz#cbc6cb3ae63c7a635eef0e25e4fbaf5ac366eeaa" - integrity sha512-5PWOGOW7VXks/l3JzlOU9NIxRpuaSS8d9zA3UULUCuTKnpwBHNvv1jSJzxgbbCQeYzROWUpgKI4za3X4C/mKmQ== - dependencies: - babel-runtime "6.x" - classnames "^2.2.5" - roarr@^2.15.3: version "2.15.4" resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" @@ -32380,30 +29331,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= - -rx@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" - integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= - -rxjs@^5.4.3: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.6.0, rxjs@^6.6.6: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -32544,14 +29471,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@0.15.0, scheduler@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e" - integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler@^0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" @@ -32602,7 +29521,7 @@ schema-utils@^3.0.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" -scriptjs@2.5.9, scriptjs@^2.5.8: +scriptjs@2.5.9: version "2.5.9" resolved "https://registry.yarnpkg.com/scriptjs/-/scriptjs-2.5.9.tgz#343915cd2ec2ed9bfdde2b9875cd28f59394b35f" integrity sha512-qGVDoreyYiP1pkQnbnFAUIS5AjenNwwQBdl7zeos9etl+hYKWahjRTfzAZZYBv5xNHx7vNKCmaLDQZ6Fr2AEXg== @@ -32617,11 +29536,6 @@ search-insights@^2.6.0: resolved "https://registry.yarnpkg.com/search-insights/-/search-insights-2.6.0.tgz#bb8771a73b83c4a0f1f207c2f64fea01acd3e7d0" integrity sha512-vU2/fJ+h/Mkm/DJOe+EaM5cafJv/1rRTZpGJTuFPf/Q5LjzgMDsqPdSaZsAe+GAWHHsfsu+rQSAn6c8IGtBEVw== -section-iterator@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a" - integrity sha1-v0RNev7rlK1Dw5rS+yYVFifMuio= - section-matter@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" @@ -32798,14 +29712,6 @@ sentence-case@^3.0.3: tslib "^2.0.3" upper-case-first "^2.0.2" -"seq@>= 0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/seq/-/seq-0.3.5.tgz#ae02af3a424793d8ccbf212d69174e0c54dffe38" - integrity sha1-rgKvOkJHk9jMvyEtaRdODFTf/jg= - dependencies: - chainsaw ">=0.0.7 <0.1" - hashish ">=0.0.2 <0.1" - serialize-error@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-6.0.0.tgz#ccfb887a1dd1c48d6d52d7863b92544331fd752b" @@ -32989,16 +29895,6 @@ sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" - integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= - dependencies: - is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" - mixin-object "^2.0.1" - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -33006,12 +29902,12 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shallow-equal@^1.0.0, shallow-equal@^1.1.0: +shallow-equal@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.0.tgz#fd828d2029ff4e19569db7e19e535e94e2d1f5cc" integrity sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA== -shallowequal@1.1.0, shallowequal@^1.0.1, shallowequal@^1.0.2, shallowequal@^1.1.0: +shallowequal@1.1.0, shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== @@ -33084,11 +29980,6 @@ shelljs@0.8.5, shelljs@^0.8.3, shelljs@^0.8.4: interpret "^1.0.0" rechoir "^0.6.2" -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - shiki@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.11.1.tgz#df0f719e7ab592c484d8b73ec10e215a503ab8cc" @@ -33273,11 +30164,6 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -slide@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= - slugify@^1.3.4: version "1.6.5" resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.5.tgz#c8f5c072bf2135b80703589b39a3d41451fbe8c8" @@ -33561,11 +30447,6 @@ speedometer@~1.0.0: resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-1.0.0.tgz#cd671cb06752c22bca3370e2f334440be4fc62e2" integrity sha1-zWccsGdSwivKM3Di8zREC+T8YuI= -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -33919,21 +30800,11 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - string-argv@~0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== -string-convert@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" - integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c= - string-hash@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" @@ -33947,14 +30818,6 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" -string-length@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" - integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== - dependencies: - astral-regex "^1.0.0" - strip-ansi "^5.2.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -34267,11 +31130,6 @@ styled-jsx@5.0.2: resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.2.tgz#ff230fd593b737e9e68b630a694d460425478729" integrity sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ== -styled-jsx@5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.7.tgz#be44afc53771b983769ac654d355ca8d019dff48" - integrity sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA== - stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" @@ -34533,17 +31391,12 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-parser@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" - integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== - svg-tags@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== -svgo@^1.0.0, svgo@^1.2.2: +svgo@^1.0.0: version "1.3.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== @@ -34583,11 +31436,6 @@ swap-case@^1.1.0: lower-case "^1.1.1" upper-case "^1.1.1" -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= - symbol-observable@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -35011,11 +31859,6 @@ thread-loader@^3.0.4: neo-async "^2.6.2" schema-utils "^3.0.0" -throat@^4.0.0, throat@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" - integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= - throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -35074,11 +31917,6 @@ time-fix-plugin@^2.0.7: resolved "https://registry.yarnpkg.com/time-fix-plugin/-/time-fix-plugin-2.0.7.tgz#4ba70ae2e40cedf34dabe505eda7b71b1b244f50" integrity sha512-uVFet1LQToeUX0rTcSiYVYVoGuBpc8gP/2jnlUzuHMHe+gux6XLsNzxLUweabMwiUj5ejhoIMsUI55nVSEa/Vw== -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - timed-out@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -35116,21 +31954,6 @@ tiny-glob@^0.2.9: globalyzer "0.1.0" globrex "^0.1.2" -tiny-invariant@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" - integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== - -tiny-queue@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" - integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY= - -tiny-warning@^1.0.0, tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== - tinycolor2@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" @@ -35308,16 +32131,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -traverse@>=0.2.4, traverse@^0.6.6, traverse@~0.6.6: +traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= - tree-kill@1.2.2, tree-kill@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -35643,11 +32461,6 @@ typescript@*, typescript@5.1.3: resolved "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== -typescript@3.7.3: - version "3.7.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" - integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== - "typescript@^3 || ^4": version "4.9.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" @@ -35673,7 +32486,7 @@ typical@^6.0.1: resolved "https://registry.yarnpkg.com/typical/-/typical-6.0.1.tgz#89bd1a6aa5e5e96fa907fb6b7579223bff558a06" integrity sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A== -ua-parser-js@^0.7.18, ua-parser-js@^0.7.28, ua-parser-js@^0.7.30: +ua-parser-js@^0.7.28, ua-parser-js@^0.7.30: version "0.7.32" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.32.tgz#cd8c639cdca949e30fa68c44b7813ef13e36d211" integrity sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw== @@ -35846,61 +32659,6 @@ unified@^6.1.6: vfile "^2.0.0" x-is-string "^0.1.0" -unimodules-app-loader@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unimodules-app-loader/-/unimodules-app-loader-1.0.2.tgz#0af821db8ae5d7c09d2be3c62ca5aab571d6e9ac" - integrity sha512-ryRAqSndIkCnWAr5jzSL6yjOrCBeIihItELUTykzi4ZxYV9j4Yl0Sd5+VKl1v/UvB6UNdgIGY4oU7S1b173/FA== - -unimodules-barcode-scanner-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-barcode-scanner-interface/-/unimodules-barcode-scanner-interface-5.1.0.tgz#6d24322b6db556b21eca99a130673c7e07d86559" - integrity sha512-FUau0mm4sBOGmlekltY0iAimJ438w3rtWiv6hcjE77Map527aCH3GyjnZSw78raVxe598EXhWHviuwRxOGINYg== - -unimodules-camera-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-camera-interface/-/unimodules-camera-interface-5.1.0.tgz#ea43a8d05b7b1a9053e6b2281b428a1e80853661" - integrity sha512-uwBmZ3XS6vkdzRAmiDhUE/P7fafN7ufXoRuBDGoX/Q9kIiKg61D8HzTmhLMelvJFW6eCjoBJfh/zRyZ54qcjGg== - -unimodules-constants-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-constants-interface/-/unimodules-constants-interface-5.1.0.tgz#916a8203a887b53cdbcd80b63bc6fd56c85ccfd2" - integrity sha512-TlrqwtKt2G0QH4Fn1ko3tRtLX+eUGSnCBuu1TiAGlsQ5FM/1+AGuJNftHdUwZY1DncIAlw6lhNW+amv0hw5ocg== - -unimodules-face-detector-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-face-detector-interface/-/unimodules-face-detector-interface-5.1.0.tgz#56b4e8c238d8b38f7937f2eb87212d5f87c463f9" - integrity sha512-0qDA6j1WvPM98q32aKvRdFhgSa9Nu8lqNUlrgE740UTYsAmfQl8lM/r2TOuR1k3dVC14q33YvLizSOYM5FLhAw== - -unimodules-file-system-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-file-system-interface/-/unimodules-file-system-interface-5.1.0.tgz#adcba6d6dbb58d889175425dedcbb1501f498ab7" - integrity sha512-G2QXhEXY3uHuDD50MWI7C/nesbVlf2C0QHTs+fAt1VpmWYWfdDaeqgO67f/QRz8FH8xm3ul9XvgP6nA+P0xfIg== - -unimodules-font-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-font-interface/-/unimodules-font-interface-5.1.0.tgz#953c1eb6e1f221f0c7d427d7aba78cce599b4b27" - integrity sha512-ZKycNecNN0xxGIo9Db2n8RYU+ijlc+hzpE5acVSiIlmMjTsiOODRLkF++yKsZxglGXn/blgtBLrcTQr4jJV4MQ== - -unimodules-image-loader-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-image-loader-interface/-/unimodules-image-loader-interface-5.1.0.tgz#40eeecb1d9409b51595b559023230ce50485b626" - integrity sha512-yU1OPWMtZ9QcW5CxLE1DYWrpJGZ1hRGdoFG3vyk4syUS8QsCPR0HXqcI6KlTpI6wcLA0+HtS+1CmgJCMCUDd4w== - -unimodules-permissions-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-permissions-interface/-/unimodules-permissions-interface-5.1.0.tgz#146062ee5cde1f00f34ba2692efab5f0c6f55d02" - integrity sha512-3Mz9A4a+iYF57ZeE99nidRPNM7dX3dzTZRvRQyCP5+CvsEmGNlLTIbTQ7fxKECoe3I6cjw94gNSirxIbwb3lDg== - -unimodules-sensors-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-sensors-interface/-/unimodules-sensors-interface-5.1.0.tgz#2d8f5f15a8b00b3f0aab59c3ff474f39735d634f" - integrity sha512-v8nRFRHtl4jFI1aiAmWurPKDuvboSxj0qoqpy/IB3xkkzBfw4KsZQ1b1yomwNbv9cCqIkFxaNAOzyrvVZrz/dA== - -unimodules-task-manager-interface@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unimodules-task-manager-interface/-/unimodules-task-manager-interface-5.1.0.tgz#49fe4431464faa576ba3453a1824030debbf8d35" - integrity sha512-t7FSWOdw4ev9SlqPzfw9rOKlFyryZbrcmjEr0n6HtPXqZ4NRfPqXtYSjoVWswGb3iGr3GPOIHZ/OQ6Z6StL1NA== - union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" @@ -36327,7 +33085,7 @@ use-subscription@^1.0.0: dependencies: object-assign "^4.1.1" -use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0: +use-sync-external-store@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== @@ -36438,7 +33196,7 @@ v8-compile-cache@2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== -v8-compile-cache@2.3.0, v8-compile-cache@^2.0.0, v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1, v8-compile-cache@^2.3.0: +v8-compile-cache@2.3.0, v8-compile-cache@^2.0.0, v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== @@ -36452,15 +33210,6 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -v8-to-istanbul@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" - integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - valid-url@~1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" @@ -36503,11 +33252,6 @@ validator@^8.0.0: resolved "https://registry.yarnpkg.com/validator/-/validator-8.2.0.tgz#3c1237290e37092355344fef78c231249dab77b9" integrity sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA== -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== - vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -36953,20 +33697,13 @@ ware@^1.3.0: dependencies: wrap-fn "^0.1.0" -warning@4.x, warning@^4.0.1, warning@^4.0.2, warning@^4.0.3, warning@~4.0.3: +warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== dependencies: loose-envify "^1.0.0" -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" - integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= - dependencies: - loose-envify "^1.0.0" - watchpack-chokidar2@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" @@ -37159,23 +33896,6 @@ webpack-cli@3.3.7: v8-compile-cache "2.0.3" yargs "13.2.4" -webpack-cli@^3.3.7: - version "3.3.12" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" - integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== - dependencies: - chalk "^2.4.2" - cross-spawn "^6.0.5" - enhanced-resolve "^4.1.1" - findup-sync "^3.0.0" - global-modules "^2.0.0" - import-local "^2.0.0" - interpret "^1.4.0" - loader-utils "^1.4.0" - supports-color "^6.1.0" - v8-compile-cache "^2.1.1" - yargs "^13.3.2" - webpack-dev-middleware@^3.4.0, webpack-dev-middleware@^3.7.0, webpack-dev-middleware@^3.7.2: version "3.7.3" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" @@ -37336,7 +34056,7 @@ webpack-virtual-modules@^0.2.0: dependencies: debug "^3.0.0" -webpack@4.41.5, webpack@4.43.0, webpack@4.46.0, webpack@^4.0.0, webpack@^4.33.0, webpack@^4.38.0, webpack@^4.41.5, webpack@^4.46.0: +webpack@4.41.5, webpack@4.43.0, webpack@4.46.0, webpack@^4.0.0, webpack@^4.33.0, webpack@^4.38.0, webpack@^4.46.0: version "4.46.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== @@ -37502,7 +34222,7 @@ which-pm@2.0.0: load-yaml-file "^0.2.0" path-exists "^4.0.0" -which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -37688,15 +34408,6 @@ write-file-atomic@2.4.1: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^1.2.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" - integrity sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8= - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - slide "^1.1.5" - write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" @@ -37784,7 +34495,7 @@ ws@^1.1.0, ws@^1.1.5: options ">=0.0.5" ultron "1.0.x" -ws@^3.0.0, ws@^3.3.1: +ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== @@ -37918,7 +34629,7 @@ xml-name-validator@^4.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== -xml2js@0.4.23, xml2js@^0.4.23: +xml2js@0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== @@ -37973,11 +34684,6 @@ xmldoc@^1.1.2: dependencies: sax "^1.2.1" -xpipe@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf" - integrity sha1-jdi/Rfw/f1Xw4FS4ePQ6YmFNr98= - "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -38000,7 +34706,7 @@ y18n@^3.2.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== @@ -38040,7 +34746,7 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^20.2.9: +yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== @@ -38050,14 +34756,6 @@ yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^13.1.0, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -38081,13 +34779,6 @@ yargs-parser@^4.2.0: dependencies: camelcase "^3.0.0" -yargs-parser@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" - integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k= - dependencies: - camelcase "^4.1.0" - yargs@13.2.4: version "13.2.4" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" @@ -38124,25 +34815,7 @@ yargs@6.6.0: y18n "^3.2.1" yargs-parser "^4.2.0" -yargs@^12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" - -yargs@^13.2.2, yargs@^13.3.0, yargs@^13.3.2: +yargs@^13.2.2, yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== @@ -38201,44 +34874,6 @@ yargs@^17.0.0, yargs@^17.6.2: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" - integrity sha1-YpmpBVsc78lp/355wdkY3Osiw2A= - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - -yargs@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c" - integrity sha1-UqzCP+7Kw0BCB47njAwAf1CF20w= - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"

    xuj?g&5fJSa-nJYtSrId!N!U9sjO! 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