Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to Alert component to match the others #33402

Merged
merged 2 commits into from Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
69 changes: 32 additions & 37 deletions js/src/alert.js
Expand Up @@ -7,7 +7,8 @@

import {
defineJQueryPlugin,
getElementFromSelector
getElementFromSelector,
isDisabled
} from './util/index'
import EventHandler from './dom/event-handler'
import BaseComponent from './base-component'
Expand Down Expand Up @@ -48,38 +49,24 @@ class Alert extends BaseComponent {

// Public

close(element) {
const rootElement = element ? this._getRootElement(element) : this._element
const customEvent = this._triggerCloseEvent(rootElement)
close() {
const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)

if (customEvent === null || customEvent.defaultPrevented) {
if (closeEvent.defaultPrevented) {
return
}

this._removeElement(rootElement)
}

// Private

_getRootElement(element) {
return getElementFromSelector(element) || element.closest(`.${CLASS_NAME_ALERT}`)
}
this._element.classList.remove(CLASS_NAME_SHOW)

_triggerCloseEvent(element) {
return EventHandler.trigger(element, EVENT_CLOSE)
const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)
this._queueCallback(() => this._destroyElement(), this._element, isAnimated)
}

_removeElement(element) {
element.classList.remove(CLASS_NAME_SHOW)

const isAnimated = element.classList.contains(CLASS_NAME_FADE)
this._queueCallback(() => this._destroyElement(element), element, isAnimated)
}

_destroyElement(element) {
element.remove()

EventHandler.trigger(element, EVENT_CLOSED)
// Private
_destroyElement() {
this._element.remove()
EventHandler.trigger(this._element, EVENT_CLOSED)
this.dispose()
}

// Static
Expand All @@ -88,20 +75,16 @@ class Alert extends BaseComponent {
return this.each(function () {
const data = Alert.getOrCreateInstance(this)

if (config === 'close') {
data[config](this)
if (typeof config !== 'string') {
return
}
})
}

static handleDismiss(alertInstance) {
return function (event) {
if (event) {
event.preventDefault()
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}

alertInstance.close(this)
}
data[config](this)
})
}
}

Expand All @@ -111,7 +94,19 @@ class Alert extends BaseComponent {
* ------------------------------------------------------------------------
*/

EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDismiss(new Alert()))
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, function (event) {
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
}

if (isDisabled(this)) {
return
}
rohit2sharma95 marked this conversation as resolved.
Show resolved Hide resolved

const target = getElementFromSelector(this) || this.closest(`.${CLASS_NAME_ALERT}`)
const alert = Alert.getOrCreateInstance(target)
alert.close()
})

/**
* ------------------------------------------------------------------------
Expand Down
21 changes: 8 additions & 13 deletions js/tests/unit/alert.spec.js
Expand Up @@ -2,7 +2,7 @@ import Alert from '../../src/alert'
import { getTransitionDurationFromElement } from '../../src/util/index'

/** Test helpers */
import { getFixture, clearFixture, jQueryMock } from '../helpers/fixture'
import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'

describe('Alert', () => {
let fixtureEl
Expand Down Expand Up @@ -102,25 +102,20 @@ describe('Alert', () => {
it('should not remove alert if close event is prevented', done => {
fixtureEl.innerHTML = '<div class="alert"></div>'

const alertEl = document.querySelector('.alert')
const getAlert = () => document.querySelector('.alert')
const alertEl = getAlert()
const alert = new Alert(alertEl)

const endTest = () => {
alertEl.addEventListener('close.bs.alert', event => {
event.preventDefault()
setTimeout(() => {
expect(alert._removeElement).not.toHaveBeenCalled()
expect(getAlert()).not.toBeNull()
done()
}, 10)
}

spyOn(alert, '_removeElement')

alertEl.addEventListener('close.bs.alert', event => {
event.preventDefault()
endTest()
})

alertEl.addEventListener('closed.bs.alert', () => {
endTest()
throw new Error('should not fire closed event')
})

alert.close()
Expand Down Expand Up @@ -167,9 +162,9 @@ describe('Alert', () => {
jQueryMock.fn.alert = Alert.jQueryInterface
jQueryMock.elements = [alertEl]

expect(Alert.getInstance(alertEl)).toBeNull()
jQueryMock.fn.alert.call(jQueryMock, 'close')

expect(Alert.getInstance(alertEl)).not.toBeNull()
expect(fixtureEl.querySelector('.alert')).toBeNull()
})

Expand Down
32 changes: 18 additions & 14 deletions site/content/docs/5.0/components/alerts.md
Expand Up @@ -147,35 +147,39 @@ Loop that generates the modifier classes with the `alert-variant()` mixin.

## JavaScript behavior

### Triggers
### Initialize

Enable dismissal of an alert via JavaScript:
Initialize elements as alerts

```js
var alertList = document.querySelectorAll('.alert')
alertList.forEach(function (alert) {
new bootstrap.Alert(alert)
var alerts = [].slice.call(alertList).map(function (element) {
return new bootstrap.Alert(element)
})
```
{{< callout info >}}
For the sole purpose of dismissing an alert, it isn't necessary to initialize the component manually via the JS API. By making use of `data-bs-dismiss="alert"`, the component will be initialized automatically and properly dismissed.

Or with `data` attributes on a button **within the alert**, as demonstrated above:
See the [triggers](#triggers) section for more details.
{{< /callout >}}

### Triggers

Dismissal can be achieved with `data` attributes on a button **within the alert** as demonstrated above:

```html
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
```

Note that closing an alert will remove it from the DOM.

### Methods

You can create an alert instance with the alert constructor, for example:
or on a button **outside the alert** using the `data-bs-target` as demonstrated above:

```js
var myAlert = document.getElementById('myAlert')
var bsAlert = new bootstrap.Alert(myAlert)
```html
<button type="button" class="btn-close" data-bs-dismiss="alert" data-bs-target="#my-alert" aria-label="Close"></button>
```

This makes an alert listen for click events on descendant elements which have the `data-bs-dismiss="alert"` attribute. (Not necessary when using the data-api's auto-initialization.)
**Note that closing an alert will remove it from the DOM.**

### Methods

<table class="table">
<thead>
Expand Down