Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
317a5f7
force tmp to 0.2.7
Gaurav0 Oct 15, 2026
3af9a87
SCF-2415: normalize tmp resolution key in yarn.lock
Gaurav0 Oct 28, 2026
97f9ee5
SCF-2415: force brace-expansion to 1.1.21
Gaurav0 Oct 28, 2026
b0c1cec
SCF-2415: force minimatch to 3.1.5
Gaurav0 Oct 28, 2026
6922a1a
SCF-2415: force picomatch to 2.3.2
Gaurav0 Oct 28, 2026
47be990
force braces to 3.0.3
Gaurav0 Oct 28, 2026
5151df0
SCF-2415: force fast-uri to 3.1.9
Gaurav0 Oct 28, 2026
05042c2
SCF-2415: force nanoid to 3.3.20
Gaurav0 Oct 28, 2026
c4f0123
force markdown-it to 14.3.3
Gaurav0 Oct 28, 2026
6eab1d8
force lodash.template to 4.18.1
Gaurav0 Oct 28, 2026
7c84bf5
force ansi-html to 0.0.9
Gaurav0 Oct 28, 2026
3d675cd
refresh express to 4.22.3 and body-parser to 1.20.9
Gaurav0 Oct 28, 2026
a90304e
refresh compression to 1.8.2
Gaurav0 Oct 28, 2026
e080496
refresh engine.io to 6.6.11
Gaurav0 Oct 28, 2026
5f5dd51
refresh socket.io-parser to 4.2.7
Gaurav0 Oct 28, 2026
538755c
refresh ws to 8.21.3/8.22.0
Gaurav0 Oct 28, 2026
4c878d0
refresh websocket-driver to 0.7.5
Gaurav0 Oct 28, 2026
617be53
refresh proxy-addr to 2.0.8
Gaurav0 Oct 28, 2026
80d18f1
refresh terser-webpack-plugin to 5.6.1
Gaurav0 Oct 28, 2026
1e10ea8
refresh shell-quote to 1.12.0
Gaurav0 Oct 28, 2026
fb57ffd
refresh form-data to 4.0.6
Gaurav0 Oct 28, 2026
7221bd3
refresh @xmldom/xmldom to 0.8.15
Gaurav0 Oct 28, 2026
4c1dd50
refresh flatted to 3.4.4
Gaurav0 Oct 28, 2026
a60344f
refresh js-yaml to 3.15.2 and force mocha's copy to 4.3.2
Gaurav0 Oct 28, 2026
33d2591
refresh source-map-js to 1.2.2
Gaurav0 Oct 28, 2026
fa369d5
refresh underscore to 1.13.8
Gaurav0 Oct 28, 2026
9ec9243
SCF-2415: bump version to 4.10.3
Gaurav0 Oct 29, 2026
b4f43a5
SCF-2415: run CI on push only for scf_master and tags
Gaurav0 Nov 3, 2026
4b70cb2
SCF-2415: make the modal footer's submit button a submit button via t…
Gaurav0 Nov 3, 2026
4614a42
SCF-2415: keep the modal footer's native submission unless onSubmit h…
Gaurav0 Nov 3, 2026
52be692
SCF-2415: hide BsAlert for @visible={{null}} again
Gaurav0 Nov 3, 2026
ad6dd68
SCF-2415: follow BsAlert's @visible in the same render
Gaurav0 Nov 3, 2026
ee88a0f
SCF-2415: skip the "does not catch errors thrown by onSubmit" test on…
Gaurav0 Nov 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on:
push:
branches:
- '*'
- scf_master
tags:
- '*'
pull_request:
Expand Down
3 changes: 1 addition & 2 deletions addon/components/bs-alert.hbs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<div
class="{{unless this.hidden "alert"}} {{if this.fade "fade"}} {{if this.dismissible "alert-dismissible"}} {{bs-type-class "alert" @type}} {{if this.showAlert (if (macroCondition (macroGetOwnConfig "isNotBS3")) "show" "in")}}"
...attributes
{{did-update this.showOrHide this._visible}}
{{did-update this.updateVisibility @visible}}
{{did-update this.showOrHide this.visible}}
>
{{#unless this.hidden}}
{{#if this.dismissible}}
Expand Down
40 changes: 29 additions & 11 deletions addon/components/bs-alert.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { later } from '@ember/runloop';
import { later, schedule } from '@ember/runloop';
import usesTransition from 'ember-bootstrap/utils/decorators/uses-transition';
import deprecateSubclassing from 'ember-bootstrap/utils/deprecate-subclassing';
import arg from 'ember-bootstrap/utils/decorators/arg';
Expand Down Expand Up @@ -86,15 +86,33 @@ export default class Alert extends Component {
* @public
*/
get visible() {
return this._visible ?? true;
let visible = this.args.visible;
if (visible !== this._visibleArg) {
// untracked bookkeeping, so the alert can be shown again after being dismissed once `@visible` changes
// eslint-disable-next-line ember/no-side-effects
this._visibleArg = visible;
this._visibleArgChanges++;
}
if (this._dismissedAt === this._visibleArgChanges) {
return false;
}
return visible !== undefined ? visible : true;
}

/**
* @property _visible
* `@visible` as last seen by `visible`, and how often it has changed. These are untracked, as `visible` updates them
* while rendering.
* @private
*/
_visibleArg = this.args.visible;
_visibleArgChanges = 0;

/**
* The value of `_visibleArgChanges` when the alert was dismissed, so the alert stays hidden until `@visible` changes.
* @private
*/
@tracked
_visible = this.args.visible;
_dismissedAt = -1;

/**
* Set to false to disable the fade out animation when hiding the alert.
Expand Down Expand Up @@ -163,7 +181,7 @@ export default class Alert extends Component {
@action
dismiss() {
if (this.args.onDismiss?.() !== false) {
this._visible = false;
this._dismissedAt = this._visibleArgChanges;
}
}

Expand Down Expand Up @@ -202,7 +220,12 @@ export default class Alert extends Component {
);
} else {
this.hidden = true;
this.args.onDismissed?.();
// hide() runs while rendering, and onDismissed may set @visible, which that render has already read
schedule('afterRender', this, function () {
if (!this.isDestroyed) {
this.args.onDismissed?.();
}
});
}
}

Expand All @@ -214,9 +237,4 @@ export default class Alert extends Component {
this.hide();
}
}

@action
updateVisibility() {
this._visible = this.args.visible;
}
}
2 changes: 1 addition & 1 deletion addon/components/bs-modal/footer.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{{else}}
{{#if @submitTitle}}
<Button @onClick={{@onClose}}>{{bs-default @closeTitle "Ok"}}</Button>
<Button @type={{bs-default @submitButtonType "primary"}} @buttonType="submit" disabled={{bs-default @submitDisabled false}}>{{@submitTitle}}</Button>
<Button @type={{bs-default @submitButtonType "primary"}} type="submit" disabled={{bs-default @submitDisabled false}}>{{@submitTitle}}</Button>
{{else}}
<Button @type="primary" @onClick={{@onClose}}>{{bs-default @closeTitle "Ok"}}</Button>
{{/if}}
Expand Down
12 changes: 9 additions & 3 deletions addon/components/bs-modal/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,14 @@ import { action } from '@ember/object';
export default class ModalFooter extends Component {
@action
handleSubmit(event) {
// the footer is a <form>, so prevent the browser's native submission (and the page reload that comes with it)
event.preventDefault();
this.args.onSubmit?.();
if (!this.args.onSubmit) {
return;
}
// the footer is a <form>, so prevent the browser's native submission (and the page reload that comes with it),
// unless an `action` attribute asks for it
if (!event.target.hasAttribute('action')) {
event.preventDefault();
}
this.args.onSubmit();
}
}
15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ember-bootstrap",
"version": "4.10.2",
"version": "4.10.3",
"description": "Bootstrap components for Ember.js",
"keywords": [
"ember-addon",
Expand Down Expand Up @@ -55,7 +55,18 @@
"ember-a11y-testing/ember-auto-import": "^2.10.0",
"**/ember-try-config/ember-source-channel-url": "^3.0.0",
"@babel/plugin-transform-modules-systemjs": "^7.29.4",
"browserslist": "^4.28.7"
"browserslist": "^4.28.7",
"tmp": "^0.2.7",
"brace-expansion": "^1.1.21",
"minimatch": "^3.1.5",
"picomatch": "^2.3.2",
"braces": "^3.0.3",
"fast-uri": "^3.1.9",
"nanoid": "^3.3.18",
"markdown-it": "^14.3.3",
"lodash.template": "^4.18.1",
"ansi-html": "0.0.9",
"mocha/js-yaml": "^4.3.2"
},
"dependencies": {
"@ember/render-modifiers": "^2.1.0",
Expand Down
16 changes: 16 additions & 0 deletions tests/integration/components/bs-alert-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ module('Integration | Component | bs-alert', function (hooks) {
assert.dom('*').hasText('', 'alert has no content');
});

test('alert is initially hidden when visible=null', async function (assert) {
await render(hbs`<BsAlert @type="success" @fade={{false}} @visible={{null}}>Test</BsAlert>`);

assert.dom('.alert').doesNotExist('alert has no alert class');
assert.dom('*').hasText('', 'alert has no content');
});

test('alert can be hidden by setting visible=null', async function (assert) {
this.set('visible', true);
await render(hbs`<BsAlert @type="success" @fade={{false}} @visible={{this.visible}}>Test</BsAlert>`);
this.set('visible', null);
await settled();

assert.dom('.alert').doesNotExist('alert has no alert class');
});

test('alert can be made visible when setting visible=true', async function (assert) {
this.set('visible', false);
await render(hbs`<BsAlert @type="success" @visible={{this.visible}} @fade={{false}}>Test</BsAlert>`);
Expand Down
6 changes: 5 additions & 1 deletion tests/integration/components/bs-form-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ import setupNoDeprecations from '../../helpers/setup-no-deprecations';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import sinon from 'sinon';
import Ember from 'ember';
import { dependencySatisfies, macroCondition } from '@embroider/macros';

// see https://github.com/kaliber5/ember-bootstrap/issues/1682
const testUnlessEmbroider = macroCondition(dependencySatisfies('@embroider/core', '*')) ? skip : test;

const nextRunloop = function () {
return new Promise((resolve) => {
Expand Down Expand Up @@ -241,7 +245,7 @@ module('Integration | Component | bs-form', function (hooks) {
assert.dom(`.${formFeedbackClass()}`).hasText('There is an error');
});

skip('it does not catch errors thrown by onSubmit action', async function (assert) {
testUnlessEmbroider('it does not catch errors thrown by onSubmit action', async function (assert) {
let onErrorStub = sinon.stub();
let expectedError = new Error();

Expand Down
53 changes: 53 additions & 0 deletions tests/integration/components/bs-modal/footer-test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { setComponentTemplate } from '@ember/component';
import templateOnly from '@ember/component/template-only';
import { module } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { click, render } from '@ember/test-helpers';
Expand Down Expand Up @@ -64,6 +66,57 @@ module('Integration | Component | bs-modal/footer', function (hooks) {
assert.true(defaultPrevented, 'Native form submission is prevented.');
});

module('native form submission', function (hooks) {
hooks.beforeEach(function () {
this.defaultPrevented = undefined;
this.listener = (event) => {
this.defaultPrevented = event.defaultPrevented;
// never let the test runner page navigate
event.preventDefault();
};
window.addEventListener('submit', this.listener);
});

hooks.afterEach(function () {
window.removeEventListener('submit', this.listener);
});

test('it is not prevented without onSubmit', async function (assert) {
await render(hbs`<BsModal::Footer @closeTitle="close" @submitTitle="submit" />`);
await click('.modal-footer button:last-child');

assert.false(this.defaultPrevented, 'Native form submission is not prevented.');
});

test('it is not prevented when the footer has an action attribute', async function (assert) {
let submitCount = 0;
this.set('submit', () => submitCount++);
await render(
hbs`<BsModal::Footer @closeTitle="close" @submitTitle="submit" @onSubmit={{this.submit}} action="/export" method="post" />`
);
await click('.modal-footer button:last-child');

assert.strictEqual(submitCount, 1, 'onSubmit is called once.');
assert.false(this.defaultPrevented, 'Native form submission is not prevented.');
});

test('a custom buttonComponent submits the footer', async function (assert) {
let submitCount = 0;
this.set('submit', () => submitCount++);
this.CustomButton = setComponentTemplate(
hbs`<button type="button" class="custom-button" ...attributes>{{yield}}</button>`,
templateOnly()
);
await render(
hbs`<BsModal::Footer @closeTitle="close" @submitTitle="submit" @onSubmit={{this.submit}} @buttonComponent={{this.CustomButton}} />`
);
await click('.modal-footer button:last-child');

assert.strictEqual(submitCount, 1, 'onSubmit is called once.');
assert.true(this.defaultPrevented, 'Native form submission is prevented.');
});
});

test('Footer can have a custom submitButtonType', async function (assert) {
await render(hbs`<BsModal::Footer @closeTitle="close" @submitTitle="submit" @submitButtonType="danger" />`);
assert.dom('.modal-footer button:last-child').hasClass('btn-danger', 'Submit button is a danger button.');
Expand Down
Loading
Loading