feat: make notes keep open when suddenly closed (#7)
Main CI-Frontend / Build & Push (push) Successful in 39s

## What

- New feature allowing open notes to be restore, if the tab is closed

## Why

- Making users' life easier

## Mood
<img width="200" src="https://media3.giphy.com/media/S5uASkxZ5eGcwf2vHM/giphy.gif?cid=36b14facu3gasrwzy8axqb43wcuo1tsdjvjcslyk9t9jzvab&ep=v1_gifs_search&rid=giphy.gif&ct=g"/>

Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
2026-07-01 15:17:41 +00:00
parent 4164b7dd97
commit acb08f7e14
2 changed files with 117 additions and 3 deletions
+94 -2
View File
@@ -55,7 +55,19 @@ vi.mock('../../components/AlertError', () => ({
}));
vi.mock('../../components/ModalMarkdown', () => ({
default: (props: any) => <div data-testid="modal-markdown">{props.show ? 'Modal Open' : ''}</div>
default: (props: any) => (
<div data-testid="modal-markdown">
{props.show ? (
<div>
<div data-testid="modal-title">{props.title}</div>
<div data-testid="modal-content">{props.markdownText}</div>
<button data-testid="modal-close" onClick={props.onHide}>Close</button>
</div>
) : (
''
)}
</div>
)
}));
vi.mock('../../components/TaskTitle', () => ({
@@ -67,7 +79,14 @@ vi.mock('../../components/TaskTimeLeft', () => ({
}));
vi.mock('../../components/TaskTag', () => ({
default: (props: any) => <div data-testid="task-tag">{props.tag}</div>
default: (props: any) => (
<div data-testid="task-tag">
{props.tag}
{props.taskOrNote === 'note' && props.onClick && (
<a href="#" data-testid="open-it" onClick={props.onClick}>Open it</a>
)}
</div>
)
}));
vi.mock('../../components/NoteTitle', () => ({
@@ -545,6 +564,79 @@ describe('Home Component', () => {
expect(screen.getAllByTestId('task-title')[0].textContent).toBe('Task 1');
});
});
test('saves note ID to localStorage when opening modal', async () => {
await act(async () => {
renderHome();
});
await waitFor(() => {
expect(screen.getAllByTestId('open-it').length).toBe(2);
});
const openItLinks = screen.getAllByTestId('open-it');
await act(async () => {
fireEvent.click(openItLinks[1]);
});
expect(localStorage.getItem('OPEN_NOTE_ID')).toBe('1');
expect(screen.getByTestId('modal-title').textContent).toBe('Note 1');
expect(screen.getByTestId('modal-content').textContent).toBe('Line 1\nLine 2\nLine 3');
});
test('restores open note modal from localStorage on reload', async () => {
localStorage.setItem('OPEN_NOTE_ID', '2');
await act(async () => {
renderHome();
});
await waitFor(() => {
expect(screen.getByTestId('modal-title').textContent).toBe('Note 2');
});
expect(screen.getByTestId('modal-content').textContent).toBe('This is a sample\nnote content');
});
test('does not restore modal if localStorage note ID not found', async () => {
localStorage.setItem('OPEN_NOTE_ID', '999');
await act(async () => {
renderHome();
});
await waitFor(() => {
expect(screen.getAllByTestId('note-title').length).toBe(2);
});
const modal = screen.getByTestId('modal-markdown');
expect(modal.textContent).toBe('');
expect(localStorage.getItem('OPEN_NOTE_ID')).toBeNull();
});
test('clears localStorage when closing modal', async () => {
await act(async () => {
renderHome();
});
await waitFor(() => {
expect(screen.getAllByTestId('open-it').length).toBe(2);
});
const openItLinks = screen.getAllByTestId('open-it');
await act(async () => {
fireEvent.click(openItLinks[1]);
});
expect(localStorage.getItem('OPEN_NOTE_ID')).toBe('1');
const closeButton = screen.getByTestId('modal-close');
await act(async () => {
fireEvent.click(closeButton);
});
expect(localStorage.getItem('OPEN_NOTE_ID')).toBeNull();
});
/*
test('getFirstRows properly formats note preview', async () => {
await act(async () => {
+23 -1
View File
@@ -28,6 +28,8 @@ import TaskTimeLeft from '../../components/TaskTimeLeft';
import TaskTag from '../../components/TaskTag';
import NoteTitle from '../../components/NoteTitle';
const OPEN_NOTE_ID_KEY = 'OPEN_NOTE_ID';
/**
* Home page component.
*
@@ -299,7 +301,10 @@ function Home(): React.ReactNode {
return preview.join('\n');
};
const handleCloseModal = () => setShowMarkdownView(false);
const handleCloseModal = () => {
setShowMarkdownView(false);
localStorage.removeItem(OPEN_NOTE_ID_KEY);
};
const getSelectedLabel = (): string => {
if (selectedOption === 'everything') return t('home_radio_everything');
@@ -345,6 +350,22 @@ function Home(): React.ReactNode {
applyFilter(filterText, selectedOption, savedTasks, savedNotes);
}, [savedTasks, savedNotes, filterText, selectedOption]);
useEffect(() => {
const openNoteId = localStorage.getItem(OPEN_NOTE_ID_KEY);
if (openNoteId && notes.length > 0) {
const noteId = Number(openNoteId);
const foundNote = notes.find(n => n.id === noteId);
if (foundNote) {
setModalTitle(foundNote.title);
setModalContent(foundNote.description);
setShowMarkdownView(true);
}
else {
localStorage.removeItem(OPEN_NOTE_ID_KEY);
}
}
}, [notes]);
return (
<Container fluid>
<ContentHeader
@@ -602,6 +623,7 @@ function Home(): React.ReactNode {
setModalTitle(note.title);
setModalContent(note.description);
setShowMarkdownView(true);
localStorage.setItem(OPEN_NOTE_ID_KEY, note.id.toString());
}}
/>
</Card.Footer>