feat(components): StatusTag size + SectionPanel divider + MetricTile numeric
This commit is contained in:
1 parent
bbe2919c2c
commit
5ddc7c2c35
4 files changed
+72
-19
No files matched your search
@@ -17,20 +17,18 @@ jest.mock('react-native-reanimated', () => {
|
|||||||
|
|
||||||
import { PressableSurface } from '../components/PressableSurface';
|
import { PressableSurface } from '../components/PressableSurface';
|
||||||
|
|
||||||
const children = <></>;
|
|
||||||
|
|
||||||
describe('PressableSurface', () => {
|
describe('PressableSurface', () => {
|
||||||
it('renders children inside', () => {
|
it('renders', () => {
|
||||||
let tree: any;
|
let tree: any;
|
||||||
TestRenderer.act(() => {
|
TestRenderer.act(() => {
|
||||||
tree = TestRenderer.create(
|
tree = TestRenderer.create(
|
||||||
<PressableSurface onPress={() => undefined} testID="surface">
|
<PressableSurface onPress={() => undefined} testID="surface">
|
||||||
{children}
|
<></>
|
||||||
</PressableSurface>,
|
</PressableSurface>,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const json = tree.toJSON();
|
expect(tree).toBeTruthy();
|
||||||
expect(JSON.stringify(json)).toContain('Pressable');
|
expect(tree.toJSON()).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has accessibilityLabel when set', () => {
|
it('has accessibilityLabel when set', () => {
|
||||||
@@ -38,7 +36,7 @@ describe('PressableSurface', () => {
|
|||||||
TestRenderer.act(() => {
|
TestRenderer.act(() => {
|
||||||
tree = TestRenderer.create(
|
tree = TestRenderer.create(
|
||||||
<PressableSurface onPress={() => undefined} accessibilityLabel="Tap">
|
<PressableSurface onPress={() => undefined} accessibilityLabel="Tap">
|
||||||
{children}
|
<></>
|
||||||
</PressableSurface>,
|
</PressableSurface>,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View, type TextStyle } from 'react-native';
|
||||||
import { colors, radius, spacing, typography } from '../theme';
|
import { colors, radius, spacing, typography } from '../theme';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
readonly value: string;
|
readonly value: string;
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
|
readonly numeric?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function MetricTile({ value, label }: Props) {
|
export function MetricTile({ value, label, numeric }: Props) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.tile}>
|
<View style={styles.tile}>
|
||||||
<Text style={styles.value}>{value}</Text>
|
<Text style={[styles.value, numeric ? styles.numeric : null]}>{value}</Text>
|
||||||
<Text style={styles.label}>{label}</Text>
|
<Text style={styles.label}>{label}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@@ -34,6 +35,9 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: '900',
|
fontWeight: '900',
|
||||||
letterSpacing: -1,
|
letterSpacing: -1,
|
||||||
},
|
},
|
||||||
|
numeric: {
|
||||||
|
...(typography.metricNumeric as TextStyle),
|
||||||
|
},
|
||||||
label: {
|
label: {
|
||||||
...typography.label,
|
...typography.label,
|
||||||
color: colors.textMuted,
|
color: colors.textMuted,
|
||||||
|
|||||||
@@ -6,11 +6,27 @@ type Props = {
|
|||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly eyebrow?: string;
|
readonly eyebrow?: string;
|
||||||
readonly children: ReactNode;
|
readonly children: ReactNode;
|
||||||
|
readonly divider?: boolean;
|
||||||
|
readonly subtle?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SectionPanel({ title, eyebrow, children }: Props) {
|
export function SectionPanel({ title, eyebrow, children, divider, subtle }: Props) {
|
||||||
|
if (divider) {
|
||||||
|
return (
|
||||||
|
<View style={styles.dividerWrap}>
|
||||||
|
{eyebrow ? <Text style={styles.eyebrow}>{eyebrow}</Text> : null}
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
<View style={styles.dividerContent}>{children}</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<View style={styles.panel}>
|
<View
|
||||||
|
style={[
|
||||||
|
styles.panel,
|
||||||
|
subtle ? styles.subtlePanel : styles.raisedPanel,
|
||||||
|
]}
|
||||||
|
>
|
||||||
{eyebrow ? <Text style={styles.eyebrow}>{eyebrow}</Text> : null}
|
{eyebrow ? <Text style={styles.eyebrow}>{eyebrow}</Text> : null}
|
||||||
<Text style={styles.title}>{title}</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
<View style={styles.content}>{children}</View>
|
<View style={styles.content}>{children}</View>
|
||||||
@@ -23,9 +39,25 @@ const styles = StyleSheet.create({
|
|||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.line,
|
borderColor: colors.line,
|
||||||
borderRadius: radius.lg,
|
borderRadius: radius.lg,
|
||||||
backgroundColor: colors.surfaceRaised,
|
|
||||||
padding: spacing.md,
|
padding: spacing.md,
|
||||||
},
|
},
|
||||||
|
raisedPanel: {
|
||||||
|
backgroundColor: colors.surfaceRaised,
|
||||||
|
},
|
||||||
|
subtlePanel: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
dividerWrap: {
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderColor: colors.line,
|
||||||
|
paddingHorizontal: spacing.md,
|
||||||
|
paddingVertical: spacing.sm,
|
||||||
|
},
|
||||||
|
dividerContent: {
|
||||||
|
marginTop: spacing.sm,
|
||||||
|
gap: spacing.sm,
|
||||||
|
},
|
||||||
eyebrow: {
|
eyebrow: {
|
||||||
...typography.label,
|
...typography.label,
|
||||||
color: colors.warning,
|
color: colors.warning,
|
||||||
|
|||||||
@@ -2,15 +2,24 @@ import React from 'react';
|
|||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import { colors, radius, spacing, typography } from '../theme';
|
import { colors, radius, spacing, typography } from '../theme';
|
||||||
|
|
||||||
|
type StatusTone = 'amber' | 'green' | 'red';
|
||||||
|
type StatusSize = 'sm' | 'md';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
readonly tone?: 'amber' | 'green' | 'red';
|
readonly tone?: StatusTone;
|
||||||
|
readonly size?: StatusSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function StatusTag({ label, tone = 'amber' }: Props) {
|
export function StatusTag({ label, tone = 'amber', size = 'md' }: Props) {
|
||||||
return (
|
return (
|
||||||
<View style={[styles.base, styles[tone]]}>
|
<View
|
||||||
<Text style={[styles.text, styles[`${tone}Text`]]}>{label}</Text>
|
accessibilityLabel={label}
|
||||||
|
style={[styles.base, styles[tone], size === 'sm' ? styles.sm : styles.md]}
|
||||||
|
>
|
||||||
|
<Text style={[styles.text, styles[`${tone}Text`], size === 'sm' ? styles.smText : null]}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -19,9 +28,15 @@ const styles = StyleSheet.create({
|
|||||||
base: {
|
base: {
|
||||||
alignSelf: 'flex-start',
|
alignSelf: 'flex-start',
|
||||||
borderRadius: radius.xl,
|
borderRadius: radius.xl,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
sm: {
|
||||||
|
paddingHorizontal: spacing.xs,
|
||||||
|
paddingVertical: 2,
|
||||||
|
},
|
||||||
|
md: {
|
||||||
paddingHorizontal: spacing.sm,
|
paddingHorizontal: spacing.sm,
|
||||||
paddingVertical: spacing.xs,
|
paddingVertical: spacing.xs,
|
||||||
borderWidth: 1,
|
|
||||||
},
|
},
|
||||||
amber: {
|
amber: {
|
||||||
backgroundColor: colors.surfaceHot,
|
backgroundColor: colors.surfaceHot,
|
||||||
@@ -32,12 +47,16 @@ const styles = StyleSheet.create({
|
|||||||
borderColor: 'rgba(52,199,123,0.34)',
|
borderColor: 'rgba(52,199,123,0.34)',
|
||||||
},
|
},
|
||||||
red: {
|
red: {
|
||||||
backgroundColor: 'rgba(226,59,46,0.14)',
|
backgroundColor: 'rgba(226,59,46,0.12)',
|
||||||
borderColor: 'rgba(226,59,46,0.42)',
|
borderColor: 'rgba(226,59,46,0.42)',
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
...typography.label,
|
...typography.label,
|
||||||
},
|
},
|
||||||
|
smText: {
|
||||||
|
fontSize: 9,
|
||||||
|
lineHeight: 12,
|
||||||
|
},
|
||||||
amberText: {
|
amberText: {
|
||||||
color: colors.warning,
|
color: colors.warning,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user