diff --git a/components/cycle-day/symptom-edit-view.js b/components/cycle-day/symptom-edit-view.js index fa03867..a5efaff 100644 --- a/components/cycle-day/symptom-edit-view.js +++ b/components/cycle-day/symptom-edit-view.js @@ -23,12 +23,11 @@ import info from '../../i18n/en/symptom-info' import { Colors, Containers, Sizes, Spacing } from '../../styles' class SymptomEditView extends Component { - static propTypes = { date: PropTypes.string.isRequired, onClose: PropTypes.func.isRequired, symptom: PropTypes.string.isRequired, - symptomData: PropTypes.object + symptomData: PropTypes.object, } constructor(props) { @@ -49,7 +48,7 @@ class SymptomEditView extends Component { shouldShowInfo: false, shouldShowNote, shouldBoxGroup, - shouldTabGroup + shouldTabGroup, } } @@ -97,8 +96,8 @@ class SymptomEditView extends Component { onSaveTemperature = (value, field) => { const data = this.getParsedData() - const dataToSave = field === 'value' - ? { [field]: Number(value) } : { [field]: value } + const dataToSave = + field === 'value' ? { [field]: Number(value) } : { [field]: value } Object.assign(data, { ...dataToSave }) this.setState({ data }) @@ -106,10 +105,10 @@ class SymptomEditView extends Component { onSelectBox = (key) => { const data = this.getParsedData() - if (key === "other") { + if (key === 'other') { Object.assign(data, { note: null, - [key]: !this.state.data[key] + [key]: !this.state.data[key], }) } else { Object.assign(data, { [key]: !this.state.data[key] }) @@ -118,7 +117,7 @@ class SymptomEditView extends Component { this.setState({ data }) } - onSelectBoxNote= (value) => { + onSelectBoxNote = (value) => { const data = this.getParsedData() Object.assign(data, { note: value !== '' ? value : null }) @@ -147,12 +146,13 @@ class SymptomEditView extends Component { render() { const { symptom } = this.props - const { data, + const { + data, shouldShowExclude, shouldShowInfo, shouldShowNote, shouldBoxGroup, - shouldTabGroup + shouldTabGroup, } = this.state const iconName = shouldShowInfo ? 'chevron-up' : 'chevron-down' const noteText = symptom === 'note' ? data.value : data.note @@ -166,73 +166,73 @@ class SymptomEditView extends Component { - {symptom === 'temperature' && + {symptom === 'temperature' && ( this.onSaveTemperature(value, field)} /> - } - {shouldTabGroup && symtomPage[symptom].selectTabGroups.map(group => { - return ( - - {group.title} - this.onSelectTab(group, value)} - /> - - ) - }) - } - {shouldBoxGroup && symtomPage[symptom].selectBoxGroups.map(group => { - const isOtherSelected = - data['other'] !== null - && data['other'] !== false - && Object.keys(group.options).includes('other') - - return ( - - {group.title} - this.onSelectBox(value)} - optionsState={data} - /> - {isOtherSelected && - this.onSelectBoxNote(value)} + )} + {shouldTabGroup && + symtomPage[symptom].selectTabGroups.map((group) => { + return ( + + {group.title} + this.onSelectTab(group, value)} /> - } - - ) - }) - } - {shouldShowExclude && - + + ) + })} + {shouldBoxGroup && + symtomPage[symptom].selectBoxGroups.map((group) => { + const isOtherSelected = + data['other'] !== null && + data['other'] !== false && + Object.keys(group.options).includes('other') + + return ( + + {group.title} + this.onSelectBox(value)} + optionsState={data} + /> + {isOtherSelected && ( + this.onSelectBoxNote(value)} + /> + )} + + ) + })} + {shouldShowExclude && ( + - } - {shouldShowNote && - + )} + {shouldShowNote && ( + {symtomPage[symptom].note} - } + )} - {shouldShowInfo && - + {shouldShowInfo && ( + {info[symptom].text} - } + )} ) @@ -257,7 +257,7 @@ class SymptomEditView extends Component { const styles = StyleSheet.create({ buttonsContainer: { - ...Containers.rowContainer + ...Containers.rowContainer, }, headerContainer: { flexDirection: 'row', @@ -275,23 +275,20 @@ const styles = StyleSheet.create({ marginVertical: Sizes.huge * 2, position: 'absolute', minHeight: '40%', - maxHeight: Dimensions.get('window').height * 0.7 + maxHeight: Dimensions.get('window').height * 0.7, }, segmentBorder: { - borderBottomColor: Colors.greyLight + borderBottomColor: Colors.greyLight, }, title: { - fontSize: Sizes.subtitle - } + fontSize: Sizes.subtitle, + }, }) const mapStateToProps = (state) => { - return({ + return { date: getDate(state), - }) + } } -export default connect( - mapStateToProps, - null, -)(SymptomEditView) +export default connect(mapStateToProps, null)(SymptomEditView) diff --git a/components/cycle-day/temperature.js b/components/cycle-day/temperature.js index 4023d38..af1e99e 100644 --- a/components/cycle-day/temperature.js +++ b/components/cycle-day/temperature.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { useEffect, useState } from 'react' import { Platform, StyleSheet, View } from 'react-native' import PropTypes from 'prop-types' import { Keyboard } from 'react-native' @@ -12,129 +12,92 @@ import Segment from '../common/segment' import { connect } from 'react-redux' import { getDate } from '../../slices/date' import { - isTemperatureOutOfRange, + getTemperatureOutOfRangeMessage, getPreviousTemperature, + formatTemperature, } from '../helpers/cycle-day' import { temperature as labels } from '../../i18n/en/cycle-day' import { Colors, Containers, Sizes, Spacing } from '../../styles' -const formatTemperature = (value) => - value === null ? value : Number.parseFloat(value).toFixed(2) +const Temperature = ({ data, date, save }) => { + const [isTimePickerVisible, setIsTimePickerVisible] = useState(false) + const [temperature, setTemperature] = useState( + formatTemperature(data.value) || getPreviousTemperature(date) + ) -class Temperature extends Component { - static propTypes = { - data: PropTypes.object, - date: PropTypes.string.isRequired, - save: PropTypes.func, - } - - constructor(props) { - super(props) - - const { data, date } = this.props - const { value } = data - const { shouldShowSuggestion, suggestedTemperature } = - getPreviousTemperature(date) - - this.state = { - isTimePickerVisible: false, - shouldShowSuggestion, - suggestedTemperature: formatTemperature(suggestedTemperature), - value: formatTemperature(value), + // update state in parent component once to ensure + // that pre-filled values are saved on button click + useEffect(() => { + if (temperature) { + save(temperature, 'value') } + }, []) + + function onChangeTemperature(value) { + const formattedValue = value.replace(',', '.').trim() + if (!Number(formattedValue) && value !== '') return false + setTemperature(formattedValue) } - onCancelTimePicker = () => { - this.setState({ isTimePickerVisible: false }) - } - - onChangeTemperature = (value) => { - if (!Number(value)) return false - - this.setState({ - value: value.trim(), - shouldShowSuggestion: false, - }) - } - - onShowTimePicker = () => { + function onShowTimePicker() { Keyboard.dismiss() - this.setState({ isTimePickerVisible: true }) + setIsTimePickerVisible(true) } - setTemperature = () => { - const { value } = this.state - this.props.save(value, 'value') - } - - setTime = (jsDate) => { + function setTime(jsDate) { const time = moment(jsDate).format('HH:mm') - const isTimePickerVisible = false - this.props.save(time, 'time') - this.setState({ isTimePickerVisible }) + save(time, 'time') + setIsTimePickerVisible(false) } - render() { - const { shouldShowSuggestion, suggestedTemperature, value } = this.state - const { time } = this.props.data + const { time } = data - const inputStyle = - shouldShowSuggestion && value === null - ? { color: Colors.grey } - : { color: Colors.greyDark } - const outOfRangeWarning = isTemperatureOutOfRange(value) - let temperatureToShow = null + const inputStyle = { color: Colors.greyDark } + const outOfRangeWarning = getTemperatureOutOfRangeMessage(temperature) - if (value) { - temperatureToShow = value - } else if (shouldShowSuggestion) { - temperatureToShow = suggestedTemperature - } - - return ( - - - {labels.temperature.explainer} - - - °C - - {outOfRangeWarning !== null && ( - - {outOfRangeWarning} - - )} - - - {labels.time} + return ( + + + {labels.temperature.explainer} + save(temperature, 'value')} + keyboardType="numeric" + maxLength={5} + style={inputStyle} + testID="temperatureInput" + underlineColorAndroid="transparent" /> - - - - ) - } + °C + + {!!outOfRangeWarning && ( + + {outOfRangeWarning} + + )} + + + {labels.time} + + setIsTimePickerVisible(false)} + display={Platform.OS === 'ios' ? 'spinner' : 'default'} + /> + + + ) } const styles = StyleSheet.create({ @@ -153,6 +116,12 @@ const styles = StyleSheet.create({ }, }) +Temperature.propTypes = { + data: PropTypes.object.isRequired, + date: PropTypes.string.isRequired, + save: PropTypes.func.isRequired, +} + const mapStateToProps = (state) => { return { date: getDate(state), diff --git a/components/helpers/cycle-day.js b/components/helpers/cycle-day.js index 37037f0..9e1248f 100644 --- a/components/helpers/cycle-day.js +++ b/components/helpers/cycle-day.js @@ -35,22 +35,17 @@ export const getPreviousTemperature = (date) => { return formatTemperature(previousTemperature) } -export const isTemperatureOutOfRange = (temperature) => { +export const getTemperatureOutOfRangeMessage = (temperature) => { if (!temperature) return null const value = Number(temperature) - const range = { min: TEMP_MIN, max: TEMP_MAX } const scale = scaleObservable.value - let warningMsg = null - - if (value < range.min || value > range.max) { - warningMsg = labels.temperature.outOfAbsoluteRangeWarning - } else if (value < scale.min || value > scale.max) { - warningMsg = labels.temperature.outOfRangeWarning - } - - return warningMsg + return value < TEMP_MIN || value > TEMP_MAX + ? labels.temperature.outOfAbsoluteRangeWarning + : value < scale.min || value > scale.max + ? labels.temperature.outOfRangeWarning + : '' } export const blank = {