Skip to content

Commit

Permalink
feat: add task search ui
Browse files Browse the repository at this point in the history
  • Loading branch information
tosaken1116 committed Apr 5, 2024
1 parent 01ef6b8 commit 1652474
Show file tree
Hide file tree
Showing 30 changed files with 345 additions and 146 deletions.
16 changes: 3 additions & 13 deletions src/components/ui/List/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import type { CSSProperties, FC, ReactNode } from 'react';
import type { FC, ReactNode } from 'react';

import clsx from 'clsx';

type ListProps = {
children: ReactNode;
className?: string;
wrap?: CSSProperties['flexWrap'];
};

export const List: FC<ListProps> = ({ children, className, wrap }) => (
<ul
className={clsx(
'flex flex-row gap-4',
wrap && 'flex-wrap',
!wrap && 'flex-nowrap',
className
)}
>
{children}
</ul>
export const List: FC<ListProps> = ({ children, className }) => (
<ul className={clsx('flex flex-row gap-4', className)}>{children}</ul>
);

type ListItemProps = {
Expand Down
24 changes: 24 additions & 0 deletions src/domains/Tag/components/TagList/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TagListLoading } from './loading';
import { TagList } from './main';

import type { Meta, StoryObj } from '@storybook/react';

const meta: Meta<typeof TagList> = {
component: TagList,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof TagList>;

export const Default: Story = {
args: {},
};

export const Loading: Story = {
render: ()=> <TagListLoading />
};
7 changes: 7 additions & 0 deletions src/domains/Tag/components/TagList/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '@testing-library/jest-dom';

describe('model/TagList', () => {
it('title is exist', () => {
expect(true);
});
});
3 changes: 3 additions & 0 deletions src/domains/Tag/components/TagList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './main';

export * from './loading';
14 changes: 14 additions & 0 deletions src/domains/Tag/components/TagList/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { FC, ReactNode } from 'react';

import { List } from '@/components/ui/List';
import { Skeleton } from '@/components/ui/Skeleton';

const TagSkeleton = (): ReactNode => <Skeleton className="w-24 h-6" />;

export const TagListLoading: FC = () => (
<List className="flex-wrap gap-1">
{Array.from({ length: 5 }).map((_, index) => (
<TagSkeleton key={index} />
))}
</List>
);
31 changes: 31 additions & 0 deletions src/domains/Tag/components/TagList/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { ReactNode, FC } from 'react';

import type { Tag } from '../../types';

import { List, ListItem } from '@/components/ui/List';
import { cn } from '@/libs/utils';

type Props = {
tags: Tag[];
render?: (tag: Tag) => ReactNode;
className?: string;
};

export const defaultTagClassName =
'border-orange-pop hover:bg-orange-sub border-2 bg-white text-black h-6 py-0.5 content-center rounded-sm px-2 text-sm font-bold';

const tagRender = (tag: Tag): ReactNode => (
<ListItem key={tag.id} className={defaultTagClassName}>
{tag.name}
</ListItem>
);

export const TagList: FC<Props> = ({
tags,
className,
render = tagRender,
}: Props) => (
<List className={cn('flex-nowrap gap-1', className)}>
{tags.map((tag) => render(tag))}
</List>
);
20 changes: 20 additions & 0 deletions src/domains/Tag/components/TagSearchBox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { FC } from 'react';

import { TagSearchInput } from '../TagSearchInput';
import { TagSearchResult } from '../TagSearchResult';

import type { Tag } from '../../types';

import { Horizontal } from '@/components/Layout/Horizontal';

type Props = {
handleTagClick: (tag: Tag) => void;
};
export const TagSearchBox: FC<Props> = ({ handleTagClick }) => (
<Horizontal className="border-orange-pop border-2 rounded-md overflow-scroll">
<TagSearchInput />
<div className="px-4 pb-4">
<TagSearchResult handleClickTag={handleTagClick} />
</div>
</Horizontal>
);
17 changes: 17 additions & 0 deletions src/domains/Tag/components/TagSearchInput/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TagSearchInputPresentation } from './presentations';

import type { Meta, StoryObj } from '@storybook/react';

const meta: Meta<typeof TagSearchInputPresentation> = {
component: TagSearchInputPresentation,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof TagSearchInputPresentation>;

export const Default: Story = {};
7 changes: 7 additions & 0 deletions src/domains/Tag/components/TagSearchInput/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '@testing-library/jest-dom';

describe('model/TagSearchInput', () => {
it('title is exist', () => {
expect(true);
});
});
17 changes: 17 additions & 0 deletions src/domains/Tag/components/TagSearchInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

import type { FC } from 'react';

import { useTagUsecase } from '../../usecase/usecase';

import { TagSearchInputPresentation } from './presentations';

export const TagSearchInput: FC = () => {
const { setSearchWord, searchWord } = useTagUsecase();
return (
<TagSearchInputPresentation
searchWord={searchWord}
setSearchWord={setSearchWord}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './main';
28 changes: 28 additions & 0 deletions src/domains/Tag/components/TagSearchInput/presentations/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { FC } from 'react';

import { Search } from 'lucide-react';

import { Horizontal } from '@/components/Layout/Horizontal';
import { Input } from '@/components/ui/Input';

type Props = {
searchWord: string;
setSearchWord: (searchWord: string) => void;
};

export const TagSearchInputPresentation: FC<Props> = ({
searchWord,
setSearchWord,
}: Props) => (
<Horizontal className="relative">
<Search className="absolute left-1 top-1 text-orange-pop" />
<Input
className="outline-orange-pop outline-2 outline border-none rounded-sm focus-visible:outline-pale-red h-8 px-2 w-full pl-8"
value={searchWord}
placeholder="タグを検索"
onChange={(e) => {
setSearchWord(e.target.value);
}}
/>
</Horizontal>
);
13 changes: 13 additions & 0 deletions src/domains/Tag/components/TagSearchResult/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Tag } from '@/domains/Tag/types';

import { useTagUsecase } from '@/domains/Tag/usecase/usecase';

type IUseTagSearchResult = {
tags: Tag[];
isEmpty: boolean;
};

export const useTagSearchResult = (): IUseTagSearchResult => {
const { tags } = useTagUsecase();
return { tags, isEmpty: tags !== undefined && tags.length === 0 };
};
27 changes: 27 additions & 0 deletions src/domains/Tag/components/TagSearchResult/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { TagSearchResultPresentation } from './presentations';
import { TagSearchResultEmptyPresentation } from './presentations/empty';
import { TagSearchResultErrorPresentation } from './presentations/error';

import type { Meta, StoryObj } from '@storybook/react';

const meta: Meta<typeof TagSearchResultPresentation> = {
component: TagSearchResultPresentation,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof TagSearchResultPresentation>;

export const Default: Story = {};

export const Empty: Story = {
render: () => <TagSearchResultEmptyPresentation />,
};

export const Error: Story = {
render: () => <TagSearchResultErrorPresentation />,
};
7 changes: 7 additions & 0 deletions src/domains/Tag/components/TagSearchResult/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '@testing-library/jest-dom';

describe('model/TagSearchResult', () => {
it('title is exist', () => {
expect(true);
});
});
23 changes: 23 additions & 0 deletions src/domains/Tag/components/TagSearchResult/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { FC } from 'react';
import { Suspense } from 'react';

import { TagListLoading } from '../TagList';

import { TagSearchResultContainer } from './presentations';
import { TagSearchResultErrorPresentation } from './presentations/error';

import type { Tag } from '../../types';

import { ErrorBoundary } from '@/libs/ErrorBoundary';

type Props = {
handleClickTag: (tag: Tag) => void;
};

export const TagSearchResult: FC<Props> = ({ handleClickTag }) => (
<ErrorBoundary fallback={<TagSearchResultErrorPresentation />}>
<Suspense fallback={<TagListLoading />}>
<TagSearchResultContainer handleClickTag={handleClickTag} />
</Suspense>
</ErrorBoundary>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

import type { FC } from 'react';

import { useTagSearchResult } from '../hooks';

import { TagSearchResultEmptyPresentation } from './empty';
import { TagSearchResultPresentation } from './main';

import type { Tag } from '@/domains/Tag/types';

type Props = {
handleClickTag: (tag: Tag) => void;
};
export const TagSearchResultContainer: FC<Props> = ({ handleClickTag }) => {
const { isEmpty, tags } = useTagSearchResult();
if (isEmpty) {
return <TagSearchResultEmptyPresentation />;
}
return (
<TagSearchResultPresentation tags={tags} handleClickTag={handleClickTag} />
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { FC } from 'react';

import { Typography } from '@/components/ui/Typography';

export const TagSearchResultEmptyPresentation: FC = () => (
<Typography>タグが見つかりませんでした</Typography>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { FC } from 'react';

import { Typography } from '@/components/ui/Typography';

export const TagSearchResultErrorPresentation: FC = () => (
<Typography>エラーが発生しました</Typography>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './container';

export * from './main';

export { TagSearchResultEmptyPresentation } from './empty';

export { TagSearchResultErrorPresentation } from './error';
32 changes: 32 additions & 0 deletions src/domains/Tag/components/TagSearchResult/presentations/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { FC } from 'react';

import { TagList, defaultTagClassName } from '../../TagList';

import type { Tag } from '@/domains/Tag/types';

import { Button } from '@/components/ui/Button';

type Props = {
tags: Tag[];
handleClickTag: (tag: Tag) => void;
};

export const TagSearchResultPresentation: FC<Props> = ({
tags,
handleClickTag,
}: Props) => (
<TagList
className="flex-wrap"
tags={tags}
render={(tag) => (
<Button
className={defaultTagClassName}
onClick={() => {
handleClickTag(tag);
}}
>
{tag.name}
</Button>
)}
/>
);
Loading

0 comments on commit 1652474

Please sign in to comment.