@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.
- package/.babelrc +3 -0
- package/.storybook/main.js +26 -0
- package/.storybook/preview.js +24 -0
- package/README.md +69 -0
- package/dist/App.d.ts +4 -0
- package/dist/bundle.js +406 -0
- package/dist/components/ErrorBoundary/ErrorBoundary.d.ts +10 -0
- package/dist/components/Loader/Loader.d.ts +6 -0
- package/dist/components/SearchAutocomplete/SearchAutocomplete.d.ts +7 -0
- package/dist/components/SearchAutocompleteSection/SearchAutocompleteSection.d.ts +9 -0
- package/dist/components/SearchAutocompleteTag/SearchAutocompleteTag.d.ts +10 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/reportWebVitals.d.ts +3 -0
- package/dist/search-result.d.ts +42 -0
- package/dist/setupTests.d.ts +1 -0
- package/generate-react-cli.json +15 -0
- package/package.json +110 -0
- package/postcss.config.js +6 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +43 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/rollup.config.js +35 -0
- package/src/App.scss +0 -0
- package/src/App.tsx +13 -0
- package/src/assets/img/circle-close.svg +12 -0
- package/src/assets/img/search.svg +18 -0
- package/src/components/ErrorBoundary/ErrorBoundary.module.scss +1 -0
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +33 -0
- package/src/components/Loader/Loader.module.scss +21 -0
- package/src/components/Loader/Loader.stories.tsx +12 -0
- package/src/components/Loader/Loader.test.tsx +12 -0
- package/src/components/Loader/Loader.tsx +18 -0
- package/src/components/SearchAutocomplete/SearchAutocomplete.module.scss +3 -0
- package/src/components/SearchAutocomplete/SearchAutocomplete.stories.tsx +41 -0
- package/src/components/SearchAutocomplete/SearchAutocomplete.test.tsx +86 -0
- package/src/components/SearchAutocomplete/SearchAutocomplete.tsx +310 -0
- package/src/components/SearchAutocompleteSection/SearchAutocompleteSection.module.scss +64 -0
- package/src/components/SearchAutocompleteSection/SearchAutocompleteSection.test.tsx +54 -0
- package/src/components/SearchAutocompleteSection/SearchAutocompleteSection.tsx +74 -0
- package/src/components/SearchAutocompleteTag/SearchAutocompleteTag.module.scss +6 -0
- package/src/components/SearchAutocompleteTag/SearchAutocompleteTag.test.tsx +52 -0
- package/src/components/SearchAutocompleteTag/SearchAutocompleteTag.tsx +67 -0
- package/src/components/index.tsx +4 -0
- package/src/index.css +320 -0
- package/src/index.tsx +19 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/reportWebVitals.ts +15 -0
- package/src/search-result.ts +49 -0
- package/src/setupTests.ts +5 -0
- package/src/stories/Introduction.stories.mdx +211 -0
- package/src/stories/assets/code-brackets.svg +1 -0
- package/src/stories/assets/colors.svg +1 -0
- package/src/stories/assets/comments.svg +1 -0
- package/src/stories/assets/direction.svg +1 -0
- package/src/stories/assets/flow.svg +1 -0
- package/src/stories/assets/plugin.svg +1 -0
- package/src/stories/assets/repo.svg +1 -0
- package/src/stories/assets/stackalt.svg +1 -0
- package/tailwind.config.js +93 -0
- 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,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;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./index.css";
|
|
@@ -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
|
+
}
|
|
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
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -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
|
+
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,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
|
+
});
|