feature(Project): add ability to create new task groups

This commit is contained in:
Jordan Knott
2020-04-10 21:22:58 -05:00
parent e022e7914a
commit bd73717deb
27 changed files with 847 additions and 248 deletions

View File

@ -0,0 +1,19 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import AddList from '.';
export default {
component: AddList,
title: 'AddList',
parameters: {
backgrounds: [
{ name: 'gray', value: '#262c49', default: true },
{ name: 'white', value: '#ffffff' },
],
},
};
export const Default = () => {
return <AddList onSave={action('on save')} />;
};

View File

@ -0,0 +1,98 @@
import styled, { css } from 'styled-components';
import TextareaAutosize from 'react-autosize-textarea/lib';
export const Wrapper = styled.div<{ editorOpen: boolean }>`
display: inline-block;
background-color: hsla(0, 0%, 100%, 0.24);
cursor: pointer;
border-radius: 3px;
height: auto;
min-height: 32px;
padding: 4px;
transition: background 85ms ease-in, opacity 40ms ease-in, border-color 85ms ease-in;
width: 272px;
margin: 0 4px;
margin-right: 8px;
${props =>
!props.editorOpen &&
css`
&:hover {
background-color: hsla(0, 0%, 100%, 0.32);
}
`}
${props =>
props.editorOpen &&
css`
background-color: #ebecf0;
border-radius: 3px;
height: auto;
min-height: 32px;
padding: 4px;
transition: background 85ms ease-in, opacity 40ms ease-in, border-color 85ms ease-in;
`}
`;
export const Placeholder = styled.span`
color: #c2c6dc;
display: flex;
align-items: center;
padding: 6px 8px;
transition: color 85ms ease-in;
`;
export const AddIconWrapper = styled.div`
color: #fff;
margin-right: 6px;
`;
export const ListNameEditorWrapper = styled.div`
display: flex;
`;
export const ListNameEditor = styled(TextareaAutosize)`
background: #fff;
border: none;
box-shadow: inset 0 0 0 2px #0079bf;
display: block;
margin: 0;
transition: margin 85ms ease-in, background 85ms ease-in;
width: 100%;
line-height: 20px;
padding: 8px 12px;
font-size: 14px;
outline: none;
`;
export const ListAddControls = styled.div`
height: 32px;
transition: margin 85ms ease-in, height 85ms ease-in;
overflow: hidden;
margin: 4px 0 0;
`;
export const AddListButton = styled.button`
background-color: #5aac44;
box-shadow: none;
border: none;
color: #fff;
float: left;
margin: 0 4px 0 0;
cursor: pointer;
display: inline-block;
font-weight: 400;
line-height: 20px;
padding: 6px 12px;
text-align: center;
border-radius: 3px;
font-size: 14px;
`;
export const CancelAdd = styled.div`
display: flex;
align-items: center;
justify-content: center;
height: 32px;
width: 32px;
cursor: pointer;
`;

View File

@ -0,0 +1,105 @@
import React, { useState, useRef, useEffect } from 'react';
import { Plus, Cross } from 'shared/icons';
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
import {
Wrapper,
Placeholder,
AddIconWrapper,
ListNameEditor,
ListAddControls,
CancelAdd,
AddListButton,
ListNameEditorWrapper,
} from './Styles';
type NameEditorProps = {
onSave: (listName: string) => void;
onCancel: () => void;
};
const NameEditor: React.FC<NameEditorProps> = ({ onSave, onCancel }) => {
const $editorRef = useRef<HTMLTextAreaElement>(null);
const [listName, setListName] = useState('');
useEffect(() => {
if ($editorRef && $editorRef.current) {
$editorRef.current.focus();
}
});
const onKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
onSave(listName);
setListName('');
if ($editorRef && $editorRef.current) {
$editorRef.current.focus();
}
}
};
return (
<>
<ListNameEditorWrapper>
<ListNameEditor
ref={$editorRef}
onKeyDown={onKeyDown}
value={listName}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setListName(e.currentTarget.value)}
/>
</ListNameEditorWrapper>
<ListAddControls>
<AddListButton
onClick={() => {
onSave(listName);
setListName('');
if ($editorRef && $editorRef.current) {
$editorRef.current.focus();
}
}}
>
Save
</AddListButton>
<CancelAdd onClick={() => onCancel()}>
<Cross />
</CancelAdd>
</ListAddControls>
</>
);
};
type AddListProps = {
onSave: (listName: string) => void;
};
const AddList: React.FC<AddListProps> = ({ onSave }) => {
const [editorOpen, setEditorOpen] = useState(false);
const $wrapperRef = useRef<HTMLDivElement>(null);
const onOutsideClick = () => {
setEditorOpen(false);
};
useOnOutsideClick($wrapperRef, editorOpen, onOutsideClick, null);
return (
<Wrapper
ref={$wrapperRef}
editorOpen={editorOpen}
onClick={() => {
if (!editorOpen) {
setEditorOpen(true);
}
}}
>
{editorOpen ? (
<NameEditor onCancel={() => setEditorOpen(false)} onSave={onSave} />
) : (
<Placeholder>
<AddIconWrapper>
<Plus size={12} color="#c2c6dc" />
</AddIconWrapper>
Add another list
</Placeholder>
)}
</Wrapper>
);
};
export default AddList;

View File

@ -25,20 +25,17 @@ type Props = {
const CardComposer = ({ isOpen, onCreateCard, onClose }: Props) => {
const [cardName, setCardName] = useState('');
const $cardEditor: any = useRef(null);
const onClick = () => {
const handleCreateCard = () => {
onCreateCard(cardName);
setCardName('');
if ($cardEditor && $cardEditor.current) {
$cardEditor.current.focus();
}
};
const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter') {
e.preventDefault();
onCreateCard(cardName);
}
};
const onBlur = () => {
if (cardName === '') {
onClose();
} else {
onCreateCard(cardName);
handleCreateCard();
}
};
useOnEscapeKeyDown(isOpen, onClose);
@ -63,7 +60,7 @@ const CardComposer = ({ isOpen, onCreateCard, onClose }: Props) => {
</ListCard>
<ComposerControls>
<ComposerControlsSaveSection>
<AddCardButton onClick={onClick}>Add Card</AddCardButton>
<AddCardButton onClick={handleCreateCard}>Add Card</AddCardButton>
<CancelIcon onClick={onClose} icon={faTimes} color="#42526e" />
</ComposerControlsSaveSection>
<ComposerControlsActionsSection />

View File

@ -59,6 +59,7 @@ export const Default = () => {
onSaveName={action('on save name')}
onOpenComposer={action('on open composer')}
tasks={[]}
onExtraMenuOpen={action('extra menu open')}
>
<ListCards>
<CardComposer
@ -84,6 +85,7 @@ export const WithCardComposer = () => {
onSaveName={action('on save name')}
onOpenComposer={action('on open composer')}
tasks={[]}
onExtraMenuOpen={action('extra menu open')}
>
<ListCards>
<CardComposer
@ -110,6 +112,7 @@ export const WithCard = () => {
onSaveName={action('on save name')}
onOpenComposer={action('on open composer')}
tasks={[]}
onExtraMenuOpen={action('extra menu open')}
>
<ListCards>
<Card
@ -148,6 +151,7 @@ export const WithCardAndComposer = () => {
onSaveName={action('on save name')}
onOpenComposer={action('on open composer')}
tasks={[]}
onExtraMenuOpen={action('extra menu open')}
>
<ListCards>
<Card

View File

@ -117,3 +117,12 @@ export const ListCards = styled.div`
overflow-y: auto;
overflow-x: hidden;
`;
export const ListExtraMenuButtonWrapper = styled.div`
cursor: pointer;
position: absolute;
right: 4px;
top: 4px;
z-index: 1;
padding: 6px;
`;

View File

@ -1,7 +1,6 @@
import React, { useState, useRef } from 'react';
import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlus } from '@fortawesome/free-solid-svg-icons';
import { Plus, Ellipsis } from 'shared/icons';
import {
Container,
@ -13,6 +12,7 @@ import {
AddCardButton,
AddCardButtonText,
ListCards,
ListExtraMenuButtonWrapper,
} from './Styles';
type Props = {
@ -26,20 +26,32 @@ type Props = {
wrapperProps?: any;
headerProps?: any;
index?: number;
onExtraMenuOpen: (taskGroupID: string, pos: ElementPosition, size: ElementSize) => void;
};
const List = React.forwardRef(
(
{ id, name, onSaveName, isComposerOpen, onOpenComposer, children, wrapperProps, headerProps }: Props,
{
id,
name,
onSaveName,
isComposerOpen,
onOpenComposer,
children,
wrapperProps,
headerProps,
onExtraMenuOpen,
}: Props,
$wrapperRef: any,
) => {
const [listName, setListName] = useState(name);
const [isEditingTitle, setEditingTitle] = useState(false);
const $listNameRef: any = useRef<HTMLTextAreaElement>();
const $listNameRef = useRef<HTMLTextAreaElement>(null);
const $extraActionsRef = useRef<HTMLDivElement>(null);
const onClick = () => {
setEditingTitle(true);
if ($listNameRef) {
if ($listNameRef && $listNameRef.current) {
$listNameRef.current.select();
}
};
@ -48,7 +60,9 @@ const List = React.forwardRef(
onSaveName(listName);
};
const onEscape = () => {
$listNameRef.current.blur();
if ($listNameRef && $listNameRef.current) {
$listNameRef.current.blur();
}
};
const onChange = (event: React.FormEvent<HTMLTextAreaElement>): void => {
setListName(event.currentTarget.value);
@ -56,7 +70,28 @@ const List = React.forwardRef(
const onKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
$listNameRef.current.blur();
if ($listNameRef && $listNameRef.current) {
$listNameRef.current.blur();
}
}
};
const handleExtraMenuOpen = () => {
if ($extraActionsRef && $extraActionsRef.current) {
const pos = $extraActionsRef.current.getBoundingClientRect();
onExtraMenuOpen(
id,
{
top: pos.top,
left: pos.left,
right: pos.right,
bottom: pos.bottom,
},
{
width: pos.width,
height: pos.height,
},
);
}
};
useOnEscapeKeyDown(isEditingTitle, onEscape);
@ -74,11 +109,14 @@ const List = React.forwardRef(
spellCheck={false}
value={listName}
/>
<ListExtraMenuButtonWrapper ref={$extraActionsRef} onClick={handleExtraMenuOpen}>
<Ellipsis size={16} color="#c2c6dc" />
</ListExtraMenuButtonWrapper>
</Header>
{children && children}
<AddCardContainer hidden={isComposerOpen}>
<AddCardButton onClick={() => onOpenComposer(id)}>
<FontAwesomeIcon icon={faPlus} size="xs" color="#42526e" />
<Plus size={12} color="#42526e" />
<AddCardButtonText>Add another card</AddCardButtonText>
</AddCardButton>
</AddCardContainer>

View File

@ -0,0 +1,17 @@
import React from 'react';
import ListActions from '.';
export default {
component: ListActions,
title: 'ListActions',
parameters: {
backgrounds: [
{ name: 'white', value: '#ffffff', default: true },
{ name: 'gray', value: '#f8f8f8' },
],
},
};
export const Default = () => {
return <ListActions taskGroupID="1" />;
};

View File

@ -0,0 +1,35 @@
import styled from 'styled-components';
export const ListActionsWrapper = styled.ul`
list-style-type: none;
margin: 0;
padding: 0;
`;
export const ListActionItemWrapper = styled.li`
margin: 0;
padding: 0;
`;
export const ListActionItem = styled.span`
cursor: pointer;
display: block;
font-size: 14px;
color: #172b4d;
font-weight: 400;
padding: 6px 12px;
position: relative;
margin: 0 -12px;
text-decoration: none;
&:hover {
background-color: rgba(9, 30, 66, 0.04);
}
`;
export const ListSeparator = styled.hr`
background-color: rgba(9, 30, 66, 0.13);
border: 0;
height: 1px;
margin: 8px 0;
padding: 0;
width: 100%;
`;

View File

@ -0,0 +1,48 @@
import React from 'react';
import { ListActionsWrapper, ListActionItemWrapper, ListActionItem, ListSeparator } from './Styles';
type Props = {
taskGroupID: string;
};
const LabelManager = ({ taskGroupID }: Props) => {
return (
<>
<ListActionsWrapper>
<ListActionItemWrapper>
<ListActionItem>Add card...</ListActionItem>
</ListActionItemWrapper>
<ListActionItemWrapper>
<ListActionItem>Copy List...</ListActionItem>
</ListActionItemWrapper>
<ListActionItemWrapper>
<ListActionItem>Move card...</ListActionItem>
</ListActionItemWrapper>
<ListActionItemWrapper>
<ListActionItem>Watch</ListActionItem>
</ListActionItemWrapper>
</ListActionsWrapper>
<ListSeparator />
<ListActionsWrapper>
<ListActionItemWrapper>
<ListActionItem>Sort By...</ListActionItem>
</ListActionItemWrapper>
</ListActionsWrapper>
<ListSeparator />
<ListActionsWrapper>
<ListActionItemWrapper>
<ListActionItem>Move All Cards in This List...</ListActionItem>
</ListActionItemWrapper>
<ListActionItemWrapper>
<ListActionItem>Archive All Cards in This List...</ListActionItem>
</ListActionItemWrapper>
</ListActionsWrapper>
<ListSeparator />
<ListActionsWrapper>
<ListActionItemWrapper>
<ListActionItem>Archive This List</ListActionItem>
</ListActionItemWrapper>
</ListActionsWrapper>
</>
);
};
export default LabelManager;

View File

@ -7,8 +7,8 @@ export default {
title: 'Lists',
parameters: {
backgrounds: [
{ name: 'white', value: '#ffffff', default: true },
{ name: 'gray', value: '#f8f8f8' },
{ name: 'gray', value: '#262c49', default: true },
{ name: 'white', value: '#ffffff' },
],
},
};
@ -33,28 +33,28 @@ const initialListsData = {
tasks: {
'task-1': {
taskID: 'task-1',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
name: 'Create roadmap',
position: 2,
labels: [],
},
'task-2': {
taskID: 'task-2',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
position: 1,
name: 'Create authentication',
labels: [],
},
'task-3': {
taskID: 'task-3',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
position: 3,
name: 'Create login',
labels: [],
},
'task-4': {
taskID: 'task-4',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
position: 4,
name: 'Create plugins',
labels: [],
@ -93,6 +93,29 @@ export const Default = () => {
onCardDrop={onCardDrop}
onListDrop={onListDrop}
onCardCreate={action('card create')}
onCreateList={listName => {
const [lastColumn] = Object.values(listsData.columns)
.sort((a, b) => a.position - b.position)
.slice(-1);
let position = 1;
if (lastColumn) {
position = lastColumn.position + 1;
}
const taskGroupID = Math.random().toString();
const newListsData = {
...listsData,
columns: {
...listsData.columns,
[taskGroupID]: {
taskGroupID,
name: listName,
position,
tasks: [],
},
},
};
setListsData(newListsData);
}}
/>
);
};
@ -121,28 +144,28 @@ const initialListsDataLarge = {
tasks: {
'task-1': {
taskID: 'task-1',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
name: 'Create roadmap',
position: 2,
labels: [],
},
'task-2': {
taskID: 'task-2',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
position: 1,
name: 'Create authentication',
labels: [],
},
'task-3': {
taskID: 'task-3',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
position: 3,
name: 'Create login',
labels: [],
},
'task-4': {
taskID: 'task-4',
taskGroupID: 'column-1',
taskGroup: { taskGroupID: 'column-1' },
position: 4,
name: 'Create plugins',
labels: [],
@ -179,6 +202,7 @@ export const ListsWithManyList = () => {
onCardCreate={action('card create')}
onCardDrop={onCardDrop}
onListDrop={onListDrop}
onCreateList={action('create list')}
/>
);
};

View File

@ -3,6 +3,7 @@ import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautif
import List, { ListCards } from 'shared/components/List';
import Card from 'shared/components/Card';
import CardComposer from 'shared/components/CardComposer';
import AddList from 'shared/components/AddList';
import {
isPositionChanged,
getSortedDraggables,
@ -26,9 +27,10 @@ type Props = {
onListDrop: any;
onCardCreate: (taskGroupID: string, name: string) => void;
onQuickEditorOpen: (e: ContextMenuEvent) => void;
onCreateList: (listName: string) => void;
};
const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEditorOpen }: Props) => {
const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEditorOpen, onCreateList }: Props) => {
const onDragEnd = ({ draggableId, source, destination, type }: DropResult) => {
if (typeof destination === 'undefined') return;
if (!isPositionChanged(source, destination)) return;
@ -74,7 +76,7 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd
<Container {...provided.droppableProps} ref={provided.innerRef}>
{orderedColumns.map((column: TaskGroup, index: number) => {
const columnCards = getSortedDraggables(
Object.values(tasks).filter((t: any) => t.taskGroupID === column.taskGroupID),
Object.values(tasks).filter((t: Task) => t.taskGroup.taskGroupID === column.taskGroupID),
);
return (
<Draggable draggableId={column.taskGroupID} key={column.taskGroupID} index={index}>
@ -91,6 +93,7 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd
ref={columnDragProvided.innerRef}
wrapperProps={columnDragProvided.draggableProps}
headerProps={columnDragProvided.dragHandleProps}
onExtraMenuOpen={(taskGroupID, pos, size) => console.log(taskGroupID, pos, size)}
>
<Droppable type="tasks" droppableId={column.taskGroupID}>
{columnDropProvided => (
@ -127,7 +130,6 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd
setCurrentComposer('');
}}
onCreateCard={name => {
setCurrentComposer('');
onCardCreate(column.taskGroupID, name);
}}
isOpen
@ -142,6 +144,7 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd
);
})}
{provided.placeholder}
<AddList onSave={onCreateList} />
</Container>
)}
</Droppable>

View File

@ -8,7 +8,7 @@ type Props = {
onLabelToggle: (labelId: string) => void;
onLabelEdit: (labelId: string, labelName: string, color: string) => void;
};
const LabelManager = ({ labels, onLabelToggle, onLabelEdit }: Props) => {
const LabelManager: React.FC<Props> = ({ labels, onLabelToggle, onLabelEdit }) => {
const [currentLabel, setCurrentLabel] = useState('');
return (
<>

View File

@ -1,7 +1,10 @@
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import { action } from '@storybook/addon-actions';
import LabelColors from 'shared/constants/labelColors';
import MenuTypes from 'shared/constants/menuTypes';
import LabelManager from 'shared/components/PopupMenu/LabelManager';
import LabelEditor from 'shared/components/PopupMenu/LabelEditor';
import ListActions from 'shared/components/ListActions';
import PopupMenu from '.';
export default {
@ -34,16 +37,9 @@ export const LabelsPopup = () => {
return (
<>
{isPopupOpen && (
<PopupMenu
title="Label"
menuType={MenuTypes.LABEL_MANAGER}
top={10}
onClose={() => setPopupOpen(false)}
left={10}
onLabelEdit={action('label edit')}
onLabelToggle={action('label toggle')}
labels={labelData}
/>
<PopupMenu title="Label" top={10} onClose={() => setPopupOpen(false)} left={10}>
<LabelManager labels={labelData} onLabelToggle={action('label toggle')} onLabelEdit={action('label edit')} />
</PopupMenu>
)}
<button type="submit" onClick={() => setPopupOpen(true)}>
Open
@ -57,16 +53,9 @@ export const LabelsLabelEditor = () => {
return (
<>
{isPopupOpen && (
<PopupMenu
title="Change Label"
menuType={MenuTypes.LABEL_EDITOR}
top={10}
onClose={() => setPopupOpen(false)}
left={10}
onLabelEdit={action('label edit')}
onLabelToggle={action('label toggle')}
labels={labelData}
/>
<PopupMenu title="Change Label" top={10} onClose={() => setPopupOpen(false)} left={10}>
<LabelEditor label={labelData[0]} onLabelEdit={action('label edit')} />
</PopupMenu>
)}
<button type="submit" onClick={() => setPopupOpen(true)}>
Open
@ -74,3 +63,39 @@ export const LabelsLabelEditor = () => {
</>
);
};
const initalState = { left: 0, top: 0, isOpen: false };
export const ListActionsPopup = () => {
const $buttonRef = useRef<HTMLButtonElement>(null);
const [popupData, setPopupData] = useState(initalState);
return (
<>
{popupData.isOpen && (
<PopupMenu
title="List Actions"
top={popupData.top}
onClose={() => setPopupData(initalState)}
left={popupData.left}
>
<ListActions taskGroupID="1" />
</PopupMenu>
)}
<button
ref={$buttonRef}
type="submit"
onClick={() => {
if ($buttonRef && $buttonRef.current) {
const pos = $buttonRef.current.getBoundingClientRect();
setPopupData({
isOpen: true,
left: pos.left,
top: pos.top + pos.height + 10,
});
}
}}
>
Open
</button>
</>
);
};

View File

@ -1,25 +1,16 @@
import React, { useRef } from 'react';
import { Cross } from 'shared/icons';
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
import MenuTypes from 'shared/constants/menuTypes';
import LabelColors from 'shared/constants/labelColors';
import LabelManager from './LabelManager';
import LabelEditor from './LabelEditor';
import { Container, Header, HeaderTitle, Content, Label, CloseButton } from './Styles';
import { Container, Header, HeaderTitle, Content, CloseButton } from './Styles';
type Props = {
title: string;
top: number;
left: number;
menuType: number;
labels?: Label[];
onClose: () => void;
onLabelToggle: (labelId: string) => void;
onLabelEdit: (labelId: string, labelName: string, color: string) => void;
};
const PopupMenu = ({ title, menuType, labels, top, left, onClose, onLabelToggle, onLabelEdit }: Props) => {
const PopupMenu: React.FC<Props> = ({ title, top, left, onClose, children }) => {
const $containerRef = useRef();
useOnOutsideClick($containerRef, true, onClose, null);
@ -31,17 +22,7 @@ const PopupMenu = ({ title, menuType, labels, top, left, onClose, onLabelToggle,
<Cross />
</CloseButton>
</Header>
<Content>
{menuType === MenuTypes.LABEL_MANAGER && (
<LabelManager onLabelEdit={onLabelEdit} onLabelToggle={onLabelToggle} labels={labels} />
)}
{menuType === MenuTypes.LABEL_EDITOR && (
<LabelEditor
onLabelEdit={onLabelEdit}
label={{ active: false, color: LabelColors.GREEN, name: 'General', labelId: 'general' }}
/>
)}
</Content>
<Content>{children}</Content>
</Container>
);
};

View File

@ -61,6 +61,7 @@ export const Default = () => {
onSaveName={action('on save name')}
onOpenComposer={action('on open composer')}
tasks={[]}
onExtraMenuOpen={(taskGroupID, pos, size) => console.log(taskGroupID, pos, size)}
>
<ListCards>
<Card

View File

@ -30,13 +30,15 @@ export const Default = () => {
<TaskDetails
task={{
taskID: '1',
taskGroupID: '1',
taskGroup: { taskGroupID: '1' },
name: 'Hello, world',
position: 1,
labels: [],
description,
}}
onTaskDescriptionChange={(_task, desc) => setDescription(desc)}
onDeleteTask={action('delete task')}
onCloseModal={action('close modal')}
/>
);
}}

View File

@ -68,7 +68,7 @@ const DetailsEditor: React.FC<DetailsEditorProps> = ({
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setDescription(e.currentTarget.value)}
/>
<TaskDetailsControls>
<ConfirmSave>Save</ConfirmSave>
<ConfirmSave onClick={handleOutsideClick}>Save</ConfirmSave>
<CancelEdit onClick={onCancel}>
<Cross size={16} />
</CancelEdit>
@ -80,22 +80,27 @@ const DetailsEditor: React.FC<DetailsEditorProps> = ({
type TaskDetailsProps = {
task: Task;
onTaskDescriptionChange: (task: Task, newDescription: string) => void;
onDeleteTask: (task: Task) => void;
onCloseModal: () => void;
};
const TaskDetails: React.FC<TaskDetailsProps> = ({ task, onTaskDescriptionChange }) => {
const TaskDetails: React.FC<TaskDetailsProps> = ({ task, onTaskDescriptionChange, onDeleteTask, onCloseModal }) => {
const [editorOpen, setEditorOpen] = useState(false);
const handleClick = () => {
setEditorOpen(!editorOpen);
};
const handleDeleteTask = () => {
onDeleteTask(task);
};
return (
<>
<TaskHeader>
<TaskMeta />
<TaskActions>
<TaskAction>
<TaskAction onClick={handleDeleteTask}>
<Bin size={20} />
</TaskAction>
<TaskAction>
<TaskAction onClick={onCloseModal}>
<Cross size={20} />
</TaskAction>
</TaskActions>