feat(sync-help): ConflictModal inline 설명 + 자세히 보기 링크 (onOpenHelp prop)

This commit is contained in:
altair823
2026-05-10 23:29:29 +09:00
parent 5e55cd3469
commit 54ef394bb4
2 changed files with 36 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ import { inboxApi } from '../api.js';
interface Props {
onClose: () => void;
onResolved: () => void;
onOpenHelp?: (anchor: 'main-conflict' | 'auto' | 'silent' | 'setup') => void;
}
const overlayStyle: React.CSSProperties = {
@@ -22,7 +23,7 @@ const rowStyle: React.CSSProperties = {
border: '1px solid #eee', borderRadius: 6, padding: 10, marginTop: 8
};
export function ConflictModal({ onClose, onResolved }: Props): React.ReactElement {
export function ConflictModal({ onClose, onResolved, onOpenHelp }: Props): React.ReactElement {
const [conflicts, setConflicts] = useState<SyncConflict[]>([]);
const [busy, setBusy] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
@@ -74,6 +75,18 @@ export function ConflictModal({ onClose, onResolved }: Props): React.ReactElemen
<pre style={preStyle()}>{c.remoteText || '(미리보기 없음)'}</pre>
</div>
</div>
<div style={{ marginTop: 8, fontSize: 11, color: '#666', lineHeight: 1.5 }}>
<div><b> </b>: .</div>
<div><b> </b>: .</div>
{onOpenHelp && (
<button
onClick={() => onOpenHelp('main-conflict')}
style={{ background: 'none', border: 'none', color: '#0a4b80', cursor: 'pointer', fontSize: 11, padding: 0, marginTop: 2, textDecoration: 'underline' }}
>
</button>
)}
</div>
<div style={{ marginTop: 8, display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
<button
onClick={() => { void onChoose(c.path, 'local'); }}

View File

@@ -58,4 +58,26 @@ describe('ConflictModal', () => {
expect(onClose).toHaveBeenCalled();
});
});
it('각 conflict row 에 local/remote inline 설명 표시', async () => {
render(<ConflictModal onClose={() => {}} onResolved={() => {}} onOpenHelp={() => {}} />);
await waitFor(() => screen.getByText(/local A/));
expect(screen.getAllByText(/이 기기의 변경을 보존/).length).toBeGreaterThanOrEqual(2);
expect(screen.getAllByText(/원격의 변경을 가져오고/).length).toBeGreaterThanOrEqual(2);
});
it('"자세히 보기" 클릭 → onOpenHelp("main-conflict") 호출', async () => {
const onOpenHelp = vi.fn();
render(<ConflictModal onClose={() => {}} onResolved={() => {}} onOpenHelp={onOpenHelp} />);
await waitFor(() => screen.getByText(/local A/));
const links = screen.getAllByRole('button', { name: /자세히 보기/ });
fireEvent.click(links[0]!);
expect(onOpenHelp).toHaveBeenCalledWith('main-conflict');
});
it('onOpenHelp 미제공 → "자세히 보기" 링크 미렌더', async () => {
render(<ConflictModal onClose={() => {}} onResolved={() => {}} />);
await waitFor(() => screen.getByText(/local A/));
expect(screen.queryByRole('button', { name: /자세히 보기/ })).toBeNull();
});
});