@cn-npm/search-autocomplete 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.babelrc +3 -0
  2. package/.storybook/main.js +26 -0
  3. package/.storybook/preview.js +24 -0
  4. package/README.md +69 -0
  5. package/dist/App.d.ts +4 -0
  6. package/dist/bundle.js +406 -0
  7. package/dist/components/ErrorBoundary/ErrorBoundary.d.ts +10 -0
  8. package/dist/components/Loader/Loader.d.ts +6 -0
  9. package/dist/components/SearchAutocomplete/SearchAutocomplete.d.ts +7 -0
  10. package/dist/components/SearchAutocompleteSection/SearchAutocompleteSection.d.ts +9 -0
  11. package/dist/components/SearchAutocompleteTag/SearchAutocompleteTag.d.ts +10 -0
  12. package/dist/components/index.d.ts +3 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/reportWebVitals.d.ts +3 -0
  15. package/dist/search-result.d.ts +42 -0
  16. package/dist/setupTests.d.ts +1 -0
  17. package/generate-react-cli.json +15 -0
  18. package/package.json +110 -0
  19. package/postcss.config.js +6 -0
  20. package/public/favicon.ico +0 -0
  21. package/public/index.html +43 -0
  22. package/public/logo192.png +0 -0
  23. package/public/logo512.png +0 -0
  24. package/public/manifest.json +25 -0
  25. package/public/robots.txt +3 -0
  26. package/rollup.config.js +35 -0
  27. package/src/App.scss +0 -0
  28. package/src/App.tsx +13 -0
  29. package/src/assets/img/circle-close.svg +12 -0
  30. package/src/assets/img/search.svg +18 -0
  31. package/src/components/ErrorBoundary/ErrorBoundary.module.scss +1 -0
  32. package/src/components/ErrorBoundary/ErrorBoundary.tsx +33 -0
  33. package/src/components/Loader/Loader.module.scss +21 -0
  34. package/src/components/Loader/Loader.stories.tsx +12 -0
  35. package/src/components/Loader/Loader.test.tsx +12 -0
  36. package/src/components/Loader/Loader.tsx +18 -0
  37. package/src/components/SearchAutocomplete/SearchAutocomplete.module.scss +3 -0
  38. package/src/components/SearchAutocomplete/SearchAutocomplete.stories.tsx +41 -0
  39. package/src/components/SearchAutocomplete/SearchAutocomplete.test.tsx +86 -0
  40. package/src/components/SearchAutocomplete/SearchAutocomplete.tsx +310 -0
  41. package/src/components/SearchAutocompleteSection/SearchAutocompleteSection.module.scss +64 -0
  42. package/src/components/SearchAutocompleteSection/SearchAutocompleteSection.test.tsx +54 -0
  43. package/src/components/SearchAutocompleteSection/SearchAutocompleteSection.tsx +74 -0
  44. package/src/components/SearchAutocompleteTag/SearchAutocompleteTag.module.scss +6 -0
  45. package/src/components/SearchAutocompleteTag/SearchAutocompleteTag.test.tsx +52 -0
  46. package/src/components/SearchAutocompleteTag/SearchAutocompleteTag.tsx +67 -0
  47. package/src/components/index.tsx +4 -0
  48. package/src/index.css +320 -0
  49. package/src/index.tsx +19 -0
  50. package/src/react-app-env.d.ts +1 -0
  51. package/src/reportWebVitals.ts +15 -0
  52. package/src/search-result.ts +49 -0
  53. package/src/setupTests.ts +5 -0
  54. package/src/stories/Introduction.stories.mdx +211 -0
  55. package/src/stories/assets/code-brackets.svg +1 -0
  56. package/src/stories/assets/colors.svg +1 -0
  57. package/src/stories/assets/comments.svg +1 -0
  58. package/src/stories/assets/direction.svg +1 -0
  59. package/src/stories/assets/flow.svg +1 -0
  60. package/src/stories/assets/plugin.svg +1 -0
  61. package/src/stories/assets/repo.svg +1 -0
  62. package/src/stories/assets/stackalt.svg +1 -0
  63. package/tailwind.config.js +93 -0
  64. package/tsconfig.json +22 -0
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ declare class ErrorBoundary extends React.Component<any, any> {
3
+ constructor(props: any);
4
+ static getDerivedStateFromError(error: any): {
5
+ hasError: boolean;
6
+ };
7
+ componentDidCatch(error: any, errorInfo: any): void;
8
+ render(): any;
9
+ }
10
+ export default ErrorBoundary;
@@ -0,0 +1,6 @@
1
+ import { FC } from "react";
2
+ interface LoaderProps {
3
+ className?: string;
4
+ }
5
+ declare const Loader: FC<LoaderProps>;
6
+ export default Loader;
@@ -0,0 +1,7 @@
1
+ import { FC } from "react";
2
+ interface SearchAutocompleteProps {
3
+ label?: string;
4
+ open?: boolean;
5
+ }
6
+ declare const SearchAutocomplete: FC<SearchAutocompleteProps>;
7
+ export default SearchAutocomplete;
@@ -0,0 +1,9 @@
1
+ import { FC } from "react";
2
+ import { Result } from "../../search-result";
3
+ export interface SearchAutocompleteSectionProps {
4
+ searchTerm: string;
5
+ title?: string;
6
+ results?: Result[];
7
+ }
8
+ declare const SearchAutocompleteSection: FC<SearchAutocompleteSectionProps>;
9
+ export default SearchAutocompleteSection;
@@ -0,0 +1,10 @@
1
+ import { FC } from "react";
2
+ export interface SearchAutocompleteTagProps {
3
+ section: string;
4
+ searchTerm: string;
5
+ ein?: string;
6
+ name: string;
7
+ url?: string;
8
+ }
9
+ declare const SearchAutocompleteTag: FC<SearchAutocompleteTagProps>;
10
+ export default SearchAutocompleteTag;
@@ -0,0 +1,3 @@
1
+ import SearchAutocomplete from "./SearchAutocomplete/SearchAutocomplete";
2
+ import "../index.css";
3
+ export { SearchAutocomplete };
@@ -0,0 +1 @@
1
+ import "./index.css";
@@ -0,0 +1,3 @@
1
+ import { ReportHandler } from 'web-vitals';
2
+ declare const reportWebVitals: (onPerfEntry?: ReportHandler) => void;
3
+ export default reportWebVitals;
@@ -0,0 +1,42 @@
1
+ export interface Result {
2
+ ein?: string;
3
+ url?: string;
4
+ logo?: string;
5
+ title: string;
6
+ city?: string;
7
+ state?: string;
8
+ rating?: string;
9
+ highest_level_advisory?: string;
10
+ }
11
+ export interface Section {
12
+ title: string;
13
+ results: Result[];
14
+ }
15
+ export interface Option {
16
+ title: string;
17
+ count: number;
18
+ }
19
+ export interface Filter {
20
+ name: string;
21
+ options: Option[];
22
+ }
23
+ export interface FacetedResult {
24
+ title: string;
25
+ url: string;
26
+ ein?: string;
27
+ }
28
+ export interface FacetedResponse {
29
+ size: number;
30
+ from: number;
31
+ term: string;
32
+ result_count: number;
33
+ results: FacetedResult[];
34
+ filters: Filter[];
35
+ }
36
+ export interface SearchResponseData {
37
+ searchFaceted?: FacetedResponse;
38
+ searchAutocomplete?: Section[];
39
+ }
40
+ export interface SearchResponse {
41
+ data: SearchResponseData;
42
+ }
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,15 @@
1
+ {
2
+ "usesTypeScript": true,
3
+ "usesCssModule": true,
4
+ "cssPreprocessor": "scss",
5
+ "testLibrary": "None",
6
+ "component": {
7
+ "default": {
8
+ "path": "src/components",
9
+ "withStyle": true,
10
+ "withTest": true,
11
+ "withStory": true,
12
+ "withLazy": false
13
+ }
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,110 @@
1
+ {
2
+ "name": "@cn-npm/search-autocomplete",
3
+ "version": "0.2.16",
4
+ "main": "dist/bundle.js",
5
+ "peerDependencies": {
6
+ "@headlessui/react": "^1.7.1",
7
+ "axios": "^0.27.2",
8
+ "classnames": "^2.3.1",
9
+ "react": "^18.2.0",
10
+ "react-debounce-input": "^3.3.0",
11
+ "react-dom": "^18.2.0",
12
+ "react-highlight-words": "^0.18.0",
13
+ "react-scripts": "5.0.1",
14
+ "react-transition-group": "^4.4.5",
15
+ "web-vitals": "^2.1.4"
16
+ },
17
+ "scripts": {
18
+ "start": "react-scripts start",
19
+ "clean": "rm -rf dist",
20
+ "build": "npm run clean && rollup -c",
21
+ "test": "react-scripts test",
22
+ "eject": "react-scripts eject",
23
+ "storybook": "start-storybook -p 6006 -s public",
24
+ "build-storybook": "build-storybook -s public",
25
+ "release": "npm run build && npm publish"
26
+ },
27
+ "eslintConfig": {
28
+ "extends": [
29
+ "react-app",
30
+ "react-app/jest"
31
+ ],
32
+ "overrides": [
33
+ {
34
+ "files": [
35
+ "**/*.stories.*"
36
+ ],
37
+ "rules": {
38
+ "import/no-anonymous-default-export": "off"
39
+ }
40
+ }
41
+ ]
42
+ },
43
+ "browserslist": {
44
+ "production": [
45
+ ">0.2%",
46
+ "not dead",
47
+ "not op_mini all"
48
+ ],
49
+ "development": [
50
+ "last 1 chrome version",
51
+ "last 1 firefox version",
52
+ "last 1 safari version"
53
+ ]
54
+ },
55
+ "devDependencies": {
56
+ "@headlessui/react": "^1.7.1",
57
+ "@rollup/plugin-commonjs": "^22.0.2",
58
+ "@rollup/plugin-node-resolve": "^14.1.0",
59
+ "@rollup/plugin-typescript": "^8.5.0",
60
+ "@rollup/plugin-url": "^7.0.0",
61
+ "@storybook/addon-actions": "^6.5.10",
62
+ "@storybook/addon-controls": "^6.5.10",
63
+ "@storybook/addon-essentials": "^6.5.10",
64
+ "@storybook/addon-interactions": "^6.5.10",
65
+ "@storybook/addon-links": "^6.5.10",
66
+ "@storybook/addon-postcss": "^3.0.0-alpha.1",
67
+ "@storybook/builder-webpack5": "^6.5.10",
68
+ "@storybook/manager-webpack5": "^6.5.10",
69
+ "@storybook/node-logger": "^6.5.10",
70
+ "@storybook/preset-create-react-app": "^4.1.2",
71
+ "@storybook/react": "^6.5.10",
72
+ "@storybook/testing-library": "^0.0.13",
73
+ "@svgr/rollup": "^6.3.1",
74
+ "@tailwindcss/forms": "^0.5.2",
75
+ "@testing-library/jest-dom": "^5.16.5",
76
+ "@testing-library/react": "^13.3.0",
77
+ "@testing-library/user-event": "^13.5.0",
78
+ "@types/enzyme": "^3.10.12",
79
+ "@types/enzyme-adapter-react-16": "^1.0.6",
80
+ "@types/jest": "^27.5.2",
81
+ "@types/node": "^16.11.47",
82
+ "@types/react": "^18.0.17",
83
+ "@types/react-animate-on-scroll": "^2.1.5",
84
+ "@types/react-dom": "^18.0.6",
85
+ "@types/react-highlight-words": "^0.16.4",
86
+ "@types/react-transition-group": "^4.4.5",
87
+ "autoprefixer": "^10.4.8",
88
+ "axios": "^0.27.2",
89
+ "babel-plugin-named-exports-order": "^0.0.2",
90
+ "classnames": "^2.3.1",
91
+ "postcss": "^8.4.16",
92
+ "prop-types": "^15.8.1",
93
+ "react": "^18.2.0",
94
+ "react-debounce-input": "^3.3.0",
95
+ "react-dom": "^18.2.0",
96
+ "react-highlight-words": "^0.18.0",
97
+ "react-scripts": "5.0.1",
98
+ "react-transition-group": "^4.4.5",
99
+ "rollup": "^2.79.1",
100
+ "rollup-plugin-babel": "^4.4.0",
101
+ "rollup-plugin-peer-deps-external": "^2.2.4",
102
+ "rollup-plugin-postcss": "^4.0.2",
103
+ "rollup-plugin-svg": "^2.0.0",
104
+ "sass": "^1.54.3",
105
+ "tailwindcss": "^3.1.8",
106
+ "typescript": "^4.7.4",
107
+ "web-vitals": "^2.1.4",
108
+ "webpack": "^5.74.0"
109
+ }
110
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
Binary file
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="theme-color" content="#000000" />
8
+ <meta
9
+ name="description"
10
+ content="Web site created using create-react-app"
11
+ />
12
+ <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13
+ <!--
14
+ manifest.json provides metadata used when your web app is installed on a
15
+ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16
+ -->
17
+ <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
18
+ <!--
19
+ Notice the use of %PUBLIC_URL% in the tags above.
20
+ It will be replaced with the URL of the `public` folder during the build.
21
+ Only files inside the `public` folder can be referenced from the HTML.
22
+
23
+ Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24
+ work correctly both with client-side routing and a non-root public URL.
25
+ Learn how to configure a non-root public URL by running `npm run build`.
26
+ -->
27
+ <title>React App</title>
28
+ </head>
29
+ <body>
30
+ <noscript>You need to enable JavaScript to run this app.</noscript>
31
+ <div id="root"></div>
32
+ <!--
33
+ This HTML file is a template.
34
+ If you open it directly in the browser, you will see an empty page.
35
+
36
+ You can add webfonts, meta tags, or analytics to this file.
37
+ The build step will place the bundled scripts into the <body> tag.
38
+
39
+ To begin the development, run `npm start` or `yarn start`.
40
+ To create a production bundle, use `npm run build` or `yarn build`.
41
+ -->
42
+ </body>
43
+ </html>
Binary file
Binary file
@@ -0,0 +1,25 @@
1
+ {
2
+ "short_name": "React App",
3
+ "name": "Create React App Sample",
4
+ "icons": [
5
+ {
6
+ "src": "favicon.ico",
7
+ "sizes": "64x64 32x32 24x24 16x16",
8
+ "type": "image/x-icon"
9
+ },
10
+ {
11
+ "src": "logo192.png",
12
+ "type": "image/png",
13
+ "sizes": "192x192"
14
+ },
15
+ {
16
+ "src": "logo512.png",
17
+ "type": "image/png",
18
+ "sizes": "512x512"
19
+ }
20
+ ],
21
+ "start_url": ".",
22
+ "display": "standalone",
23
+ "theme_color": "#000000",
24
+ "background_color": "#ffffff"
25
+ }
@@ -0,0 +1,3 @@
1
+ # https://www.robotstxt.org/robotstxt.html
2
+ User-agent: *
3
+ Disallow:
@@ -0,0 +1,35 @@
1
+ /* eslint-disable import/no-anonymous-default-export */
2
+ import typescript from "@rollup/plugin-typescript";
3
+ import external from "rollup-plugin-peer-deps-external";
4
+ import nodeResolve from "@rollup/plugin-node-resolve";
5
+ import commonjs from "@rollup/plugin-commonjs";
6
+ import postcss from "rollup-plugin-postcss";
7
+ import pkg from "./package.json";
8
+ import babel from "rollup-plugin-babel";
9
+ import svgr from "@svgr/rollup";
10
+
11
+ export default {
12
+ input: "src/components/index.tsx",
13
+ output: {
14
+ file: pkg.main,
15
+ format: "cjs",
16
+ },
17
+ plugins: [
18
+ svgr({
19
+ exportType: "named",
20
+ }),
21
+ external(),
22
+ postcss(),
23
+ babel(),
24
+ nodeResolve(),
25
+ typescript({
26
+ exclude: [
27
+ "**/__tests__",
28
+ "**/*.test.tsx",
29
+ "**/*.test.ts",
30
+ "**/*.stories.tsx",
31
+ ],
32
+ }),
33
+ commonjs(),
34
+ ],
35
+ };
package/src/App.scss ADDED
File without changes
package/src/App.tsx ADDED
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import "./App.scss";
3
+ import SearchAutocomplete from "./components/SearchAutocomplete/SearchAutocomplete";
4
+
5
+ function App() {
6
+ return (
7
+ <div className="App">
8
+ <SearchAutocomplete />
9
+ </div>
10
+ );
11
+ }
12
+
13
+ export default App;
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
3
+ <g id="_System_Icons" data-name="*System_Icons">
4
+ <g id="System_Icons" data-name="System/Icons">
5
+ <g id="Group-9">
6
+ <g id="streamline-icon-remove-circle_48x48" data-name="streamline-icon-remove-circle@48x48">
7
+ <path id="Shape" d="M9,0C4.03,0,0,4.03,0,9s4.03,9,9,9,9-4.03,9-9c0-2.39-.95-4.68-2.64-6.36-1.69-1.69-3.98-2.64-6.36-2.64Zm4.12,12.05c.29,.29,.3,.77,0,1.06-.29,.29-.77,.3-1.06,0l-2.93-2.93s-.08-.06-.14-.06-.1,.02-.14,.06l-2.92,2.93c-.29,.29-.77,.29-1.06,0-.29-.29-.29-.77,0-1.06l2.92-2.92s.06-.08,.06-.14-.02-.1-.06-.14l-2.92-2.92c-.25-.3-.24-.74,.04-1.02,.28-.28,.72-.29,1.02-.04l2.92,2.92s.08,.06,.14,.06,.1-.02,.14-.06l2.93-2.92c.3-.25,.74-.24,1.02,.04,.28,.28,.29,.72,.04,1.02l-2.93,2.92s-.06,.08-.06,.14,.02,.1,.06,.14l2.93,2.92Z"/>
8
+ </g>
9
+ </g>
10
+ </g>
11
+ </g>
12
+ </svg>
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16.14 16.14">
3
+ <g id="_System_Icons" data-name="*System_Icons">
4
+ <g id="System_Icons" data-name="System/Icons">
5
+ <g id="Group-8">
6
+ <g id="Group-7">
7
+ <g id="atom_icon_search" data-name="atom/icon/search">
8
+ <g id="icon_dark_magnifying-glass" data-name="icon/dark/magnifying-glass">
9
+ <g id="Mask">
10
+ <path id="path-1" d="M7,.02C3.14,.02,0,3.16,0,7.02s3.14,7,7,7c1.18,0,2.34-.28,3.32-.82,.08,.09,.17,.18,.26,.26l2,2c.5,.57,1.28,.81,2.01,.63,.74-.18,1.31-.76,1.49-1.49,.18-.74-.06-1.51-.63-2.01l-2-2c-.1-.1-.2-.18-.32-.26,.54-.98,.88-2.12,.88-3.32C14.02,3.14,10.88,0,7.02,0l-.02,.02Zm0,2c2.78,0,5,2.22,5,5,0,1.32-.48,2.54-1.32,3.44-.02,.02-.04,.04-.06,.06-.09,.08-.18,.17-.26,.26-.88,.8-2.08,1.26-3.38,1.26-2.78,0-5-2.22-5-5S4.2,2.04,6.98,2.04l.02-.02Z" style="fill-rule: evenodd;"/>
11
+ </g>
12
+ </g>
13
+ </g>
14
+ </g>
15
+ </g>
16
+ </g>
17
+ </g>
18
+ </svg>
@@ -0,0 +1 @@
1
+ .ErrorBoundary {}
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+
3
+ class ErrorBoundary extends React.Component<any, any> {
4
+ constructor(props: any) {
5
+ super(props);
6
+ this.state = { hasError: false };
7
+ }
8
+
9
+ static getDerivedStateFromError(error: any) {
10
+ // Update state so the next render will show the fallback UI.
11
+ return { hasError: true };
12
+ }
13
+
14
+ componentDidCatch(error: any, errorInfo: any) {
15
+ // You can also log the error to an error reporting service
16
+ // logErrorToMyService(error, errorInfo);
17
+ }
18
+
19
+ render() {
20
+ if (this.state.hasError) {
21
+ // Render fallback UI
22
+ return (
23
+ <div className="px-6 h-full mb-12 max-h-[70vh] overflow-y-auto">
24
+ <p>Sorry, there was a problem. Please try your search again.</p>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ return this.props.children;
30
+ }
31
+ }
32
+
33
+ export default ErrorBoundary;
@@ -0,0 +1,21 @@
1
+ .Loader {
2
+ width: 24px;
3
+ height: 24px;
4
+ border-radius: 50%;
5
+ display: inline-block;
6
+ border-top: 3px solid #fff;
7
+ border-right: 3px solid transparent;
8
+ box-sizing: border-box;
9
+ animation: rotation 1s linear infinite;
10
+ @apply border-t-blue-500;
11
+ }
12
+
13
+ // Rotation animation
14
+ @keyframes rotation {
15
+ 0% {
16
+ transform: rotate(0deg);
17
+ }
18
+ 100% {
19
+ transform: rotate(360deg);
20
+ }
21
+ }
@@ -0,0 +1,12 @@
1
+ /* eslint-disable */
2
+ import Loader from './Loader';
3
+
4
+ export default {
5
+ title: "Loader",
6
+ };
7
+
8
+ export const Default = () => <Loader />;
9
+
10
+ Default.story = {
11
+ name: 'default',
12
+ };
@@ -0,0 +1,12 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import Loader from "./Loader";
3
+
4
+ // Loader test block
5
+ describe("<Loader />", () => {
6
+ // Component should mount
7
+ test("it should mount", () => {
8
+ render(<Loader />);
9
+ const saveButton = screen.getByTestId("Loader");
10
+ expect(saveButton).toBeInTheDocument();
11
+ });
12
+ });
@@ -0,0 +1,18 @@
1
+ import classNames from "classnames";
2
+ import React, { FC } from "react";
3
+ import styles from "./Loader.module.scss";
4
+
5
+ // Loader properties
6
+ interface LoaderProps {
7
+ className?: string;
8
+ }
9
+
10
+ // Loader component
11
+ const Loader: FC<LoaderProps> = (props) => (
12
+ <span
13
+ className={classNames(styles.Loader, props.className)}
14
+ data-testid="Loader"
15
+ ></span>
16
+ );
17
+
18
+ export default Loader;
@@ -0,0 +1,3 @@
1
+ .SearchAutocomplete {
2
+ @apply relative font-sofia-pro;
3
+ }
@@ -0,0 +1,41 @@
1
+ /* eslint-disable */
2
+ import SearchAutocomplete from "./SearchAutocomplete";
3
+
4
+ export default {
5
+ title: "SearchAutocomplete",
6
+ };
7
+
8
+ export const Default = (args: any) => <SearchAutocomplete {...args} />;
9
+
10
+ // Default arguments
11
+ Default.args = {
12
+ open: false,
13
+ label: "",
14
+ };
15
+
16
+ // Storybook controls
17
+ Default.argTypes = {
18
+ open: {
19
+ name: "Show Results",
20
+ description: "Show or hide the search results popover (for dev only).",
21
+ control: "boolean",
22
+ table: {
23
+ defaultValue: {
24
+ summary: false,
25
+ },
26
+ },
27
+ },
28
+ label: {
29
+ name: "Label",
30
+ description: "Text for label and placeholder",
31
+ table: {
32
+ defaultValue: {
33
+ summary: "Search by Charity or Cause",
34
+ },
35
+ },
36
+ },
37
+ };
38
+
39
+ Default.story = {
40
+ name: "Default",
41
+ };
@@ -0,0 +1,86 @@
1
+ import React from "react";
2
+ import {
3
+ fireEvent,
4
+ render,
5
+ screen,
6
+ waitFor,
7
+ within,
8
+ } from "@testing-library/react";
9
+ import "@testing-library/jest-dom/extend-expect";
10
+ import SearchAutocomplete from "./SearchAutocomplete";
11
+
12
+ describe("<SearchAutocomplete />", () => {
13
+ // Component should mount
14
+ test("it should mount", async () => {
15
+ await render(<SearchAutocomplete />);
16
+ const searchAutocomplete = screen.getByTestId("SearchAutocomplete");
17
+ expect(searchAutocomplete).toBeInTheDocument();
18
+ });
19
+
20
+ // Component should have an input
21
+ test("it should have an input", async () => {
22
+ render(<SearchAutocomplete />);
23
+ const searchInput = screen.getByRole("textbox", { name: /search/i });
24
+ expect(searchInput).toBeInTheDocument();
25
+ });
26
+
27
+ // Component should have a placeholder
28
+ test("it should have a placeholder", async () => {
29
+ await render(<SearchAutocomplete />);
30
+ const searchInput = screen.getByRole("textbox", { name: /search/i });
31
+ expect(searchInput.hasAttribute("placeholder")).toBeTruthy();
32
+ });
33
+
34
+ // Component should have a search button
35
+ test("it should have a seach button", async () => {
36
+ await render(<SearchAutocomplete />);
37
+ const saveButton = screen.getByRole("button");
38
+ expect(saveButton).toBeInTheDocument();
39
+ });
40
+
41
+ // Component should show modal on focus
42
+ test("it should show modal on focus", async () => {
43
+ await render(<SearchAutocomplete />);
44
+ const searchInput = screen.getByRole("textbox", { name: /search/i });
45
+ fireEvent.focus(searchInput);
46
+ const searchResults = screen.getByTestId("searchResults");
47
+ expect(searchResults).toBeInTheDocument();
48
+ });
49
+
50
+ // Component should show loading on search
51
+ test("it should show loading on search", async () => {
52
+ await render(<SearchAutocomplete />);
53
+ const searchInput = screen.getByRole("textbox", { name: /search/i });
54
+ await fireEvent.focus(searchInput);
55
+ await fireEvent.change(searchInput, { target: { value: "hear" } });
56
+
57
+ const loader = screen.getByTestId("Loader");
58
+ expect(loader).toBeInTheDocument();
59
+ });
60
+
61
+ // Component should show message on error
62
+ test("it should show message on error", async () => {
63
+ await render(<SearchAutocomplete />);
64
+ const searchInput = screen.getByRole("textbox", { name: /search/i });
65
+ await fireEvent.focus(searchInput);
66
+ await fireEvent.change(searchInput, { target: { value: "///\"''" } });
67
+ const errorText = await screen.findByText(
68
+ "Sorry, there was a problem. Please try your search again."
69
+ );
70
+ expect(errorText).toBeInTheDocument();
71
+ });
72
+
73
+ // Component should show results on search
74
+ test("it should show results on search", async () => {
75
+ await render(<SearchAutocomplete />);
76
+ const searchInput = screen.getByRole("textbox", { name: /search/i });
77
+ await fireEvent.focus(searchInput);
78
+ await fireEvent.change(searchInput, { target: { value: "hear" } });
79
+
80
+ await waitFor(async () => {
81
+ const sections = await screen.findByTestId("Sections");
82
+ const results = within(sections).getAllByRole("link");
83
+ expect(results.length).toBeGreaterThan(0);
84
+ });
85
+ });
86
+ });