tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit b67c6262ecbe7d4c9385a1bd335fd7dd1d3f887d
parent f3e34aecf3f480fe4c9c5bc7999bd8039e7ec86c
Author: Reem H <42309026+reemhamz@users.noreply.github.com>
Date:   Mon, 29 Dec 2025 06:15:48 +0000

Bug 2000494 - Update ConfirmDialog component to use `dialog` element. r=home-newtab-reviewers,ini

Differential Revision: https://phabricator.services.mozilla.com/D272828

Diffstat:
Mbrowser/extensions/newtab/content-src/components/ConfirmDialog/ConfirmDialog.jsx | 67+++++++++++++++++++++++++++++++++++++++++++------------------------
Mbrowser/extensions/newtab/content-src/components/ConfirmDialog/_ConfirmDialog.scss | 52+++++++++++-----------------------------------------
Mbrowser/extensions/newtab/css/activity-stream.css | 49+++++++++++--------------------------------------
Mbrowser/extensions/newtab/data/content/activity-stream.bundle.js | 44++++++++++++++++++++++++++++++--------------
Mbrowser/extensions/newtab/test/unit/content-src/components/ConfirmDialog.test.jsx | 34+++++++++++++++++++++-------------
5 files changed, 116 insertions(+), 130 deletions(-)

diff --git a/browser/extensions/newtab/content-src/components/ConfirmDialog/ConfirmDialog.jsx b/browser/extensions/newtab/content-src/components/ConfirmDialog/ConfirmDialog.jsx @@ -30,6 +30,23 @@ export class _ConfirmDialog extends React.PureComponent { super(props); this._handleCancelBtn = this._handleCancelBtn.bind(this); this._handleConfirmBtn = this._handleConfirmBtn.bind(this); + this.dialogRef = React.createRef(); + } + + componentDidUpdate() { + const dialogElement = this.dialogRef.current; + if (!dialogElement) { + return; + } + + // Open dialog when visible becomes true + if (this.props.visible && !dialogElement.open) { + dialogElement.showModal(); + } + // Close dialog when visible becomes false + else if (!this.props.visible && dialogElement.open) { + dialogElement.close(); + } } _handleCancelBtn() { @@ -63,17 +80,17 @@ export class _ConfirmDialog extends React.PureComponent { } render() { - if (!this.props.visible) { - return null; - } - return ( - <div className="confirmation-dialog"> - <div - className="modal-overlay" - onClick={this._handleCancelBtn} - role="presentation" - /> + <dialog + ref={this.dialogRef} + className="confirmation-dialog" + onClick={e => { + // Close modal when clicking on the backdrop pseudo element (the background of the modal) + if (e.target === this.dialogRef.current) { + this._handleCancelBtn(); + } + }} + > <div className="modal"> <section className="modal-message"> {this.props.data.icon && ( @@ -83,22 +100,24 @@ export class _ConfirmDialog extends React.PureComponent { )} {this._renderModalMessage()} </section> - <section className="actions"> - <button - onClick={this._handleCancelBtn} - data-l10n-id={this.props.data.cancel_button_string_id} - /> - <button - className="done" - onClick={this._handleConfirmBtn} - data-l10n-id={this.props.data.confirm_button_string_id} - data-l10n-args={JSON.stringify( - this.props.data.confirm_button_string_args - )} - /> + <section className="button-group"> + <moz-button-group> + <moz-button + onClick={this._handleCancelBtn} + data-l10n-id={this.props.data.cancel_button_string_id} + ></moz-button> + <moz-button + type="primary" + onClick={this._handleConfirmBtn} + data-l10n-id={this.props.data.confirm_button_string_id} + data-l10n-args={JSON.stringify( + this.props.data.confirm_button_string_args + )} + ></moz-button> + </moz-button-group> </section> </div> - </div> + </dialog> ); } } diff --git a/browser/extensions/newtab/content-src/components/ConfirmDialog/_ConfirmDialog.scss b/browser/extensions/newtab/content-src/components/ConfirmDialog/_ConfirmDialog.scss @@ -1,12 +1,13 @@ .confirmation-dialog { - .modal { - box-shadow: $shadow-secondary; - inset-inline-start: 0; - margin: auto; - position: fixed; - inset-inline-end: 0; - inset-block-start: 20%; - width: 400px; + border: 1px solid var(--border-color); + border-radius: var(--border-radius-small); + padding: 0; + width: 400px; + box-shadow: var(--box-shadow-popup); + background: var(--newtab-background-color-secondary); + + &::backdrop { + background: var(--newtab-overlay-color); } section { @@ -24,43 +25,12 @@ } } - .actions { + .button-group { border: 0; - display: flex; - flex-wrap: nowrap; - padding: 0 var(--space-large); - - button { - margin-inline-end: var(--space-large); - padding-inline: var(--space-large); - white-space: normal; - width: 50%; - - &.done { - margin-inline-end: 0; - margin-inline-start: 0; - } - } + padding: var(--space-large); } .icon { margin-inline-end: var(--space-large); } } - -.modal-overlay { - background: var(--newtab-overlay-color); - height: 100%; - inset-inline-start: 0; - position: fixed; - inset-block-start: 0; - width: 100%; - z-index: 11001; -} - -.modal { - background: var(--newtab-background-color-secondary); - border: 1px solid var(--border-color); - border-radius: var(--border-radius-small); - z-index: 11002; -} diff --git a/browser/extensions/newtab/css/activity-stream.css b/browser/extensions/newtab/css/activity-stream.css @@ -1824,14 +1824,16 @@ main section { pointer-events: none; } -.confirmation-dialog .modal { - box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2); - inset-inline-start: 0; - margin: auto; - position: fixed; - inset-inline-end: 0; - inset-block-start: 20%; +.confirmation-dialog { + border: 1px solid var(--border-color); + border-radius: var(--border-radius-small); + padding: 0; width: 400px; + box-shadow: var(--box-shadow-popup); + background: var(--newtab-background-color-secondary); +} +.confirmation-dialog::backdrop { + background: var(--newtab-overlay-color); } .confirmation-dialog section { margin: 0; @@ -1845,43 +1847,14 @@ main section { margin: 0; margin-block-end: var(--space-large); } -.confirmation-dialog .actions { +.confirmation-dialog .button-group { border: 0; - display: flex; - flex-wrap: nowrap; - padding: 0 var(--space-large); -} -.confirmation-dialog .actions button { - margin-inline-end: var(--space-large); - padding-inline: var(--space-large); - white-space: normal; - width: 50%; -} -.confirmation-dialog .actions button.done { - margin-inline-end: 0; - margin-inline-start: 0; + padding: var(--space-large); } .confirmation-dialog .icon { margin-inline-end: var(--space-large); } -.modal-overlay { - background: var(--newtab-overlay-color); - height: 100%; - inset-inline-start: 0; - position: fixed; - inset-block-start: 0; - width: 100%; - z-index: 11001; -} - -.modal { - background: var(--newtab-background-color-secondary); - border: 1px solid var(--border-color); - border-radius: var(--border-radius-small); - z-index: 11002; -} - .personalizeButtonWrapper { margin: 0; padding: 0; diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -1301,6 +1301,22 @@ class _ConfirmDialog extends (external_React_default()).PureComponent { super(props); this._handleCancelBtn = this._handleCancelBtn.bind(this); this._handleConfirmBtn = this._handleConfirmBtn.bind(this); + this.dialogRef = /*#__PURE__*/external_React_default().createRef(); + } + componentDidUpdate() { + const dialogElement = this.dialogRef.current; + if (!dialogElement) { + return; + } + + // Open dialog when visible becomes true + if (this.props.visible && !dialogElement.open) { + dialogElement.showModal(); + } + // Close dialog when visible becomes false + else if (!this.props.visible && dialogElement.open) { + dialogElement.close(); + } } _handleCancelBtn() { this.props.dispatch({ @@ -1325,32 +1341,32 @@ class _ConfirmDialog extends (external_React_default()).PureComponent { }))); } render() { - if (!this.props.visible) { - return null; - } - return /*#__PURE__*/external_React_default().createElement("div", { - className: "confirmation-dialog" + return /*#__PURE__*/external_React_default().createElement("dialog", { + ref: this.dialogRef, + className: "confirmation-dialog", + onClick: e => { + // Close modal when clicking on the backdrop pseudo element (the background of the modal) + if (e.target === this.dialogRef.current) { + this._handleCancelBtn(); + } + } }, /*#__PURE__*/external_React_default().createElement("div", { - className: "modal-overlay", - onClick: this._handleCancelBtn, - role: "presentation" - }), /*#__PURE__*/external_React_default().createElement("div", { className: "modal" }, /*#__PURE__*/external_React_default().createElement("section", { className: "modal-message" }, this.props.data.icon && /*#__PURE__*/external_React_default().createElement("span", { className: `icon icon-spacer icon-${this.props.data.icon}` }), this._renderModalMessage()), /*#__PURE__*/external_React_default().createElement("section", { - className: "actions" - }, /*#__PURE__*/external_React_default().createElement("button", { + className: "button-group" + }, /*#__PURE__*/external_React_default().createElement("moz-button-group", null, /*#__PURE__*/external_React_default().createElement("moz-button", { onClick: this._handleCancelBtn, "data-l10n-id": this.props.data.cancel_button_string_id - }), /*#__PURE__*/external_React_default().createElement("button", { - className: "done", + }), /*#__PURE__*/external_React_default().createElement("moz-button", { + type: "primary", onClick: this._handleConfirmBtn, "data-l10n-id": this.props.data.confirm_button_string_id, "data-l10n-args": JSON.stringify(this.props.data.confirm_button_string_args) - })))); + }))))); } } const ConfirmDialog = (0,external_ReactRedux_namespaceObject.connect)(state => state.Dialog)(_ConfirmDialog); diff --git a/browser/extensions/newtab/test/unit/content-src/components/ConfirmDialog.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/ConfirmDialog.test.jsx @@ -23,7 +23,7 @@ describe("<ConfirmDialog>", () => { ); }); it("should render an overlay", () => { - assert.ok(wrapper.find(".modal-overlay").exists()); + assert.ok(wrapper.find("dialog").exists()); }); it("should render a modal", () => { assert.ok(wrapper.find(".confirmation-dialog").exists()); @@ -34,7 +34,7 @@ describe("<ConfirmDialog>", () => { <ConfirmDialog dispatch={dispatch} {...ConfirmDialogProps} /> ); - assert.lengthOf(wrapper.find(".confirmation-dialog"), 0); + assert.lengthOf(wrapper.find("dialog"), 1); }); it("should display an icon if we provide one in props", () => { const iconName = "modal-icon"; @@ -74,7 +74,7 @@ describe("<ConfirmDialog>", () => { <ConfirmDialog dispatch={dispatch} {...ConfirmDialogProps} /> ); - let doneLabel = wrapper.find(".actions").childAt(1); + let doneLabel = wrapper.find("moz-button[type='primary']"); assert.ok(doneLabel.exists()); assert.equal( doneLabel.prop("data-l10n-id"), @@ -84,10 +84,12 @@ describe("<ConfirmDialog>", () => { }); describe("click events", () => { it("should emit AlsoToMain DIALOG_CANCEL when you click the overlay", () => { - let overlay = wrapper.find(".modal-overlay"); + let dialog = wrapper.find("dialog"); - assert.ok(overlay.exists()); - overlay.simulate("click"); + assert.ok(dialog.exists()); + dialog.simulate("click", { + target: wrapper.instance().dialogRef.current, + }); // Two events are emitted: UserEvent+AlsoToMain. assert.calledTwice(dispatch); @@ -95,10 +97,12 @@ describe("<ConfirmDialog>", () => { assert.calledWith(dispatch, { type: at.DIALOG_CANCEL }); }); it("should emit UserEvent DIALOG_CANCEL when you click the overlay", () => { - let overlay = wrapper.find(".modal-overlay"); + let dialog = wrapper.find("dialog"); - assert.ok(overlay); - overlay.simulate("click"); + assert.ok(dialog); + dialog.simulate("click", { + target: wrapper.instance().dialogRef.current, + }); // Two events are emitted: UserEvent+AlsoToMain. assert.calledTwice(dispatch); @@ -109,7 +113,9 @@ describe("<ConfirmDialog>", () => { ); }); it("should emit AlsoToMain DIALOG_CANCEL on cancel", () => { - let cancelButton = wrapper.find(".actions").childAt(0); + let cancelButton = wrapper + .find("moz-button") + .filterWhere(n => !n.prop("type")); assert.ok(cancelButton); cancelButton.simulate("click"); @@ -120,7 +126,9 @@ describe("<ConfirmDialog>", () => { assert.calledWith(dispatch, { type: at.DIALOG_CANCEL }); }); it("should emit UserEvent DIALOG_CANCEL on cancel", () => { - let cancelButton = wrapper.find(".actions").childAt(0); + let cancelButton = wrapper + .find("moz-button") + .filterWhere(n => !n.prop("type")); assert.ok(cancelButton); cancelButton.simulate("click"); @@ -144,7 +152,7 @@ describe("<ConfirmDialog>", () => { wrapper = shallow( <ConfirmDialog dispatch={dispatch} {...ConfirmDialogProps} /> ); - let doneButton = wrapper.find(".actions").childAt(1); + let doneButton = wrapper.find("moz-button[type='primary']"); assert.ok(doneButton); doneButton.simulate("click"); @@ -166,7 +174,7 @@ describe("<ConfirmDialog>", () => { wrapper = shallow( <ConfirmDialog dispatch={dispatch} {...ConfirmDialogProps} /> ); - let doneButton = wrapper.find(".actions").childAt(1); + let doneButton = wrapper.find("moz-button[type='primary']"); assert.ok(doneButton); doneButton.simulate("click");