Skip to content

Commit

Permalink
헤더 검색바 (#18)
Browse files Browse the repository at this point in the history
* feat: 헤더 컴포넌트 초안 및 스토리북 추가

* feat: 헤더 검색바 컴포넌트 추가

* feat: 홈페이지 헤더 검색바 숨김처리

* feat: 헤더 검색바 인터렉션 테스트 작성
  • Loading branch information
howons authored May 16, 2024
1 parent efb943c commit 3f9679c
Show file tree
Hide file tree
Showing 11 changed files with 1,308 additions and 671 deletions.
13 changes: 10 additions & 3 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@ const config: StorybookConfig = {
"../stories/**/*.mdx",
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
],

addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
],

framework: {
name: "@storybook/nextjs",
options: {},
},
docs: {
autodocs: "tag",
},

docs: {},

staticDirs: ["../public"],

webpackFinal: async (config) => {
const imageRule = config.module?.rules?.find((rule) => {
const test = (rule as { test: RegExp }).test;
Expand All @@ -39,5 +42,9 @@ const config: StorybookConfig = {

return config;
},

typescript: {
reactDocgen: "react-docgen-typescript",
},
};
export default config;
2 changes: 1 addition & 1 deletion .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const preview: Preview = {
},
decorators: [
(Story) => (
<main className={nanumGoth.className}>
<main className={nanumGoth.className} data-testid="screen">
<Story />
</main>
),
Expand Down
6 changes: 5 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./globals.css";

import { PlatformStoreProvider } from "@lib/providers/PlatformStoreProvider";
import Header from "@ui/Header";
import type { Metadata } from "next";
import { Nanum_Gothic } from "next/font/google";

Expand All @@ -19,7 +20,10 @@ export default function RootLayout({
return (
<html lang="en">
<body className={nanumGoth.className}>
<PlatformStoreProvider>{children}</PlatformStoreProvider>
<PlatformStoreProvider>
<Header />
{children}
</PlatformStoreProvider>
</body>
</html>
);
Expand Down
70 changes: 70 additions & 0 deletions app/ui/Header/HeaderSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";

import {
Popover,
PopoverButton,
PopoverPanel,
PopoverProps,
Transition,
} from "@headlessui/react";
import { SearchStoreProvider } from "@lib/providers/SearchStoreProvider";
import SearchBar from "@ui/SearchBar";
import SearchList from "@ui/SearchList";
import { usePathname } from "next/navigation";
import { MouseEventHandler } from "react";

interface HeaderSearchProps extends PopoverProps {}

function HeaderSearch({ className = "", ...props }: HeaderSearchProps) {
const pathname = usePathname();
if (pathname === "/") return null;

const handlePopoverButtonClick: (
open: boolean
) => MouseEventHandler<HTMLDivElement> = (open) => (e) => {
if (open) {
e.preventDefault();
}

const eventTarget = e.target as HTMLElement;
if (eventTarget.tagName === "INPUT") {
eventTarget.focus();
}
};

const popoverDefaultStyle =
"flex h-14 flex-col items-center overflow-hidden transition-all duration-300";
const popoverOpenStyle =
"data-[open]:h-[70vh] data-[open]:pb-16 data-[open]:shadow-2xl data-[open]:rounded-b-2xl";

return (
<Popover
className={`${popoverDefaultStyle} ${popoverOpenStyle} ${className}`}
{...props}>
{({ open }) => (
<SearchStoreProvider>
<PopoverButton
as={SearchBar}
role="button"
aria-label="헤더 검색바"
className={`shrink-0 transition-transform duration-300 focus:outline-none ${open ? "translate-y-7" : ""}`}
onClick={handlePopoverButtonClick(open)}
/>
<Transition
enter="duration-200 ease-out"
enterFrom="scale-75 opacity-0 traslate-y-0"
enterTo="scale-100 opacity-100 translate-y-16"
leave="duration-200 ease-out"
leaveFrom="scale-100 opacity-100 translate-y-16"
leaveTo="scale-75 opacity-0 translate-y-0">
<PopoverPanel className="w-full min-w-64 max-w-[40rem] shrink grow origin-top overflow-scroll">
<SearchList />
</PopoverPanel>
</Transition>
</SearchStoreProvider>
)}
</Popover>
);
}

export default HeaderSearch;
15 changes: 15 additions & 0 deletions app/ui/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import HeaderSearch from "@ui/Header/HeaderSearch";

function Header() {
return (
<div className="fixed left-0 top-0 flex h-14 w-full items-center justify-between border-b border-zinc-300">
<div>로고</div>
<div className="relative h-full grow">
<HeaderSearch />
</div>
<div>계정</div>
</div>
);
}

export default Header;
4 changes: 2 additions & 2 deletions app/ui/SearchBar/SearchBarCore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ function SearchBarCore() {
etc: "focus:outline-zinc-400",
};

const inputDefaultStyle =
"h-full grow rounded-r-full bg-transparent px-5 min-w-0";
const inputDefaultStyle = "h-full grow rounded-r-full bg-white px-5 min-w-0";

return (
<input
className={`${inputDefaultStyle} ${platformStyle[platform]}`}
value={query}
onChange={(e) => updateQuery(e.target.value)}
aria-label="검색바"
/>
);
}
Expand Down
38 changes: 24 additions & 14 deletions app/ui/SearchBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,30 @@ import CategoryDivider from "@ui/SearchBar/CategoryDivider";
import CategorySelector from "@ui/SearchBar/CategorySelector";
import SearchBarCore from "@ui/SearchBar/SearchBarCore";
import SearchBarWrapper from "@ui/SearchBar/SearchBarWrapper";
import { forwardRef, HTMLAttributes } from "react";

function SearchBar() {
return (
<div className="relative m-3 ml-9 w-3/5 min-w-64 max-w-[50rem]">
<CategoryStoreProvider>
<CategorySelector />
<SearchBarWrapper>
<CategoryDivider />
<Category />
<SearchBarCore />
</SearchBarWrapper>
</CategoryStoreProvider>
</div>
);
}
interface SearchBarProps extends HTMLAttributes<HTMLDivElement> {}

const SearchBar = forwardRef<HTMLDivElement, SearchBarProps>(
({ className = "", ...props }, ref) => {
return (
<div
ref={ref}
className={`relative m-3 ml-9 w-3/5 min-w-64 max-w-[37rem] ${className}`}
{...props}>
<CategoryStoreProvider>
<CategorySelector />
<SearchBarWrapper>
<CategoryDivider />
<Category />
<SearchBarCore />
</SearchBarWrapper>
</CategoryStoreProvider>
</div>
);
}
);

SearchBar.displayName = "SearchBar";

export default SearchBar;
5 changes: 4 additions & 1 deletion app/ui/SearchList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ function SearchList({ className, ...props }: SearchListProps) {
const troubleMakerList = query.length > 0 ? searchResults : tempList;

return (
<ul className={`flex w-full flex-col ${className}`} {...props}>
<ul
className={`flex w-full flex-col ${className}`}
aria-label="검색목록"
{...props}>
<Divider direction="horizon" />
{troubleMakerList.map((troublemaker) => (
<Fragment key={troublemaker.id}>
Expand Down
Loading

0 comments on commit 3f9679c

Please sign in to comment.