diff --git a/src/__tests__/EmptyState.test.tsx b/src/__tests__/EmptyState.test.tsx new file mode 100644 index 0000000..935f283 --- /dev/null +++ b/src/__tests__/EmptyState.test.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import TestRenderer from 'react-test-renderer'; + +import { EmptyState } from '../components/EmptyState'; + +describe('EmptyState', () => { + it('renders eyebrow, title, and copy', () => { + let tree: any; + TestRenderer.act(() => { + tree = TestRenderer.create( + React.createElement(EmptyState, { eyebrow: 'SEM USO', title: 'Nenhum treino salvo', copy: 'Comece uma janela.' }), + ); + }); + const json = JSON.stringify(tree.toJSON()); + expect(json).toContain('SEM USO'); + expect(json).toContain('Nenhum treino salvo'); + expect(json).toContain('Comece uma janela.'); + }); + + it('uses amber tone by default', () => { + let tree: any; + TestRenderer.act(() => { + tree = TestRenderer.create( + React.createElement(EmptyState, { eyebrow: 'X', title: 'Y', copy: 'Z', testID: 'es' }), + ); + }); + const json = JSON.stringify(tree.toJSON()); + expect(json).toContain('rgba(255, 180, 61'); + }); +}); diff --git a/src/components/EmptyState.tsx b/src/components/EmptyState.tsx new file mode 100644 index 0000000..d4b7306 --- /dev/null +++ b/src/components/EmptyState.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { StyleSheet, Text, View } from 'react-native'; +import Svg, { Circle, Line, Rect } from 'react-native-svg'; +import { colors, radius, spacing, typography } from '../theme'; + +type Tone = 'amber' | 'red' | 'green'; + +type Props = { + readonly eyebrow: string; + readonly title: string; + readonly copy: string; + readonly tone?: Tone; + readonly testID?: string; +}; + +function toneColor(tone: Tone): string { + if (tone === 'red') return colors.action; + if (tone === 'green') return colors.success; + return colors.warning; +} + +function toneBackground(tone: Tone): string { + if (tone === 'red') return 'rgba(226, 59, 46, 0.12)'; + if (tone === 'green') return 'rgba(52, 199, 123, 0.12)'; + return 'rgba(255, 180, 61, 0.10)'; +} + +export function EmptyState({ + eyebrow, + title, + copy, + tone = 'amber', + testID, +}: Props) { + return ( + + + + + + + + + {eyebrow} + {title} + {copy} + + ); +} + +const styles = StyleSheet.create({ + wrap: { + borderTopWidth: 1, + borderBottomWidth: 1, + borderColor: colors.line, + borderRadius: radius.md, + paddingVertical: spacing.xl, + paddingHorizontal: spacing.md, + minHeight: 220, + alignItems: 'center', + justifyContent: 'center', + gap: spacing.sm, + }, + geom: { + marginBottom: spacing.sm, + }, + eyebrow: { + ...typography.label, + }, + title: { + ...typography.display, + fontSize: 36, + lineHeight: 38, + textAlign: 'center', + }, + copy: { + ...typography.body, + color: colors.textMuted, + textAlign: 'center', + maxWidth: 280, + }, +});