From ea58095ca121be302f0f75c2dfce35328e443ca9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tadeusz=20Miesi=C4=85c?= <tadeusz.miesiac@gmail.com>
Date: Fri, 22 Sep 2023 15:54:29 +0200
Subject: [PATCH] feat(top bar component init): added basic style, avarat and
 search input component

---
 .eslintrc.json                                |   3 +-
 jest.config.mjs => jest.config.ts             |   2 ++
 package.json                                  |   6 ++--
 setupTests.ts                                 |   1 +
 src/assets/.gitkeep                           |   0
 src/assets/images/user-avatar.png             | Bin 0 -> 2627 bytes
 src/assets/vectors/icons/.gitkeep             |   0
 src/assets/vectors/icons/lens.svg             |  10 ++++++
 .../SearchBar/SearchBar.component.test.tsx    |  15 +++++++++
 .../TopBar/SearchBar/SearchBar.component.tsx  |  30 ++++++++++++++++++
 .../FunctionalArea/TopBar/SearchBar/index.ts  |   1 +
 .../TopBar/TopBar.component.test.tsx          |  13 ++++++++
 .../TopBar/TopBar.component.tsx               |  15 ++++++++-
 .../UserAvatar/UserAvatar.component.test.tsx  |  12 +++++++
 .../UserAvatar/UserAvatar.component.tsx       |   8 +++++
 .../FunctionalArea/TopBar/UserAvatar/index.ts |   1 +
 tsconfig.json                                 |  10 +++++-
 17 files changed, 121 insertions(+), 6 deletions(-)
 rename jest.config.mjs => jest.config.ts (90%)
 create mode 100644 setupTests.ts
 delete mode 100644 src/assets/.gitkeep
 create mode 100644 src/assets/images/user-avatar.png
 delete mode 100644 src/assets/vectors/icons/.gitkeep
 create mode 100644 src/assets/vectors/icons/lens.svg
 create mode 100644 src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx
 create mode 100644 src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx
 create mode 100644 src/components/FunctionalArea/TopBar/SearchBar/index.ts
 create mode 100644 src/components/FunctionalArea/TopBar/TopBar.component.test.tsx
 create mode 100644 src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.test.tsx
 create mode 100644 src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx
 create mode 100644 src/components/FunctionalArea/TopBar/UserAvatar/index.ts

diff --git a/.eslintrc.json b/.eslintrc.json
index 9b67c6a2..94cbfd15 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -62,7 +62,8 @@
           "test-*.{ts,tsx}", // repos with multiple top-level test files
           "**/*{.,_}{test,spec}.{ts,tsx}", // tests where the extension or filename suffix denotes that it is a test
           "**/jest.config.ts", // jest config
-          "**/jest.setup.ts" // jest setup
+          "**/jest.setup.ts", // jest setup
+          "**/setupTests.ts"
         ],
         "optionalDependencies": false
       }
diff --git a/jest.config.mjs b/jest.config.ts
similarity index 90%
rename from jest.config.mjs
rename to jest.config.ts
index 2d42f861..7e7dad84 100644
--- a/jest.config.mjs
+++ b/jest.config.ts
@@ -1,3 +1,4 @@
+// eslint-disable-next-line import/extensions
 import nextJest from 'next/jest.js';
 
 const createJestConfig = nextJest({
@@ -23,6 +24,7 @@ const config = {
     '!**/node_modules/**',
   ],
   coverageReporters: ['html', 'text', 'text-summary', 'cobertura'],
+  setupFilesAfterEnv: ['<rootDir>/setupTests.ts'],
 };
 
 // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
diff --git a/package.json b/package.json
index b4864fbb..a840cf2f 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,9 @@
     "lint:ts": "node_modules/eslint/bin/eslint.js src --ext .ts,.tsx",
     "prepare": "husky install",
     "postinstall": "husky install",
-    "test": "jest --watch --config ./jest.config.mjs",
-    "test:ci": "jest --config ./jest.config.mjs --collectCoverage --coverageDirectory=\"./coverage\" --ci --reporters=default --reporters=jest-junit --watchAll=false --passWithNoTests",
-    "test:coverage": "jest --watchAll --coverage --config ./jest.config.mjs",
+    "test": "jest --watch --config ./jest.config.ts",
+    "test:ci": "jest --config ./jest.config.ts --collectCoverage --coverageDirectory=\"./coverage\" --ci --reporters=default --reporters=jest-junit --watchAll=false --passWithNoTests",
+    "test:coverage": "jest --watchAll --coverage --config ./jest.config.ts",
     "test:coveragee": "jest --coverage",
     "coverage": "open ./coverage/lcov-report/index.html",
     "cypress": "cypress open"
diff --git a/setupTests.ts b/setupTests.ts
new file mode 100644
index 00000000..7b0828bf
--- /dev/null
+++ b/setupTests.ts
@@ -0,0 +1 @@
+import '@testing-library/jest-dom';
diff --git a/src/assets/.gitkeep b/src/assets/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/assets/images/user-avatar.png b/src/assets/images/user-avatar.png
new file mode 100644
index 0000000000000000000000000000000000000000..3775ddea4346e27cc72a938e3bc1bedbe81f66ce
GIT binary patch
literal 2627
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

literal 0
HcmV?d00001

diff --git a/src/assets/vectors/icons/.gitkeep b/src/assets/vectors/icons/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/assets/vectors/icons/lens.svg b/src/assets/vectors/icons/lens.svg
new file mode 100644
index 00000000..9e2627a9
--- /dev/null
+++ b/src/assets/vectors/icons/lens.svg
@@ -0,0 +1,10 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2474_4682)">
+<path d="M12.0208 11.0767L14.8762 13.9314L13.9328 14.8747L11.0782 12.0194C10.016 12.8708 8.69483 13.334 7.3335 13.332C4.0215 13.332 1.3335 10.644 1.3335 7.33203C1.3335 4.02003 4.0215 1.33203 7.3335 1.33203C10.6455 1.33203 13.3335 4.02003 13.3335 7.33203C13.3354 8.69337 12.8723 10.0145 12.0208 11.0767ZM10.6835 10.582C11.5296 9.71196 12.0021 8.54565 12.0002 7.33203C12.0002 4.75336 9.9115 2.66536 7.3335 2.66536C4.75483 2.66536 2.66683 4.75336 2.66683 7.33203C2.66683 9.91003 4.75483 11.9987 7.3335 11.9987C8.54712 12.0006 9.71342 11.5281 10.5835 10.682L10.6835 10.582Z" fill="#6A6977"/>
+</g>
+<defs>
+<clipPath id="clip0_2474_4682">
+<rect width="16" height="16" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx
new file mode 100644
index 00000000..b8f11bb2
--- /dev/null
+++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx
@@ -0,0 +1,15 @@
+import { screen, render, RenderResult, fireEvent } from '@testing-library/react';
+import { SearchBar } from './SearchBar.component';
+
+const renderComponent = (): RenderResult => render(<SearchBar />);
+
+describe('SearchBar - component', () => {
+  it('should let user type text', () => {
+    renderComponent();
+
+    const input = screen.getByRole('input', { name: 'search-input' });
+    fireEvent.change(input, { target: { value: 'test value' } });
+
+    expect(screen.getByDisplayValue('test value')).toBeInTheDocument();
+  });
+});
diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx
new file mode 100644
index 00000000..82f82bb9
--- /dev/null
+++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx
@@ -0,0 +1,30 @@
+import Image from 'next/image';
+import { ChangeEvent, useState } from 'react';
+import lensIcon from '@/assets/vectors/icons/lens.svg';
+
+export const SearchBar = (): JSX.Element => {
+  const [searchValue, setSearchValue] = useState<string>('');
+
+  const onSearchChange = (event: ChangeEvent<HTMLInputElement>): void => {
+    setSearchValue(event.target.value);
+  };
+
+  return (
+    <div className="relative" data-testid="search-bar">
+      <input
+        value={searchValue}
+        name="search-input"
+        aria-label="search-input"
+        onChange={onSearchChange}
+        className="bg-cultured r-[64px] w-72 rounded-[64px] py-2.5 px-4 text-xs outline-none border border-transparent focus:border-greyscale-600 hover:border-greyscale-600  font-medium text-font-400"
+      />
+      <Image
+        src={lensIcon}
+        alt="lens icon"
+        height={16}
+        width={16}
+        className="absolute right-4 top-2.5"
+      />
+    </div>
+  );
+};
diff --git a/src/components/FunctionalArea/TopBar/SearchBar/index.ts b/src/components/FunctionalArea/TopBar/SearchBar/index.ts
new file mode 100644
index 00000000..e5d1538e
--- /dev/null
+++ b/src/components/FunctionalArea/TopBar/SearchBar/index.ts
@@ -0,0 +1 @@
+export { SearchBar } from './SearchBar.component';
diff --git a/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx
new file mode 100644
index 00000000..a611ffd3
--- /dev/null
+++ b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx
@@ -0,0 +1,13 @@
+import { screen, render, RenderResult } from '@testing-library/react';
+import { TopBar } from './TopBar.component';
+
+const renderComponent = (): RenderResult => render(<TopBar />);
+
+describe('TopBar - component', () => {
+  it('Should contain user avatar, search bar', () => {
+    renderComponent();
+
+    expect(screen.getByTestId('user-avatar')).toBeInTheDocument();
+    expect(screen.getByTestId('search-bar')).toBeInTheDocument();
+  });
+});
diff --git a/src/components/FunctionalArea/TopBar/TopBar.component.tsx b/src/components/FunctionalArea/TopBar/TopBar.component.tsx
index fd98c130..a6929281 100644
--- a/src/components/FunctionalArea/TopBar/TopBar.component.tsx
+++ b/src/components/FunctionalArea/TopBar/TopBar.component.tsx
@@ -1 +1,14 @@
-export const TopBar = (): JSX.Element => <div className="w-100 bg-slate-600 h-16">.</div>;
+import { SearchBar } from '@/components/FunctionalArea/TopBar/SearchBar';
+import { UserAvatar } from '@/components/FunctionalArea/TopBar/UserAvatar';
+
+export const TopBar = (): JSX.Element => (
+  <div className="w-100 bg-white h-16 flex flex-row items-center py-4 pl-7 pr-6 justify-between">
+    <div className="flex flex-row items-center">
+      <UserAvatar />
+      <SearchBar />
+    </div>
+    <div className="bg-primary-100 px-4 py-1 leading-6 text-xs text-primary-500">
+      Parkinson disease map
+    </div>
+  </div>
+);
diff --git a/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.test.tsx b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.test.tsx
new file mode 100644
index 00000000..dbe50d02
--- /dev/null
+++ b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.test.tsx
@@ -0,0 +1,12 @@
+import { screen, render, RenderResult } from '@testing-library/react';
+import { UserAvatar } from './UserAvatar.component';
+
+const renderComponent = (): RenderResult => render(<UserAvatar />);
+
+describe('UserAvatar - component ', () => {
+  it('should render placeholder image', () => {
+    renderComponent();
+
+    expect(screen.getByAltText('user avatar')).toBeInTheDocument();
+  });
+});
diff --git a/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx
new file mode 100644
index 00000000..c48e49cc
--- /dev/null
+++ b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx
@@ -0,0 +1,8 @@
+import Image from 'next/image';
+import avatarImg from '@/assets/images/user-avatar.png';
+
+export const UserAvatar = (): JSX.Element => (
+  <div className="w-8 h-8 mr-7" data-testid="user-avatar">
+    <Image src={avatarImg} fill alt="user avatar" />
+  </div>
+);
diff --git a/src/components/FunctionalArea/TopBar/UserAvatar/index.ts b/src/components/FunctionalArea/TopBar/UserAvatar/index.ts
new file mode 100644
index 00000000..384816dc
--- /dev/null
+++ b/src/components/FunctionalArea/TopBar/UserAvatar/index.ts
@@ -0,0 +1 @@
+export { UserAvatar } from './UserAvatar.component';
diff --git a/tsconfig.json b/tsconfig.json
index 53d7a2e2..a44f42a5 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -23,7 +23,15 @@
       "@/*": ["./src/*"]
     }
   },
-  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "pages"],
+  "include": [
+    "next-env.d.ts",
+    "**/*.ts",
+    "**/*.tsx",
+    ".next/types/**/*.ts",
+    "pages",
+    "jest.config.ts",
+    "setupTests.ts"
+  ],
   "exclude": ["node_modules", "cypress/**/*.ts", "cypress.config.ts"],
   "noFallthroughCasesInSwitch": true, //Ensures that any non-empty case inside a switch statement includes either break, return, or throw.
   "noImplicitReturns": true, // check all code paths in a function to ensure they return a value.
-- 
GitLab