diff --git a/Libraries/Alert/Alert.js b/Libraries/Alert/Alert.js index 091ff74ab654..da713ac46101 100644 --- a/Libraries/Alert/Alert.js +++ b/Libraries/Alert/Alert.js @@ -21,6 +21,7 @@ export type AlertButtonStyle = 'default' | 'cancel' | 'destructive'; export type Buttons = Array<{ text?: string, onPress?: ?Function, + isPreferred?: boolean, style?: AlertButtonStyle, ... }>; @@ -113,6 +114,7 @@ class Alert { const buttons = []; let cancelButtonKey; let destructiveButtonKey; + let preferredButtonKey; if (typeof callbackOrButtons === 'function') { callbacks = [callbackOrButtons]; } else if (Array.isArray(callbackOrButtons)) { @@ -122,6 +124,8 @@ class Alert { cancelButtonKey = String(index); } else if (btn.style === 'destructive') { destructiveButtonKey = String(index); + } else if (btn.isPreferred) { + preferredButtonKey = String(index); } if (btn.text || index < (callbackOrButtons || []).length - 1) { const btnDef = {}; @@ -140,6 +144,7 @@ class Alert { defaultValue, cancelButtonKey, destructiveButtonKey, + preferredButtonKey, keyboardType, }, (id, value) => { diff --git a/Libraries/Alert/NativeAlertManager.js b/Libraries/Alert/NativeAlertManager.js index ec01b4a916e9..11d7ed6b4d0a 100644 --- a/Libraries/Alert/NativeAlertManager.js +++ b/Libraries/Alert/NativeAlertManager.js @@ -19,6 +19,7 @@ export type Args = {| defaultValue?: string, cancelButtonKey?: string, destructiveButtonKey?: string, + preferredButtonKey?: string, keyboardType?: string, |}; diff --git a/React/CoreModules/RCTAlertManager.mm b/React/CoreModules/RCTAlertManager.mm index e0f450e480e7..5d2f2400cb1e 100644 --- a/React/CoreModules/RCTAlertManager.mm +++ b/React/CoreModules/RCTAlertManager.mm @@ -80,6 +80,7 @@ - (void)invalidate NSString *defaultValue = [RCTConvert NSString:args.defaultValue()]; NSString *cancelButtonKey = [RCTConvert NSString:args.cancelButtonKey()]; NSString *destructiveButtonKey = [RCTConvert NSString:args.destructiveButtonKey()]; + NSString *preferredButtonKey = [RCTConvert NSString:args.preferredButtonKey()]; UIKeyboardType keyboardType = [RCTConvert UIKeyboardType:args.keyboardType()]; if (!title && !message) { @@ -152,32 +153,37 @@ - (void)invalidate buttonStyle = UIAlertActionStyleDestructive; } __weak RCTAlertController *weakAlertController = alertController; + + UIAlertAction *action = [UIAlertAction actionWithTitle:buttonTitle + style:buttonStyle + handler:^(__unused UIAlertAction *action) { + switch (type) { + case RCTAlertViewStylePlainTextInput: + case RCTAlertViewStyleSecureTextInput: + callback(@[ buttonKey, [weakAlertController.textFields.firstObject text] ]); + [weakAlertController hide]; + break; + case RCTAlertViewStyleLoginAndPasswordInput: { + NSDictionary *loginCredentials = @{ + @"login" : [weakAlertController.textFields.firstObject text], + @"password" : [weakAlertController.textFields.lastObject text] + }; + callback(@[ buttonKey, loginCredentials ]); + [weakAlertController hide]; + break; + } + case RCTAlertViewStyleDefault: + callback(@[ buttonKey ]); + [weakAlertController hide]; + break; + } + }]; [alertController - addAction:[UIAlertAction - actionWithTitle:buttonTitle - style:buttonStyle - handler:^(__unused UIAlertAction *action) { - switch (type) { - case RCTAlertViewStylePlainTextInput: - case RCTAlertViewStyleSecureTextInput: - callback(@[ buttonKey, [weakAlertController.textFields.firstObject text] ]); - [weakAlertController hide]; - break; - case RCTAlertViewStyleLoginAndPasswordInput: { - NSDictionary *loginCredentials = @{ - @"login" : [weakAlertController.textFields.firstObject text], - @"password" : [weakAlertController.textFields.lastObject text] - }; - callback(@[ buttonKey, loginCredentials ]); - [weakAlertController hide]; - break; - } - case RCTAlertViewStyleDefault: - callback(@[ buttonKey ]); - [weakAlertController hide]; - break; - } - }]]; + addAction:action]; + + if ([buttonKey isEqualToString:preferredButtonKey]) { + [alertController setPreferredAction:action]; + } } if (!_alertControllers) { diff --git a/packages/rn-tester/js/examples/Alert/AlertExample.js b/packages/rn-tester/js/examples/Alert/AlertExample.js index d17f8ed93573..ebad451d9be8 100644 --- a/packages/rn-tester/js/examples/Alert/AlertExample.js +++ b/packages/rn-tester/js/examples/Alert/AlertExample.js @@ -190,6 +190,39 @@ const AlertWithStyles = () => { ); }; +const AlertWithStylesPreferred = () => { + const [message, setMessage] = useState(''); + + const alertMessage = + "The OK button is styled with 'preferred', so it is emphasized over the cancel button."; + + return ( + + + Alert.alert('Foo Title', alertMessage, [ + { + text: 'OK', + isPreferred: 'preferred', + onPress: () => setMessage('OK Pressed!'), + }, + { + text: 'Cancel', + style: 'cancel', + onPress: () => setMessage('Cancel Pressed!'), + }, + ]) + }> + + Tap to view alert + + + + + ); +}; + const styles = StyleSheet.create({ wrapper: { borderRadius: 5, @@ -262,4 +295,13 @@ exports.examples = [ return ; }, }, + { + title: 'Alert with styles + preferred', + platform: 'ios', + description: + "Alert buttons with 'isPreferred' will be emphasized, even over cancel buttons", + render(): React.Node { + return ; + }, + }, ];