dialog_widget: Migrate modal to Micromodal.

Also removed the `danger_submit_button` config option
from the dialog_widget since it isn't needed in the new modals.
This commit is contained in:
Ganesh Pawar
2021-07-04 12:17:08 +05:30
committed by Tim Abbott
parent 6a07a90499
commit 1e8bfa710e
17 changed files with 363 additions and 119 deletions

View File

@@ -496,6 +496,16 @@ class CommonUtils {
); );
} }
async wait_for_micromodal_to_open(page: Page): Promise<void> {
// We manually add the `modal--open` class to the modal after the modal animation completes.
await page.waitForFunction(() => document.querySelector(".modal--open") !== null);
}
async wait_for_micromodal_to_close(page: Page): Promise<void> {
// This function will ensure that the mouse events are enabled for the background for further tests.
await page.waitForFunction(() => document.querySelector(".modal--open") === null);
}
async run_test_async(test_function: (page: Page) => Promise<void>): Promise<void> { async run_test_async(test_function: (page: Page) => Promise<void>): Promise<void> {
// Pass a page instance to test so we can take // Pass a page instance to test so we can take
// a screenshot of it when the test fails. // a screenshot of it when the test fails.

View File

@@ -18,13 +18,11 @@ async function delete_message_test(page: Page): Promise<void> {
const messages_quantitiy = await page.evaluate(() => $("#zhome .message_row").length); const messages_quantitiy = await page.evaluate(() => $("#zhome .message_row").length);
const last_message_id = await click_delete_and_return_last_msg_id(page); const last_message_id = await click_delete_and_return_last_msg_id(page);
await page.waitForSelector("#dialog_widget_modal", {visible: true}); await common.wait_for_micromodal_to_open(page);
await page.click(".dialog_submit_button"); await page.evaluate(() => {
(document.querySelector(".dialog_submit_button") as HTMLButtonElement)?.click();
const confirm_span = ".dialog_submit_button span"; });
await page.waitForSelector(confirm_span, {hidden: true}); await common.wait_for_micromodal_to_close(page);
await page.waitForSelector("#dialog_widget_modal", {hidden: true});
await page.waitForFunction( await page.waitForFunction(
(expected_length: number) => $("#zhome .message_row").length === expected_length, (expected_length: number) => $("#zhome .message_row").length === expected_length,

View File

@@ -56,7 +56,7 @@ async function test_add_invalid_linkifier_pattern(page: Page): Promise<void> {
async function test_edit_linkifier(page: Page): Promise<void> { async function test_edit_linkifier(page: Page): Promise<void> {
await page.click(".linkifier_row .edit"); await page.click(".linkifier_row .edit");
await page.waitForFunction(() => document.activeElement?.id === "dialog_widget_modal"); await common.wait_for_micromodal_to_open(page);
await common.fill_form(page, "form.linkifier-edit-form", { await common.fill_form(page, "form.linkifier-edit-form", {
pattern: "(?P<num>[0-9a-f]{40})", pattern: "(?P<num>[0-9a-f]{40})",
url_format_string: "https://trac.example.com/commit/%(num)s", url_format_string: "https://trac.example.com/commit/%(num)s",
@@ -64,7 +64,7 @@ async function test_edit_linkifier(page: Page): Promise<void> {
await page.click(".dialog_submit_button"); await page.click(".dialog_submit_button");
await page.waitForSelector("#dialog_widget_modal", {hidden: true}); await page.waitForSelector("#dialog_widget_modal", {hidden: true});
await common.wait_for_modal_to_close(page); await common.wait_for_micromodal_to_close(page);
await page.waitForSelector(".linkifier_row", {visible: true}); await page.waitForSelector(".linkifier_row", {visible: true});
await page.waitForFunction( await page.waitForFunction(
@@ -81,7 +81,7 @@ async function test_edit_linkifier(page: Page): Promise<void> {
async function test_edit_invalid_linkifier(page: Page): Promise<void> { async function test_edit_invalid_linkifier(page: Page): Promise<void> {
await page.click(".linkifier_row .edit"); await page.click(".linkifier_row .edit");
await page.waitForFunction(() => document.activeElement?.id === "dialog_widget_modal"); await common.wait_for_micromodal_to_open(page);
await common.fill_form(page, "form.linkifier-edit-form", { await common.fill_form(page, "form.linkifier-edit-form", {
pattern: "#(?P<id>d????)", pattern: "#(?P<id>d????)",
url_format_string: "????", url_format_string: "????",
@@ -107,7 +107,7 @@ async function test_edit_invalid_linkifier(page: Page): Promise<void> {
); );
assert.strictEqual(edit_linkifier_format_status, "Failed: Enter a valid URL."); assert.strictEqual(edit_linkifier_format_status, "Failed: Enter a valid URL.");
await page.click(".close-modal-btn"); await page.click(".dialog_cancel_button");
await page.waitForSelector("#dialog_widget_modal", {hidden: true}); await page.waitForSelector("#dialog_widget_modal", {hidden: true});
await page.waitForSelector(".linkifier_row", {visible: true}); await page.waitForSelector(".linkifier_row", {visible: true});

View File

@@ -23,7 +23,7 @@ async function test_deactivate_user(page: Page): Promise<void> {
await page.waitForSelector(cordelia_user_row, {visible: true}); await page.waitForSelector(cordelia_user_row, {visible: true});
await page.waitForSelector(cordelia_user_row + " .fa-user-times"); await page.waitForSelector(cordelia_user_row + " .fa-user-times");
await page.click(cordelia_user_row + " .deactivate"); await page.click(cordelia_user_row + " .deactivate");
await page.waitForSelector("#dialog_widget_modal", {visible: true}); await common.wait_for_micromodal_to_open(page);
assert.strictEqual( assert.strictEqual(
await common.get_text_from_selector(page, ".dialog_heading"), await common.get_text_from_selector(page, ".dialog_heading"),
@@ -36,7 +36,7 @@ async function test_deactivate_user(page: Page): Promise<void> {
"Deactivate button has incorrect text.", "Deactivate button has incorrect text.",
); );
await page.click("#dialog_widget_modal .dialog_submit_button"); await page.click("#dialog_widget_modal .dialog_submit_button");
await page.waitForSelector("#user-field-status", {hidden: true}); await common.wait_for_micromodal_to_close(page);
} }
async function test_reactivate_user(page: Page): Promise<void> { async function test_reactivate_user(page: Page): Promise<void> {

View File

@@ -47,6 +47,7 @@
"jquery-validation": "^1.19.0", "jquery-validation": "^1.19.0",
"katex": "^0.13.2", "katex": "^0.13.2",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"micromodal": "^0.4.6",
"mini-css-extract-plugin": "^2.2.2", "mini-css-extract-plugin": "^2.2.2",
"plotly.js": "^2.0.0", "plotly.js": "^2.0.0",
"postcss": "^8.0.3", "postcss": "^8.0.3",

View File

@@ -872,6 +872,7 @@ export function initialize() {
!$(e.target).closest(".overlay").length && !$(e.target).closest(".overlay").length &&
!$(e.target).closest(".popover").length && !$(e.target).closest(".popover").length &&
!$(e.target).closest(".modal").length && !$(e.target).closest(".modal").length &&
!$(e.target).closest(".micromodal").length &&
!$(e.target).closest("[data-tippy-root]").length && !$(e.target).closest("[data-tippy-root]").length &&
!$(e.target).closest(".modal-backdrop").length && !$(e.target).closest(".modal-backdrop").length &&
$(e.target).closest("body").length $(e.target).closest("body").length

View File

@@ -5,7 +5,6 @@ export function launch(conf) {
dialog_widget.launch({ dialog_widget.launch({
...conf, ...conf,
close_on_submit: true, close_on_submit: true,
danger_submit_button: true,
focus_submit_on_open: true, focus_submit_on_open: true,
html_submit_button: $t_html({defaultMessage: "Confirm"}), html_submit_button: $t_html({defaultMessage: "Confirm"}),
// Used to control button colors in the template. // Used to control button colors in the template.

View File

@@ -1,62 +1,66 @@
import $ from "jquery"; import $ from "jquery";
import Micromodal from "micromodal";
import render_dialog_widget from "../templates/dialog_widget.hbs"; import render_dialog_widget from "../templates/dialog_widget.hbs";
import render_dialog_heading from "../templates/dialog_widget_heading.hbs";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
import {$t_html} from "./i18n"; import {$t_html} from "./i18n";
import * as loading from "./loading"; import * as loading from "./loading";
import * as overlays from "./overlays"; import * as overlays from "./overlays";
import * as settings_data from "./settings_data";
/* /*
Look for dialog_widget in settings_users * Look for confirm_dialog in settings_user_groups
to see an example of how to use this widget. It's * to see an example of how to use this widget. It's
pretty simple to use! * pretty simple to use!
*
Some things to note: * Some things to note:
* 1) We create DOM on the fly, and we remove
1) We create DOM on the fly, and we remove * the DOM once it's closed.
the DOM once it's closed. *
* 2) We attach the DOM for the modal to the body element
2) We attach the DOM for the modal to the body element * to avoid interference from other elements.
to avoid style interference from other elements. *
* 3) For settings, we have a click handler in settings.js
3) The cancel button is driven by bootstrap.js. * that will close the dialog via overlays.close_active_modal.
*
4) For settings, we have a click handler in settings.js * 4) We assume that since this is a modal, you will
that will close the dialog via overlays.close_active_modal. * only ever have one confirm dialog active at any
* time.
5) We assume that since this is a modal, you will *
only ever have one dialog active at any * 5) If a modal wants a loading spinner, it should pass loading_spinner: true.
time. * This will show a loading spinner when the yes button is clicked.
* The caller is responsible for calling hide_confirm_dialog_spinner()
6) If a modal wants a loading spinner, it should pass loading_spinner: true. * to hide the spinner in both success and error handlers.
This will show a loading spinner when the yes button is clicked. *
The caller is responsible for calling hide_dialog_spinner() * 6) If loading_spinner is used, don't hide it on `success`. This modal has a fade out
to hide the spinner in both success and error handlers. * animation. This causes the `Confirm` button to be shown for a split second if the
* spinner is hidden.
7) If a caller needs to run code after the modal body is added * Just close the modal. This will remove the whole modal from the DOM without
to DOM, it can do so by passing a post_render hook. * needing to remove the spinner.
*
* 7) If a caller needs to run code after the modal body is added
* to DOM, it can do so by passing a post_render hook.
*/ */
export function hide_dialog_spinner() { export function hide_dialog_spinner() {
$(".dialog_submit_button .loader").hide();
$(".dialog_submit_button span").show(); $(".dialog_submit_button span").show();
$(".dialog_submit_button").prop("disabled", false); $("#dialog_widget_modal .modal__btn").prop("disabled", false);
$("#dialog_widget_modal .close-modal-btn").prop("disabled", false);
const spinner = $("#dialog_widget_modal .modal__spinner");
loading.destroy_indicator(spinner);
} }
export function show_dialog_spinner() { export function show_dialog_spinner() {
const using_dark_theme = settings_data.using_dark_theme();
loading.show_button_spinner($(".dialog_submit_button .loader"), using_dark_theme);
$(".dialog_submit_button span").hide(); $(".dialog_submit_button span").hide();
$(".dialog_submit_button").prop("disabled", true); // Disable both the buttons.
$("#dialog_widget_modal .close-modal-btn").prop("disabled", true); $("#dialog_widget_modal .modal__btn").prop("disabled", true);
const spinner = $("#dialog_widget_modal .modal__spinner");
loading.make_indicator(spinner);
} }
export function close_modal() { export function close_modal() {
overlays.close_modal("#dialog_widget_modal"); Micromodal.close("dialog_widget_modal");
} }
export function launch(conf) { export function launch(conf) {
@@ -73,7 +77,6 @@ export function launch(conf) {
// * html_submit_button: Submit button text. // * html_submit_button: Submit button text.
// * close_on_submit: Whether to close modal on clicking submit. // * close_on_submit: Whether to close modal on clicking submit.
// * focus_submit_on_open: Whether to focus submit button on open. // * focus_submit_on_open: Whether to focus submit button on open.
// * danger_submit_button: Whether to use danger button styling for submit button.
// * help_link: A help link in the heading area. // * help_link: A help link in the heading area.
for (const f of mandatory_fields) { for (const f of mandatory_fields) {
@@ -89,15 +92,11 @@ export function launch(conf) {
} }
const html_submit_button = conf.html_submit_button || $t_html({defaultMessage: "Save changes"}); const html_submit_button = conf.html_submit_button || $t_html({defaultMessage: "Save changes"});
const html_dialog_heading = render_dialog_heading({ const html = render_dialog_widget({
heading_text: conf.html_heading, heading_text: conf.html_heading,
link: conf.help_link, link: conf.help_link,
});
const html = render_dialog_widget({
html_submit_button, html_submit_button,
html_dialog_heading,
html_body: conf.html_body, html_body: conf.html_body,
danger_submit_button: conf.danger_submit_button,
}); });
const dialog = $(html); const dialog = $(html);
$("body").append(dialog); $("body").append(dialog);
@@ -118,16 +117,15 @@ export function launch(conf) {
conf.on_click(e); conf.on_click(e);
}); });
dialog.on("hidden.bs.modal", () => { overlays.open_modal("dialog_widget_modal", {
dialog.remove(); autoremove: true,
}); micromodal: true,
micromodal_opts: {
onShow: () => {
if (conf.focus_submit_on_open) { if (conf.focus_submit_on_open) {
dialog.on("shown.bs.modal", () => {
submit_button.trigger("focus"); submit_button.trigger("focus");
}
},
},
}); });
} }
// Open the modal
overlays.open_modal("#dialog_widget_modal");
}

View File

@@ -1,4 +1,5 @@
import $ from "jquery"; import $ from "jquery";
import Micromodal from "micromodal";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
import * as browser_history from "./browser_history"; import * as browser_history from "./browser_history";
@@ -19,7 +20,8 @@ export function is_active() {
} }
export function is_modal_open() { export function is_modal_open() {
return $(".modal").hasClass("in"); // Check for both Bootstrap and Micromodal modals.
return $(".modal").hasClass("in") || $(".micromodal").hasClass("modal--open");
} }
export function info_overlay_open() { export function info_overlay_open() {
@@ -65,6 +67,12 @@ export function active_modal() {
blueslip.error("Programming error — Called active_modal when there is no modal open"); blueslip.error("Programming error — Called active_modal when there is no modal open");
return undefined; return undefined;
} }
// Check for Micromodal modals.
const micromodal = $(".micromodal.modal--open");
if (micromodal.length) {
return `#${CSS.escape(micromodal.attr("id"))}`;
}
return `#${CSS.escape($(".modal.in").attr("id"))}`; return `#${CSS.escape($(".modal.in").attr("id"))}`;
} }
@@ -113,17 +121,25 @@ export function open_overlay(opts) {
// If conf.autoremove is true, the modal element will be removed from the DOM // If conf.autoremove is true, the modal element will be removed from the DOM
// once the modal is hidden. // once the modal is hidden.
// If conf.micromodal is true, open a micromodal modal else open a bootstrap modal
export function open_modal(selector, conf) { export function open_modal(selector, conf) {
if (selector === undefined) { if (selector === undefined) {
blueslip.error("Undefined selector was passed into open_modal"); blueslip.error("Undefined selector was passed into open_modal");
return; return;
} }
if (selector[0] !== "#") { if ((!conf || (conf && !conf.micromodal)) && selector[0] !== "#") {
blueslip.error("Non-id-based selector passed in to open_modal: " + selector); blueslip.error("Non-id-based selector passed in to open_modal: " + selector);
return; return;
} }
// Don't accept hash-based selector to enforce modals to have unique ids and
// since micromodal doesn't accept hash based selectors.
if (conf && conf.micromodal && selector[0] === "#") {
blueslip.error("hash-based selector passed in to micromodal-based open_modal: " + selector);
return;
}
if (is_modal_open()) { if (is_modal_open()) {
blueslip.error("open_modal() was called while " + active_modal() + " modal was open."); blueslip.error("open_modal() was called while " + active_modal() + " modal was open.");
return; return;
@@ -131,6 +147,46 @@ export function open_modal(selector, conf) {
blueslip.debug("open modal: " + selector); blueslip.debug("open modal: " + selector);
// Show a modal using micromodal.
if (conf && conf.micromodal) {
// Micromodal gets elements using the getElementById DOM function
// which doesn't require the hash. We add it manually here.
const id_selector = `#${selector}`;
const micromodal = $(id_selector);
micromodal.find(".modal__container").on("animationend", (event) => {
// Micromodal doesn't support Bootstrap-style `shown.bs.modal` and
// `hidden.bs.modal` events. We workaround this by using the animationName
// from the native event and running the required functions after the
// animation ends.
const animation_name = event.originalEvent.animationName;
if (animation_name === "mmfadeIn") {
// Equivalent to bootstrap's "shown.bs.modal" event
// Micromodal adds the is-open class before the modal animation
// is complete, which isn't really helpful since a modal is open after the
// animation is complete. So, we manually add a class after the
// animation is complete.
micromodal.addClass("modal--open");
micromodal.removeClass("modal--opening");
} else if (animation_name === "mmfadeOut") {
// Equivalent to bootstrap's "hidden.bs.modal" event
micromodal.removeClass("modal--open");
if (conf.autoremove) {
micromodal.remove();
}
}
});
Micromodal.show(selector, {
disableFocus: true,
openClass: "modal--opening",
...conf.micromodal_opts,
});
return;
}
const elem = $(selector).expectOne(); const elem = $(selector).expectOne();
elem.modal("show").attr("aria-hidden", false); elem.modal("show").attr("aria-hidden", false);
// Disable background mouse events when modal is active // Disable background mouse events when modal is active
@@ -185,7 +241,8 @@ export function close_active() {
close_overlay(open_overlay_name); close_overlay(open_overlay_name);
} }
export function close_modal(selector) { // If conf.micromodal is true, close a micromodal modal else close a bootstrap modal
export function close_modal(selector, conf) {
if (selector === undefined) { if (selector === undefined) {
blueslip.error("Undefined selector was passed into close_modal"); blueslip.error("Undefined selector was passed into close_modal");
return; return;
@@ -196,7 +253,10 @@ export function close_modal(selector) {
return; return;
} }
if (active_modal() !== selector) { if (
(!conf && active_modal() !== selector) ||
(conf && conf.micromodal && active_modal() !== `#${selector}`)
) {
blueslip.error( blueslip.error(
"Trying to close " + selector + " modal when " + active_modal() + " is open.", "Trying to close " + selector + " modal when " + active_modal() + " is open.",
); );
@@ -205,6 +265,11 @@ export function close_modal(selector) {
blueslip.debug("close modal: " + selector); blueslip.debug("close modal: " + selector);
if (conf && conf.micromodal) {
Micromodal.close(selector);
return;
}
const elem = $(selector).expectOne(); const elem = $(selector).expectOne();
elem.modal("hide").attr("aria-hidden", true); elem.modal("hide").attr("aria-hidden", true);
} }
@@ -215,6 +280,13 @@ export function close_active_modal() {
return; return;
} }
// Check for Micromodal modals.
const micromodal = $(".micromodal.modal--open");
if (micromodal.length) {
Micromodal.close(`${CSS.escape(micromodal.attr("id"))}`);
return;
}
$(".modal.in").modal("hide").attr("aria-hidden", true); $(".modal.in").modal("hide").attr("aria-hidden", true);
} }

View File

@@ -28,7 +28,7 @@ $("body").ready(() => {
if (!overlays.is_modal_open()) { if (!overlays.is_modal_open()) {
return; return;
} }
if ($(e.target).closest(".modal").length > 0) { if ($(e.target).closest(".modal, .micromodal").length > 0) {
return; return;
} }
e.preventDefault(); e.preventDefault();

View File

@@ -35,3 +35,170 @@
.modal-bg { .modal-bg {
background-color: hsl(0, 0%, 98%); background-color: hsl(0, 0%, 98%);
} }
/* Styles for the Micromodal-based modals */
.modal__overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: hsla(0, 0%, 0%, 0.6);
display: flex;
justify-content: center;
align-items: center;
z-index: 105;
}
.modal__container {
display: flex;
flex-direction: column;
background-color: hsl(0, 0%, 100%);
max-width: calc(100% - 32px);
max-height: 96%;
width: 32.5rem;
border-radius: 4px;
box-sizing: border-box;
}
.modal__header {
padding: 16px 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal__footer {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 20px 24px;
}
.modal__title {
margin: 0;
font-size: 1.375rem;
line-height: 1.25;
}
.modal__close {
&::before {
content: "\2715";
}
margin-right: -4px;
background: transparent;
border: 0;
&:hover {
background: hsl(0, 0%, 90%);
}
}
.modal__content {
font-size: 1rem;
overflow-y: auto;
padding: 0 24px;
line-height: 1.5;
}
.modal__btn {
font-size: 0.875rem;
padding: 0.5rem 1rem;
background-color: hsl(0, 0%, 90%);
border-radius: 0.25rem;
border-width: 0;
cursor: pointer;
appearance: button;
text-transform: none;
overflow: visible;
outline: none !important;
line-height: 1.15;
margin: 0;
will-change: transform;
backface-visibility: hidden;
transform: translateZ(0);
transition: transform 0.25s ease-out;
&:focus {
box-shadow: hsl(198, 76%, 47%) 0 0 0 1px,
hsla(198, 76%, 47%, 0.3) 0 0 0 5px;
}
}
.modal__btn:focus,
.modal__btn:hover {
transform: scale(1.05);
}
.dialog_cancel_button {
background: hsl(0, 0%, 100%);
border: 1px solid hsla(300, 2%, 11%, 0.3);
&:hover {
background: hsl(0, 0%, 97%);
}
}
.dialog_submit_button {
margin-left: 12px;
background-color: hsl(214, 100%, 31%);
color: hsl(0, 0%, 100%);
}
@keyframes mmfadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes mmfadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.micromodal {
display: none;
}
.micromodal.modal--opening,
.micromodal.modal--open {
display: block;
}
.micromodal[aria-hidden="true"] .modal__overlay {
animation: mmfadeOut 75ms cubic-bezier(0, 0, 0.2, 1);
}
.micromodal[aria-hidden="false"] .modal__overlay {
animation: mmfadeIn 120ms cubic-bezier(0, 0, 0.2, 1);
}
.micromodal[aria-hidden="true"] .modal__container {
animation: mmfadeOut 75ms cubic-bezier(0, 0, 0.2, 1);
}
.micromodal[aria-hidden="false"] .modal__container {
animation: mmfadeIn 120ms cubic-bezier(0, 0, 0.2, 1);
}
.micromodal .modal__container,
.micromodal .modal__overlay {
will-change: transform;
}
.modal__spinner .loading_indicator_spinner {
height: 16px;
path {
fill: hsl(0, 0%, 100%);
}
}

View File

@@ -128,10 +128,21 @@ body.night-mode {
border-color: hsla(0, 0%, 100%, 0.4); border-color: hsla(0, 0%, 100%, 0.4);
} }
.modal-bg { .modal-bg,
.modal__container {
background-color: hsl(212, 28%, 18%); background-color: hsl(212, 28%, 18%);
} }
.modal__close {
&::before {
color: hsl(236, 33%, 90%);
}
&:hover {
background: hsla(0, 0%, 91%, 0.1);
}
}
.streams_popover .sp-container { .streams_popover .sp-container {
background-color: transparent; background-color: transparent;

View File

@@ -1577,17 +1577,6 @@ input[type="checkbox"] {
} }
} }
/* Dialog widgets should be centered, which this roughly achieves. */
#dialog_widget_modal {
top: calc(50% - 120px);
}
/* In the settings overlay, we need slightly different CSS for alignment. */
#settings_overlay_container #dialog_widget_modal {
top: 50%;
vertical-align: center;
}
/* These have enough space for all the options in German. */ /* These have enough space for all the options in German. */
.setting_desktop_icon_count_display, .setting_desktop_icon_count_display,
#id_realm_waiting_period_setting, #id_realm_waiting_period_setting,
@@ -1618,16 +1607,6 @@ input[type="checkbox"] {
margin-top: 10px; margin-top: 10px;
} }
.dialog_submit_button .loader {
display: none;
vertical-align: top;
position: relative;
height: 30px;
margin-top: -10px;
top: 5px;
width: 30px;
}
.dropdown-list-widget { .dropdown-list-widget {
button { button {
margin: 0 5px; margin: 0 5px;

View File

@@ -1,17 +1,26 @@
<div class="modal modal-bg new-style hide" id="dialog_widget_modal" tabindex="-1" role="dialog" aria-labelledby="dialog_widget_modal" aria-hidden="true"> <div class="micromodal" id="dialog_widget_modal" aria-hidden="true">
<div class="modal-header"> <div class="modal__overlay" tabindex="-1" data-micromodal-close>
<button type="button" class="close close-modal-btn" data-dismiss="modal" aria-label="{{t 'Close' }}"><span aria-hidden="true">&times;</span></button> <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="dialog_title">
<div class="dialog_heading">{{{ html_dialog_heading }}}</div> <header class="modal__header">
</div> <h1 class="modal__title dialog_heading">
<div class="modal-body dialog_body"> {{{ heading_text }}}
<div id="dialog_error" class="alert"></div> {{#if link}}
{{> help_link_widget }}
{{/if}}
</h1>
<button class="modal__close" aria-label="{{t 'Close modal' }}" data-micromodal-close></button>
</header>
<main class="modal__content">
<div class="alert" id="dialog_error"></div>
{{{ html_body }}} {{{ html_body }}}
</div> </main>
<div class="modal-footer"> <footer class="modal__footer">
<button class="button rounded close-modal-btn" data-dismiss="modal">{{t "Cancel" }}</button> <button class="modal__btn dialog_cancel_button" aria-label="{{t 'Close this dialog window' }}" data-micromodal-close>{{t "Cancel" }}</button>
<button class="button rounded {{#if danger_submit_button}}btn-danger{{else}}sea-green{{/if}} dialog_submit_button"> <button class="modal__btn dialog_submit_button">
<img class="loader" alt="" src="" />
<span>{{{ html_submit_button }}}</span> <span>{{{ html_submit_button }}}</span>
<div class="modal__spinner"></div>
</button> </button>
</footer>
</div>
</div> </div>
</div> </div>

View File

@@ -1,6 +0,0 @@
<h3>
{{{ heading_text }}}
{{#if link}}
{{> help_link_widget }}
{{/if}}
</h3>

View File

@@ -48,4 +48,4 @@ API_FEATURE_LEVEL = 106
# historical commits sharing the same major version, in which case a # historical commits sharing the same major version, in which case a
# minor version bump suffices. # minor version bump suffices.
PROVISION_VERSION = "164.1" PROVISION_VERSION = "164.2"

View File

@@ -7985,6 +7985,11 @@ micromist@1.1.0:
dependencies: dependencies:
lodash.camelcase "^4.3.0" lodash.camelcase "^4.3.0"
micromodal@^0.4.6:
version "0.4.6"
resolved "https://registry.yarnpkg.com/micromodal/-/micromodal-0.4.6.tgz#0425ad026c47923208cf826de6b58ed0693cb25a"
integrity sha512-2VDso2a22jWPpqwuWT/4RomVpoU3Bl9qF9D01xzwlNp5UVsImeA0gY4nSpF44vqcQtQOtkiMUV9EZkAJSRxBsg==
mime-db@1.50.0, "mime-db@>= 1.43.0 < 2": mime-db@1.50.0, "mime-db@>= 1.43.0 < 2":
version "1.50.0" version "1.50.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f"