Commit 321b10a0 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch 'dismissible-banners' into 'master'

Expands the alert dismisser to dismiss banners

See merge request gitlab-org/gitlab!42110
parents ee94ad6b 971a2879
// This allows us to dismiss alerts that we've migrated from bootstrap // This allows us to dismiss alerts and banners that we've migrated from bootstrap
// Note: This ONLY works on alerts that are created on page load // Note: This ONLY works on elements that are created on page load
// You can follow this effort in the following epic // You can follow this effort in the following epic
// https://gitlab.com/groups/gitlab-org/-/epics/4070 // https://gitlab.com/groups/gitlab-org/-/epics/4070
export default function initAlertHandler() { export default function initAlertHandler() {
const ALERT_SELECTOR = '.gl-alert'; const DISMISSIBLE_SELECTORS = ['.gl-alert', '.gl-banner'];
const CLOSE_SELECTOR = '.gl-alert-dismiss'; const DISMISS_LABEL = '[aria-label="Dismiss"]';
const DISMISS_CLASS = '.gl-alert-dismiss';
const dismissAlert = ({ target }) => target.closest(ALERT_SELECTOR).remove(); DISMISSIBLE_SELECTORS.forEach(selector => {
const closeButtons = document.querySelectorAll(`${ALERT_SELECTOR} ${CLOSE_SELECTOR}`); const elements = document.querySelectorAll(selector);
closeButtons.forEach(alert => alert.addEventListener('click', dismissAlert)); elements.forEach(element => {
const button = element.querySelector(DISMISS_LABEL) || element.querySelector(DISMISS_CLASS);
if (!button) {
return;
}
button.addEventListener('click', () => element.remove());
});
});
} }
...@@ -2,18 +2,26 @@ import { setHTMLFixture } from 'helpers/fixtures'; ...@@ -2,18 +2,26 @@ import { setHTMLFixture } from 'helpers/fixtures';
import initAlertHandler from '~/alert_handler'; import initAlertHandler from '~/alert_handler';
describe('Alert Handler', () => { describe('Alert Handler', () => {
const ALERT_SELECTOR = 'gl-alert'; const ALERT_CLASS = 'gl-alert';
const CLOSE_SELECTOR = 'gl-alert-dismiss'; const BANNER_CLASS = 'gl-banner';
const ALERT_HTML = `<div class="${ALERT_SELECTOR}"><button class="${CLOSE_SELECTOR}">Dismiss</button></div>`; const DISMISS_CLASS = 'gl-alert-dismiss';
const DISMISS_LABEL = 'Dismiss';
const findFirstAlert = () => document.querySelector(`.${ALERT_SELECTOR}`); const generateHtml = parentClass =>
const findAllAlerts = () => document.querySelectorAll(`.${ALERT_SELECTOR}`); `<div class="${parentClass}">
const findFirstCloseButton = () => document.querySelector(`.${CLOSE_SELECTOR}`); <button aria-label="${DISMISS_LABEL}">Dismiss</button>
</div>`;
const findFirstAlert = () => document.querySelector(`.${ALERT_CLASS}`);
const findFirstBanner = () => document.querySelector(`.${BANNER_CLASS}`);
const findAllAlerts = () => document.querySelectorAll(`.${ALERT_CLASS}`);
const findFirstDismissButton = () => document.querySelector(`[aria-label="${DISMISS_LABEL}"]`);
const findFirstDismissButtonByClass = () => document.querySelector(`.${DISMISS_CLASS}`);
describe('initAlertHandler', () => { describe('initAlertHandler', () => {
describe('with one alert', () => { describe('with one alert', () => {
beforeEach(() => { beforeEach(() => {
setHTMLFixture(ALERT_HTML); setHTMLFixture(generateHtml(ALERT_CLASS));
initAlertHandler(); initAlertHandler();
}); });
...@@ -22,14 +30,14 @@ describe('Alert Handler', () => { ...@@ -22,14 +30,14 @@ describe('Alert Handler', () => {
}); });
it('should dismiss the alert on click', () => { it('should dismiss the alert on click', () => {
findFirstCloseButton().click(); findFirstDismissButton().click();
expect(findFirstAlert()).not.toExist(); expect(findFirstAlert()).not.toExist();
}); });
}); });
describe('with two alerts', () => { describe('with two alerts', () => {
beforeEach(() => { beforeEach(() => {
setHTMLFixture(ALERT_HTML + ALERT_HTML); setHTMLFixture(generateHtml(ALERT_CLASS) + generateHtml(ALERT_CLASS));
initAlertHandler(); initAlertHandler();
}); });
...@@ -38,9 +46,46 @@ describe('Alert Handler', () => { ...@@ -38,9 +46,46 @@ describe('Alert Handler', () => {
}); });
it('should dismiss only one alert on click', () => { it('should dismiss only one alert on click', () => {
findFirstCloseButton().click(); findFirstDismissButton().click();
expect(findAllAlerts()).toHaveLength(1); expect(findAllAlerts()).toHaveLength(1);
}); });
}); });
describe('with a dismissible banner', () => {
beforeEach(() => {
setHTMLFixture(generateHtml(BANNER_CLASS));
initAlertHandler();
});
it('should render the banner', () => {
expect(findFirstBanner()).toExist();
});
it('should dismiss the banner on click', () => {
findFirstDismissButton().click();
expect(findFirstBanner()).not.toExist();
});
});
// Dismiss buttons *should* have the correct aria labels, but some of them won't
// because legacy code isn't always a11y compliant.
// This tests that the fallback for the incorrectly labelled buttons works.
describe('with a mislabelled dismiss button', () => {
beforeEach(() => {
setHTMLFixture(`<div class="${ALERT_CLASS}">
<button class="${DISMISS_CLASS}">Dismiss</button>
</div>`);
initAlertHandler();
});
it('should render the banner', () => {
expect(findFirstAlert()).toExist();
});
it('should dismiss the banner on click', () => {
findFirstDismissButtonByClass().click();
expect(findFirstAlert()).not.toExist();
});
});
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment