Skip to content

Commit 8fec9c4

Browse files
committed
fix: update Tasks, tests, and tagSelector
1 parent bc01a43 commit 8fec9c4

File tree

3 files changed

+19
-147
lines changed

3 files changed

+19
-147
lines changed

frontend/src/components/HomeComponents/Tasks/Tasks.tsx

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -574,14 +574,6 @@ export const Tasks = (
574574
}
575575
};
576576

577-
// Handle removing a tag
578-
const handleRemoveTag = (tagToRemove: string) => {
579-
setNewTask({
580-
...newTask,
581-
tags: newTask.tags.filter((tag) => tag !== tagToRemove),
582-
});
583-
};
584-
585577
const sortWithOverdueOnTop = (tasks: Task[]) => {
586578
return [...tasks].sort((a, b) => {
587579
const aOverdue = a.status === 'pending' && isOverdue(a.due);
@@ -946,28 +938,6 @@ export const Tasks = (
946938
/>
947939
</div>
948940
</div>
949-
950-
<div className="mt-2">
951-
{newTask.tags.length > 0 && (
952-
<div className="grid grid-cols-4 items-center">
953-
<div> </div>
954-
<div className="flex flex-wrap gap-2 col-span-3">
955-
{newTask.tags.map((tag, index) => (
956-
<Badge key={index}>
957-
<span>{tag}</span>
958-
<button
959-
type="button"
960-
className="ml-2 text-red-500"
961-
onClick={() => handleRemoveTag(tag)}
962-
>
963-
964-
</button>
965-
</Badge>
966-
))}
967-
</div>
968-
</div>
969-
)}
970-
</div>
971941
</div>
972942
<DialogFooter>
973943
<Button

frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx

Lines changed: 4 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render, screen, fireEvent, act, within } from '@testing-library/react';
1+
import { render, screen, fireEvent, act } from '@testing-library/react';
22
import { Tasks } from '../Tasks';
33

44
// Mock props for the Tasks component
@@ -180,117 +180,6 @@ describe('Tasks Component', () => {
180180
expect(screen.getByTestId('current-page')).toHaveTextContent('1');
181181
});
182182

183-
test('shows tags as badges in task dialog and allows editing (add on Enter)', async () => {
184-
render(<Tasks {...mockProps} />);
185-
186-
expect(await screen.findByText('Task 1')).toBeInTheDocument();
187-
188-
const taskRow = screen.getByText('Task 1');
189-
fireEvent.click(taskRow);
190-
191-
expect(await screen.findByText('Tags:')).toBeInTheDocument();
192-
193-
expect(screen.getByText('tag1')).toBeInTheDocument();
194-
195-
const tagsLabel = screen.getByText('Tags:');
196-
const tagsRow = tagsLabel.closest('tr') as HTMLElement;
197-
const pencilButton = within(tagsRow).getByRole('button');
198-
fireEvent.click(pencilButton);
199-
200-
const editInput = await screen.findByPlaceholderText(
201-
'Add a tag (press enter to add)'
202-
);
203-
204-
fireEvent.change(editInput, { target: { value: 'newtag' } });
205-
fireEvent.keyDown(editInput, { key: 'Enter', code: 'Enter' });
206-
207-
expect(await screen.findByText('newtag')).toBeInTheDocument();
208-
209-
expect((editInput as HTMLInputElement).value).toBe('');
210-
});
211-
212-
test('adds a tag while editing and saves updated tags to backend', async () => {
213-
render(<Tasks {...mockProps} />);
214-
215-
expect(await screen.findByText('Task 1')).toBeInTheDocument();
216-
217-
const taskRow = screen.getByText('Task 1');
218-
fireEvent.click(taskRow);
219-
220-
expect(await screen.findByText('Tags:')).toBeInTheDocument();
221-
222-
const tagsLabel = screen.getByText('Tags:');
223-
const tagsRow = tagsLabel.closest('tr') as HTMLElement;
224-
const pencilButton = within(tagsRow).getByRole('button');
225-
fireEvent.click(pencilButton);
226-
227-
const editInput = await screen.findByPlaceholderText(
228-
'Add a tag (press enter to add)'
229-
);
230-
231-
fireEvent.change(editInput, { target: { value: 'addedtag' } });
232-
fireEvent.keyDown(editInput, { key: 'Enter', code: 'Enter' });
233-
234-
expect(await screen.findByText('addedtag')).toBeInTheDocument();
235-
236-
const actionContainer = editInput.parentElement as HTMLElement;
237-
const actionButtons = within(actionContainer).getAllByRole('button');
238-
fireEvent.click(actionButtons[0]);
239-
240-
const hooks = require('../hooks');
241-
expect(hooks.editTaskOnBackend).toHaveBeenCalled();
242-
243-
const callArg = hooks.editTaskOnBackend.mock.calls[0][0];
244-
expect(callArg.tags).toEqual(expect.arrayContaining(['tag1', 'addedtag']));
245-
});
246-
247-
test('removes a tag while editing and saves updated tags to backend', async () => {
248-
render(<Tasks {...mockProps} />);
249-
250-
expect(await screen.findByText('Task 1')).toBeInTheDocument();
251-
252-
const taskRow = screen.getByText('Task 1');
253-
fireEvent.click(taskRow);
254-
255-
expect(await screen.findByText('Tags:')).toBeInTheDocument();
256-
257-
const tagsLabel = screen.getByText('Tags:');
258-
const tagsRow = tagsLabel.closest('tr') as HTMLElement;
259-
const pencilButton = within(tagsRow).getByRole('button');
260-
fireEvent.click(pencilButton);
261-
262-
const editInput = await screen.findByPlaceholderText(
263-
'Add a tag (press enter to add)'
264-
);
265-
266-
fireEvent.change(editInput, { target: { value: 'newtag' } });
267-
fireEvent.keyDown(editInput, { key: 'Enter', code: 'Enter' });
268-
269-
expect(await screen.findByText('newtag')).toBeInTheDocument();
270-
271-
const tagBadge = screen.getByText('tag1');
272-
const badgeContainer = (tagBadge.closest('div') ||
273-
tagBadge.parentElement) as HTMLElement;
274-
275-
const removeButton = within(badgeContainer).getByText('✖');
276-
fireEvent.click(removeButton);
277-
278-
expect(screen.queryByText('tag2')).not.toBeInTheDocument();
279-
280-
const actionContainer = editInput.parentElement as HTMLElement;
281-
282-
const actionButtons = within(actionContainer).getAllByRole('button');
283-
284-
fireEvent.click(actionButtons[0]);
285-
286-
const hooks = require('../hooks');
287-
expect(hooks.editTaskOnBackend).toHaveBeenCalled();
288-
289-
const callArg = hooks.editTaskOnBackend.mock.calls[0][0];
290-
291-
expect(callArg.tags).toEqual(expect.arrayContaining(['newtag', '-tag1']));
292-
});
293-
294183
test('shows red background on task ID and Overdue badge for overdue tasks', async () => {
295184
render(<Tasks {...mockProps} />);
296185

@@ -336,12 +225,13 @@ describe('Tasks Component', () => {
336225
test('renders mocked TagSelector in Add Task dialog', async () => {
337226
render(<Tasks {...mockProps} />);
338227

339-
const addButton = screen.getAllByText('Add Task')[0];
228+
const addButton = screen.getByRole('button', { name: /add task/i });
229+
340230
fireEvent.click(addButton);
341231

342232
expect(await screen.findByTestId('mock-tag-selector')).toBeInTheDocument();
343233
});
344-
234+
345235
test('TagSelector receives correct options and selected values', async () => {
346236
render(<Tasks {...mockProps} />);
347237

frontend/src/components/ui/tagSelector.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { Check, ChevronsUpDown, Plus } from 'lucide-react';
2+
import { Check, ChevronsUpDown, Plus, X } from 'lucide-react';
33

44
import { cn } from '@/components/utils/utils';
55
import { Button } from '@/components/ui/button';
@@ -53,6 +53,13 @@ export function TagSelector({
5353
onChange([...selected, tag]);
5454
}
5555
};
56+
57+
// Remove a tag when X inside the chip is clicked
58+
const removeTagInsideChip = (tag: string, e: React.MouseEvent) => {
59+
e.stopPropagation();
60+
onChange(selected.filter((t) => t !== tag));
61+
};
62+
5663
return (
5764
<>
5865
<Popover open={open} onOpenChange={setOpen}>
@@ -61,7 +68,7 @@ export function TagSelector({
6168
variant="outline"
6269
role="combobox"
6370
aria-expanded={open}
64-
className="w-full justify-between h-auto min-h-[40px]"
71+
className="w-full justify-between h-auto min-h-[40px] group"
6572
>
6673
<div className="flex flex-wrap gap-2 items-center">
6774
{selected.length === 0 ? (
@@ -70,9 +77,14 @@ export function TagSelector({
7077
selected.map((tag) => (
7178
<span
7279
key={tag}
73-
className="px-2 py-1 rounded-md bg-muted text-sm"
80+
className="px-2 py-1 rounded-md bg-muted text-sm flex items-center gap-1 hover:text-neutral-300
81+
group-hover:bg-black group-hover:text-muted-foreground transition-colors"
7482
>
7583
{tag}
84+
<X
85+
className="w-3 h-3 cursor-pointer hover:text-red-500"
86+
onClick={(e) => removeTagInsideChip(tag, e)}
87+
/>
7688
</span>
7789
))
7890
)}

0 commit comments

Comments
 (0)