Skip to content

Commit c323ee7

Browse files
feat:reset button
1 parent 7feea45 commit c323ee7

File tree

4 files changed

+138
-3
lines changed

4 files changed

+138
-3
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React from 'react';
2+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3+
import { ResetButton } from '@/components/ResetButton';
4+
5+
describe('ResetButton', () => {
6+
let mockOnReset: jest.Mock;
7+
8+
beforeEach(() => {
9+
mockOnReset = jest.fn();
10+
// Mock window.confirm
11+
global.confirm = jest.fn();
12+
});
13+
14+
afterEach(() => {
15+
jest.clearAllMocks();
16+
});
17+
18+
it('should render the reset button', () => {
19+
render(<ResetButton onReset={mockOnReset} />);
20+
21+
const button = screen.getByRole('button', { name: 'メモをリセット' });
22+
expect(button).toBeInTheDocument();
23+
expect(button).toHaveTextContent('リセット');
24+
});
25+
26+
it('should show confirmation dialog when clicked', () => {
27+
(global.confirm as jest.Mock).mockReturnValue(true);
28+
29+
render(<ResetButton onReset={mockOnReset} />);
30+
31+
const button = screen.getByRole('button', { name: 'メモをリセット' });
32+
fireEvent.click(button);
33+
34+
expect(global.confirm).toHaveBeenCalledWith(
35+
'メモをリセットしますか?\nこの操作は取り消せません。'
36+
);
37+
});
38+
39+
it('should call onReset when user confirms', () => {
40+
(global.confirm as jest.Mock).mockReturnValue(true);
41+
42+
render(<ResetButton onReset={mockOnReset} />);
43+
44+
const button = screen.getByRole('button', { name: 'メモをリセット' });
45+
fireEvent.click(button);
46+
47+
expect(mockOnReset).toHaveBeenCalledTimes(1);
48+
});
49+
50+
it('should not call onReset when user cancels', () => {
51+
(global.confirm as jest.Mock).mockReturnValue(false);
52+
53+
render(<ResetButton onReset={mockOnReset} />);
54+
55+
const button = screen.getByRole('button', { name: 'メモをリセット' });
56+
fireEvent.click(button);
57+
58+
expect(mockOnReset).not.toHaveBeenCalled();
59+
});
60+
61+
it('should be disabled when disabled prop is true', () => {
62+
render(<ResetButton onReset={mockOnReset} disabled={true} />);
63+
64+
const button = screen.getByRole('button', { name: 'メモをリセット' });
65+
expect(button).toBeDisabled();
66+
});
67+
68+
it('should not show confirmation when disabled', () => {
69+
render(<ResetButton onReset={mockOnReset} disabled={true} />);
70+
71+
const button = screen.getByRole('button', { name: 'メモをリセット' });
72+
fireEvent.click(button);
73+
74+
expect(global.confirm).not.toHaveBeenCalled();
75+
expect(mockOnReset).not.toHaveBeenCalled();
76+
});
77+
78+
it('should have proper styling classes', () => {
79+
render(<ResetButton onReset={mockOnReset} />);
80+
81+
const button = screen.getByRole('button', { name: 'メモをリセット' });
82+
expect(button).toHaveClass('bg-red-600');
83+
expect(button).toHaveClass('hover:bg-red-700');
84+
});
85+
});

url-memo-app/app/page.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import React, { useState, useEffect, Suspense } from 'react';
44
import { Editor } from '@/components/Editor';
55
import { SaveButton } from '@/components/SaveButton';
66
import { ShareButton } from '@/components/ShareButton';
7+
import { ResetButton } from '@/components/ResetButton';
78
import { StatusMessage } from '@/components/StatusMessage';
89
import { useUrlState } from '@/hooks/useUrlState';
910
import { useBookmark } from '@/hooks/useBookmark';
1011
import { encodeTextToUrl } from '@/utils/urlEncoder';
1112
import { decodeTextFromUrl } from '@/utils/urlDecoder';
1213

13-
const URL_LENGTH_LIMIT = 2048;
14+
const URL_LENGTH_LIMIT = 8192; // 8KB - supported by most modern browsers and servers
1415

1516
interface StatusMessageState {
1617
message: string;
@@ -68,6 +69,15 @@ function MemoApp() {
6869
showStatusMessage('URLがクリップボードにコピーされました。', 'success');
6970
};
7071

72+
const handleReset = () => {
73+
setMemoText('');
74+
// Clear URL parameters
75+
const url = new URL(window.location.href);
76+
url.searchParams.delete('memo');
77+
window.history.replaceState({}, '', url.toString());
78+
showStatusMessage('メモがリセットされました。', 'info');
79+
};
80+
7181
const getCurrentUrl = () => {
7282
if (typeof window !== 'undefined') {
7383
return window.location.href;
@@ -103,7 +113,7 @@ function MemoApp() {
103113
<Editor
104114
initialText={memoText}
105115
onChange={handleMemoChange}
106-
maxLength={1500} // Conservative limit to ensure URL stays under limit after encoding
116+
maxLength={5000} // Increased limit with compression
107117
/>
108118
</div>
109119

@@ -117,6 +127,9 @@ function MemoApp() {
117127
currentUrl={getCurrentUrl()}
118128
onShare={handleShare}
119129
/>
130+
<ResetButton
131+
onReset={handleReset}
132+
/>
120133
</div>
121134

122135
{/* Help Section */}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
3+
interface ResetButtonProps {
4+
onReset: () => void;
5+
disabled?: boolean;
6+
}
7+
8+
export const ResetButton: React.FC<ResetButtonProps> = ({ onReset, disabled = false }) => {
9+
const handleClick = () => {
10+
if (window.confirm('メモをリセットしますか?\nこの操作は取り消せません。')) {
11+
onReset();
12+
}
13+
};
14+
15+
return (
16+
<button
17+
onClick={handleClick}
18+
disabled={disabled}
19+
className="inline-flex items-center gap-2 px-6 py-3 bg-red-600 text-white font-medium rounded-lg hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
20+
aria-label="メモをリセット"
21+
>
22+
<svg
23+
xmlns="http://www.w3.org/2000/svg"
24+
className="h-5 w-5"
25+
viewBox="0 0 20 20"
26+
fill="currentColor"
27+
>
28+
<path
29+
fillRule="evenodd"
30+
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm6-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
31+
clipRule="evenodd"
32+
/>
33+
</svg>
34+
リセット
35+
</button>
36+
);
37+
};

url-memo-app/utils/urlEncoder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import LZString from 'lz-string';
22

3-
const MAX_URL_LENGTH = 2048;
3+
const MAX_URL_LENGTH = 8192; // 8KB - supported by most modern browsers and servers
44

55
export function encodeTextToUrl(text: string): string {
66
if (text === '') {

0 commit comments

Comments
 (0)