Refactor markup and styles to re-use existing code where possible

This commit is contained in:
2026-08-20 14:58:00 -06:00
parent 99ebdf3378
commit 135a30f1cd
5 changed files with 290 additions and 466 deletions
+13 -9
View File
@@ -30,6 +30,10 @@ export default class Modal extends Component {
return config.environment === 'test'; return config.environment === 'test';
} }
get shouldPortal() {
return !this.isTesting && !this.args.inline;
}
get destinationElement() { get destinationElement() {
return document.getElementById('modal-portal') || document.body; return document.getElementById('modal-portal') || document.body;
} }
@@ -48,15 +52,7 @@ export default class Modal extends Component {
} }
<template> <template>
{{#if this.isTesting}} {{#if this.shouldPortal}}
<ModalContent
@close={{this.close}}
@stopProp={{this.stopProp}}
@disableClose={{@disableClose}}
>
{{yield}}
</ModalContent>
{{else}}
{{#in-element this.destinationElement}} {{#in-element this.destinationElement}}
<ModalContent <ModalContent
@close={{this.close}} @close={{this.close}}
@@ -66,6 +62,14 @@ export default class Modal extends Component {
{{yield}} {{yield}}
</ModalContent> </ModalContent>
{{/in-element}} {{/in-element}}
{{else}}
<ModalContent
@close={{this.close}}
@stopProp={{this.stopProp}}
@disableClose={{@disableClose}}
>
{{yield}}
</ModalContent>
{{/if}} {{/if}}
</template> </template>
} }
+1 -1
View File
@@ -180,7 +180,7 @@ export default class PhotoGallery extends Component {
e.target.closest('.carousel-nav-btn') || e.target.closest('.carousel-nav-btn') ||
e.target.closest('.close-btn') || e.target.closest('.close-btn') ||
e.target.closest('.photo-gallery-header') || e.target.closest('.photo-gallery-header') ||
e.target.closest('.zap-photo-modal-overlay') e.target.closest('.modal-overlay')
) { ) {
return; return;
} }
+38 -70
View File
@@ -7,6 +7,7 @@ import { task } from 'ember-concurrency';
import { eq } from 'ember-truth-helpers'; import { eq } from 'ember-truth-helpers';
import qrCode from '../modifiers/qr-code'; import qrCode from '../modifiers/qr-code';
import Icon from './icon'; import Icon from './icon';
import Modal from './modal';
const SLIDER_MIN_SATS = 10; const SLIDER_MIN_SATS = 10;
const SLIDER_MAX_SATS = 100_000; const SLIDER_MAX_SATS = 100_000;
@@ -210,11 +211,6 @@ export default class ZapPhotoModal extends Component {
} }
} }
@action
stopPropagation(e) {
e.stopPropagation();
}
_startReceiptSubscription(zapRequest) { _startReceiptSubscription(zapRequest) {
this._cleanupReceiptSub(); this._cleanupReceiptSub();
this._zapRequest = zapRequest; this._zapRequest = zapRequest;
@@ -291,69 +287,52 @@ export default class ZapPhotoModal extends Component {
} }
<template> <template>
{{! template-lint-disable no-invalid-interactive }} <Modal @inline={{true}} @onClose={{this.resetAndClose}}>
<div
class="zap-photo-modal-overlay"
role="dialog"
aria-modal="true"
{{on "click" this.stopPropagation}}
>
<div class="zap-photo-modal"> <div class="zap-photo-modal">
<div class="zap-photo-modal-header"> <h2>Zap Photo</h2>
<h3>Zap Photo</h3>
<button
type="button"
class="btn-text zap-modal-close"
{{on "click" this.resetAndClose}}
aria-label="Close"
>
<Icon @name="x" @size={{20}} />
</button>
</div>
{{! Lightning address status }} {{! Lightning address status }}
<div class="zap-modal-status">
{{#if this.lnurlLoading}} {{#if this.lnurlLoading}}
<div class="zap-modal-loading">Loading lightning address...</div> <p class="meta-info">Loading lightning address...</p>
{{else if this.lightningAddress}} {{else if this.lightningAddress}}
<div class="zap-modal-address"> <div class="lightning-address">
<span class="zap-modal-badge">Lightning</span> <span class="lightning-badge">Lightning</span>
<span <span
class="zap-modal-address-text" class="lightning-address-text"
>{{this.lightningAddress}}</span> >{{this.lightningAddress}}</span>
</div> </div>
{{/if}} {{/if}}
{{#if this.lnurlError}} {{#if this.lnurlError}}
<div class="zap-modal-error-msg">{{this.lnurlError}}</div> <div class="alert alert-error">{{this.lnurlError}}</div>
{{/if}} {{/if}}
</div>
{{! Step: Select Amount }} {{! Step: Select Amount }}
{{#if (eq this.step "select-amount")}} {{#if (eq this.step "select-amount")}}
<div class="zap-modal-body"> <div class="amount-display">
<div class="zap-amount-display"> <span
<span class="zap-amount-value">{{this.formattedAmount}}</span> class="amount-value zap-amount"
<span class="zap-amount-unit">sats</span> >{{this.formattedAmount}}</span>
<span class="amount-unit">sats</span>
</div> </div>
<input <input
type="range" type="range"
class="zap-slider"
min="0" min="0"
max={{this.sliderSteps}} max={{this.sliderSteps}}
value={{this.sliderPosition}} value={{this.sliderPosition}}
aria-label="Zap amount in sats" aria-label="Zap amount in sats"
{{on "input" this.updateSlider}} {{on "input" this.updateSlider}}
class="zap-slider"
/> />
<div class="zap-slider-ticks"> <div class="slider-ticks">
{{#each this.sliderTicks as |label|}} {{#each this.sliderTicks as |label|}}
<span class="zap-tick">{{label}}</span> <span>{{label}}</span>
{{/each}} {{/each}}
</div> </div>
{{#if this.lnurlEndpoint}} {{#if this.lnurlEndpoint}}
<div class="zap-min-max"> <div class="min-max">
Min: Min:
{{this.minSats}} {{this.minSats}}
sats · Max: sats · Max:
@@ -362,9 +341,10 @@ export default class ZapPhotoModal extends Component {
</div> </div>
{{/if}} {{/if}}
<div class="zap-message-field"> <div class="form-group">
<label>Message (optional)</label> <label>Message (optional)</label>
<textarea <textarea
class="form-control"
rows="2" rows="2"
placeholder="Say something nice..." placeholder="Say something nice..."
aria-label="Zap message" aria-label="Zap message"
@@ -373,7 +353,7 @@ export default class ZapPhotoModal extends Component {
></textarea> ></textarea>
</div> </div>
<div class="zap-modal-actions"> <div class="edit-actions">
<button <button
type="button" type="button"
class="btn btn-outline" class="btn btn-outline"
@@ -392,64 +372,57 @@ export default class ZapPhotoModal extends Component {
sats sats
</button> </button>
</div> </div>
</div>
{{/if}} {{/if}}
{{! Step: Fetching Invoice }} {{! Step: Fetching Invoice }}
{{#if (eq this.step "fetching-invoice")}} {{#if (eq this.step "fetching-invoice")}}
<div class="zap-modal-body zap-modal-center"> <div class="centered">
<div class="zap-spinner"></div> <Icon @name="loading-ring" @size={{32}} class="spin-animation" />
<p>Creating zap request and fetching invoice...</p> <p>Creating zap request and fetching invoice...</p>
</div> </div>
{{/if}} {{/if}}
{{! Step: Awaiting Payment }} {{! Step: Awaiting Payment }}
{{#if (eq this.step "awaiting-payment")}} {{#if (eq this.step "awaiting-payment")}}
<div class="zap-modal-body">
<p class="zap-pay-prompt"> <p class="zap-pay-prompt">
Scan to pay Scan to pay
<strong>{{this.formattedAmount}} sats</strong> <strong>{{this.formattedAmount}} sats</strong>
</p> </p>
<div class="zap-qr-container"> <div class="qr-code-container">
<canvas {{qrCode this.invoice}}></canvas> <canvas {{qrCode this.invoice}}></canvas>
</div> </div>
<a <a href={{this.lightningUri}} class="btn btn-outline btn-full">
href={{this.lightningUri}}
class="btn btn-outline zap-open-wallet-link"
>
Open in Lightning wallet Open in Lightning wallet
</a> </a>
{{#if this.hasWebLN}} {{#if this.hasWebLN}}
<button <button
type="button" type="button"
class="btn btn-primary zap-webln-btn" class="btn btn-primary btn-full webln-pay-btn"
{{on "click" this.performWebLNPay}} {{on "click" this.performWebLNPay}}
> >
Pay with WebLN Pay with WebLN
</button> </button>
{{/if}} {{/if}}
<div class="zap-invoice-details">
<button <button
type="button" type="button"
class="btn-text" class="btn-text copy-invoice-btn"
{{on "click" this.copyInvoice}} {{on "click" this.copyInvoice}}
> >
Copy invoice Copy invoice
</button> </button>
</div>
{{#if this.paymentNotDetected}} {{#if this.paymentNotDetected}}
<p class="zap-modal-hint"> <p class="meta-info">
Payment not detected yet. Keep the QR code open and try paying Payment not detected yet. Keep the QR code open and try paying
again, or use a different Lightning wallet. again, or use a different Lightning wallet.
</p> </p>
{{/if}} {{/if}}
<div class="zap-modal-actions"> <div class="edit-actions">
<button <button
type="button" type="button"
class="btn btn-outline" class="btn btn-outline"
@@ -458,26 +431,24 @@ export default class ZapPhotoModal extends Component {
Cancel Cancel
</button> </button>
</div> </div>
</div>
{{/if}} {{/if}}
{{! Step: Paying with Wallet }} {{! Step: Paying with Wallet }}
{{#if (eq this.step "paying-with-wallet")}} {{#if (eq this.step "paying-with-wallet")}}
<div class="zap-modal-body zap-modal-center"> <div class="centered">
<div class="zap-spinner"></div> <Icon @name="loading-ring" @size={{32}} class="spin-animation" />
<p>Paying invoice with WebLN...</p> <p>Paying invoice with WebLN...</p>
</div> </div>
{{/if}} {{/if}}
{{! Step: Wallet Pay Error }} {{! Step: Wallet Pay Error }}
{{#if (eq this.step "wallet-pay-error")}} {{#if (eq this.step "wallet-pay-error")}}
<div class="zap-modal-body"> <div class="alert alert-error">{{this.error}}</div>
<div class="zap-modal-error-msg">{{this.error}}</div> <p class="meta-info">
<p class="zap-modal-hint">
The WebLN payment failed. You can still scan the QR code with a The WebLN payment failed. You can still scan the QR code with a
Lightning wallet. Lightning wallet.
</p> </p>
<div class="zap-modal-actions"> <div class="edit-actions">
<button <button
type="button" type="button"
class="btn btn-outline" class="btn btn-outline"
@@ -493,13 +464,12 @@ export default class ZapPhotoModal extends Component {
Show Invoice Show Invoice
</button> </button>
</div> </div>
</div>
{{/if}} {{/if}}
{{! Step: Success }} {{! Step: Success }}
{{#if (eq this.step "success")}} {{#if (eq this.step "success")}}
<div class="zap-modal-body zap-modal-center zap-success"> <div class="success zap-success">
<div class="zap-success-icon">&#9889;</div> <div class="success-icon">&#9889;</div>
<h4>Zap Sent!</h4> <h4>Zap Sent!</h4>
<p> <p>
You zapped You zapped
@@ -520,9 +490,8 @@ export default class ZapPhotoModal extends Component {
{{! Step: Error }} {{! Step: Error }}
{{#if (eq this.step "error")}} {{#if (eq this.step "error")}}
<div class="zap-modal-body"> <div class="alert alert-error">{{this.error}}</div>
<div class="zap-modal-error-msg">{{this.error}}</div> <div class="edit-actions">
<div class="zap-modal-actions">
<button <button
type="button" type="button"
class="btn btn-outline" class="btn btn-outline"
@@ -538,9 +507,8 @@ export default class ZapPhotoModal extends Component {
Try Again Try Again
</button> </button>
</div> </div>
</div>
{{/if}} {{/if}}
</div> </div>
</div> </Modal>
</template> </template>
} }
+72 -218
View File
@@ -2427,79 +2427,69 @@ button.create-place {
border-radius: 10px; border-radius: 10px;
} }
/* Zap Photo Modal */ /* Generic modal heading reset (shared by all modals) */
.zap-photo-modal-overlay { .modal-content h2,
position: fixed; .modal-content h3 {
inset: 0; margin-top: 0;
background: rgb(0 0 0 / 70%);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
} }
/* Zap Photo Modal — scoped, nested styles */
.zap-photo-modal { .zap-photo-modal {
background: white; text-align: center;
border-radius: 12px;
box-shadow: 0 8px 32px rgb(0 0 0 / 30%); & h2 {
margin-bottom: 1rem;
}
& .amount-display {
display: flex;
align-items: baseline;
justify-content: center;
gap: 0.4rem;
}
& .amount-value {
font-size: 2rem;
font-weight: 700;
color: var(--default-list-color);
}
& .amount-unit {
font-size: 0.9rem;
color: var(--body-text-color);
opacity: 0.7;
}
& .zap-slider {
width: 100%; width: 100%;
max-width: 420px; accent-color: var(--default-list-color);
max-height: 90vh;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.zap-photo-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1.25rem;
border-bottom: 1px solid var(--divider-color);
}
.zap-photo-modal-header h3 {
margin: 0;
}
.zap-modal-close {
background: none;
border: none;
padding: 0.25rem;
cursor: pointer; cursor: pointer;
}
& .slider-ticks {
display: flex;
justify-content: space-between;
font-size: 0.7rem;
color: var(--body-text-color);
opacity: 0.5;
padding: 0 0.25rem;
}
& .min-max {
font-size: 0.75rem;
color: var(--body-text-color);
opacity: 0.7;
}
& .lightning-address {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #898989;
}
.zap-modal-close:hover {
color: var(--body-text-color);
}
.zap-modal-status {
padding: 0.75rem 1.25rem;
border-bottom: 1px solid var(--divider-color);
min-height: 2.5rem;
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.zap-modal-loading {
font-size: 0.85rem;
color: #898989;
}
.zap-modal-address {
display: flex;
align-items: center;
gap: 0.5rem; gap: 0.5rem;
} }
.zap-modal-badge { & .lightning-badge {
background: #f7b500; background: var(--default-list-color);
color: white; color: white;
font-size: 0.7rem; font-size: 0.7rem;
font-weight: 600; font-weight: 600;
@@ -2507,179 +2497,43 @@ button.create-place {
border-radius: 10px; border-radius: 10px;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
} }
.zap-modal-address-text { & .lightning-address-text {
font-family: monospace; font-family: monospace;
font-size: 0.8rem; font-size: 0.8rem;
color: #555; color: var(--body-text-color);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
}
.zap-modal-error-msg {
color: var(--danger-color);
font-size: 0.85rem;
padding: 0.5rem 0.75rem;
background: rgb(234 67 53 / 8%);
border-radius: 4px;
}
.zap-modal-body {
padding: 1.25rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.zap-modal-center {
align-items: center;
text-align: center;
}
.zap-amount-display {
display: flex;
align-items: baseline;
justify-content: center;
gap: 0.4rem;
}
.zap-amount-value {
font-size: 2rem;
font-weight: 700;
color: #f7b500;
}
.zap-amount-unit {
font-size: 0.9rem;
color: #898989;
}
.zap-slider {
width: 100%;
accent-color: #f7b500;
cursor: pointer;
}
.zap-slider-ticks {
display: flex;
justify-content: space-between;
font-size: 0.7rem;
color: #aaa;
padding: 0 0.25rem;
}
.zap-min-max {
font-size: 0.75rem;
color: #898989;
text-align: center;
}
.zap-message-field {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.zap-message-field label {
font-size: 0.8rem;
color: #898989;
}
.zap-message-field textarea {
width: 100%;
border: 1px solid #ddd;
border-radius: 4px;
padding: 0.5rem;
font-family: inherit;
font-size: 0.9rem;
resize: vertical;
}
.zap-message-field textarea:focus {
outline: none;
border-color: var(--link-color);
}
.zap-modal-actions {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
}
.zap-modal-hint {
font-size: 0.8rem;
color: #898989;
text-align: center;
}
.zap-spinner {
width: 32px;
height: 32px;
border: 3px solid #eee;
border-top-color: #f7b500;
border-radius: 50%;
animation: zap-spin 0.8s linear infinite;
}
@keyframes zap-spin {
to {
transform: rotate(360deg);
} }
}
.zap-pay-prompt { & .centered {
text-align: center;
font-size: 0.9rem;
}
.zap-qr-container {
display: flex; display: flex;
justify-content: center; flex-direction: column;
} align-items: center;
gap: 1rem;
}
.zap-qr-container canvas { & .success {
border-radius: 8px;
background: white;
padding: 8px;
}
.zap-qr-container a {
display: block;
}
.zap-webln-btn {
width: 100%;
}
.zap-open-wallet-link {
width: 100%;
text-align: center;
text-decoration: none;
}
.zap-invoice-details {
display: flex; display: flex;
justify-content: center; flex-direction: column;
font-size: 0.8rem; align-items: center;
}
.zap-success {
gap: 0.5rem; gap: 0.5rem;
} }
.zap-success-icon { & .success-icon {
font-size: 3rem; font-size: 3rem;
} }
.zap-success h4 { & .success h4 {
margin: 0; margin: 0;
font-size: 1.2rem; font-size: 1.2rem;
} }
.zap-success p { & .success p {
margin: 0; margin: 0;
font-size: 0.9rem; font-size: 0.9rem;
color: #555; color: var(--body-text-color);
}
} }
@@ -42,11 +42,11 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
); );
assert.dom('.zap-slider').exists('slider is rendered'); assert.dom('.zap-slider').exists('slider is rendered');
assert.dom('.zap-amount-value').exists('amount is displayed'); assert.dom('.zap-amount').exists('amount is displayed');
assert assert
.dom('.zap-modal-address-text') .dom('.lightning-address-text')
.hasText('user@example.com', 'lightning address is shown'); .hasText('user@example.com', 'lightning address is shown');
assert.dom('.zap-modal-badge').hasText('Lightning', 'badge is shown'); assert.dom('.lightning-badge').hasText('Lightning', 'badge is shown');
}); });
test('it shows error when user has no lightning address', async function (assert) { test('it shows error when user has no lightning address', async function (assert) {
@@ -60,10 +60,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
); );
assert assert
.dom('.zap-modal-error-msg') .dom('.alert-error')
.exists('error message is shown when no lightning address'); .exists('error message is shown when no lightning address');
assert assert
.dom('.zap-modal-actions .btn-primary') .dom('.edit-actions .btn-primary')
.isDisabled('zap button is disabled when no address'); .isDisabled('zap button is disabled when no address');
}); });
@@ -80,11 +80,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
assert.dom('.alert-error').exists('error shown when allowsNostr is false');
assert assert
.dom('.zap-modal-error-msg') .dom('.edit-actions .btn-primary')
.exists('error shown when allowsNostr is false');
assert
.dom('.zap-modal-actions .btn-primary')
.isDisabled('zap button is disabled'); .isDisabled('zap button is disabled');
}); });
@@ -103,10 +101,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
); );
assert assert
.dom('.zap-modal-actions .btn-primary') .dom('.edit-actions .btn-primary')
.isNotDisabled('zap button is enabled when endpoint is valid'); .isNotDisabled('zap button is enabled when endpoint is valid');
await click('.zap-modal-actions .btn-primary'); await click('.edit-actions .btn-primary');
assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called'); assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called');
assert.deepEqual( assert.deepEqual(
@@ -120,9 +118,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
'zap amount is 1000 sats in millisats' 'zap amount is 1000 sats in millisats'
); );
await waitFor('.zap-qr-container'); await waitFor('.qr-code-container');
assert.dom('.zap-qr-container').exists('QR code is shown'); assert.dom('.qr-code-container').exists('QR code is shown');
assert.dom('.zap-pay-prompt').exists('payment prompt is shown'); assert.dom('.zap-pay-prompt').exists('payment prompt is shown');
assert.notOk(closed, 'modal was not closed by clicking the Zap button'); assert.notOk(closed, 'modal was not closed by clicking the Zap button');
}); });
@@ -137,9 +135,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
await fillIn('.zap-message-field textarea', 'Great photo!'); await fillIn('.form-group textarea', 'Great photo!');
await click('.zap-modal-actions .btn-primary'); await click('.edit-actions .btn-primary');
assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called'); assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called');
assert.strictEqual( assert.strictEqual(
@@ -159,10 +157,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
await click('.zap-modal-actions .btn-primary'); await click('.edit-actions .btn-primary');
await waitFor('.zap-webln-btn'); await waitFor('.webln-pay-btn');
assert.dom('.zap-webln-btn').exists('WebLN button is shown when available'); assert.dom('.webln-pay-btn').exists('WebLN button is shown when available');
}); });
test('it shows success after WebLN payment', async function (assert) { test('it shows success after WebLN payment', async function (assert) {
@@ -175,9 +173,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
await click('.zap-modal-actions .btn-primary'); await click('.edit-actions .btn-primary');
await waitFor('.zap-webln-btn'); await waitFor('.webln-pay-btn');
await click('.zap-webln-btn'); await click('.webln-pay-btn');
await waitFor('.zap-success'); await waitFor('.zap-success');
@@ -193,10 +191,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
await click('.zap-modal-actions .btn-primary'); await click('.edit-actions .btn-primary');
await waitFor('.zap-qr-container'); await waitFor('.qr-code-container');
assert.dom('.zap-qr-container').exists('QR code is shown'); assert.dom('.qr-code-container').exists('QR code is shown');
assert.ok( assert.ok(
this.nostrZap._receiptCallback, this.nostrZap._receiptCallback,
@@ -227,7 +225,7 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
await click('.zap-modal-close'); await click('.close-modal-btn');
assert.ok(closed, 'onClose was called'); assert.ok(closed, 'onClose was called');
}); });
@@ -245,7 +243,7 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
await click('.zap-modal-actions .btn-outline'); await click('.edit-actions .btn-outline');
assert.ok(closed, 'onClose was called from Cancel button'); assert.ok(closed, 'onClose was called from Cancel button');
}); });
@@ -268,7 +266,7 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
assert.notOk(closed, 'onClose was not called when clicking inside modal'); assert.notOk(closed, 'onClose was not called when clicking inside modal');
}); });
test('it does not close when clicking the overlay background', async function (assert) { test('it closes when clicking the overlay background', async function (assert) {
let closed = false; let closed = false;
this.handleClose = () => { this.handleClose = () => {
closed = true; closed = true;
@@ -281,8 +279,8 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
</template> </template>
); );
await click('.zap-photo-modal-overlay'); await click('.modal-overlay');
assert.notOk(closed, 'onClose was not called when clicking overlay'); assert.ok(closed, 'onClose was called when clicking overlay');
}); });
}); });