diff --git a/Libraries/Alert/Alert.js b/Libraries/Alert/Alert.js index 57d07fb542393f..e87b61e06cf0ec 100644 --- a/Libraries/Alert/Alert.js +++ b/Libraries/Alert/Alert.js @@ -46,10 +46,11 @@ class Alert { buttons?: Buttons, options?: Options, ): void { - if (Platform.OS === 'ios') { + if ( + Platform.OS === 'ios' || + Platform.OS === 'macos' /* TODO(macOS ISS#2323203) */ + ) { Alert.prompt(title, message, buttons, 'default'); - } else if (Platform.OS === 'macos' /* TODO[(macOS ISS#2323203) */) { - AlertMacOS.prompt(title, message, buttons); // TODO](macOS ISS#2323203) } else if (Platform.OS === 'android') { if (!NativeDialogManagerAndroid) { return; @@ -172,7 +173,12 @@ class Alert { cb && cb(value); }, ); + // [TODO(macOS ISS#2323203) + } else if (Platform.OS === 'macos') { + const defaultInputs = [{default: defaultValue}]; + AlertMacOS.prompt(title, message, callbackOrButtons, type, defaultInputs); } + // ]TODO(macOS ISS#2323203) } } diff --git a/RNTester/js/examples/Alert/AlertMacOSExample.js b/RNTester/js/examples/Alert/AlertMacOSExample.js new file mode 100644 index 00000000000000..297f21083c1848 --- /dev/null +++ b/RNTester/js/examples/Alert/AlertMacOSExample.js @@ -0,0 +1,264 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var {StyleSheet, View, Text, TouchableHighlight, AlertMacOS} = ReactNative; + +var {SimpleAlertExampleBlock} = require('./AlertExample'); + +exports.framework = 'React'; +exports.title = 'AlertMacOS'; +exports.description = 'macOS alerts'; +exports.examples = [ + { + title: 'Alerts', + render() { + return ; + }, + }, + { + title: 'Prompt Options', + render(): React.Element { + return ; + }, + }, + { + title: 'Prompt Types', + render() { + return ( + + AlertMacOS.prompt('Plain Text Entry')}> + + plain-text + + + + AlertMacOS.prompt('Secure Text', null, null, 'secure-text') + }> + + secure-text + + + + AlertMacOS.prompt( + 'Login & Password', + null, + null, + 'login-password', + [ + {default: '', placeholder: 'login'}, + {default: '', placeholder: 'Password'}, + ], + ) + }> + + login-password + + + + ); + }, + }, + { + title: 'Prompt Presentation', + render() { + return ( + + + AlertMacOS.prompt( + 'Default sheet', + null, + null, + 'default', + [{default: '', placeholder: ''}], + false, + ) + }> + + Default sheet + + + + AlertMacOS.prompt( + 'Modal', + null, + null, + 'default', + [{default: '', placeholder: ''}], + true, + ) + }> + + Modal + + + + ); + }, + }, + { + title: 'Prompt Style', + render() { + return ( + + + AlertMacOS.prompt('Default warning style', null, null, 'default') + }> + + Default warning style + + + + AlertMacOS.prompt( + 'Critical', + null, + null, + 'default', + [{default: '', placeholder: ''}], + false, + true, + ) + }> + + Critical + + + + ); + }, + }, +]; + +class PromptOptions extends React.Component<$FlowFixMeProps, any> { + state: any; + customButtons: Array; + + constructor(props) { + super(props); + + // $FlowFixMe this seems to be a Flow bug, `saveResponse` is defined below + this.saveResponse = this.saveResponse.bind(this); + + this.customButtons = [ + { + text: 'Custom OK', + onPress: this.saveResponse, + }, + { + text: 'Custom Cancel', + style: 'cancel', + }, + ]; + + this.state = { + promptValue: undefined, + }; + } + + render() { + return ( + + + Prompt value:{' '} + {this.state.promptValue} + + + + AlertMacOS.prompt('Type a value', null, this.saveResponse) + }> + + prompt with title & callback + + + + + AlertMacOS.prompt('Type a value', null, this.customButtons) + }> + + prompt with title & custom buttons + + + + + AlertMacOS.prompt( + 'Type a value', + null, + this.saveResponse, + undefined, + [{default: 'Default value', placeholder: ''}], + ) + }> + + prompt with title, callback & default inputs + + + + + AlertMacOS.prompt( + 'Type a value', + null, + this.customButtons, + 'login-password', + [ + {default: 'admin@site.com', placeholder: 'login'}, + {default: '', placeholder: 'password'}, + ], + ) + }> + + + prompt with title, custom buttons, login/password & default inputs + + + + + ); + } + + saveResponse(promptValue) { + this.setState({promptValue: JSON.stringify(promptValue)}); + } +} + +var styles = StyleSheet.create({ + wrapper: { + borderRadius: 5, + marginBottom: 5, + }, + button: { + backgroundColor: '#eeeeee', + padding: 10, + }, +}); diff --git a/RNTester/js/utils/RNTesterList.ios.js b/RNTester/js/utils/RNTesterList.ios.js index 49aeedd9045271..19878aac0ce565 100644 --- a/RNTester/js/utils/RNTesterList.ios.js +++ b/RNTester/js/utils/RNTesterList.ios.js @@ -249,6 +249,12 @@ const APIExamples: Array = [ module: require('../examples/Alert/AlertIOSExample'), supportsTVOS: true, }, + // [TODO(macOS ISS#2323203) + { + key: 'AlertMacOSExample', + module: require('../examples/Alert/AlertMacOSExample'), + supportsTVOS: true, + }, // ]TODO(macOS ISS#2323203) { key: 'AnimatedExample', module: require('../examples/Animated/AnimatedExample'), diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m index 2cda02c5c2bacc..b7d02cdf383b2f 100644 --- a/React/Modules/RCTAlertManager.m +++ b/React/Modules/RCTAlertManager.m @@ -263,21 +263,21 @@ - (void)invalidate } void (^callbacksHandlers)(NSModalResponse response) = ^void(NSModalResponse response) { - + NSString *buttonKey = @"0"; if (response >= NSAlertFirstButtonReturn) { - NSString *buttonKey = buttons[response - NSAlertFirstButtonReturn].allKeys.firstObject; - NSArray *textfields = [accessoryView isKindOfClass:NSTextField.class] ? @[accessoryView] : accessoryView.subviews; - if (textfields.count == 2) { - NSDictionary *loginCredentials = @{ - @"login": textfields.firstObject.stringValue, - @"password": textfields.lastObject.stringValue - }; - callback(@[buttonKey, loginCredentials]); - } else if (textfields.count == 1) { - callback(@[buttonKey, textfields.firstObject.stringValue]); - } else { - callback(@[buttonKey]); - } + buttonKey = buttons[response - NSAlertFirstButtonReturn].allKeys.firstObject; + } + NSArray *textfields = [accessoryView isKindOfClass:NSTextField.class] ? @[accessoryView] : accessoryView.subviews; + if (textfields.count == 2) { + NSDictionary *loginCredentials = @{ + @"login": textfields.firstObject.stringValue, + @"password": textfields.lastObject.stringValue + }; + callback(@[buttonKey, loginCredentials]); + } else if (textfields.count == 1) { + callback(@[buttonKey, textfields.firstObject.stringValue]); + } else { + callback(@[buttonKey]); } }; diff --git a/bots/dangerfile.js b/bots/dangerfile.js index e9529acff3abb4..7ad1d779736c5e 100644 --- a/bots/dangerfile.js +++ b/bots/dangerfile.js @@ -51,7 +51,7 @@ if (!includesTestPlan) { } // Regex looks for given categories, types, a file/framework/component, and a message - broken into 4 capture groups -const changelogRegex = /\[\s?(ANDROID|GENERAL|IOS|JS|JAVASCRIPT|INTERNAL)\s?\]\s*?\[\s?(ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY)\s?\]\s*?\-?\s*?(.*)/gi; +const changelogRegex = /\[\s?(ANDROID|GENERAL|MACOS|IOS|JS|JAVASCRIPT|INTERNAL)\s?\]\s*?\[\s?(ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY)\s?\]\s*?\-?\s*?(.*)/gi; const includesChangelog = danger.github.pr.body && (danger.github.pr.body.toLowerCase().includes('## changelog') || @@ -60,7 +60,7 @@ const correctlyFormattedChangelog = changelogRegex.test(danger.github.pr.body); // Provides advice if a changelog is missing const changelogInstructions = - 'A changelog entry has the following format: `[CATEGORY] [TYPE] - Message`.\n\n
CATEGORY may be:\n\n- General\n- iOS\n- Android\n- JavaScript\n- Internal (for changes that do not need to be called out in the release notes)\n\nTYPE may be:\n\n- Added, for new features.\n- Changed, for changes in existing functionality.\n- Deprecated, for soon-to-be removed features.\n- Removed, for now removed features.\n- Fixed, for any bug fixes.\n- Security, in case of vulnerabilities.\n\nMESSAGE may answer "what and why" on a feature level. Use this to briefly tell React Native users about notable changes.
'; + 'A changelog entry has the following format: `[CATEGORY] [TYPE] - Message`.\n\n
CATEGORY may be:\n\n- General\n- macOS\n- iOS\n- Android\n- JavaScript\n- Internal (for changes that do not need to be called out in the release notes)\n\nTYPE may be:\n\n- Added, for new features.\n- Changed, for changes in existing functionality.\n- Deprecated, for soon-to-be removed features.\n- Removed, for now removed features.\n- Fixed, for any bug fixes.\n- Security, in case of vulnerabilities.\n\nMESSAGE may answer "what and why" on a feature level. Use this to briefly tell React Native users about notable changes.
'; if (!includesChangelog) { const title = ':clipboard: Missing Changelog'; const idea =