@hashicorp/design-system-components
Version:
Helios Design System Components
146 lines (143 loc) • 6.2 kB
JavaScript
import Component from '@glimmer/component';
import { assert } from '@ember/debug';
import { guidFor } from '@ember/object/internals';
import { tracked } from '@glimmer/tracking';
import { eq } from 'ember-truth-helpers';
import { hash } from '@ember/helper';
import { on } from '@ember/modifier';
import didInsert from '@ember/render-modifiers/modifiers/did-insert';
import { HdsAlertColorValues, HdsAlertTypeValues } from './types.js';
import HdsDismissButton from '../dismiss-button/index.js';
import HdsIcon from '../icon/index.js';
import HdsAlertTitle from './title.js';
import HdsAlertDescription from './description.js';
import HdsButton from '../button/index.js';
import HdsLinkStandalone from '../link/standalone.js';
import HdsYield from '../yield/index.js';
import { precompileTemplate } from '@ember/template-compilation';
import { setComponentTemplate } from '@ember/component';
import { g, i } from 'decorator-transforms/runtime';
/**
* Copyright IBM Corp. 2021, 2025
* SPDX-License-Identifier: MPL-2.0
*/
const TYPES = Object.values(HdsAlertTypeValues);
const DEFAULT_COLOR = HdsAlertColorValues.Neutral;
const COLORS = Object.values(HdsAlertColorValues);
const MAPPING_COLORS_TO_ICONS = {
[HdsAlertColorValues.Neutral]: 'info',
[HdsAlertColorValues.Highlight]: 'info',
[HdsAlertColorValues.Success]: 'check-circle',
[HdsAlertColorValues.Warning]: 'alert-triangle',
[HdsAlertColorValues.Critical]: 'alert-diamond'
};
const CONTENT_ELEMENT_SELECTOR = '.hds-alert__content';
const TITLE_ELEMENT_SELECTOR = '.hds-alert__title';
const DESCRIPTION_ELEMENT_SELECTOR = '.hds-alert__description';
class HdsAlert extends Component {
static {
g(this.prototype, "_role", [tracked]);
}
#_role = (i(this, "_role"), void 0);
static {
g(this.prototype, "_ariaLabelledBy", [tracked]);
}
#_ariaLabelledBy = (i(this, "_ariaLabelledBy"), void 0);
constructor(owner, args) {
super(owner, args);
assert(` for "Hds::Alert" must be one of the following: ${TYPES.join(', ')}; received: ${this.args.type}`, TYPES.includes(this.args.type));
}
get color() {
const {
color = DEFAULT_COLOR
} = this.args;
assert(` for "Hds::Alert" must be one of the following: ${COLORS.join(', ')}; received: ${color}`, COLORS.includes(color));
return color;
}
get icon() {
const {
icon
} = this.args;
// If `icon` isn't passed, use the pre-defined one from `color`
if (icon === undefined) {
if (this.args.type === 'compact') {
// for the "compact" type by default we use filled icons
return `${MAPPING_COLORS_TO_ICONS[this.color]}-fill`;
} else {
// for all the other types by default we use outlined icons
return MAPPING_COLORS_TO_ICONS[this.color];
}
// If `icon` is set explicitly to false, user doesn't want any icon in the alert
} else if (icon === false) {
assert(` for "Hds::Alert" with "compact" is required`, this.args.type !== 'compact');
return false;
} else {
// If a name for `icon` is passed, set HdsIcon to that name
return icon;
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
get onDismiss() {
const {
onDismiss
} = this.args;
if (typeof onDismiss === 'function') {
return onDismiss;
} else {
return false;
}
}
get iconSize() {
if (this.args.type === 'compact') {
return '16';
} else {
return '24';
}
}
get classNames() {
const classes = ['hds-alert'];
// Add a class based on the @type argument
classes.push(`hds-alert--type-${this.args.type}`);
// Add a class based on the @color argument
classes.push(`hds-alert--color-${this.color}`);
return classes.join(' ');
}
didInsert = element => {
const actions = element.querySelectorAll(`${CONTENT_ELEMENT_SELECTOR} button, ${CONTENT_ELEMENT_SELECTOR} a`);
// an Alert which actually alerts users (has role="alert" & aria-live="polite") as opposed to an informational or promo "alert"
const isSemanticAlert = this.color === 'warning' || this.color === 'critical' || this.color === 'success';
if (isSemanticAlert && actions.length) {
this._role = 'alertdialog';
} else if (isSemanticAlert) {
this._role = 'alert';
}
// `alertdialog` must have an accessible name so we use either the
// title or the description as label for the alert
const label = element.querySelector(TITLE_ELEMENT_SELECTOR) || element.querySelector(DESCRIPTION_ELEMENT_SELECTOR);
if (label) {
const labelId = label.getAttribute('id') || guidFor(element);
label.setAttribute('id', labelId);
this._ariaLabelledBy = labelId;
}
};
static {
setComponentTemplate(precompileTemplate("<div class={{this.classNames}} role={{this._role}} aria-live={{if this._role \"polite\"}} aria-labelledby={{this._ariaLabelledBy}} {{didInsert this.didInsert}} ...attributes>\n {{#if this.icon}}\n <div class=\"hds-alert__icon\">\n <HdsIcon @name={{this.icon}} @size={{this.iconSize}} @stretched={{true}} />\n </div>\n {{/if}}\n\n <div class=\"hds-alert__content\">\n <div class=\"hds-alert__text\n {{if (eq @type \"compact\") \"hds-typography-body-100\" \"hds-typography-body-200\"}}\">\n {{yield (hash Title=HdsAlertTitle)}}\n {{yield (hash Description=HdsAlertDescription)}}\n </div>\n\n <div class=\"hds-alert__actions\">\n {{yield (hash Button=(component HdsButton size=\"small\") LinkStandalone=(component HdsLinkStandalone size=\"small\"))}}\n </div>\n {{yield (hash Generic=HdsYield)}}\n </div>\n\n {{#if this.onDismiss}}\n <HdsDismissButton class=\"hds-alert__dismiss\" {{on \"click\" this.onDismiss}} />\n {{/if}}\n</div>", {
strictMode: true,
scope: () => ({
didInsert,
HdsIcon,
eq,
hash,
HdsAlertTitle,
HdsAlertDescription,
HdsButton,
HdsLinkStandalone,
HdsYield,
HdsDismissButton,
on
})
}), this);
}
}
export { COLORS, DEFAULT_COLOR, MAPPING_COLORS_TO_ICONS, TYPES, HdsAlert as default };
//# sourceMappingURL=index.js.map