Skip to content

Commit

Permalink
feat: implement imperative api for android picker
Browse files Browse the repository at this point in the history
  • Loading branch information
vonovak committed Mar 7, 2022
1 parent 2e4d3da commit c5f04f4
Show file tree
Hide file tree
Showing 21 changed files with 1,433 additions and 926 deletions.
2 changes: 1 addition & 1 deletion RNDateTimePicker.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Pod::Spec.new do |s|
s.license = package['license']
s.author = package['author']
s.homepage = package['homepage']
s.platform = :ios, "10.0"
s.platform = :ios, "11.0"
s.source = { :git => "https://github.com/react-native-community/datetimepicker", :tag => "v#{s.version}" }
s.source_files = "ios/*.{h,m}"
s.requires_arc = true
Expand Down
126 changes: 91 additions & 35 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import {
useColorScheme,
Switch,
} from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';
import DateTimePicker, {
DateTimePickerAndroid,
} from '@react-native-community/datetimepicker';
import SegmentedControl from '@react-native-segmented-control/segmented-control';
import {Colors} from 'react-native/Libraries/NewAppScreen';
import React, {useState} from 'react';
import React, {useCallback, useEffect, useReducer, useState} from 'react';
import {Picker} from 'react-native-windows';
import moment from 'moment';
import {
Expand All @@ -26,6 +28,9 @@ import {
} from '../src/constants';
import * as RNLocalize from 'react-native-localize';

const isIos = Platform.OS === 'ios';
const isAndroid = Platform.OS === 'android';

const ThemedText = (props) => {
const isDarkMode = useColorScheme() === 'dark';

Expand Down Expand Up @@ -75,6 +80,7 @@ export const App = () => {
const [disabled, setDisabled] = useState(false);
const [minimumDate, setMinimumDate] = useState();
const [maximumDate, setMaximumDate] = useState();
const [androidVariant, setAndroidVariant] = useState('imperative');

// Windows-specific
const [time, setTime] = useState(undefined);
Expand All @@ -92,13 +98,11 @@ export const App = () => {
};

const onChange = (event, selectedDate) => {
const currentDate = selectedDate || date;

setShow(Platform.OS === 'ios');
setShow(isIos);
if (event.type === 'neutralButtonPressed') {
setDate(new Date(0));
} else {
setDate(currentDate);
setDate(selectedDate);
}
};

Expand All @@ -114,6 +118,38 @@ export const App = () => {
backgroundColor: isDarkMode ? Colors.dark : Colors.lighter,
};

// eslint-disable-next-line react-hooks/exhaustive-deps
const pickerProps = {
mode,
value: date,
display,
onChange,
timeZoneOffsetInMinutes: tzOffsetInMinutes,
minuteInterval: interval,
minimumDate: minimumDate,
maximumDate: maximumDate,
is24Hour: true,
neutralButtonLabel,
onError: console.error,
};

const openPicker = () => {
setShow(true);
};

useEffect(() => {
if (Platform.OS === 'android' && androidVariant === 'imperative' && show) {
// in your app, you probably would open the picker with a button
// and not in an effect. We do this here because the components needs to re-render
// with updated props, and once that happens, then we want to show the android picker
// if we don't wait for re-render before showing the picker,
// it will not be shown with the latest props
DateTimePickerAndroid.open({
...pickerProps,
});
}
}, [show, neutralButtonLabel, pickerProps, androidVariant]);

const toggleMinMaxDateInUTC = () => {
setTzOffsetInMinutes(0);

Expand Down Expand Up @@ -213,21 +249,18 @@ export const App = () => {
testID="neutralButtonLabelTextInput"
/>
</View>
<View style={styles.header}>
<ThemedText style={{margin: 10, flex: 1}}>
[android] show and dismiss picker after 3 secs
</ThemedText>
</View>

<View style={styles.button}>
<Button
testID="showAndDismissPickerButton"
onPress={() => {
setShow(true);
const openedMode = mode;
openPicker();
setTimeout(() => {
setShow(false);
}, 6000);
DateTimePickerAndroid.dismiss(openedMode);
}, 5000);
}}
title="Show and dismiss picker!"
title="Show and dismiss picker after 5 secs (android)!"
/>
</View>
<View
Expand All @@ -237,9 +270,7 @@ export const App = () => {
]}>
<Button
testID="showPickerButton"
onPress={() => {
setShow(true);
}}
onPress={openPicker}
title="Show picker!"
/>
<Button
Expand All @@ -265,49 +296,74 @@ export const App = () => {
tzOffset: {tzOffsetInMinutes ?? 'auto'}
</ThemedText>
</View>
<View style={styles.button}>
<View
style={[
styles.button,
{flexDirection: 'row', justifyContent: 'space-around'},
]}>
<Button
testID="setTzOffsetToZero"
onPress={() => {
setTzOffsetInMinutes(0);
}}
title="setTzOffsetInMinutes to 0"
title="setTzOffset to 0"
/>
</View>
<View style={styles.button}>
<Button
testID="setTzOffset"
onPress={() => {
setTzOffsetInMinutes(120);
}}
title="setTzOffsetInMinutes to 120"
title="setTzOffsetIn to 120min"
/>
</View>
{isAndroid && (
<>
<ThemedText>
currently testing (only has effect on android):{' '}
{androidVariant} api
</ThemedText>
<View
style={[
styles.button,
{flexDirection: 'row', justifyContent: 'space-around'},
]}>
<Button
testID={'androidVariantComponent'}
title={'component api'}
onPress={() => {
console.log('setting android variant to component');
setAndroidVariant('component');
}}
/>

<Button
testID={'androidVariantImperative'}
title={'imperative api'}
onPress={() => {
console.log('setting android variant to imperative');
setAndroidVariant('imperative');
}}
/>
</View>
</>
)}

<View style={styles.button}>
<Button
testID="setMinMax"
onPress={() => {
toggleMinMaxDateInUTC();
setShow(true);
openPicker();
}}
title="toggleMinMaxDate"
/>
</View>
{show && (
{show && isAndroid && androidVariant === 'component' && (
<DateTimePicker
testID="dateTimePicker"
timeZoneOffsetInMinutes={tzOffsetInMinutes}
minuteInterval={interval}
maximumDate={maximumDate}
minimumDate={minimumDate}
value={date}
mode={mode}
is24Hour
display={display}
onChange={onChange}
{...pickerProps}
style={styles.iOsPicker}
textColor={color || undefined}
neutralButtonLabel={neutralButtonLabel}
disabled={disabled}
/>
)}
Expand Down
4 changes: 3 additions & 1 deletion example/e2e/detoxTest.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ const {
} = require('./utils/actions');
const {isIOS, wait, Platform} = require('./utils/utils');
const {device} = require('detox');
const {describe} = require('jest-circus');

describe('Example', () => {
describe('e2e tests', () => {
const getPickerDisplay = () => {
return isIOS() ? 'spinner' : 'default';
};
Expand All @@ -38,6 +39,7 @@ describe('Example', () => {
await expect(elementById('timeInfo')).toHaveText(
'TZ: Europe/Prague, TZOffset: -1 original: 11/13/2021 11:00',
);
console.log('test done');
});

it('should show date picker after tapping datePicker button', async () => {
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ target "example" do
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_flipper!()
# use_flipper!()
end

post_install do |installer|
Expand Down
Loading

0 comments on commit c5f04f4

Please sign in to comment.