ConfirmDialog.js (5512B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 loader.lazyRequireGetter( 8 this, 9 "PropTypes", 10 "resource://devtools/client/shared/vendor/react-prop-types.js" 11 ); 12 loader.lazyRequireGetter( 13 this, 14 "HTMLTooltip", 15 "resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js", 16 true 17 ); 18 19 // React & Redux 20 const { 21 Component, 22 } = require("resource://devtools/client/shared/vendor/react.mjs"); 23 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 24 const { 25 createPortal, 26 } = require("resource://devtools/client/shared/vendor/react-dom.mjs"); 27 const { 28 connect, 29 } = require("resource://devtools/client/shared/vendor/react-redux.js"); 30 31 const { 32 getAutocompleteState, 33 } = require("resource://devtools/client/webconsole/selectors/autocomplete.js"); 34 const autocompleteActions = require("resource://devtools/client/webconsole/actions/autocomplete.js"); 35 const { 36 l10n, 37 } = require("resource://devtools/client/webconsole/utils/messages.js"); 38 39 const LEARN_MORE_URL = `https://firefox-source-docs.mozilla.org/devtools-user/web_console/invoke_getters_from_autocomplete/`; 40 41 class ConfirmDialog extends Component { 42 static get propTypes() { 43 return { 44 // Console object. 45 webConsoleUI: PropTypes.object.isRequired, 46 // Update autocomplete popup state. 47 autocompleteUpdate: PropTypes.func.isRequired, 48 autocompleteClear: PropTypes.func.isRequired, 49 // Data to be displayed in the confirm dialog. 50 getterPath: PropTypes.array, 51 serviceContainer: PropTypes.object.isRequired, 52 }; 53 } 54 55 constructor(props) { 56 super(props); 57 58 const { webConsoleUI } = props; 59 webConsoleUI.confirmDialog = this; 60 61 this.cancel = this.cancel.bind(this); 62 this.confirm = this.confirm.bind(this); 63 this.onLearnMoreClick = this.onLearnMoreClick.bind(this); 64 } 65 66 componentDidMount() { 67 const doc = this.props.webConsoleUI.document; 68 const { toolbox } = this.props.webConsoleUI.wrapper; 69 const tooltipDoc = toolbox ? toolbox.doc : doc; 70 // The popup will be attached to the toolbox document or HUD document in the case 71 // such as the browser console which doesn't have a toolbox. 72 this.tooltip = new HTMLTooltip(tooltipDoc, { 73 className: "invoke-confirm", 74 }); 75 } 76 77 componentDidUpdate() { 78 const { getterPath, serviceContainer } = this.props; 79 80 if (getterPath) { 81 this.tooltip.show(serviceContainer.getJsTermTooltipAnchor(), { y: 5 }); 82 } else { 83 this.tooltip.hide(); 84 this.props.webConsoleUI.jsterm.focus(); 85 } 86 } 87 88 componentDidThrow(e) { 89 console.error("Error in ConfirmDialog", e); 90 this.setState(state => ({ ...state, hasError: true })); 91 } 92 93 onLearnMoreClick(e) { 94 this.props.serviceContainer.openLink(LEARN_MORE_URL, e); 95 } 96 97 cancel() { 98 this.tooltip.hide(); 99 this.props.autocompleteClear(); 100 } 101 102 confirm() { 103 this.tooltip.hide(); 104 this.props.autocompleteUpdate(this.props.getterPath); 105 } 106 107 render() { 108 if ( 109 (this.state && this.state.hasError) || 110 !this.props || 111 !this.props.getterPath 112 ) { 113 return null; 114 } 115 116 const { getterPath } = this.props; 117 const getterName = getterPath.join("."); 118 119 // We deliberately use getStr, and not getFormatStr, because we want getterName to 120 // be wrapped in its own span. 121 const description = l10n.getStr("webconsole.confirmDialog.getter.label"); 122 const [descriptionPrefix, descriptionSuffix] = description.split("%S"); 123 124 const closeButtonTooltip = l10n.getFormatStr( 125 "webconsole.confirmDialog.getter.closeButton.tooltip", 126 ["Esc"] 127 ); 128 const invokeButtonLabel = l10n.getFormatStr( 129 "webconsole.confirmDialog.getter.invokeButtonLabelWithShortcut", 130 ["Tab"] 131 ); 132 133 const learnMoreElement = dom.a( 134 { 135 className: "learn-more-link", 136 key: "learn-more-link", 137 title: LEARN_MORE_URL.split("?")[0], 138 onClick: this.onLearnMoreClick, 139 }, 140 l10n.getStr("webConsoleMoreInfoLabel") 141 ); 142 143 return createPortal( 144 [ 145 dom.div( 146 { 147 className: "confirm-label", 148 key: "confirm-label", 149 }, 150 dom.p( 151 {}, 152 dom.span({}, descriptionPrefix), 153 dom.span({ className: "emphasized" }, getterName), 154 dom.span({}, descriptionSuffix) 155 ), 156 dom.button({ 157 className: "devtools-button close-confirm-dialog-button", 158 key: "close-button", 159 title: closeButtonTooltip, 160 onClick: this.cancel, 161 }) 162 ), 163 dom.button( 164 { 165 className: "confirm-button", 166 key: "confirm-button", 167 onClick: this.confirm, 168 }, 169 invokeButtonLabel 170 ), 171 learnMoreElement, 172 ], 173 this.tooltip.panel 174 ); 175 } 176 } 177 178 // Redux connect 179 function mapStateToProps(state) { 180 const autocompleteData = getAutocompleteState(state); 181 return { 182 getterPath: autocompleteData.getterPath, 183 }; 184 } 185 186 function mapDispatchToProps(dispatch) { 187 return { 188 autocompleteUpdate: getterPath => 189 dispatch(autocompleteActions.autocompleteUpdate(true, getterPath)), 190 autocompleteClear: () => dispatch(autocompleteActions.autocompleteClear()), 191 }; 192 } 193 194 module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDialog);