feat(components): EmptyState with instrument geometry
This commit is contained in:
1 parent
fc42e1da43
commit
bea019fb0f
2 files changed
+138
No files matched your search
@@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in new issue
Block a user