From 0deca68ca81ed7b0ea7ab8505159d494174e76e2 Mon Sep 17 00:00:00 2001 From: Katalina Okano Date: Sun, 4 Jul 2021 19:57:12 -0500 Subject: [PATCH] add short editor item and data model for categories --- .../atoms/tab-view/TabView.styled.ts | 6 +- .../EditorCategoryShort.spec.tsx | 12 ++++ .../EditorCategoryShort.stories.tsx | 30 ++++++++++ .../EditorCategoryShort.styled.ts | 60 +++++++++++++++++++ .../EditorCategoryShort.tsx | 31 ++++++++++ .../molecules/editor-category-short/index.ts | 0 .../EditorCategoriesTab.tsx | 49 ++++++++++----- .../editor-details-tab/EditorDetailsTab.tsx | 16 +++-- .../organisms/editor-shell/EditorShell.tsx | 56 ++++++++++++++--- 9 files changed, 231 insertions(+), 29 deletions(-) create mode 100644 packages/design-system/molecules/editor-category-short/EditorCategoryShort.spec.tsx create mode 100644 packages/design-system/molecules/editor-category-short/EditorCategoryShort.stories.tsx create mode 100644 packages/design-system/molecules/editor-category-short/EditorCategoryShort.styled.ts create mode 100644 packages/design-system/molecules/editor-category-short/EditorCategoryShort.tsx create mode 100644 packages/design-system/molecules/editor-category-short/index.ts diff --git a/packages/design-system/atoms/tab-view/TabView.styled.ts b/packages/design-system/atoms/tab-view/TabView.styled.ts index c570534..e5901d3 100644 --- a/packages/design-system/atoms/tab-view/TabView.styled.ts +++ b/packages/design-system/atoms/tab-view/TabView.styled.ts @@ -65,5 +65,9 @@ export const TabContentTitle = styled.div` ${text500} border-bottom: 1px solid rgba(0, 0, 0, 0.1); - padding: 10px; + padding: 0.5em 7px; +`; + +export const TabDepth = styled.div` + margin-left: 7px; `; diff --git a/packages/design-system/molecules/editor-category-short/EditorCategoryShort.spec.tsx b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.spec.tsx new file mode 100644 index 0000000..e6c6cf7 --- /dev/null +++ b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.spec.tsx @@ -0,0 +1,12 @@ +import { mockCategory } from '@roleypoly/design-system/fixtures/storyData'; +import { render } from '@testing-library/react'; +import { EditorCategoryShort } from './EditorCategoryShort'; + +it('triggers onOpen when clicked', async () => { + const onOpen = jest.fn(); + const view = render(); + + view.getByRole('menuitem')?.click(); + + expect(onOpen).toHaveBeenCalled(); +}); diff --git a/packages/design-system/molecules/editor-category-short/EditorCategoryShort.stories.tsx b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.stories.tsx new file mode 100644 index 0000000..bc2b0a5 --- /dev/null +++ b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.stories.tsx @@ -0,0 +1,30 @@ +import { mockCategory } from '@roleypoly/design-system/fixtures/storyData'; +import ReactTooltip from 'react-tooltip'; +import styled from 'styled-components'; +import { EditorCategoryShort } from './EditorCategoryShort'; + +const decorator = (story) => ( + + {story()} + + +); + +export default { + title: 'Molecules/Short Category', + component: EditorCategoryShort, + args: { + category: mockCategory, + }, + decorators: [decorator], +}; + +const Wrapper = styled.div` + box-shadow: 0 0 10px #000; +`; + +export const shortEditor = (args) => ; +export const shortEditorHidden = (args) => ; +shortEditorHidden.args = { + category: { ...mockCategory, hidden: true }, +}; diff --git a/packages/design-system/molecules/editor-category-short/EditorCategoryShort.styled.ts b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.styled.ts new file mode 100644 index 0000000..2b9c2a0 --- /dev/null +++ b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.styled.ts @@ -0,0 +1,60 @@ +import { transitions } from '@roleypoly/design-system/atoms/timings'; +import styled from 'styled-components'; + +export const GrabberBox = styled.div` + display: flex; + align-items: center; + justify-content: center; + margin-right: 1em; + flex: 0; + cursor: grab; + position: relative; + + ::before { + content: ''; + position: absolute; + top: -5px; + left: -8px; + width: 32px; + height: 32px; + background-color: rgba(0, 0, 0, 0.25); + opacity: 0; + border-radius: 50%; + transition: opacity ${transitions.actionable}s ease-in-out; + } + + &:hover { + ::before { + opacity: 1; + } + } +`; + +export const Opener = styled.div` + opacity: 0; + transition: opacity ${transitions.actionable}s ease-in-out; +`; + +export const Container = styled.div` + display: flex; + padding: 1em; + cursor: pointer; + + &:hover { + ${Opener} { + opacity: 1; + } + } +`; + +export const Name = styled.div` + position: relative; + top: -2px; + margin-right: 1em; +`; + +export const Flags = styled.div``; + +export const Void = styled.div` + flex: 1; +`; diff --git a/packages/design-system/molecules/editor-category-short/EditorCategoryShort.tsx b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.tsx new file mode 100644 index 0000000..67f1712 --- /dev/null +++ b/packages/design-system/molecules/editor-category-short/EditorCategoryShort.tsx @@ -0,0 +1,31 @@ +import { Category } from '@roleypoly/types'; +import { DragEventHandler, MouseEventHandler } from 'react'; +import { GoEyeClosed, GoGrabber, GoKebabHorizontal } from 'react-icons/go'; +import { + Container, + Flags, + GrabberBox, + Name, + Opener, + Void, +} from './EditorCategoryShort.styled'; + +type ShortProps = { + category: Category; + onDrag?: DragEventHandler; + onOpen?: MouseEventHandler; +}; + +export const EditorCategoryShort = (props: ShortProps) => ( + + + + + {props.category.name} + {props.category.hidden && } + + + + + +); diff --git a/packages/design-system/molecules/editor-category-short/index.ts b/packages/design-system/molecules/editor-category-short/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/design-system/organisms/editor-categories-tab/EditorCategoriesTab.tsx b/packages/design-system/organisms/editor-categories-tab/EditorCategoriesTab.tsx index b535ed9..3ce66d0 100644 --- a/packages/design-system/organisms/editor-categories-tab/EditorCategoriesTab.tsx +++ b/packages/design-system/organisms/editor-categories-tab/EditorCategoriesTab.tsx @@ -1,18 +1,39 @@ +import { TabDepth } from '@roleypoly/design-system/atoms/tab-view/TabView.styled'; import { EditorCategory } from '@roleypoly/design-system/molecules/editor-category'; +import { EditorCategoryShort } from '@roleypoly/design-system/molecules/editor-category-short/EditorCategoryShort'; import { EditorShellProps } from '@roleypoly/design-system/organisms/editor-shell'; +import { Category } from '@roleypoly/types'; +import { sortBy } from 'lodash'; +import * as React from 'react'; import { CategoryContainer } from './EditorCategoriesTab.styled'; -export const EditorCategoriesTab = (props: EditorShellProps) => ( -
- {props.guild.data.categories.map((category, idx) => ( - - console.log(x)} - /> - - ))} -
-); +export const EditorCategoriesTab = (props: EditorShellProps) => { + const [openStates, setOpenStates] = React.useState([]); + + const onCategoryOpen = (id: Category['id']) => () => { + setOpenStates([...new Set(openStates).add(id)]); + }; + + return ( + + {sortBy(props.guild.data.categories, ['position', 'id']).map((category, idx) => + openStates.includes(category.id) ? ( + + props.onCategoryChange?.(category)} + /> + + ) : ( + + ) + )} + + ); +}; diff --git a/packages/design-system/organisms/editor-details-tab/EditorDetailsTab.tsx b/packages/design-system/organisms/editor-details-tab/EditorDetailsTab.tsx index 041c605..e647c61 100644 --- a/packages/design-system/organisms/editor-details-tab/EditorDetailsTab.tsx +++ b/packages/design-system/organisms/editor-details-tab/EditorDetailsTab.tsx @@ -1,16 +1,20 @@ +import { Space } from '@roleypoly/design-system/atoms/space'; +import { TabDepth } from '@roleypoly/design-system/atoms/tab-view/TabView.styled'; import { MultilineTextInput } from '@roleypoly/design-system/atoms/text-input'; +import { Text } from '@roleypoly/design-system/atoms/typography'; import { EditorShellProps } from '@roleypoly/design-system/organisms/editor-shell'; import * as React from 'react'; export const EditorDetailsTab = (props: EditorShellProps) => { - const [serverMessage, updateServerMessage] = React.useState(props.guild.data.message); return ( -
- Server Message + + + Server Message updateServerMessage(eventData.target.value)} + value={props.guild.data.message} + onChange={(eventData) => props.onMessageChange?.(eventData.target.value)} /> -
+ + ); }; diff --git a/packages/design-system/organisms/editor-shell/EditorShell.tsx b/packages/design-system/organisms/editor-shell/EditorShell.tsx index 93a2ac4..1398037 100644 --- a/packages/design-system/organisms/editor-shell/EditorShell.tsx +++ b/packages/design-system/organisms/editor-shell/EditorShell.tsx @@ -1,16 +1,56 @@ import { Tab, TabView } from '@roleypoly/design-system/atoms/tab-view'; import { EditorCategoriesTab } from '@roleypoly/design-system/organisms/editor-categories-tab'; import { EditorDetailsTab } from '@roleypoly/design-system/organisms/editor-details-tab'; -import { PresentableGuild } from '@roleypoly/types'; +import { Category, PresentableGuild } from '@roleypoly/types'; +import React from 'react'; export type EditorShellProps = { guild: PresentableGuild; + onGuildChange?: (guild: PresentableGuild) => void; + onCategoryChange?: (category: Category) => void; + onMessageChange?: (message: PresentableGuild['data']['message']) => void; }; -export const EditorShell = (props: EditorShellProps) => ( - - {() => } - {() => } - {() =>
hi2!
}
-
-); +export const EditorShell = (props: EditorShellProps) => { + const [guild, setGuild] = React.useState(props.guild); + + const reset = () => { + setGuild(props.guild); + }; + + const onCategoryChange = (category: Category) => { + setGuild((currentGuild) => { + const categories = [ + ...currentGuild.data.categories.filter((x) => x.id !== category.id), + category, + ]; + return { ...currentGuild, data: { ...currentGuild.data, categories } }; + }); + }; + + const onMessageChange = (message: PresentableGuild['data']['message']) => { + setGuild((currentGuild) => { + return { ...currentGuild, data: { ...guild.data, message } }; + }); + }; + + return ( + + + {() => ( + + )} + + + {() => ( + + )} + + {() =>
hi2!
}
+
+ ); +};