-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
Dismiss
+
+
+
+
+
+ Dismiss
+
+
\ No newline at end of file
diff --git a/src/lib/banner/banner.scss b/src/lib/banner/banner.scss
index cbbdfc2ee..3c8076de8 100644
--- a/src/lib/banner/banner.scss
+++ b/src/lib/banner/banner.scss
@@ -1,18 +1,118 @@
-@use './mixins';
+@use './core' as *;
+@use '../button';
+@use '../icon-button';
+@use '../core/styles/theme';
-@include mixins.core-styles;
+//
+// Host
+//
:host {
- display: block;
+ @include host;
}
:host([hidden]) {
display: none;
}
-@include mixins.default-theme();
-@include mixins.theme('danger');
-@include mixins.theme('warning');
-@include mixins.theme('success');
-@include mixins.theme('info-primary');
-@include mixins.theme('info-secondary');
+//
+// Base
+//
+
+.forge-banner {
+ @include tokens;
+}
+
+.forge-banner {
+ @include base;
+
+ .inner {
+ @include inner;
+ }
+
+ .container {
+ @include container;
+ }
+
+ forge-icon-button {
+ @include icon-button.provide-theme((
+ focus-indicator-color: #{token(color)}
+ ));
+ }
+}
+
+//
+// Slotted - button
+//
+
+::slotted(forge-button[slot=button]) {
+ @include button.provide-theme((
+ primary-color: #{token(color)}
+ ));
+}
+
+//
+// Dismissed
+//
+
+:host([dismissed]) {
+ .forge-banner {
+ @include dismissed;
+ }
+}
+
+//
+// Theme
+//
+
+@each $theme in [primary secondary tertiary success error warning] {
+ :host([theme=#{$theme}]) {
+ .forge-banner {
+ @include override(background, theme.variable(#{$theme}-container), value);
+ }
+ }
+}
+
+// Backwards compatibility with the new error theme
+:host([theme=danger]) {
+ .forge-banner {
+ @include override(background, theme.variable(error-container), value);
+ }
+}
+
+// Use surface theme for info-secondary
+:host([theme=info-secondary]) {
+ .forge-banner {
+ @include override(background, theme.variable(surface-container), value);
+ }
+}
+
+//
+// Responsive
+//
+
+@container (max-width: 600px) {
+ .forge-banner {
+ .container {
+ display: grid;
+ grid-template-rows: [content] 1fr [button] auto;
+ grid-template-columns: [content] 1fr;
+ place-items: normal;
+ }
+
+ &.has-icon {
+ .container {
+ grid-template-columns: [icon] auto [content] 1fr;
+ }
+ }
+
+ .inner {
+ place-items: normal;
+ }
+
+ .button-container {
+ grid-row: button;
+ grid-column: content;
+ }
+ }
+}
diff --git a/src/lib/banner/banner.test.ts b/src/lib/banner/banner.test.ts
new file mode 100644
index 000000000..1c34398a6
--- /dev/null
+++ b/src/lib/banner/banner.test.ts
@@ -0,0 +1,190 @@
+import { expect } from '@esm-bundle/chai';
+import { spy } from 'sinon';
+import { elementUpdated, fixture, html } from '@open-wc/testing';
+import { IBannerComponent } from './banner';
+import { BannerTheme, BANNER_CONSTANTS } from './banner-constants';
+
+import './banner';
+
+describe('Banner', () => {
+ it('should contain shadow root', async () => {
+ const el = await fixture
(html`Test`);
+ expect(el.shadowRoot).not.to.be.null;
+ });
+
+ it('should add and remove class when icon slot is populated', async () => {
+ const el = await fixture(html`iconTest`);
+ const iconEl = el.querySelector('[slot=icon]') as HTMLSpanElement;
+ const rootEl = el.shadowRoot?.querySelector('.forge-banner');
+
+ expect(rootEl?.classList.contains(BANNER_CONSTANTS.classes.HAS_ICON)).to.be.true;
+
+ iconEl.remove();
+ await elementUpdated(el);
+ expect(rootEl?.classList.contains(BANNER_CONSTANTS.classes.HAS_ICON)).to.be.false;
+
+ el.appendChild(iconEl);
+ await elementUpdated(el);
+ expect(rootEl?.classList.contains(BANNER_CONSTANTS.classes.HAS_ICON)).to.be.true;
+ });
+
+ describe('accessibility', () => {
+ it('should be accessible', async () => {
+ const el = await fixture(html`Test`);
+ await expect(el).to.be.accessible();
+ });
+
+ it('should be accessible in all theme colors', async () => {
+ const el = await fixture(html`Test`);
+
+ const themes: BannerTheme[] = ['primary', 'secondary', 'tertiary', 'success', 'error', 'warning', 'info', 'info-secondary'];
+ for (const theme of themes) {
+ el.theme = theme;
+ await elementUpdated(el);
+ await expect(el).to.be.accessible();
+ }
+ });
+ });
+
+ describe('dismissed', () => {
+ it('should set dismissed attribute when dismissed property is set', async () => {
+ const el = await fixture(html``);
+ el.dismissed = true;
+
+ expect(el.hasAttribute(BANNER_CONSTANTS.attributes.DISMISSED)).to.be.true;
+ expect(el.offsetHeight).to.equal(0);
+ });
+
+ it('should set dismissed property when dismissed attribute is set', async () => {
+ const el = await fixture(html``);
+
+ expect(el.dismissed).to.be.true;
+ expect(el.offsetHeight).to.equal(0);
+ });
+
+ it('should set inert attribute when dismissed', async () => {
+ const el = await fixture(html``);
+ const rootEl = el.shadowRoot?.querySelector('.forge-banner') as HTMLElement;
+ el.dismissed = true;
+
+ expect(rootEl.hasAttribute('inert')).to.be.true;
+ });
+ });
+
+ describe('dismissible/persistent', () => {
+ it('should be dismissible (not peristent) by default', async () => {
+ const el = await fixture(html``);
+
+ const dismissButton = el.shadowRoot?.querySelector(BANNER_CONSTANTS.selectors.DISMISS_BUTTON);
+ expect(el.persistent).to.be.false;
+ expect(dismissButton).not.to.be.null;
+ });
+
+ it('should hide dismiss button when persistent', async () => {
+ const el = await fixture(html``);
+ el.persistent = true;
+
+ const dismissButton = el.shadowRoot?.querySelector(BANNER_CONSTANTS.selectors.DISMISS_BUTTON) as HTMLElement;
+ expect(dismissButton.hidden).to.be.true;
+ });
+
+ it('should dispatch dismissed event when toggling persistent', async () => {
+ const el = await fixture(html``);
+ const dismissButton = el.shadowRoot?.querySelector(BANNER_CONSTANTS.selectors.DISMISS_BUTTON) as HTMLButtonElement;
+
+ const dismissSpy = spy();
+ el.addEventListener(BANNER_CONSTANTS.events.DISMISSED, dismissSpy);
+
+ el.persistent = true;
+ dismissButton.click();
+ expect(dismissButton.hidden).to.be.true;
+ expect(dismissSpy.calledOnce).to.be.false;
+
+ el.persistent = false;
+ dismissButton.click();
+ expect(dismissButton.hidden).to.be.false;
+ expect(dismissSpy.calledOnce).to.be.true;
+ });
+
+ it('should dispatch dismissed event when dismiss button is clicked', async () => {
+ const el = await fixture(html``);
+ const dismissButton = el.shadowRoot?.querySelector(BANNER_CONSTANTS.selectors.DISMISS_BUTTON) as HTMLButtonElement;
+
+ const dismissSpy = spy();
+ el.addEventListener(BANNER_CONSTANTS.events.DISMISSED, dismissSpy);
+
+ dismissButton.click();
+
+ expect(el.dismissed).to.be.true;
+ expect(dismissSpy.calledOnce).to.be.true;
+ });
+
+ it('should not dismiss when dismissed event is prevented', async () => {
+ const el = await fixture(html``);
+ const dismissButton = el.shadowRoot?.querySelector(BANNER_CONSTANTS.selectors.DISMISS_BUTTON) as HTMLButtonElement;
+
+ el.addEventListener(BANNER_CONSTANTS.events.DISMISSED, (evt: CustomEvent) => evt.preventDefault());
+
+ dismissButton.click();
+
+ expect(el.dismissed).to.be.false;
+ });
+
+ it('should not dispatch dismissed event when persistent', async () => {
+ const el = await fixture(html``);
+ const dismissButton = el.shadowRoot?.querySelector(BANNER_CONSTANTS.selectors.DISMISS_BUTTON) as HTMLButtonElement;
+
+ expect(dismissButton.hidden).to.be.true;
+
+ const dismissSpy = spy();
+ el.addEventListener(BANNER_CONSTANTS.events.DISMISSED, dismissSpy);
+
+ dismissButton.click();
+
+ expect(dismissSpy.called).to.be.false;
+ });
+ });
+
+ describe('theme', () => {
+ it('should set theme attribute when theme property is set', async () => {
+ const el = await fixture(html``);
+ el.theme = 'error';
+ expect(el.getAttribute(BANNER_CONSTANTS.attributes.THEME)).to.equal('error');
+ });
+
+ it('should set theme property when theme attribute is set', async () => {
+ const el = await fixture(html``);
+ expect(el.theme).to.equal('error');
+ });
+
+ it('should use default theme when set to null', async () => {
+ const el = await fixture(html``);
+
+ expect(el.theme).to.equal('error');
+
+ el.theme = null as any;
+
+ expect(el.theme).to.equal(BANNER_CONSTANTS.defaults.THEME);
+ });
+ });
+
+ describe('deprecated', () => {
+ it('should proxy deprecated can-dismiss attribute to persistent property', async () => {
+ const el = await fixture(html``);
+ expect(el.persistent).to.be.true;
+
+ el.setAttribute('can-dismiss', '');
+ expect(el.persistent).to.be.false;
+ });
+
+ it('should proxy deprecated canDismiss property to persistent property', async () => {
+ const el = await fixture(html``);
+
+ el.canDismiss = false;
+ expect(el.persistent).to.be.true;
+
+ el.canDismiss = true;
+ expect(el.persistent).to.be.false;
+ });
+ });
+});
diff --git a/src/lib/banner/banner.ts b/src/lib/banner/banner.ts
index e75ecb17a..fac871677 100644
--- a/src/lib/banner/banner.ts
+++ b/src/lib/banner/banner.ts
@@ -1,11 +1,11 @@
import { attachShadowTemplate, coerceBoolean, CustomElement, FoundationProperty } from '@tylertech/forge-core';
import { tylIconCancel } from '@tylertech/tyler-icons/standard';
import { BaseComponent, IBaseComponent } from '../core/base/base-component';
-import { IconComponent, IconRegistry } from '../icon';
+import { IconRegistry } from '../icon';
import { IconButtonComponent } from '../icon-button';
import { TooltipComponent } from '../tooltip';
import { BannerAdapter } from './banner-adapter';
-import { BANNER_CONSTANTS } from './banner-constants';
+import { BannerTheme, BANNER_CONSTANTS } from './banner-constants';
import { BannerFoundation } from './banner-foundation';
import template from './banner.html';
@@ -13,6 +13,9 @@ import styles from './banner.scss';
export interface IBannerComponent extends IBaseComponent {
dismissed: boolean;
+ persistent: boolean;
+ theme: BannerTheme;
+ /** @deprecated Use `persistent` instead. */
canDismiss: boolean;
}
@@ -23,29 +26,46 @@ declare global {
interface HTMLElementEventMap {
'forge-banner-dismissed': CustomEvent;
- 'forge-banner-undismissed': CustomEvent;
}
}
/**
- * The custom element class behind the `` element.
- *
* @tag forge-banner
+ *
+ * @summary Banners are used to inform users of important information, such as errors, warnings, or success messages.
+ *
+ * @property {boolean} dismissed - Controls the visibility of the banner.
+ * @property {boolean} persistent - Controls the visibility of the built-in dismiss button.
+ * @property {BannerTheme} theme - The theme of the banner.
+ *
+ * @attribute {boolean} dismissed - Controls the visibility of the banner.
+ * @attribute {boolean} persistent - Controls the visibility of the built-in dismiss button.
+ * @attribute {BannerTheme} theme - The theme of the banner.
+ *
+ * @event {CustomEvent} forge-banner-dismissed - Dispatched when the banner is dismissed.
+ *
+ * @cssproperty --forge-banner-background - The background color of the banner.
+ * @cssproperty --forge-banner-color - The text color of the banner.
+ * @cssproperty --forge-banner-gap - The gap between the contents.
+ * @cssproperty --forge-banner-padding-inline - The inline padding.
+ * @cssproperty --forge-banner-padding-block - The block padding.
+ * @cssproperty --forge-banner-transition-duration - The transition duration.
+ * @cssproperty --forge-banner-transition-easing - The transition easing function.
+ *
+ * @slot - The content of the banner.
+ * @slot icon - The icon to display.
+ * @slot button - The optional button to display.
*/
@CustomElement({
name: BANNER_CONSTANTS.elementName,
dependencies: [
IconButtonComponent,
- IconComponent,
TooltipComponent
]
})
export class BannerComponent extends BaseComponent implements IBannerComponent {
public static get observedAttributes(): string[] {
- return [
- BANNER_CONSTANTS.attributes.DISMISSED,
- BANNER_CONSTANTS.attributes.CAN_DISMISS
- ];
+ return Object.values(BANNER_CONSTANTS.observedAttributes);
}
protected _foundation: BannerFoundation;
@@ -58,29 +78,40 @@ export class BannerComponent extends BaseComponent implements IBannerComponent {
}
public connectedCallback(): void {
- this._foundation.connect();
- }
-
- public disconnectedCallback(): void {
- this._foundation.disconnect();
+ this._foundation.initialize();
}
public attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
switch (name) {
- case BANNER_CONSTANTS.attributes.DISMISSED:
+ case BANNER_CONSTANTS.observedAttributes.DISMISSED:
this.dismissed = coerceBoolean(newValue);
break;
- case BANNER_CONSTANTS.attributes.CAN_DISMISS:
- this.canDismiss = coerceBoolean(newValue);
+ case BANNER_CONSTANTS.observedAttributes.PERSISTENT:
+ this.persistent = coerceBoolean(newValue);
+ break;
+ case BANNER_CONSTANTS.observedAttributes.CAN_DISMISS:
+ this.persistent = coerceBoolean(newValue) === false;
+ break;
+ case BANNER_CONSTANTS.observedAttributes.THEME:
+ this.theme = newValue as BannerTheme;
break;
}
}
- /** Controls whether the component is dismissed (hidden) or not. */
@FoundationProperty()
public declare dismissed: boolean;
- /** Controls the visibility of the dismiss button. */
@FoundationProperty()
- public declare canDismiss: boolean;
+ public declare persistent: boolean;
+
+ @FoundationProperty()
+ public declare theme: BannerTheme;
+
+ /** @deprecated Use `persistent` instead. */
+ public get canDismiss(): boolean {
+ return this.persistent;
+ }
+ public set canDismiss(value: boolean) {
+ this.persistent = !value;
+ }
}
diff --git a/src/lib/banner/build.json b/src/lib/banner/build.json
deleted file mode 100644
index 07c79a465..000000000
--- a/src/lib/banner/build.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "$schema": "../../../node_modules/@tylertech/forge-cli/config/build-schema.json",
- "extends": "../build.json"
-}
\ No newline at end of file
diff --git a/src/lib/banner/index.scss b/src/lib/banner/index.scss
new file mode 100644
index 000000000..98a38c0d9
--- /dev/null
+++ b/src/lib/banner/index.scss
@@ -0,0 +1 @@
+@forward './core';
diff --git a/src/lib/core/styles/tokens/banner/_tokens.scss b/src/lib/core/styles/tokens/banner/_tokens.scss
new file mode 100644
index 000000000..014096994
--- /dev/null
+++ b/src/lib/core/styles/tokens/banner/_tokens.scss
@@ -0,0 +1,20 @@
+@use 'sass:map';
+@use '../../animation';
+@use '../../spacing';
+@use '../../theme';
+@use '../../border';
+@use '../../utils';
+
+$tokens: (
+ background: utils.module-val(banner, background, theme.variable(info-container)),
+ color: utils.module-val(banner, color, theme.variable(text-high)),
+ gap: utils.module-val(banner, gap, spacing.variable(small)),
+ padding-inline: utils.module-val(banner, padding-inline, spacing.variable(large)),
+ padding-block: utils.module-val(banner, padding-block, spacing.variable(small)),
+ transition-duration: utils.module-val(banner, transition-duration, animation.variable(duration-short4)),
+ transition-easing: utils.module-val(banner, transition-easing, animation.variable(easing-standard))
+) !default;
+
+@function get($key) {
+ @return map.get($tokens, $key);
+}
diff --git a/src/lib/theme/_theme-dark.scss b/src/lib/theme/_theme-dark.scss
index 3fd67c572..598264af6 100644
--- a/src/lib/theme/_theme-dark.scss
+++ b/src/lib/theme/_theme-dark.scss
@@ -10,10 +10,6 @@
@use '../skeleton/variables' as skeleton-variables;
@use '../toast/mixins' as toast-mixins;
@use '../toast/variables' as toast-variables;
-@use '../badge/mixins' as badge-mixins;
-@use '../badge/variables' as badge-variables;
-@use '../banner/mixins' as banner-mixins;
-@use '../banner/variables' as banner-variables;
@use '../table/mixins' as table-mixins;
@use '../table/variables' as table-variables;
@@ -40,6 +36,4 @@
@include skeleton-mixins.provide-theme(skeleton-variables.$theme-values-dark);
@include toast-mixins.provide-theme(toast-variables.$theme-values-dark);
@include table-mixins.provide-theme(table-variables.$theme-values-dark);
- @include badge-mixins.provide-theme(badge-variables.$theme-values-dark);
- @include banner-mixins.provide-theme(banner-variables.$theme-values-dark);
}
diff --git a/src/stories/src/components/banner/banner-args.ts b/src/stories/src/components/banner/banner-args.ts
index 4fecfd714..e8e7c1f3b 100644
--- a/src/stories/src/components/banner/banner-args.ts
+++ b/src/stories/src/components/banner/banner-args.ts
@@ -1,6 +1,6 @@
export interface IBannerProps {
dismissed: boolean;
- canDismiss: boolean;
+ persistent: boolean;
theme: string;
hasIcon: boolean;
hasButton: boolean;
@@ -13,7 +13,7 @@ export const argTypes = {
category: 'Properties'
},
},
- canDismiss: {
+ persistent: {
control: 'boolean',
table: {
category: 'Properties'
@@ -23,16 +23,18 @@ export const argTypes = {
control: {
type: 'select',
labels: {
- 'default': 'Default',
- 'danger': 'Danger',
- 'warning': 'Warning',
+ 'primary': 'Primary',
+ 'secondary': 'Secondary',
+ 'tertiary': 'Tertiary',
'success': 'Success',
- 'info-primary': 'Info primary',
+ 'error': 'Error',
+ 'warning': 'Warning',
+ 'info': 'Info (default)',
'info-secondary': 'Info secondary',
},
},
description: 'Use theme to change the color of the banner',
- options: ['default', 'danger', 'warning', 'success', 'info-primary', 'info-secondary'],
+ options: ['primary', 'secondary', 'tertiary', 'success', 'error', 'warning', 'info', 'info-secondary'],
table: {
category: 'Attributes'
},
diff --git a/src/stories/src/components/banner/banner.mdx b/src/stories/src/components/banner/banner.mdx
index b4c39c0fe..34bc5ec1f 100644
--- a/src/stories/src/components/banner/banner.mdx
+++ b/src/stories/src/components/banner/banner.mdx
@@ -68,9 +68,9 @@ Sets the banner to either the dismissed state or the undismissed state.
-
+
-Controls the visibility of the dismiss icon-button.
+Controls the visibility of the dismiss button.
diff --git a/src/stories/src/components/banner/banner.stories.tsx b/src/stories/src/components/banner/banner.stories.tsx
index 03a303a93..1eab8acbe 100644
--- a/src/stories/src/components/banner/banner.stories.tsx
+++ b/src/stories/src/components/banner/banner.stories.tsx
@@ -20,7 +20,7 @@ export default {
export const Default: Story = ({
dismissed = false,
- canDismiss = true,
+ persistent = false,
theme = 'default',
hasIcon = false,
hasButton = false
@@ -30,7 +30,7 @@ export const Default: Story = ({
}, []);
return (
-
+
{hasIcon && }
Minim sunt eu laborum labore minim.
{hasButton && Learn more...}
@@ -39,7 +39,7 @@ export const Default: Story = ({
};
Default.args = {
dismissed: false,
- canDismiss: true,
+ persistent: false,
theme: 'default',
hasIcon: true,
hasButton: false,
diff --git a/src/test/spec/banner/banner.fixture.html b/src/test/spec/banner/banner.fixture.html
deleted file mode 100644
index ad7968f90..000000000
--- a/src/test/spec/banner/banner.fixture.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- add_alert
- Laboris cillum dolore id incididunt consequat nisi nisi. Consequat cillum officia quis quis.
- Learn more...
-
-
\ No newline at end of file
diff --git a/src/test/spec/banner/banner.spec.ts b/src/test/spec/banner/banner.spec.ts
deleted file mode 100644
index bc9d3271c..000000000
--- a/src/test/spec/banner/banner.spec.ts
+++ /dev/null
@@ -1,187 +0,0 @@
-import { removeElement, getShadowElement } from '@tylertech/forge-core';
-import { defineBannerComponent, IBannerComponent, BANNER_CONSTANTS, BannerComponent, BUTTON_CONSTANTS, IButtonComponent } from '@tylertech/forge';
-import { tick } from '@tylertech/forge-testing';
-
-const DEFAULT_TEXT_CONTENT = 'Laboris cillum dolore id incididunt consequat nisi nisi. Consequat cillum officia quis quis.';
-
-interface ITestContext {
- context: ITestBannerContext;
-}
-
-interface ITestBannerContext {
- component: IBannerComponent;
- destroy(): void;
-}
-
-describe('BannerComponent', function(this: ITestContext) {
-
- let component: IBannerComponent;
-
- beforeAll(function(this: ITestContext) {
- defineBannerComponent();
- });
-
- beforeEach(function(this: ITestContext) {
- this.context = setupTestContext();
- });
-
- afterEach(function(this: ITestContext) {
- this.context.destroy();
- });
-
- describe('Instantiation', function(this: ITestContext) {
- it('should be connected', function(this: ITestContext) {
- expect(this.context.component.isConnected).toBe(true);
- });
-
- it('should instantiate component with shadow dom', function(this: ITestContext) {
- expect(this.context.component.shadowRoot).toBeDefined();
- });
-
- it('should instantiate component as BannerComponent', function(this: ITestContext) {
- expect(this.context.component instanceof BannerComponent).toBe(true, 'Component should be an instance of BannerComponent.');
- });
-
- it('should initially be in undismissed state by default', function(this: ITestContext) {
- expect(isDismissedClassApplied(this.context.component)).toBe(false, 'dismissed class should not be applied');
- expect(this.context.component.dismissed).toBe(false, 'dismissed property should be false');
- });
-
- it('should be shown by default', function(this: ITestContext) {
- expect(this.context.component.canDismiss === true).toBe(true, 'canDismiss property should be true');
- expect(dismissButtonIsVisible(this.context.component)).toBe(true, 'dismiss button should be visible');
- });
- });
-
- describe('API (Properties) - dismissed', function(this: ITestContext) {
- it('should set dismissed state when dismissed property is set to true', function(this: ITestContext) {
- this.context.component.dismissed = true;
- expect(isDismissedClassApplied(this.context.component)).toBe(true, 'dismissed class should be applied');
- expect(this.context.component.dismissed).toBe(true, 'dismissed property should be true');
- });
-
- it('should set undismissed state when dismissed property is set to false from true', async function(this: ITestContext) {
- this.context.component.dismissed = true;
- await tick();
-
- this.context.component.dismissed = false;
- expect(isDismissedClassApplied(this.context.component)).toBe(false, 'dismissed class should not be applied');
- expect(this.context.component.dismissed).toBe(false, 'dismissed property should be false');
- });
- });
-
- describe('API (Attributes) - dismissed', function(this: ITestContext) {
- it('should set dismissed state when dismissed attribute is set to true', function(this: ITestContext) {
- this.context.component.setAttribute(BANNER_CONSTANTS.attributes.DISMISSED, 'true');
- expect(isDismissedClassApplied(this.context.component)).toBe(true, 'dismissed class should be applied');
- expect(this.context.component.dismissed).toBe(true, 'dismissed property should be true');
- });
-
- it('should set undismissed state when dismissed attribute is set to true from false', async function(this: ITestContext) {
- this.context.component.setAttribute(BANNER_CONSTANTS.attributes.DISMISSED, 'true');
- await tick();
-
- this.context.component.setAttribute(BANNER_CONSTANTS.attributes.DISMISSED, 'false');
- expect(isDismissedClassApplied(this.context.component)).toBe(false, 'dismissed class should not be applied');
- expect(this.context.component.dismissed).toBe(false, 'dismissed property should be false');
- });
- });
-
- describe('API (Properties) - canDismiss', function(this: ITestContext) {
- it('should not be shown when canDismiss is false', function(this: ITestContext) {
- this.context.component.canDismiss = false;
- expect(this.context.component.canDismiss === false).toBe(true, 'canDismiss property should be false');
- expect(dismissButtonIsVisible(this.context.component)).toBe(false, 'dismiss button should not be visible');
- });
-
- it('should be shown when canDismiss flips from false to true', async function(this: ITestContext) {
- this.context.component.canDismiss = false;
- await tick();
- this.context.component.canDismiss = true;
- expect(this.context.component.canDismiss === true).toBe(true, 'canDismiss property should be true');
- expect(dismissButtonIsVisible(this.context.component)).toBe(true, 'dismiss button should be visible');
- });
- });
-
- describe('API (Attributes) - can-dismiss', function(this: ITestContext) {
- it('should not be shown when can-dismiss is false', function(this: ITestContext) {
- this.context.component.setAttribute(BANNER_CONSTANTS.attributes.CAN_DISMISS, 'false');
- expect(this.context.component.canDismiss === false).toBe(true, 'canDismiss property should be false');
- expect(dismissButtonIsVisible(this.context.component)).toBe(false, 'dismiss button should not be visible');
- });
-
- it('should be shown when can-dismiss flips from false to true', async function(this: ITestContext) {
- this.context.component.setAttribute(BANNER_CONSTANTS.attributes.CAN_DISMISS, 'false');
- await tick();
- this.context.component.setAttribute(BANNER_CONSTANTS.attributes.CAN_DISMISS, 'true');
- expect(this.context.component.canDismiss === true).toBe(true, 'canDismiss property should be true');
- expect(dismissButtonIsVisible(this.context.component)).toBe(true, 'dismiss button should be visible');
- });
- });
-
- describe('Events', function(this: ITestContext) {
- it('should emit dismissed event when banner is dismissed', async function(this: ITestContext) {
- this.context.component.dismissed = false;
- const dismissedSpy = jasmine.createSpy('dismissed event');
- this.context.component.addEventListener(BANNER_CONSTANTS.events.DISMISSED, dismissedSpy)
- this.context.component.dismissed = true;
- await tick();
- expect(dismissedSpy).toHaveBeenCalled();
- });
-
- it('should emit undismissed event when banner is undismissed', async function(this: ITestContext) {
- this.context.component.dismissed = true;
- const undismissedSpy = jasmine.createSpy('undismissed event');
- this.context.component.addEventListener(BANNER_CONSTANTS.events.UNDISMISSED, undismissedSpy)
- this.context.component.dismissed = false;
- await tick();
- expect(undismissedSpy).toHaveBeenCalled();
- });
- });
-
- describe('Internal dismiss', function(this: ITestContext) {
- it('should emit dismiss event when banner is dismissed from internal icon button', function(this: ITestContext) {
- this.context.component.dismissed = false;
- const dismissBtn = getShadowElement(this.context.component, BANNER_CONSTANTS.selectors.DISMISS_BUTTON);
- dismissBtn.click();
- expect(isDismissedClassApplied(this.context.component)).toBe(true, 'dismissed class should be applied');
- expect(this.context.component.dismissed).toBe(true, 'dismissed should be true');
- });
- });
-
- function setupTestContext(): ITestBannerContext {
- const fixture = document.createElement('div');
- fixture.id = 'banner-test-fixture';
- const component = document.createElement(BANNER_CONSTANTS.elementName) as IBannerComponent;
- const icon = document.createElement('i') as HTMLElement;
- icon.textContent = 'add_alert';
- icon.classList.add('tyler-icons');
- component.appendChild(icon);
- const text = document.createElement('div') as HTMLElement;
- text.slot = 'text';
- text.textContent = DEFAULT_TEXT_CONTENT;
- component.appendChild(text);
- const buttonComponent = document.createElement(BUTTON_CONSTANTS.elementName) as IButtonComponent;
- buttonComponent.setAttribute('variant', 'outlined');
- buttonComponent.type = 'button';
- buttonComponent.textContent = 'Learn more...';
- component.appendChild(buttonComponent);
- fixture.appendChild(component);
- document.body.appendChild(fixture);
- return {
- component,
- destroy: () => removeElement(fixture)
- };
- }
-
- function isDismissedClassApplied(component: IBannerComponent): boolean {
- const rootElement = getShadowElement(component, BANNER_CONSTANTS.selectors.BANNER);
- return rootElement.classList.contains(BANNER_CONSTANTS.classes.DISMISSED);
-
- }
-
- function dismissButtonIsVisible(component: IBannerComponent): boolean {
- const dismissBtn = getShadowElement(component, BANNER_CONSTANTS.selectors.FORGE_DISMISS_BUTTON);
- return !dismissBtn.hasAttribute('hidden');
- }
-});