feat(10-02): create RemoteNamePreview component with TDD
- Stateless component accepting value prop (string) - Empty/whitespace state: shows grayed [my-remote] placeholder + guidance text - Value state: shows [typed-value] in on-surface-variant color - Renders only div/span elements — no form controls - 9 tests covering empty state, value state, and element type constraints
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
// @vitest-environment jsdom
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { RemoteNamePreview } from './RemoteNamePreview';
|
||||||
|
|
||||||
|
describe('RemoteNamePreview', () => {
|
||||||
|
describe('empty state', () => {
|
||||||
|
it('shows placeholder [my-remote] when value is empty string', () => {
|
||||||
|
render(<RemoteNamePreview value="" />);
|
||||||
|
expect(screen.getByText('[my-remote]')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows placeholder [my-remote] when value is whitespace-only', () => {
|
||||||
|
render(<RemoteNamePreview value=" " />);
|
||||||
|
expect(screen.getByText('[my-remote]')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows guidance text when value is empty', () => {
|
||||||
|
render(<RemoteNamePreview value="" />);
|
||||||
|
expect(screen.getByText('Type a name to see how it appears in your config')).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('value state', () => {
|
||||||
|
it('shows [my-backup] when value is "my-backup"', () => {
|
||||||
|
render(<RemoteNamePreview value="my-backup" />);
|
||||||
|
expect(screen.getByText('[my-backup]')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows [azure-prod] when value is "azure-prod"', () => {
|
||||||
|
render(<RemoteNamePreview value="azure-prod" />);
|
||||||
|
expect(screen.getByText('[azure-prod]')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does NOT show guidance text when value is provided', () => {
|
||||||
|
render(<RemoteNamePreview value="my-backup" />);
|
||||||
|
expect(screen.queryByText('Type a name to see how it appears in your config')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('element types', () => {
|
||||||
|
it('renders no form controls (no textbox role)', () => {
|
||||||
|
render(<RemoteNamePreview value="" />);
|
||||||
|
expect(screen.queryByRole('textbox')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders no input elements', () => {
|
||||||
|
const { container } = render(<RemoteNamePreview value="test" />);
|
||||||
|
expect(container.querySelector('input')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders no textarea elements', () => {
|
||||||
|
const { container } = render(<RemoteNamePreview value="test" />);
|
||||||
|
expect(container.querySelector('textarea')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
interface RemoteNamePreviewProps {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoteNamePreview({ value }: RemoteNamePreviewProps) {
|
||||||
|
const trimmed = value.trim();
|
||||||
|
const isEmpty = trimmed.length === 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-2 rounded-md bg-surface-variant px-3 py-2 text-xs font-mono">
|
||||||
|
{isEmpty ? (
|
||||||
|
<>
|
||||||
|
<span className="text-on-surface-variant/50">[my-remote]</span>
|
||||||
|
<p className="mt-1 text-on-surface-variant/40 font-sans">
|
||||||
|
Type a name to see how it appears in your config
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="text-on-surface-variant">[{trimmed}]</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user