feat: enhance contacts management with sidebar and selection features
This commit is contained in:
@@ -23,33 +23,62 @@ const _emptyContact: ContactCard = {
|
||||
};
|
||||
|
||||
describe('ContactListItem', () => {
|
||||
const baseProps = {
|
||||
isSelected: false,
|
||||
isChecked: false,
|
||||
hasSelection: false,
|
||||
density: 'regular' as const,
|
||||
onClick: vi.fn(),
|
||||
onCheckboxClick: vi.fn(),
|
||||
};
|
||||
|
||||
it('renders contact name and email', () => {
|
||||
render(<ContactListItem contact={contact} isSelected={false} onClick={vi.fn()} />);
|
||||
render(<ContactListItem contact={contact} {...baseProps} />);
|
||||
expect(screen.getByText('Alice Smith')).toBeInTheDocument();
|
||||
expect(screen.getByText('alice@example.com')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders organization', () => {
|
||||
render(<ContactListItem contact={contact} isSelected={false} onClick={vi.fn()} />);
|
||||
it('renders organization in comfortable density', () => {
|
||||
render(<ContactListItem contact={contact} {...baseProps} density="comfortable" />);
|
||||
expect(screen.getByText('Acme Corp')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides organization in regular density', () => {
|
||||
render(<ContactListItem contact={contact} {...baseProps} density="regular" />);
|
||||
expect(screen.queryByText('Acme Corp')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('applies selected styling', () => {
|
||||
const { container } = render(<ContactListItem contact={contact} isSelected={true} onClick={vi.fn()} />);
|
||||
const button = container.querySelector('button');
|
||||
expect(button?.className).toContain('bg-accent');
|
||||
const { container } = render(<ContactListItem contact={contact} {...baseProps} isSelected={true} />);
|
||||
const div = container.firstElementChild;
|
||||
expect(div?.className).toContain('bg-blue-200');
|
||||
});
|
||||
|
||||
it('shows email as display name when no name exists', () => {
|
||||
render(<ContactListItem contact={noNameContact} isSelected={false} onClick={vi.fn()} />);
|
||||
render(<ContactListItem contact={noNameContact} {...baseProps} />);
|
||||
const matches = screen.getAllByText('nobody@example.com');
|
||||
expect(matches.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('calls onClick when clicked', () => {
|
||||
const onClick = vi.fn();
|
||||
render(<ContactListItem contact={contact} isSelected={false} onClick={onClick} />);
|
||||
render(<ContactListItem contact={contact} {...baseProps} onClick={onClick} />);
|
||||
fireEvent.click(screen.getByText('Alice Smith'));
|
||||
expect(onClick).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('does not show checkbox when hasSelection is false', () => {
|
||||
const { container } = render(<ContactListItem contact={contact} {...baseProps} hasSelection={false} />);
|
||||
expect(container.querySelector('button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows checkbox when hasSelection is true', () => {
|
||||
const { container } = render(<ContactListItem contact={contact} {...baseProps} hasSelection={true} />);
|
||||
expect(container.querySelector('button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides avatar in extra-compact density', () => {
|
||||
const { container } = render(<ContactListItem contact={contact} {...baseProps} density="extra-compact" />);
|
||||
expect(container.querySelector('[data-testid="avatar"]') || container.querySelector('.rounded-full')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,15 +36,15 @@ const defaultProps = {
|
||||
onSearchChange: vi.fn(),
|
||||
onSelectContact: vi.fn(),
|
||||
onCreateNew: vi.fn(),
|
||||
supportsSync: true,
|
||||
categoryLabel: 'All Contacts',
|
||||
selectedContactIds: new Set<string>(),
|
||||
onToggleSelection: vi.fn(),
|
||||
onSelectRangeContacts: vi.fn(),
|
||||
onSelectAll: vi.fn(),
|
||||
onClearSelection: vi.fn(),
|
||||
onBulkDelete: vi.fn(),
|
||||
onBulkAddToGroup: vi.fn(),
|
||||
onBulkExport: vi.fn(),
|
||||
groups: [],
|
||||
};
|
||||
|
||||
describe('ContactList', () => {
|
||||
@@ -70,32 +70,14 @@ describe('ContactList', () => {
|
||||
expect(screen.getByText('empty_search')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows local mode banner when supportsSync is false', () => {
|
||||
render(<ContactList {...defaultProps} supportsSync={false} />);
|
||||
expect(screen.getByText('local_mode')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides local mode banner when supportsSync is true', () => {
|
||||
render(<ContactList {...defaultProps} supportsSync={true} />);
|
||||
expect(screen.queryByText('local_mode')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onCreateNew when create button is clicked', () => {
|
||||
const onCreateNew = vi.fn();
|
||||
render(<ContactList {...defaultProps} onCreateNew={onCreateNew} />);
|
||||
fireEvent.click(screen.getByText('create_new'));
|
||||
expect(onCreateNew).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('shows bulk action bar when contacts are selected', () => {
|
||||
render(<ContactList {...defaultProps} selectedContactIds={new Set(['1'])} />);
|
||||
expect(screen.getByText('bulk.delete')).toBeInTheDocument();
|
||||
expect(screen.getByText('bulk.export')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('excludes groups from the list', () => {
|
||||
render(<ContactList {...defaultProps} contacts={[alice, bob, group]} />);
|
||||
expect(screen.getByText('Alice Smith')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Team')).not.toBeInTheDocument();
|
||||
it('shows category label with count', () => {
|
||||
render(<ContactList {...defaultProps} />);
|
||||
expect(screen.getByText('All Contacts (2)')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user