Refactors the header

This commit is contained in:
Sofiya Tepikin
2019-09-14 12:39:30 +02:00
parent 20595c6c3a
commit 5d436630d3
15 changed files with 175 additions and 195 deletions
+10 -7
View File
@@ -109,16 +109,19 @@ class App extends Component {
const Page = pages[currentPage] const Page = pages[currentPage]
const title = headerTitlesLowerCase[currentPage] const title = headerTitlesLowerCase[currentPage]
const hasDefaultHeader =
!this.isSymptomView() &&
currentPage !== CYCLE_DAY_PAGE
const isSettingsSubView = this.isSettingsView()
return ( return (
<View style={{flex: 1}}> <View style={{ flex: 1 }}>
{this.isDefaultView() &&
<Header title={title} /> { hasDefaultHeader &&
}
{(this.isSettingsView()) &&
<Header <Header
handleBack={isSettingsSubView ? this.handleBackButtonPress : null}
title={title} title={title}
showBackButton={true}
goBack={this.handleBackButtonPress}
/> />
} }
+26 -14
View File
@@ -9,8 +9,9 @@ import Header from '../header'
import FillerBoxes from './FillerBoxes' import FillerBoxes from './FillerBoxes'
import SymptomBox from './SymptomBox' import SymptomBox from './SymptomBox'
import { getCycleDay } from '../../db'
import cycleModule from '../../lib/cycle' import cycleModule from '../../lib/cycle'
import formatDate from '../helpers/format-date'
import { getCycleDay } from '../../db'
import styles from '../../styles' import styles from '../../styles'
class CycleDayOverView extends Component { class CycleDayOverView extends Component {
@@ -21,28 +22,34 @@ class CycleDayOverView extends Component {
} }
} }
goToCycleDay = (target) => { updateCycleDay = (date) => {
const localDate = LocalDate.parse(this.props.date) this.props.setDate(date)
const targetDate = target === 'before' ?
localDate.minusDays(1).toString() :
localDate.plusDays(1).toString()
this.props.setDate(targetDate)
this.setState({ this.setState({
cycleDay: getCycleDay(targetDate) cycleDay: getCycleDay(date)
}) })
} }
goToPrevDay = () => {
const { date } = this.props
const prevDate = LocalDate.parse(date).minusDays(1).toString()
this.updateCycleDay(prevDate)
}
goToNextDay = () => {
const { date } = this.props
const nextDate = LocalDate.parse(date).plusDays(1).toString()
this.updateCycleDay(nextDate)
}
navigate(symptom) { navigate(symptom) {
const { cycleDay } = this.state const { cycleDay } = this.state
this.props.navigate(symptom, cycleDay) this.props.navigate(symptom, cycleDay)
} }
render() { render() {
const { getCycleDayNumber } = cycleModule()
const { cycleDay } = this.state const { cycleDay } = this.state
const { date } = this.props const { date } = this.props
const cycleDayNumber = getCycleDayNumber(date)
const dateInFuture = LocalDate.now().isBefore(LocalDate.parse(date)) const dateInFuture = LocalDate.now().isBefore(LocalDate.parse(date))
const symptomBoxesList = [ const symptomBoxesList = [
@@ -57,13 +64,18 @@ class CycleDayOverView extends Component {
'note', 'note',
] ]
const { getCycleDayNumber } = cycleModule()
const cycleDayNumber = getCycleDayNumber(date)
const headerSubtitle =
cycleDayNumber && `Cycle day ${cycleDayNumber}`.toLowerCase()
return ( return (
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Header <Header
isCycleDayOverView={true} handleBack={this.goToPrevDay}
cycleDayNumber={cycleDayNumber} handleNext={this.goToNextDay}
date={date} title={formatDate(date)}
goToCycleDay={this.goToCycleDay} subtitle={headerSubtitle}
/> />
<ScrollView> <ScrollView>
<View style={styles.symptomBoxesView}> <View style={styles.symptomBoxesView}>
+26 -5
View File
@@ -6,8 +6,9 @@ import { connect } from 'react-redux'
import { getDate } from '../../../slices/date' import { getDate } from '../../../slices/date'
import { saveSymptom } from '../../../db' import { saveSymptom } from '../../../db'
import formatDate from '../../helpers/format-date'
import Header from '../../header/symptom-view' import Header from '../../header'
import SymptomInfo from './symptom-info' import SymptomInfo from './symptom-info'
import { headerTitles } from '../../../i18n/en/labels' import { headerTitles } from '../../../i18n/en/labels'
@@ -80,16 +81,36 @@ class SymptomView extends Component {
) )
} }
showConfirmationAlert = () => {
const cancelButton = {
text: sharedDialogs.cancel,
style: 'cancel'
}
const confirmationButton = {
text: sharedDialogs.reallyDeleteData,
onPress: this.onDeleteConfirmation
}
return Alert.alert(
sharedDialogs.areYouSureTitle,
sharedDialogs.areYouSureToDelete,
[cancelButton, confirmationButton]
)
}
render() { render() {
const { symptom } = this.props const { symptom } = this.props
return ( return (
<View style={{flex: 1}}> <View style={{flex: 1}}>
<Header <Header
title={headerTitles[symptom].toLowerCase()} title={headerTitles[symptom].toLowerCase()}
date={this.date} subtitle={formatDate(this.date)}
goBack={this.props.handleBackButtonPress} handleBack={this.props.handleBackButtonPress}
shouldShowDelete={this.state.shouldShowDelete} handleDelete={
onDelete={this.showConfirmationAlert} this.state.shouldShowDelete && this.showConfirmationAlert
}
/> />
<View flex={1}> <View flex={1}>
<ScrollView style={styles.page}> <ScrollView style={styles.page}>
-24
View File
@@ -1,24 +0,0 @@
import React from 'react'
import {
Text,
View
} from 'react-native'
import styles from '../../styles'
import NavigationArrow from './navigation-arrow'
export default function BackButtonHeader(props) {
return (
<View style={[styles.header, styles.headerCycleDay, styles.headerSymptom]}>
<View
style={styles.accentCircle}
left={props.middle - styles.accentCircle.width / 2}
/>
<NavigationArrow testID='backButton' direction='left' {...props}/>
<View>
<Text style={styles.headerText} testID='pageTitle'>
{props.title}
</Text>
</View>
</View>
)
}
-29
View File
@@ -1,29 +0,0 @@
import React from 'react'
import {
View,
Text} from 'react-native'
import styles from '../../styles'
import NavigationArrow from './navigation-arrow'
import formatDate from '../helpers/format-date'
export default function CycleDayHeader({ date, cycleDayNumber, ...props }) {
return (<View style={[styles.header, styles.headerCycleDay]}>
<View
style={styles.accentCircle}
left={props.middle - styles.accentCircle.width / 2}
/>
<NavigationArrow testID='previousDateButton' direction='left' {...props}/>
<View>
<Text style={styles.dateHeader} testID='cycleDayTitleDate'>
{formatDate(date)}
</Text>
{ cycleDayNumber &&
<Text style={styles.cycleDayNumber}>
{`Cycle day ${cycleDayNumber}`.toLowerCase()}
</Text>
}
</View>
<NavigationArrow direction='right' {...props}/>
</View>
)
}
-16
View File
@@ -1,16 +0,0 @@
import React from 'react'
import {
View,
Text} from 'react-native'
import styles from '../../styles'
export default function DefaultHeader(props) {
return (
<View style={styles.header}>
<View style={styles.accentCircle} />
<Text testID='pageTitle' style={styles.headerText}>
{props.title}
</Text>
</View >
)
}
+26
View File
@@ -0,0 +1,26 @@
import React from 'react'
import { TouchableOpacity } from 'react-native'
import PropTypes from 'prop-types'
import Icon from 'react-native-vector-icons/AntDesign'
import styles, { iconStyles } from '../../styles'
export default function DeleteIcon({ handleDelete }) {
return (
<TouchableOpacity
onPress={handleDelete}
style={styles.headerDeleteButton}
>
<Icon
name="delete"
{...iconStyles.symptomHeaderIcons}
/>
</TouchableOpacity>
)
}
DeleteIcon.propTypes = {
handleDelete: PropTypes.func,
}
+31 -15
View File
@@ -1,19 +1,35 @@
import React from 'react' import React from 'react'
import { Dimensions } from 'react-native' import { View } from 'react-native'
import CycleDayHeader from './cycle-day' import PropTypes from 'prop-types'
import DefaultHeader from './default'
import BackButtonHeader from './back-button'
export default function Header(p) { import Title from './title'
const middle = Dimensions.get('window').width / 2 import NavigationArrow from './navigation-arrow'
const props = Object.assign({}, p, {middle}) import DeleteIcon from './delete-icon'
if (props.isCycleDayOverView) { import styles from '../../styles'
return (<CycleDayHeader {...props} />)
} else if (props.showBackButton) { export default function Header({
return (<BackButtonHeader {...props} />) handleBack,
} handleNext,
else { handleDelete,
return (<DefaultHeader {...props} />) title,
} subtitle,
}) {
return (
<View style={styles.header}>
<View style={styles.accentCircle} />
{ handleBack && <NavigationArrow handleBack={handleBack} /> }
<Title title={title} subtitle={subtitle} />
{ handleNext && <NavigationArrow handleNext={handleNext} /> }
{ handleDelete && <DeleteIcon handleDelete={handleDelete} /> }
</View >
)
}
Header.propTypes = {
handleBack: PropTypes.func,
handleNext: PropTypes.func,
title: PropTypes.string,
subtitle: PropTypes.string,
} }
+17 -23
View File
@@ -1,36 +1,30 @@
import React from 'react' import React from 'react'
import { TouchableOpacity } from 'react-native' import { TouchableOpacity } from 'react-native'
import styles, { iconStyles } from '../../styles' import PropTypes from 'prop-types'
import Icon from 'react-native-vector-icons/Entypo' import Icon from 'react-native-vector-icons/Entypo'
export default function NavigationArrow(props) { import styles, { iconStyles } from '../../styles'
const iconName = {
left: 'chevron-thin-left', export default function NavigationArrow({ handleBack, handleNext }) {
right: 'chevron-thin-right' const navigationDirection = handleBack ? 'Left' : 'Right'
}[props.direction]
const iconPosition = {
left: 'navigationArrowLeft',
right: 'navigationArrowRight'
}[props.direction]
let pressHandler
if (props.goBack) {
pressHandler = () => props.goBack()
} else {
pressHandler = () => {
const target = props.direction === 'left' ? 'before' : 'after'
props.goToCycleDay(target)
}
}
return ( return (
<TouchableOpacity <TouchableOpacity
style={[styles.navigationArrow, styles[iconPosition]]} style={[
onPress={pressHandler} styles.navigationArrow,
testID={props.testID} styles[`navigationArrow${navigationDirection}`]
]}
onPress={ handleBack || handleNext }
testID={ handleBack ? 'backButton' : 'nextButton'}
> >
<Icon <Icon
name={iconName} name={`chevron-thin-${navigationDirection.toLowerCase()}`}
{...iconStyles.navigationArrow} {...iconStyles.navigationArrow}
/> />
</TouchableOpacity> </TouchableOpacity>
) )
} }
NavigationArrow.propTypes = {
handleBack: PropTypes.func,
handleNext: PropTypes.func,
}
-54
View File
@@ -1,54 +0,0 @@
import React from 'react'
import {
View,
Text,
TouchableOpacity,
Dimensions
} from 'react-native'
import PropTypes from 'prop-types'
import styles, { iconStyles } from '../../styles'
import Icon from 'react-native-vector-icons/AntDesign'
import NavigationArrow from './navigation-arrow'
import formatDate from '../helpers/format-date'
SymptomViewHeader.propTypes = {
title: PropTypes.string,
date: PropTypes.string,
goBack: PropTypes.func,
deleteIconActive: PropTypes.bool,
onDelete: PropTypes.func,
}
export default function SymptomViewHeader(props) {
const { goBack, title, date, shouldShowDelete, onDelete } = props
const middle = Dimensions.get('window').width / 2
return (
<View style={[styles.header, styles.headerCycleDay, styles.headerSymptom]}>
<View
style={styles.accentCircle}
left={middle - styles.accentCircle.width / 2}
/>
<NavigationArrow direction='left' goBack={goBack} />
<View>
<Text style={styles.dateHeader} testID='symptomViewTitleName'>
{title}
</Text>
{ date &&
<Text style={styles.cycleDayNumber} testID='symptomViewTitleDate'>
{formatDate(date)}
</Text>
}
</View >
{ shouldShowDelete && <DeleteButton onDelete={onDelete} />}
</View>
)
}
const DeleteButton = ({ onDelete }) => {
return (
<TouchableOpacity onPress={onDelete} style={styles.headerDeleteButton}>
<Icon name="delete" {...iconStyles.symptomHeaderIcons} />
</TouchableOpacity>
)
}
+30
View File
@@ -0,0 +1,30 @@
import React from 'react'
import { View, Text} from 'react-native'
import PropTypes from 'prop-types'
import styles from '../../styles'
export default function Title({ title, subtitle }) {
if (subtitle !== undefined) {
return (
<View>
<Text style={styles.dateHeader} testID='headerTitle'>
{title}
</Text>
{ subtitle &&
<Text style={styles.cycleDayNumber} testID='headerSubtitle'>
{subtitle}
</Text>
}
</View>
)
}
return <Text testID='headerTitle' style={styles.headerText}>{title}</Text>
}
Title.propTypes = {
title: PropTypes.string,
subtitle: PropTypes.string,
}
+1 -1
View File
@@ -98,7 +98,7 @@ describe('Symptom Data Input', () => {
await navigateToSymptomView(symptom) await navigateToSymptomView(symptom)
let expectedSymptomSummary let expectedSymptomSummary
await expect(element(by.id('symptomViewTitleName').and(by.text(symptom)))) await expect(element(by.id('headerTitle').and(by.text(symptom))))
.toBeVisible() .toBeVisible()
switch (symptom) { switch (symptom) {
+3 -3
View File
@@ -10,9 +10,9 @@ describe('Date', () => {
await element(by.text('add data for today')).tap() await element(by.text('add data for today')).tap()
await expect( await expect(
element(by.id('cycleDayTitleDate').and(by.text('today'))) element(by.id('headerTitle').and(by.text('today')))
).toBeVisible() ).toBeVisible()
await element(by.id('previousDateButton')).tap() await element(by.id('backButton')).tap()
await element(by.id('drip-icon-bleeding')).tap() await element(by.id('drip-icon-bleeding')).tap()
const today = LocalDate.now() const today = LocalDate.now()
@@ -22,7 +22,7 @@ describe('Date', () => {
.toLowerCase() .toLowerCase()
await expect( await expect(
element(by.id('symptomViewTitleDate').and(by.text(yesterdayFormatted))) element(by.id('headerSubtitle').and(by.text(yesterdayFormatted)))
).toBeVisible() ).toBeVisible()
}) })
+3 -3
View File
@@ -1,6 +1,6 @@
const isOnPage = async (page, parentPage) => { const isOnPage = async (page, parentPage) => {
await expect( await expect(
element(by.id('pageTitle').and(by.text(page))) element(by.id('headerTitle').and(by.text(page)))
).toBeVisible() ).toBeVisible()
await expect( await expect(
element( element(
@@ -35,10 +35,10 @@ describe('Home Navigation', () => {
it('should navigate to today bleeding symptom', async () => { it('should navigate to today bleeding symptom', async () => {
await element(by.text('track your period')).tap() await element(by.text('track your period')).tap()
await expect( await expect(
element(by.id('symptomViewTitleName').and(by.text('bleeding'))) element(by.id('headerTitle').and(by.text('bleeding')))
).toBeVisible() ).toBeVisible()
await expect( await expect(
element(by.id('symptomViewTitleDate').and(by.text('today'))) element(by.id('headerSubtitle').and(by.text('today')))
).toBeVisible() ).toBeVisible()
}) })
+2 -1
View File
@@ -95,7 +95,8 @@ export default StyleSheet.create({
width: 40, width: 40,
height: 40, height: 40,
borderRadius: 100, borderRadius: 100,
position: 'absolute' position: 'absolute',
alignSelf: 'center',
}, },
errorMessage: { errorMessage: {
color: shadesOfRed[2], color: shadesOfRed[2],