Skip to content

Commit 09bb6ea

Browse files
authored
feat(ios): dismiss detection support (#671)
* feat(ios): add picker dismissal detection * fix test, improve docs * fix test * change evt listeners
1 parent ab6ad9b commit 09bb6ea

17 files changed

+87
-32
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ List of possible values for iOS (maps to [preferredDatePickerStyle](https://deve
301301
Date change handler.
302302

303303
This is called when the user changes the date or time in the UI. It receives the event and the date as parameters.
304+
It is also called when user dismisses the picker, which you can detect by checking the `event.type` property.
305+
The values can be: `'set' | 'dismissed' | 'neutralButtonPressed'`. (`neutralButtonPressed` is only available on Android).
304306

305307
```js
306308
setDate = (event, date) => {};

example/App.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
TextInput,
1111
useColorScheme,
1212
Switch,
13+
Alert,
1314
} from 'react-native';
1415
import DateTimePicker from '@react-native-community/datetimepicker';
1516
import SegmentedControl from './SegmentedControl';
@@ -99,6 +100,14 @@ export const App = () => {
99100
if (Platform.OS === 'android') {
100101
setShow(false);
101102
}
103+
if (event.type === 'dismissed') {
104+
Alert.alert('picker was dismissed', undefined, [
105+
{
106+
text: 'great',
107+
},
108+
]);
109+
return;
110+
}
102111

103112
if (event.type === 'neutralButtonPressed') {
104113
setDate(new Date(0));

example/e2e/detoxTest.spec.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ describe('e2e tests', () => {
5151
}
5252
});
5353

54-
it('nothing should happen if date does not change', async () => {
55-
if (isIOS()) {
56-
await userOpensPicker({mode: 'date', display: 'compact'});
54+
it('nothing should happen if picker is dismissed / cancelled', async () => {
55+
await userOpensPicker({mode: 'date', display: 'default'});
5756

57+
if (isIOS()) {
5858
await element(
5959
by.traits(['staticText']).withAncestor(by.label('Date Picker')),
6060
).tap();
@@ -65,7 +65,6 @@ describe('e2e tests', () => {
6565
await nextMonthArrow.tap();
6666
await userDismissesCompactDatePicker();
6767
} else {
68-
await userOpensPicker({mode: 'date', display: 'default'});
6968
const calendarHorizontalScrollView = element(
7069
by
7170
.type('android.widget.ScrollView')
@@ -76,6 +75,7 @@ describe('e2e tests', () => {
7675
await userTapsCancelButtonAndroid();
7776
}
7877

78+
await elementByText('great').tap();
7979
await expect(getDateText()).toHaveText('11/13/2021');
8080
});
8181

@@ -123,6 +123,7 @@ describe('e2e tests', () => {
123123
} else {
124124
await userChangesTimeValue({minutes: '22'});
125125
await userTapsCancelButtonAndroid();
126+
await elementByText('great').tap();
126127
}
127128
await expect(getTimeText()).toHaveText('11:00');
128129
});

example/ios/Podfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
require_relative '../../node_modules/react-native-test-app/test_app'
22

3-
# Flipper causes the build to fail on release when fabric is enabled
3+
# Flipper causes the build to fail on release when fabric is enabled
44
# https://github.com/facebook/react-native/issues/33764
5-
use_flipper!()
5+
# use_flipper!()
66

77
workspace 'date-time-picker-example.xcworkspace'
88

example/ios/Podfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ PODS:
685685
- React-Core
686686
- React-jsi
687687
- ReactTestApp-Resources (1.0.0-dev)
688-
- RNDateTimePicker (6.4.0):
688+
- RNDateTimePicker (6.4.2):
689689
- RCT-Folly
690690
- RCTRequired
691691
- RCTTypeSafety
@@ -923,12 +923,12 @@ SPEC CHECKSUMS:
923923
ReactCommon: de55f940495d7bf87b5d7bf55b5b15cdd50d7d7b
924924
ReactTestApp-DevSupport: 8a8cff38c37cd8145a12ac7d7d0503dd08f97d65
925925
ReactTestApp-Resources: ff5f151e465e890010b417ce65ca6c5de6aeccbb
926-
RNDateTimePicker: 597591d8a3ae159acd7d7b19706024932da816af
926+
RNDateTimePicker: 0913d8322a3b21bb3d2010aca96c5090248223b6
927927
RNLocalize: cbcb55d0e19c78086ea4eea20e03fe8000bbbced
928928
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
929929
Yoga: 82c9e8f652789f67d98bed5aef9d6653f71b04a9
930930
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
931931

932-
PODFILE CHECKSUM: 6bca2b1533bd782af99c755ccf9b322ffa84edc5
932+
PODFILE CHECKSUM: d34f3e71e434db4f0211ec41f8c89e1a077c236f
933933

934934
COCOAPODS: 1.11.3

ios/RNDateTimePicker.m

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
@interface RNDateTimePicker ()
1414

1515
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
16+
@property (nonatomic, copy) RCTBubblingEventBlock onPickerDismiss;
1617
@property (nonatomic, assign) NSInteger reactMinuteInterval;
1718

1819
@end
@@ -22,8 +23,14 @@ @implementation RNDateTimePicker
2223
- (instancetype)initWithFrame:(CGRect)frame
2324
{
2425
if ((self = [super initWithFrame:frame])) {
25-
[self addTarget:self action:@selector(didChange)
26+
#ifndef RCT_NEW_ARCH_ENABLED
27+
// somehow, with Fabric, the callbacks are executed here as well as in RNDateTimePickerComponentView
28+
// so do not register it with Fabric, to avoid potential problems
29+
[self addTarget:self action:@selector(didChange)
2630
forControlEvents:UIControlEventValueChanged];
31+
[self addTarget:self action:@selector(onDismiss:) forControlEvents:UIControlEventEditingDidEnd];
32+
#endif
33+
2734
_reactMinuteInterval = 1;
2835
}
2936
return self;
@@ -38,6 +45,13 @@ - (void)didChange
3845
}
3946
}
4047

48+
- (void)onDismiss:(RNDateTimePicker *)sender
49+
{
50+
if (_onPickerDismiss) {
51+
_onPickerDismiss(@{});
52+
}
53+
}
54+
4155
- (void)setDatePickerMode:(UIDatePickerMode)datePickerMode
4256
{
4357
[super setDatePickerMode:datePickerMode];

ios/RNDateTimePickerManager.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ + (NSString*) datepickerStyleToString: (UIDatePickerStyle) style API_AVAILABLE(
112112
RCT_EXPORT_VIEW_PROPERTY(minuteInterval, NSInteger)
113113
RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
114114
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
115+
RCT_EXPORT_VIEW_PROPERTY(onPickerDismiss, RCTBubblingEventBlock)
115116

116117
RCT_REMAP_VIEW_PROPERTY(mode, datePickerMode, UIDatePickerMode)
117118
RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone, NSTimeZone)

ios/fabric/RNDateTimePickerComponentView.mm

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ - (instancetype)initWithFrame:(CGRect)frame
4141
_dummyPicker = [RNDateTimePicker new];
4242

4343
[_picker addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged];
44+
[_picker addTarget:self action:@selector(onDismiss:) forControlEvents:UIControlEventEditingDidEnd];
4445

4546
// Default Picker mode
4647
_picker.datePickerMode = UIDatePickerModeDate;
@@ -52,6 +53,16 @@ - (instancetype)initWithFrame:(CGRect)frame
5253
return self;
5354
}
5455

56+
-(void)onDismiss:(RNDateTimePicker *)sender
57+
{
58+
if (!_eventEmitter) {
59+
return;
60+
}
61+
RNDateTimePickerEventEmitter::OnPickerDismiss event = {};
62+
std::dynamic_pointer_cast<const RNDateTimePickerEventEmitter>(_eventEmitter)
63+
->onPickerDismiss(event);
64+
}
65+
5566
-(void)onChange:(RNDateTimePicker *)sender
5667
{
5768
if (!_eventEmitter) {
@@ -103,7 +114,7 @@ -(void)updateTextColorForPicker:(UIDatePicker *)picker color:(UIColor *)color
103114
return;
104115
}
105116
}
106-
117+
107118
if (color) {
108119
[picker setValue:color forKey:@"textColor"];
109120
[picker setValue:@(NO) forKey:@"highlightsToday"];

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
"react": "18.1.0",
9292
"react-native": "^0.70.0",
9393
"react-native-localize": "^2.2.0",
94-
"react-native-test-app": "^1.6.16",
94+
"react-native-test-app": "^1.6.19",
9595
"react-native-windows": "^0.70.0-preview.2",
9696
"react-test-renderer": "18.1.0",
9797
"semantic-release": "^19.0.3"

src/constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ export const ANDROID_DISPLAY = Object.freeze({
1212
});
1313

1414
export const EVENT_TYPE_SET = 'set';
15+
export const EVENT_TYPE_DISMISSED = 'dismissed';
1516
export const ANDROID_EVT_TYPE = Object.freeze({
1617
set: EVENT_TYPE_SET,
18+
dismissed: EVENT_TYPE_DISMISSED,
1719
neutralButtonPressed: 'neutralButtonPressed',
18-
dismissed: 'dismissed',
1920
});
2021

2122
export const IOS_DISPLAY = Object.freeze({

0 commit comments

Comments
 (0)