feat(components): EmptyState with instrument geometry

This commit is contained in:
fightrate-bot committed 2026-06-16 01:47:52 -03:00
1 parent fc42e1da43
commit bea019fb0f
2 files changed
+138

No files matched your search

+30
View File
@@ -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');
});
});
+108
View File
@@ -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 (
<View
accessibilityLabel={`${eyebrow}. ${title}. ${copy}`}
style={[styles.wrap, { backgroundColor: toneBackground(tone) }]}
testID={testID}
>
<View style={styles.geom}>
<Svg height={64} viewBox="0 0 64 64" width={64}>
<Rect
x={6}
y={6}
width={52}
height={52}
stroke={toneColor(tone)}
strokeWidth={1}
fill="none"
/>
<Circle
cx={32}
cy={32}
r={20}
stroke={toneColor(tone)}
strokeWidth={1}
fill="none"
/>
<Line
x1={6}
y1={58}
x2={58}
y2={6}
stroke={toneColor(tone)}
strokeWidth={1}
/>
</Svg>
</View>
<Text style={[styles.eyebrow, { color: toneColor(tone) }]}>{eyebrow}</Text>
<Text style={styles.title}>{title}</Text>
<Text style={styles.copy}>{copy}</Text>
</View>
);
}
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,
},
});