Feature: add stats explainer take2

This commit is contained in:
Maria Zadnepryanets
2022-09-26 17:19:23 +00:00
parent db1388c3c4
commit 7b9293f7a2
8 changed files with 224 additions and 181 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ const Home = ({ navigate, setDate }) => {
<Button isCTA isSmall={false} onPress={navigateToCycleDayView}> <Button isCTA isSmall={false} onPress={navigateToCycleDayView}>
{t('labels.home.addDataForToday')} {t('labels.home.addDataForToday')}
</Button> </Button>
{phase && <Footnote>{statusText}</Footnote>} {phase && <Footnote colorLabel="greyLight">{statusText}</Footnote>}
</ScrollView> </ScrollView>
) )
} }
+12 -5
View File
@@ -7,13 +7,16 @@ import Asterisk from '../common/Asterisk'
import { Colors, Spacing } from '../../styles' import { Colors, Spacing } from '../../styles'
const Footnote = ({ children }) => { const Footnote = ({ children, colorLabel }) => {
if (!children) return false if (!children) return false
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Asterisk /> <Asterisk />
<AppText linkStyle={styles.link} style={styles.text}> <AppText
linkStyle={styles.link}
style={{ ...styles.text, color: Colors[colorLabel] }}
>
{children} {children}
</AppText> </AppText>
</View> </View>
@@ -22,6 +25,11 @@ const Footnote = ({ children }) => {
Footnote.propTypes = { Footnote.propTypes = {
children: PropTypes.node, children: PropTypes.node,
colorLabel: PropTypes.string,
}
Footnote.defaultProps = {
colorLabel: 'greyDark',
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@@ -29,14 +37,13 @@ const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
alignContent: 'flex-start', alignContent: 'flex-start',
marginBottom: Spacing.tiny, marginBottom: Spacing.tiny,
marginTop: Spacing.small, marginTop: Spacing.base,
}, },
link: { link: {
color: 'white', color: 'white',
}, },
text: { text: {
color: Colors.greyLight, paddingLeft: Spacing.small,
paddingLeft: Spacing.base,
}, },
}) })
+40 -5
View File
@@ -1,9 +1,18 @@
import React from 'react' import React from 'react'
import { Modal, StyleSheet, TouchableOpacity } from 'react-native' import {
Dimensions,
Modal,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
const AppModal = ({ children, onClose }) => { import CloseIcon from './close-icon'
return (
import { Sizes, Spacing } from '../../styles'
const AppModal = ({ children, onClose }) => (
<Modal <Modal
animationType="fade" animationType="fade"
onRequestClose={onClose} onRequestClose={onClose}
@@ -11,10 +20,14 @@ const AppModal = ({ children, onClose }) => {
visible={true} visible={true}
> >
<TouchableOpacity onPress={onClose} style={styles.blackBackground} /> <TouchableOpacity onPress={onClose} style={styles.blackBackground} />
<View style={styles.modalWindow}>
<View style={styles.headerContainer}>
<CloseIcon onClose={onClose} />
</View>
{children} {children}
</View>
</Modal> </Modal>
) )
}
AppModal.propTypes = { AppModal.propTypes = {
children: PropTypes.node, children: PropTypes.node,
@@ -27,6 +40,28 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
opacity: 0.5, opacity: 0.5,
}, },
headerContainer: {
flexDirection: 'row',
justifyContent: 'flex-end',
paddingTop: Spacing.base,
paddingHorizontal: Spacing.base,
position: 'absolute',
width: '100%',
zIndex: 3, // works on ios
elevation: 3, // works on android
},
modalWindow: {
alignSelf: 'center',
backgroundColor: 'white',
borderRadius: 10,
marginTop: Sizes.huge * 2,
paddingVertical: Spacing.large * 2,
position: 'absolute',
maxHeight: Dimensions.get('window').height * 0.7,
zIndex: 2, // works on ios
elevation: 2, // works on android
minWidth: '80%',
},
}) })
export default AppModal export default AppModal
+1 -27
View File
@@ -1,13 +1,12 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Dimensions, ScrollView, StyleSheet, View } from 'react-native' import { ScrollView, StyleSheet, View } from 'react-native'
import AppModal from '../common/app-modal' import AppModal from '../common/app-modal'
import AppSwitch from '../common/app-switch' import AppSwitch from '../common/app-switch'
import AppText from '../common/app-text' import AppText from '../common/app-text'
import AppTextInput from '../common/app-text-input' import AppTextInput from '../common/app-text-input'
import Button from '../common/button' import Button from '../common/button'
import CloseIcon from '../common/close-icon'
import Segment from '../common/segment' import Segment from '../common/segment'
import SelectBoxGroup from './select-box-group' import SelectBoxGroup from './select-box-group'
import SelectTabGroup from './select-tab-group' import SelectTabGroup from './select-tab-group'
@@ -119,10 +118,6 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
return ( return (
<AppModal onClose={onSave}> <AppModal onClose={onSave}>
<View style={styles.modalWindow}>
<View style={styles.headerContainer}>
<CloseIcon onClose={onSave} />
</View>
<ScrollView <ScrollView
contentContainerStyle={styles.modalContainer} contentContainerStyle={styles.modalContainer}
keyboardDismissMode="on-drag" keyboardDismissMode="on-drag"
@@ -211,7 +206,6 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
</Segment> </Segment>
)} )}
</ScrollView> </ScrollView>
</View>
</AppModal> </AppModal>
) )
} }
@@ -229,32 +223,12 @@ const styles = StyleSheet.create({
paddingHorizontal: Spacing.base, paddingHorizontal: Spacing.base,
paddingBottom: Spacing.base, paddingBottom: Spacing.base,
}, },
headerContainer: {
flexDirection: 'row',
justifyContent: 'flex-end',
paddingTop: Spacing.base,
paddingHorizontal: Spacing.base,
position: 'absolute',
width: '100%',
zIndex: 3, // works on ios
elevation: 3, // works on android
},
input: { input: {
height: Sizes.base * 5, height: Sizes.base * 5,
}, },
modalContainer: { modalContainer: {
paddingHorizontal: Spacing.base, paddingHorizontal: Spacing.base,
}, },
modalWindow: {
alignSelf: 'center',
backgroundColor: 'white',
borderRadius: 10,
marginTop: Sizes.huge * 2,
paddingTop: Spacing.large * 2,
position: 'absolute',
minHeight: '40%',
maxHeight: Dimensions.get('window').height * 0.7,
},
segmentBorder: { segmentBorder: {
borderBottomColor: Colors.greyLight, borderBottomColor: Colors.greyLight,
}, },
@@ -3,6 +3,7 @@ import { FlatList, StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import AppModal from '../common/app-modal'
import AppText from '../common/app-text' import AppText from '../common/app-text'
import cycleModule from '../../lib/cycle' import cycleModule from '../../lib/cycle'
@@ -35,13 +36,15 @@ Item.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
} }
const StatsTable = () => { const PeriodDetailsModal = ({ onClose }) => {
const renderItem = ({ item }) => <Item data={item} /> const renderItem = ({ item }) => <Item data={item} />
const data = cycleModule().getStats() const data = cycleModule().getStats()
if (!data || data.length === 0) return false if (!data || data.length === 0) return false
return ( return (
<AppModal onClose={onClose}>
<View>
<FlatList <FlatList
data={data} data={data}
renderItem={renderItem} renderItem={renderItem}
@@ -52,9 +55,15 @@ const StatsTable = () => {
stickyHeaderIndices={[0]} stickyHeaderIndices={[0]}
contentContainerStyle={styles.container} contentContainerStyle={styles.container}
/> />
</View>
</AppModal>
) )
} }
PeriodDetailsModal.propTypes = {
onClose: PropTypes.func,
}
const ItemDivider = () => <View style={styles.divider} /> const ItemDivider = () => <View style={styles.divider} />
const FlatListHeader = () => ( const FlatListHeader = () => (
@@ -89,7 +98,7 @@ const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
paddingVertical: Spacing.tiny, paddingVertical: Spacing.tiny,
backgroundColor: Colors.turquoiseLight, backgroundColor: 'white',
}, },
cell: { cell: {
flex: 2, flex: 2,
@@ -100,8 +109,10 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
}, },
container: { container: {
minHeight: '40%',
minWidth: '95%',
paddingHorizontal: Spacing.base, paddingHorizontal: Spacing.base,
}, },
}) })
export default StatsTable export default PeriodDetailsModal
+6 -2
View File
@@ -15,10 +15,12 @@ StatsOverview.propTypes = {
} }
const Row = ({ rowContent }) => { const Row = ({ rowContent }) => {
const isStandardDeviation = rowContent[1].includes('deviation')
return ( return (
<View style={styles.row}> <View style={styles.row}>
<Cell content={rowContent[0]} isLeft /> <Cell content={rowContent[0]} isLeft />
<Cell content={rowContent[1]} /> <Cell content={rowContent[1]} hasAsterisk={isStandardDeviation} />
</View> </View>
) )
} }
@@ -27,7 +29,7 @@ Row.propTypes = {
rowContent: PropTypes.array.isRequired, rowContent: PropTypes.array.isRequired,
} }
const Cell = ({ content, isLeft }) => { const Cell = ({ content, isLeft, hasAsterisk }) => {
const styleContainer = isLeft ? styles.cellLeft : styles.cellRight const styleContainer = isLeft ? styles.cellLeft : styles.cellRight
const styleText = isLeft ? styles.accentPurpleBig : styles.accentOrange const styleText = isLeft ? styles.accentPurpleBig : styles.accentOrange
const numberOfLines = isLeft ? 1 : 2 const numberOfLines = isLeft ? 1 : 2
@@ -41,6 +43,7 @@ const Cell = ({ content, isLeft }) => {
style={styleText} style={styleText}
> >
{content} {content}
{hasAsterisk && <AppText style={styles.accentOrange}>*</AppText>}
</AppText> </AppText>
</View> </View>
) )
@@ -49,6 +52,7 @@ const Cell = ({ content, isLeft }) => {
Cell.propTypes = { Cell.propTypes = {
content: PropTypes.node.isRequired, content: PropTypes.node.isRequired,
isLeft: PropTypes.bool, isLeft: PropTypes.bool,
hasAsterisk: PropTypes.bool,
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
+20 -8
View File
@@ -1,11 +1,13 @@
import React from 'react' import React, { useState } from 'react'
import { ImageBackground, View } from 'react-native' import { ImageBackground, SafeAreaView, ScrollView, View } from 'react-native'
import { ScaledSheet } from 'react-native-size-matters' import { ScaledSheet } from 'react-native-size-matters'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import AppText from '../common/app-text' import AppText from '../common/app-text'
import Button from '../common/button'
import Footnote from '../common/Footnote'
import StatsOverview from './StatsOverview' import StatsOverview from './StatsOverview'
import StatsTable from './StatsTable' import PeriodDetailsModal from './PeriodDetailsModal'
import cycleModule from '../../lib/cycle' import cycleModule from '../../lib/cycle'
import { getCycleLengthStats as getCycleInfo } from '../../lib/cycle-length' import { getCycleLengthStats as getCycleInfo } from '../../lib/cycle-length'
@@ -15,6 +17,8 @@ import { Containers, Sizes, Spacing, Typography } from '../../styles'
const image = require('../../assets/cycle-icon.png') const image = require('../../assets/cycle-icon.png')
const Stats = () => { const Stats = () => {
const [isStatsVisible, setIsStatsVisible] = useState(false)
const { t } = useTranslation(null, { keyPrefix: 'stats' }) const { t } = useTranslation(null, { keyPrefix: 'stats' })
const cycleLengths = cycleModule().getAllCycleLengths() const cycleLengths = cycleModule().getAllCycleLengths()
@@ -34,12 +38,13 @@ const Stats = () => {
] ]
return ( return (
<View style={styles.pageContainer}> <SafeAreaView style={styles.pageContainer}>
<View style={styles.overviewContainer}> <ScrollView contentContainerStyle={styles.overviewContainer}>
<AppText>{t('intro')}</AppText> <AppText>{t('intro')}</AppText>
{numberOfCycles === 0 ? ( {numberOfCycles === 0 ? (
<AppText>{t('noData')}</AppText> <AppText>{t('noData')}</AppText>
) : ( ) : (
<>
<View style={styles.container}> <View style={styles.container}>
<View style={styles.columnLeft}> <View style={styles.columnLeft}>
<ImageBackground <ImageBackground
@@ -66,10 +71,17 @@ const Stats = () => {
<StatsOverview data={statsData} /> <StatsOverview data={statsData} />
</View> </View>
</View> </View>
<Button isCTA onPress={() => setIsStatsVisible(true)}>
{t('showStats')}
</Button>
{isStatsVisible && (
<PeriodDetailsModal onClose={() => setIsStatsVisible(false)} />
)} )}
</View> <Footnote>{t('footnote')}</Footnote>
<StatsTable /> </>
</View> )}
</ScrollView>
</SafeAreaView>
) )
} }
@@ -7,7 +7,7 @@ exports[`Footnote component when children are present, renders them 1`] = `
"alignContent": "flex-start", "alignContent": "flex-start",
"flexDirection": "row", "flexDirection": "row",
"marginBottom": 8.571428571428571, "marginBottom": 8.571428571428571,
"marginTop": 21.428571428571427, "marginTop": 34.285714285714285,
} }
} }
> >
@@ -41,8 +41,8 @@ exports[`Footnote component when children are present, renders them 1`] = `
"fontSize": 34.285714285714285, "fontSize": 34.285714285714285,
}, },
Object { Object {
"color": "#CCC", "color": "#555",
"paddingLeft": 34.285714285714285, "paddingLeft": 21.428571428571427,
}, },
] ]
} }