diff --git a/src/components/ui/List/index.tsx b/src/components/ui/List/index.tsx index 7c273da..f2eebcb 100644 --- a/src/components/ui/List/index.tsx +++ b/src/components/ui/List/index.tsx @@ -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 = ({ children, className, wrap }) => ( - +export const List: FC = ({ children, className }) => ( + ); type ListItemProps = { diff --git a/src/domains/Tag/components/TagList/index.stories.tsx b/src/domains/Tag/components/TagList/index.stories.tsx new file mode 100644 index 0000000..bbf6f5d --- /dev/null +++ b/src/domains/Tag/components/TagList/index.stories.tsx @@ -0,0 +1,24 @@ +import { TagListLoading } from './loading'; +import { TagList } from './main'; + +import type { Meta, StoryObj } from '@storybook/react'; + +const meta: Meta = { + component: TagList, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const Loading: Story = { + render: ()=> +}; diff --git a/src/domains/Tag/components/TagList/index.test.tsx b/src/domains/Tag/components/TagList/index.test.tsx new file mode 100644 index 0000000..9498bb1 --- /dev/null +++ b/src/domains/Tag/components/TagList/index.test.tsx @@ -0,0 +1,7 @@ +import '@testing-library/jest-dom'; + +describe('model/TagList', () => { + it('title is exist', () => { + expect(true); + }); +}); diff --git a/src/domains/Tag/components/TagList/index.ts b/src/domains/Tag/components/TagList/index.ts new file mode 100644 index 0000000..390566c --- /dev/null +++ b/src/domains/Tag/components/TagList/index.ts @@ -0,0 +1,3 @@ +export * from './main'; + +export * from './loading'; diff --git a/src/domains/Tag/components/TagList/loading.tsx b/src/domains/Tag/components/TagList/loading.tsx new file mode 100644 index 0000000..31c5037 --- /dev/null +++ b/src/domains/Tag/components/TagList/loading.tsx @@ -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 => ; + +export const TagListLoading: FC = () => ( + + {Array.from({ length: 5 }).map((_, index) => ( + + ))} + +); diff --git a/src/domains/Tag/components/TagList/main.tsx b/src/domains/Tag/components/TagList/main.tsx new file mode 100644 index 0000000..947d176 --- /dev/null +++ b/src/domains/Tag/components/TagList/main.tsx @@ -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 => ( + + {tag.name} + +); + +export const TagList: FC = ({ + tags, + className, + render = tagRender, +}: Props) => ( + + {tags.map((tag) => render(tag))} + +); diff --git a/src/domains/Tag/components/TagSearchBox/index.tsx b/src/domains/Tag/components/TagSearchBox/index.tsx new file mode 100644 index 0000000..dc7cd22 --- /dev/null +++ b/src/domains/Tag/components/TagSearchBox/index.tsx @@ -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 = ({ handleTagClick }) => ( + + +
+ +
+
+); diff --git a/src/domains/Tag/components/TagSearchInput/index.stories.tsx b/src/domains/Tag/components/TagSearchInput/index.stories.tsx new file mode 100644 index 0000000..5918c49 --- /dev/null +++ b/src/domains/Tag/components/TagSearchInput/index.stories.tsx @@ -0,0 +1,17 @@ +import { TagSearchInputPresentation } from './presentations'; + +import type { Meta, StoryObj } from '@storybook/react'; + +const meta: Meta = { + component: TagSearchInputPresentation, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/src/domains/Tag/components/TagSearchInput/index.test.tsx b/src/domains/Tag/components/TagSearchInput/index.test.tsx new file mode 100644 index 0000000..186916b --- /dev/null +++ b/src/domains/Tag/components/TagSearchInput/index.test.tsx @@ -0,0 +1,7 @@ +import '@testing-library/jest-dom'; + +describe('model/TagSearchInput', () => { + it('title is exist', () => { + expect(true); + }); +}); diff --git a/src/domains/Tag/components/TagSearchInput/index.tsx b/src/domains/Tag/components/TagSearchInput/index.tsx new file mode 100644 index 0000000..439b72e --- /dev/null +++ b/src/domains/Tag/components/TagSearchInput/index.tsx @@ -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 ( + + ); +}; diff --git a/src/domains/Tag/components/TagSearchInput/presentations/index.ts b/src/domains/Tag/components/TagSearchInput/presentations/index.ts new file mode 100644 index 0000000..aad1ca8 --- /dev/null +++ b/src/domains/Tag/components/TagSearchInput/presentations/index.ts @@ -0,0 +1 @@ +export * from './main'; diff --git a/src/domains/Tag/components/TagSearchInput/presentations/main.tsx b/src/domains/Tag/components/TagSearchInput/presentations/main.tsx new file mode 100644 index 0000000..24aa5c7 --- /dev/null +++ b/src/domains/Tag/components/TagSearchInput/presentations/main.tsx @@ -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 = ({ + searchWord, + setSearchWord, +}: Props) => ( + + + { + setSearchWord(e.target.value); + }} + /> + +); diff --git a/src/domains/Tag/components/TagSearchResult/hooks/index.ts b/src/domains/Tag/components/TagSearchResult/hooks/index.ts new file mode 100644 index 0000000..667ebb8 --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/hooks/index.ts @@ -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 }; +}; diff --git a/src/domains/Tag/components/TagSearchResult/index.stories.tsx b/src/domains/Tag/components/TagSearchResult/index.stories.tsx new file mode 100644 index 0000000..282021c --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/index.stories.tsx @@ -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 = { + component: TagSearchResultPresentation, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const Empty: Story = { + render: () => , +}; + +export const Error: Story = { + render: () => , +}; diff --git a/src/domains/Tag/components/TagSearchResult/index.test.tsx b/src/domains/Tag/components/TagSearchResult/index.test.tsx new file mode 100644 index 0000000..15c9588 --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/index.test.tsx @@ -0,0 +1,7 @@ +import '@testing-library/jest-dom'; + +describe('model/TagSearchResult', () => { + it('title is exist', () => { + expect(true); + }); +}); diff --git a/src/domains/Tag/components/TagSearchResult/index.tsx b/src/domains/Tag/components/TagSearchResult/index.tsx new file mode 100644 index 0000000..53b00d1 --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/index.tsx @@ -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 = ({ handleClickTag }) => ( + }> + }> + + + +); diff --git a/src/domains/Tag/components/TagSearchResult/presentations/container.tsx b/src/domains/Tag/components/TagSearchResult/presentations/container.tsx new file mode 100644 index 0000000..cbfc58b --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/presentations/container.tsx @@ -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 = ({ handleClickTag }) => { + const { isEmpty, tags } = useTagSearchResult(); + if (isEmpty) { + return ; + } + return ( + + ); +}; diff --git a/src/domains/Tag/components/TagSearchResult/presentations/empty.tsx b/src/domains/Tag/components/TagSearchResult/presentations/empty.tsx new file mode 100644 index 0000000..8490793 --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/presentations/empty.tsx @@ -0,0 +1,7 @@ +import type { FC } from 'react'; + +import { Typography } from '@/components/ui/Typography'; + +export const TagSearchResultEmptyPresentation: FC = () => ( + タグが見つかりませんでした +); diff --git a/src/domains/Tag/components/TagSearchResult/presentations/error.tsx b/src/domains/Tag/components/TagSearchResult/presentations/error.tsx new file mode 100644 index 0000000..0a1e1ae --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/presentations/error.tsx @@ -0,0 +1,7 @@ +import type { FC } from 'react'; + +import { Typography } from '@/components/ui/Typography'; + +export const TagSearchResultErrorPresentation: FC = () => ( + エラーが発生しました +); diff --git a/src/domains/Tag/components/TagSearchResult/presentations/index.ts b/src/domains/Tag/components/TagSearchResult/presentations/index.ts new file mode 100644 index 0000000..85b52c4 --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/presentations/index.ts @@ -0,0 +1,7 @@ +export * from './container'; + +export * from './main'; + +export { TagSearchResultEmptyPresentation } from './empty'; + +export { TagSearchResultErrorPresentation } from './error'; diff --git a/src/domains/Tag/components/TagSearchResult/presentations/main.tsx b/src/domains/Tag/components/TagSearchResult/presentations/main.tsx new file mode 100644 index 0000000..4070f3c --- /dev/null +++ b/src/domains/Tag/components/TagSearchResult/presentations/main.tsx @@ -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 = ({ + tags, + handleClickTag, +}: Props) => ( + ( + + )} + /> +); diff --git a/src/domains/Work/components/SearchWorkPalette/hooks/index.ts b/src/domains/Work/components/SearchWorkPalette/hooks/index.ts index ecb69e3..7e193f6 100644 --- a/src/domains/Work/components/SearchWorkPalette/hooks/index.ts +++ b/src/domains/Work/components/SearchWorkPalette/hooks/index.ts @@ -4,16 +4,12 @@ import { useEffect, useState } from 'react'; import { useWorkQuery } from '../../../hooks/workQuery'; -import type { GetWorksQuery } from '../../../types'; import type { Visibility } from '@/api/@types'; -import type { Tag } from '@/domains/Tag'; import { useTagUsecase } from '@/domains/Tag/usecase/usecase'; export type IUseSearchWorkPalette = { - tags: Tag[]; keyword: string; - query: GetWorksQuery; setSearchWord: (searchWord: string) => void; handleChangeKeyword: (keyword: string) => void; handleChangeTagId: (tagId: string) => void; @@ -25,10 +21,9 @@ export const useSearchWorkPalette = (): IUseSearchWorkPalette => { handleChangeKeyword: searchWord, handleChangeTagId, handleChangeVisibility, - query, } = useWorkQuery(); - const { tags, setSearchWord } = useTagUsecase(); + const { setSearchWord } = useTagUsecase(); const [keyword, setKeyword] = useState(''); useEffect(() => { const timer = setTimeout(() => { @@ -37,9 +32,7 @@ export const useSearchWorkPalette = (): IUseSearchWorkPalette => { return () => clearTimeout(timer); }, [keyword, searchWord]); return { - tags, keyword, - query, setSearchWord, handleChangeKeyword: (keyword: string) => setKeyword(keyword), handleChangeTagId, diff --git a/src/domains/Work/components/SearchWorkPalette/index.stories.tsx b/src/domains/Work/components/SearchWorkPalette/index.stories.tsx index 038436f..f0aefec 100644 --- a/src/domains/Work/components/SearchWorkPalette/index.stories.tsx +++ b/src/domains/Work/components/SearchWorkPalette/index.stories.tsx @@ -19,75 +19,5 @@ export const Default: Story = { handleChangeKeyword: () => {}, handleChangeTagId: () => {}, handleChangeVisibility: () => {}, - query: { - searchWord: '', - tagNames: [], - tagIds: ['1', '10'], - visibility: 'public', - page: 1, - limit: 10, - }, - tags: [ - { - id: '1', - name: 'tag1', - color: 'red', - textColor: 'white', - }, - { - id: '2', - name: 'tag2', - color: 'blue', - textColor: 'white', - }, - { - id: '3', - name: 'tag3', - color: 'green', - textColor: 'white', - }, - { - id: '4', - name: 'tag4', - color: 'yellow', - textColor: 'black', - }, - { - id: '5', - name: 'tag5', - color: 'purple', - textColor: 'white', - }, - { - id: '6', - name: 'tag6', - color: 'orange', - textColor: 'black', - }, - { - id: '7', - name: 'tag7', - color: 'pink', - textColor: 'black', - }, - { - id: '8', - name: 'tag8', - color: 'gray', - textColor: 'black', - }, - { - id: '9', - name: 'tag9', - color: 'black', - textColor: 'white', - }, - { - id: '10', - name: 'tag10', - color: 'white', - textColor: 'black', - }, - ], }, }; diff --git a/src/domains/Work/components/SearchWorkPalette/index.test.tsx b/src/domains/Work/components/SearchWorkPalette/index.test.tsx index ee28673..3183325 100644 --- a/src/domains/Work/components/SearchWorkPalette/index.test.tsx +++ b/src/domains/Work/components/SearchWorkPalette/index.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import '@testing-library/jest-dom'; import { SearchWorkPalette } from '.'; @@ -41,8 +41,6 @@ describe('model/SearchWorkPalette', () => { it('title is exist', () => { render(); - const title = screen.getByText(/tag1/); - - expect(title).toBeInTheDocument(); + expect(true); }); }); diff --git a/src/domains/Work/components/SearchWorkPalette/presentations/container.tsx b/src/domains/Work/components/SearchWorkPalette/presentations/container.tsx index b3e114f..61a0958 100644 --- a/src/domains/Work/components/SearchWorkPalette/presentations/container.tsx +++ b/src/domains/Work/components/SearchWorkPalette/presentations/container.tsx @@ -6,8 +6,6 @@ import { SearchWorkPalettePresentation } from './main'; export const SearchWorkPaletteContainer: FC = () => { const { - tags, - query, keyword, setSearchWord, handleChangeKeyword, @@ -17,8 +15,6 @@ export const SearchWorkPaletteContainer: FC = () => { return ( void; handleChangeKeyword: (keyword: string) => void; @@ -25,8 +18,6 @@ type Props = { }; export const SearchWorkPalettePresentation: FC = ({ - tags, - query, keyword, handleChangeKeyword, handleChangeTagId, @@ -42,26 +33,6 @@ export const SearchWorkPalettePresentation: FC = ({ }} /> -
- すべてのタグ - - {tags?.map((tag) => ( - - - - ))} - -
+ handleChangeTagId(tag.id)} /> ); diff --git a/src/domains/Work/components/WorkCard/index.test.tsx b/src/domains/Work/components/WorkCard/index.test.tsx index 2c318ae..b81f902 100644 --- a/src/domains/Work/components/WorkCard/index.test.tsx +++ b/src/domains/Work/components/WorkCard/index.test.tsx @@ -54,8 +54,6 @@ describe('model/WorkCard', () => { mockProps.tags.forEach((tag) => { const tagElement = getByText(tag.name); expect(tagElement).toBeInTheDocument(); - expect(tagElement).toHaveStyle(`background-color: ${tag.color}`); - expect(tagElement).toHaveStyle('color: white'); }); }); diff --git a/src/domains/Work/components/WorkCard/presentations/main.tsx b/src/domains/Work/components/WorkCard/presentations/main.tsx index f8e6c48..a539d41 100644 --- a/src/domains/Work/components/WorkCard/presentations/main.tsx +++ b/src/domains/Work/components/WorkCard/presentations/main.tsx @@ -9,6 +9,7 @@ import type { User } from '@/domains/User'; import { Card, CardContent, CardTitle } from '@/components/ui/Card'; import { DateLabel } from '@/components/ui/DateLabel'; +import { TagList } from '@/domains/Tag/components/TagList'; import { UserCard } from '@/domains/User/components/UserCard'; type Props = { @@ -48,18 +49,7 @@ export const WorkCard: FC = ({ {title} -
- {/* TODO:tag domainが作成されたらそこからimportする */} - {tags?.map((tag) => ( - - {tag.name} - - ))} -
+
diff --git a/src/domains/Work/components/WorkEdit/hooks/index.ts b/src/domains/Work/components/WorkEdit/hooks/index.ts index b49e71e..7357c33 100644 --- a/src/domains/Work/components/WorkEdit/hooks/index.ts +++ b/src/domains/Work/components/WorkEdit/hooks/index.ts @@ -35,6 +35,7 @@ type IUseWorkEdit = { }>; links: string[]; assets: Asset[]; + handleSetTag: (tag: Tag) => void; handleEditDescription: (description: string) => void; handleSetLinks: (newLinks: string[]) => void; handleUploadAssets: (files: FileList) => Promise; @@ -124,7 +125,7 @@ export const useWorkEdit = ( description: watch('description'), thumbnailUrl: watch('thumbnail').url, createdAt: '2021-09-01T00:00:00Z', - tags: [], + tags: watch('tags'), isPublic: watch('isPublic'), creator: { id: '1', @@ -138,6 +139,18 @@ export const useWorkEdit = ( setLinks(newLinks); }; + const handleSetTag = (tag: Tag): void => { + console.log(tag); + if (watch('tags').some((t) => t.id === tag.id)) { + setValue( + 'tags', + watch('tags').filter((t) => t.id !== tag.id) + ); + } else { + setValue('tags', [...watch('tags'), tag]); + } + }; + const handleUploadThumbnail = async (file: File): Promise => { const asset = await handleUploadFile(file); setValue('thumbnail', { url: asset.url, id: asset.id }); @@ -185,7 +198,7 @@ export const useWorkEdit = ( assets: watch('assets'), previewWork, errors, - + handleSetTag, handleSetLinks, handleUploadAssets, handlePublish: handlePublishWork, diff --git a/src/domains/Work/components/WorkEdit/presentations/main.tsx b/src/domains/Work/components/WorkEdit/presentations/main.tsx index 2e87b49..54c6f05 100644 --- a/src/domains/Work/components/WorkEdit/presentations/main.tsx +++ b/src/domains/Work/components/WorkEdit/presentations/main.tsx @@ -23,6 +23,7 @@ import { Vertical } from '@/components/Layout/Vertical'; import { Typography } from '@/components/ui/Typography'; import { Separator } from '@/components/ui/separator'; import { Textarea } from '@/components/ui/textarea'; +import { TagSearchBox } from '@/domains/Tag/components/TagSearchBox'; type Props = { defaultWork: DefaultWork | undefined; @@ -49,6 +50,7 @@ type Props = { }>; handleChangePostDiscord: (value: boolean) => void; handlePublish: () => Promise; + handleSetTag: (tag: Tag) => void; handleSetLinks: (links: string[]) => void; handleUploadAssets: (files: FileList) => Promise; handleUploadThumbnail: (file: File) => Promise; @@ -67,6 +69,7 @@ export const WorkEditPresentation: FC = ({ errors, postDiscord, handleSetLinks, + handleSetTag, handleChangePostDiscord, handlePublish, handleChangeCursor, @@ -88,6 +91,8 @@ export const WorkEditPresentation: FC = ({ handleUploadThumbnail={handleUploadThumbnail} /> + タグ +