From a14641514650aee2e0db2e3254c66b2ba044ce59 Mon Sep 17 00:00:00 2001 From: LunganC <98550729+Cata1989@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:29:05 +0200 Subject: [PATCH] feat(Alert): add new `bq-alert` component (#675) --- packages/beeq/src/components.d.ts | 103 +++++++ .../alert/__tests__/bq-alert.e2e.ts | 135 ++++++++++ .../components/alert/_storybook/bq-alert.mdx | 29 ++ .../alert/_storybook/bq-alert.stories.tsx | 157 +++++++++++ .../beeq/src/components/alert/bq-alert.tsx | 254 ++++++++++++++++++ .../src/components/alert/bq-alert.types.ts | 2 + packages/beeq/src/components/alert/readme.md | 87 ++++++ .../src/components/alert/scss/bq-alert.scss | 85 ++++++ .../alert/scss/bq-alert.variables.scss | 67 +++++ packages/beeq/src/components/button/readme.md | 2 + packages/beeq/src/components/icon/readme.md | 2 + 11 files changed, 923 insertions(+) create mode 100644 packages/beeq/src/components/alert/__tests__/bq-alert.e2e.ts create mode 100644 packages/beeq/src/components/alert/_storybook/bq-alert.mdx create mode 100644 packages/beeq/src/components/alert/_storybook/bq-alert.stories.tsx create mode 100644 packages/beeq/src/components/alert/bq-alert.tsx create mode 100644 packages/beeq/src/components/alert/bq-alert.types.ts create mode 100644 packages/beeq/src/components/alert/readme.md create mode 100644 packages/beeq/src/components/alert/scss/bq-alert.scss create mode 100644 packages/beeq/src/components/alert/scss/bq-alert.variables.scss diff --git a/packages/beeq/src/components.d.ts b/packages/beeq/src/components.d.ts index 265084ba1..71a18a7bd 100644 --- a/packages/beeq/src/components.d.ts +++ b/packages/beeq/src/components.d.ts @@ -5,6 +5,7 @@ * It contains typing information for all components that exist in this project. */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; +import { TAlertType } from "./components/alert/bq-alert.types"; import { TAvatarShape, TAvatarSize } from "./components/avatar/bq-avatar.types"; import { TBadgeSize } from "./components/badge/bq-badge.types"; import { TButtonAppearance, TButtonSize, TButtonType, TButtonVariant } from "./components/button/bq-button.types"; @@ -25,6 +26,7 @@ import { TSwitchInnerLabel, TSwitchJustifyContent } from "./components/switch/bq import { TTabSize } from "./components/tab/bq-tab.types"; import { TTextareaAutoCapitalize, TTextareaWrap } from "./components/textarea/bq-textarea.types"; import { TToastPlacement, TToastType } from "./components/toast/bq-toast.types"; +export { TAlertType } from "./components/alert/bq-alert.types"; export { TAvatarShape, TAvatarSize } from "./components/avatar/bq-avatar.types"; export { TBadgeSize } from "./components/badge/bq-badge.types"; export { TButtonAppearance, TButtonSize, TButtonType, TButtonVariant } from "./components/button/bq-button.types"; @@ -46,6 +48,44 @@ export { TTabSize } from "./components/tab/bq-tab.types"; export { TTextareaAutoCapitalize, TTextareaWrap } from "./components/textarea/bq-textarea.types"; export { TToastPlacement, TToastType } from "./components/toast/bq-toast.types"; export namespace Components { + interface BqAlert { + /** + * If true, the alert will automatically hide after the specified amount of time + */ + "autoDismiss": boolean; + /** + * If true, the close button at the top right of the alert won't be shown + */ + "disableClose": boolean; + /** + * Method to be called to hide the alert component + */ + "hide": () => Promise; + /** + * If true, the alert icon won't be shown + */ + "hideIcon": boolean; + /** + * If true, the alert will be shown + */ + "open": boolean; + /** + * Method to be called to show the alert component + */ + "show": () => Promise; + /** + * If true, the alert component will remain fixed at the top of the page, occupying the full viewport + */ + "sticky": boolean; + /** + * The length of time, in milliseconds, after which the alert will close itself. Only valid if `autoDismiss="true"` + */ + "time": number; + /** + * Type of Alert + */ + "type": TAlertType; + } interface BqAvatar { /** * Alternate text for the avatar image if the image cannot be displayed @@ -1060,6 +1100,10 @@ export namespace Components { "visible"?: boolean; } } +export interface BqAlertCustomEvent extends CustomEvent { + detail: T; + target: HTMLBqAlertElement; +} export interface BqBreadcrumbCustomEvent extends CustomEvent { detail: T; target: HTMLBqBreadcrumbElement; @@ -1153,6 +1197,24 @@ export interface BqToastCustomEvent extends CustomEvent { target: HTMLBqToastElement; } declare global { + interface HTMLBqAlertElementEventMap { + "bqHide": any; + "bqShow": any; + } + interface HTMLBqAlertElement extends Components.BqAlert, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLBqAlertElement, ev: BqAlertCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLBqAlertElement, ev: BqAlertCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLBqAlertElement: { + prototype: HTMLBqAlertElement; + new (): HTMLBqAlertElement; + }; interface HTMLBqAvatarElement extends Components.BqAvatar, HTMLStencilElement { } var HTMLBqAvatarElement: { @@ -1653,6 +1715,7 @@ declare global { new (): HTMLBqTooltipElement; }; interface HTMLElementTagNameMap { + "bq-alert": HTMLBqAlertElement; "bq-avatar": HTMLBqAvatarElement; "bq-badge": HTMLBqBadgeElement; "bq-breadcrumb": HTMLBqBreadcrumbElement; @@ -1688,6 +1751,44 @@ declare global { } } declare namespace LocalJSX { + interface BqAlert { + /** + * If true, the alert will automatically hide after the specified amount of time + */ + "autoDismiss"?: boolean; + /** + * If true, the close button at the top right of the alert won't be shown + */ + "disableClose"?: boolean; + /** + * If true, the alert icon won't be shown + */ + "hideIcon"?: boolean; + /** + * Callback handler to be called when the notification is hidden + */ + "onBqHide"?: (event: BqAlertCustomEvent) => void; + /** + * Callback handler to be called when the notification is shown + */ + "onBqShow"?: (event: BqAlertCustomEvent) => void; + /** + * If true, the alert will be shown + */ + "open"?: boolean; + /** + * If true, the alert component will remain fixed at the top of the page, occupying the full viewport + */ + "sticky"?: boolean; + /** + * The length of time, in milliseconds, after which the alert will close itself. Only valid if `autoDismiss="true"` + */ + "time"?: number; + /** + * Type of Alert + */ + "type"?: TAlertType; + } interface BqAvatar { /** * Alternate text for the avatar image if the image cannot be displayed @@ -2855,6 +2956,7 @@ declare namespace LocalJSX { "visible"?: boolean; } interface IntrinsicElements { + "bq-alert": BqAlert; "bq-avatar": BqAvatar; "bq-badge": BqBadge; "bq-breadcrumb": BqBreadcrumb; @@ -2893,6 +2995,7 @@ export { LocalJSX as JSX }; declare module "@stencil/core" { export namespace JSX { interface IntrinsicElements { + "bq-alert": LocalJSX.BqAlert & JSXBase.HTMLAttributes; "bq-avatar": LocalJSX.BqAvatar & JSXBase.HTMLAttributes; "bq-badge": LocalJSX.BqBadge & JSXBase.HTMLAttributes; "bq-breadcrumb": LocalJSX.BqBreadcrumb & JSXBase.HTMLAttributes; diff --git a/packages/beeq/src/components/alert/__tests__/bq-alert.e2e.ts b/packages/beeq/src/components/alert/__tests__/bq-alert.e2e.ts new file mode 100644 index 000000000..7cb388247 --- /dev/null +++ b/packages/beeq/src/components/alert/__tests__/bq-alert.e2e.ts @@ -0,0 +1,135 @@ +import { newE2EPage } from '@stencil/core/testing'; + +describe('bq-alert', () => { + it('should render', async () => { + const page = await newE2EPage({ + html: ``, + }); + + const element = await page.find('bq-alert'); + expect(element).toHaveClass('hydrated'); + }); + + it('should have shadow root', async () => { + const page = await newE2EPage({ + html: ``, + }); + + const element = await page.find('bq-alert'); + expect(element.shadowRoot).not.toBeNull(); + }); + + it('should render as hidden', async () => { + const page = await newE2EPage({ + html: ``, + }); + + const element = await page.find('bq-alert'); + expect(element).toEqualAttribute('aria-hidden', 'true'); + expect(element).toHaveClass('is-hidden'); + }); + + it('should render as hidden with `open="false"`', async () => { + const page = await newE2EPage({ + html: ``, + }); + + const element = await page.find('bq-alert'); + expect(element).toEqualAttribute('aria-hidden', 'true'); + expect(element).toHaveClass('is-hidden'); + }); + + it('should render as open', async () => { + const page = await newE2EPage({ + html: ``, + }); + + const element = await page.find('bq-alert'); + expect(element).not.toEqualAttribute('aria-hidden', 'true'); + expect(element).not.toHaveClass('is-hidden'); + }); + + it('should render as open with `open="true"`', async () => { + const page = await newE2EPage({ + html: ``, + }); + + const element = await page.find('bq-alert'); + expect(element).not.toEqualAttribute('aria-hidden', 'true'); + expect(element).not.toHaveClass('is-hidden'); + }); + + it('should render basic alert', async () => { + const page = await newE2EPage({ + html: ` + + Alert title + You have a new alert message + + `, + }); + + const description = await page.find('bq-alert >>> slot[name="body"]'); + expect(description).not.toBeNull(); + }); + + it('should show alert with icon', async () => { + const page = await newE2EPage({ + html: `Alert title`, + }); + + const iconHolder = await page.find('bq-alert >>> [part="icon-outline"]'); + expect(iconHolder).not.toBeNull(); + }); + + it('should show alert with close button', async () => { + const page = await newE2EPage({ + html: `Alert title`, + }); + + const iconHolder = await page.find('bq-alert >>> [part="btn-close"]'); + expect(iconHolder).not.toBeNull(); + }); + + it('should show alert footer', async () => { + const page = await newE2EPage({ + html: ` + + Alert title +
+ Button + Button +
+
+ `, + }); + + const footerSlot = await page.find('bq-alert >>> slot[name="footer"]'); + expect(footerSlot).not.toBeNull(); + }); + + it('should call methods', async () => { + const page = await newE2EPage({ + html: ` + + Alert title + You have a new alert message + + `, + }); + + const element = await page.find('bq-alert'); + + await element.callMethod('show'); + await page.waitForChanges(); + + expect(element).toEqualAttribute('aria-hidden', 'false'); + expect(element).toEqualAttribute('hidden', 'false'); + + await element.callMethod('hide'); + await page.waitForChanges(); + + expect(element).toEqualAttribute('aria-hidden', 'true'); + expect(element).toEqualAttribute('hidden', 'true'); + }); +}); diff --git a/packages/beeq/src/components/alert/_storybook/bq-alert.mdx b/packages/beeq/src/components/alert/_storybook/bq-alert.mdx new file mode 100644 index 000000000..324cb2eb9 --- /dev/null +++ b/packages/beeq/src/components/alert/_storybook/bq-alert.mdx @@ -0,0 +1,29 @@ +import { ArgTypes, Title, Subtitle } from '@storybook/addon-docs'; + +
+
+ Alert + + The Alert is a user interface component used to convey important information to the user in a clear and concise manner. + It can be used to notify users of success, failure, warning, or any other type of information that needs to be brought to their attention. + + Usage + + - Use alerts to inform users about important events or actions such as successful login, account creation, and password reset. + - Use alerts to inform users about errors or warnings such as incorrect login credentials, network errors, or session timeouts. + - Use alerts to provide users with feedback about their actions such as a successful submission of a form or a message that a file has been uploaded successfully. + - Use alerts to inform users about system status such as maintenance downtime or system upgrades. + + 👍 When to use + + - When to communicate critical updates such as important software patches, system maintenance, or urgent changes affecting the user experience. + - When to to provide real-time feedback on authentication status, including successful logins, failed login attempts, or session expirations. + - When to enhance user experience by utilizing the Alert component to confirm successful data submissions or to notify users about any errors encountered during the submission process. + - When to keep users informed about any changes in their permissions or access levels by leveraging the Alert component to deliver relevant and timely notifications. + - When to improve user engagement and timely actions by using the Alert component to remind users of upcoming events, deadlines, or other time-sensitive tasks. + + Properties + + +
+
diff --git a/packages/beeq/src/components/alert/_storybook/bq-alert.stories.tsx b/packages/beeq/src/components/alert/_storybook/bq-alert.stories.tsx new file mode 100644 index 000000000..885d87052 --- /dev/null +++ b/packages/beeq/src/components/alert/_storybook/bq-alert.stories.tsx @@ -0,0 +1,157 @@ +import type { Args, Meta, StoryObj } from '@storybook/web-components'; +import { html, nothing } from 'lit-html'; + +import mdx from './bq-alert.mdx'; +import { ALERT_TYPE } from '../bq-alert.types'; + +const meta: Meta = { + title: 'Components/Alert', + component: 'bq-alert', + parameters: { + docs: { + page: mdx, + }, + }, + argTypes: { + 'auto-dismiss': { control: 'boolean' }, + 'disable-close': { control: 'boolean' }, + 'hide-icon': { control: 'boolean' }, + sticky: { control: 'boolean' }, + open: { control: 'boolean' }, + time: { control: 'number' }, + type: { control: 'select', options: [...ALERT_TYPE] }, + }, + args: { + 'auto-dismiss': false, + 'disable-close': false, + 'hide-icon': false, + sticky: false, + open: false, + time: 3000, + type: 'default', + }, +}; +export default meta; + +type Story = StoryObj; + +const Template = (args: Args) => html` +
+ + ${args.type === 'default' ? html`` : nothing} Title + + + + ${args.type === 'default' ? html`` : nothing} Title + + Description + Link + + + + + ${args.type === 'default' ? html`` : nothing} Title + ${!args.sticky + ? html` + + Description + Link + +
+ Button + Button +
+ ` + : nothing} +
+
+`; + +const TemplateSticky = (args: Args) => html` + + ${args.type === 'default' ? html`` : nothing} Title + Button + +
+

Dashboard

+
+
+`; + +export const Default: Story = { + render: Template, + args: { + open: true, + }, +}; + +export const Info: Story = { + render: Template, + args: { + open: true, + type: 'info', + }, +}; + +export const Success: Story = { + render: Template, + args: { + open: true, + type: 'success', + }, +}; + +export const Warning: Story = { + render: Template, + args: { + open: true, + type: 'warning', + }, +}; + +export const ErrorType: Story = { + name: 'Error', + render: Template, + args: { + open: true, + type: 'error', + }, +}; + +export const Sticky: Story = { + render: TemplateSticky, + args: { + open: true, + sticky: true, + type: 'error', + }, +}; diff --git a/packages/beeq/src/components/alert/bq-alert.tsx b/packages/beeq/src/components/alert/bq-alert.tsx new file mode 100644 index 000000000..7963650b1 --- /dev/null +++ b/packages/beeq/src/components/alert/bq-alert.tsx @@ -0,0 +1,254 @@ +import { Component, Element, Event, EventEmitter, h, Host, Method, Prop, State, Watch } from '@stencil/core'; + +import { ALERT_TYPE, TAlertType } from './bq-alert.types'; +import { debounce, hasSlotContent, TDebounce, validatePropValue } from '../../shared/utils'; + +/** + * @part base - The `
` container of the predefined bq-icon component + * @part body - The container `
` that wraps the alert description content + * @part btn-close - The `bq-button` used to close the alert + * @part content - The container `
` that wraps all the alert content (title, description, footer) + * @part footer - The container `
` that wraps the alert footer content + * @part icon - The `` element used to render a predefined icon based on the alert type (info, success, warning, error, default) + * @part icon-outline - The container `
` that wraps the icon element + * @part main - The container `
` that wraps the alert main content (title, description) + * @part svg - The `` element of the predefined bq-icon component + * @part title - The container `
` that wraps the alert title content + * @part wrapper - The wrapper container `
` of the element inside the shadow DOM + */ + +@Component({ + tag: 'bq-alert', + styleUrl: './scss/bq-alert.scss', + shadow: true, +}) +export class BqAlert { + // Own Properties + // ==================== + + private autoDismissDebounce: TDebounce; + private bodyElem: HTMLDivElement; + private footerElem: HTMLDivElement; + + // Reference to host HTML element + // =================================== + + @Element() el!: HTMLBqAlertElement; + + // State() variables + // Inlined decorator, alphabetical order + // ======================================= + + @State() private hasContent = false; + @State() private hasFooter = false; + + // Public Property API + // ======================== + + /** If true, the alert will automatically hide after the specified amount of time */ + @Prop({ reflect: true }) autoDismiss: boolean; + + /** If true, the close button at the top right of the alert won't be shown */ + @Prop({ reflect: true }) disableClose: boolean; + + /** If true, the alert icon won't be shown */ + @Prop({ reflect: true }) hideIcon: boolean; + + /** If true, the alert will be shown */ + @Prop({ reflect: true, mutable: true }) open: boolean; + + /** The length of time, in milliseconds, after which the alert will close itself. Only valid if `autoDismiss="true"` */ + @Prop({ reflect: true }) time: number = 3000; + + /** Type of Alert */ + @Prop({ reflect: true }) type: TAlertType = 'default'; + + /** If true, the alert component will remain fixed at the top of the page, occupying the full viewport */ + @Prop({ reflect: true }) sticky: boolean; + + // Prop lifecycle events + // ======================= + @Watch('autoDismiss') + @Watch('time') + handleTimeout() { + this.autoDismissDebounce?.cancel(); + if (!this.autoDismiss) return; + + this.autoDismissDebounce = debounce(() => { + this.hide(); + }, this.time); + // Make sure to autodismiss the notification if the `auto-dismiss` value changed while open + if (this.open) this.autoDismissDebounce(); + } + + @Watch('open') + handleOpenChange() { + this.autoDismissDebounce?.cancel(); + + if (!(this.autoDismiss && this.open)) return; + this.autoDismissDebounce(); + } + + @Watch('type') + checkPropValues() { + validatePropValue(ALERT_TYPE, 'info', this.el, 'type'); + } + + // Events section + // Requires JSDocs for public API documentation + // ============================================== + + /** Callback handler to be called when the notification is hidden */ + @Event() bqHide: EventEmitter; + + /** Callback handler to be called when the notification is shown */ + @Event() bqShow: EventEmitter; + + // Component lifecycle events + // Ordered by their natural call order + // ===================================== + + componentWillLoad() { + this.checkPropValues(); + this.handleTimeout(); + } + // Listeners + // ============== + + // Public methods API + // These methods are exposed on the host element. + // Always use two lines. + // Public Methods must be async. + // Requires JSDocs for public API documentation. + // =============================================== + + /** Method to be called to hide the alert component */ + @Method() + async hide(): Promise { + this.handleHide(); + } + /** Method to be called to show the alert component */ + @Method() + async show(): Promise { + this.handleShow(); + } + + // Local methods + // Internal business logic. + // These methods cannot be called from the host element. + // ======================================================= + + private handleHide = () => { + const ev = this.bqHide.emit(this.el); + if (!ev.defaultPrevented) { + this.open = false; + } + }; + + private handleShow = () => { + const ev = this.bqShow.emit(this.el); + if (!ev.defaultPrevented) { + this.open = true; + } + }; + + private handleContentSlotChange = () => { + this.hasContent = hasSlotContent(this.bodyElem, 'body'); + }; + + private handleFooterSlotChange = () => { + this.hasFooter = hasSlotContent(this.footerElem, 'footer'); + }; + + private get iconName(): string { + switch (this.type) { + case 'error': + return 'x-circle'; + case 'success': + return 'check-circle'; + case 'warning': + return 'warning-circle'; + default: + return 'info'; + } + } + + // render() function + // Always the last one in the class. + // =================================== + + render() { + return ( + + ); + } +} diff --git a/packages/beeq/src/components/alert/bq-alert.types.ts b/packages/beeq/src/components/alert/bq-alert.types.ts new file mode 100644 index 000000000..768fb53b4 --- /dev/null +++ b/packages/beeq/src/components/alert/bq-alert.types.ts @@ -0,0 +1,2 @@ +export const ALERT_TYPE = ['info', 'success', 'warning', 'error', 'default'] as const; +export type TAlertType = (typeof ALERT_TYPE)[number]; diff --git a/packages/beeq/src/components/alert/readme.md b/packages/beeq/src/components/alert/readme.md new file mode 100644 index 000000000..7c89107bf --- /dev/null +++ b/packages/beeq/src/components/alert/readme.md @@ -0,0 +1,87 @@ +# bq-alert + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| -------------- | --------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ----------- | +| `autoDismiss` | `auto-dismiss` | If true, the alert will automatically hide after the specified amount of time | `boolean` | `undefined` | +| `disableClose` | `disable-close` | If true, the close button at the top right of the alert won't be shown | `boolean` | `undefined` | +| `hideIcon` | `hide-icon` | If true, the alert icon won't be shown | `boolean` | `undefined` | +| `open` | `open` | If true, the alert will be shown | `boolean` | `undefined` | +| `sticky` | `sticky` | If true, the alert component will remain fixed at the top of the page, occupying the full viewport | `boolean` | `undefined` | +| `time` | `time` | The length of time, in milliseconds, after which the alert will close itself. Only valid if `autoDismiss="true"` | `number` | `3000` | +| `type` | `type` | Type of Alert | `"default" \| "error" \| "info" \| "success" \| "warning"` | `'default'` | + + +## Events + +| Event | Description | Type | +| -------- | ------------------------------------------------------------- | ------------------ | +| `bqHide` | Callback handler to be called when the notification is hidden | `CustomEvent` | +| `bqShow` | Callback handler to be called when the notification is shown | `CustomEvent` | + + +## Methods + +### `hide() => Promise` + +Method to be called to hide the alert component + +#### Returns + +Type: `Promise` + + + +### `show() => Promise` + +Method to be called to show the alert component + +#### Returns + +Type: `Promise` + + + + +## Shadow Parts + +| Part | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `"base"` | The `
` container of the predefined bq-icon component | +| `"body"` | The container `
` that wraps the alert description content | +| `"btn-close"` | The `bq-button` used to close the alert | +| `"content"` | The container `
` that wraps all the alert content (title, description, footer) | +| `"footer"` | The container `
` that wraps the alert footer content | +| `"icon"` | The `` element used to render a predefined icon based on the alert type (info, success, warning, error, default) | +| `"icon-outline"` | The container `
` that wraps the icon element | +| `"main"` | The container `
` that wraps the alert main content (title, description) | +| `"svg"` | The `` element of the predefined bq-icon component | +| `"title"` | The container `
` that wraps the alert title content | +| `"wrapper"` | The wrapper container `
` of the element inside the shadow DOM | + + +## Dependencies + +### Depends on + +- [bq-button](../button) +- [bq-icon](../icon) + +### Graph +```mermaid +graph TD; + bq-alert --> bq-button + bq-alert --> bq-icon + bq-button --> bq-icon + style bq-alert fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/beeq/src/components/alert/scss/bq-alert.scss b/packages/beeq/src/components/alert/scss/bq-alert.scss new file mode 100644 index 000000000..f5a017e49 --- /dev/null +++ b/packages/beeq/src/components/alert/scss/bq-alert.scss @@ -0,0 +1,85 @@ +/* -------------------------------------------------------------------------- */ +/* Alert styles */ +/* -------------------------------------------------------------------------- */ + +@import './bq-alert.variables'; + +:host { + @apply block; +} + +:host(.is-hidden) { + @apply hidden; +} + +:host(.is-sticky) { + @apply fixed inset-0 top-0 z-[var(--bq-alert--z-index)] w-full; + + .bq-alert { + @apply items-center justify-center rounded-none; + } +} + +.bq-alert { + @include animation-fade-in; + + @apply relative flex min-w-[var(--bq-alert--min-width)] p-[var(--bq-alert--padding)]; + @apply rounded-[var(--bq-alert--border-radius)] border-[length:--bq-alert--border-width]; + border-style: var(--bq-alert--border-style); +} + +/** + * Set the alert background and border color based on the type value selected + */ + +.bq-alert__default { + @apply border-[color:--bq-alert--border-default] bg-[color:--bq-alert--background-default]; +} + +.bq-alert__error { + @apply border-[color:--bq-alert--border-error] bg-[color:--bq-alert--background-error]; +} + +.bq-alert__info { + @apply border-[color:--bq-alert--border-info] bg-[color:--bq-alert--background-info]; +} + +.bq-alert__success { + @apply border-[color:--bq-alert--border-success] bg-[color:--bq-alert--background-success]; +} + +.bq-alert__warning { + @apply border-[color:--bq-alert--border-warning] bg-[color:--bq-alert--background-warning]; +} + +/** + * Set the alert icon color based on the type value selected + */ +.bq-alert__icon { + &--default { + @apply text-[color:--bq-alert--icon-color-default]; + } + + &--error { + @apply text-[color:--bq-alert--icon-color-error]; + } + + &--info { + @apply text-[color:--bq-alert--icon-color-info]; + } + + &--success { + @apply text-[color:--bq-alert--icon-color-success]; + } + + &--warning { + @apply text-[color:--bq-alert--icon-color-warning]; + } +} + +/** + * Tweak the close bq-buton styles so it remain small wihout extra padding + */ +.bq-alert__close::part(button) { + @apply h-fit rounded-s border-0 p-0; +} diff --git a/packages/beeq/src/components/alert/scss/bq-alert.variables.scss b/packages/beeq/src/components/alert/scss/bq-alert.variables.scss new file mode 100644 index 000000000..659cabd16 --- /dev/null +++ b/packages/beeq/src/components/alert/scss/bq-alert.variables.scss @@ -0,0 +1,67 @@ +/* -------------------------------------------------------------------------- */ +/* Alert custom properties */ +/* -------------------------------------------------------------------------- */ + +:host { + /** + * @prop --bq-alert--background: The alert background color + * @prop --bq-alert--border-radius: The alert border radius + * @prop --bq-alert--content-footer-gap: The alert content and footer gap + * @prop --bq-alert--title-body-gap: The alert title and body gap + + * @prop --bq-alert--border-color: The alert border color + * @prop --bq-alert--border-style: The alert border style + * @prop --bq-alert--border-width: The alert border width + + * @prop --bq-alert--background-info: The alert background color for info type + * @prop --bq-alert--background-success: The alert background color for success type + * @prop --bq-alert--background-warning: The alert background color for warning type + * @prop --bq-alert--background-error: The alert background color for error type + + * @prop --bq-alert--border-info: The alert border color for info type + * @prop --bq-alert--border-success: The alert border color for success type + * @prop --bq-alert--border-warning: The alert border color for warning type + * @prop --bq-alert--border-error: The alert border color for error type + * @prop --bq-alert--border-width: The alert border width + * @prop --bq-alert--border-style: The alert border style + + * @prop --bq-alert--icon-color-info: The alert icon color for info type + * @prop --bq-alert--icon-color-success: The alert icon color for success type + * @prop --bq-alert--icon-color-warning: The alert icon color for warning type + * @prop --bq-alert--icon-color-error: The alert icon color for error type + + * @prop --bq-alert--padding: The alert padding + * @prop --bq-alert--min-width: The alert min width + */ + + --bq-alert--background: theme('colors.ui.secondary'); + + --bq-alert--border-radius: theme('borderRadius.s'); + + --bq-alert--content-footer-gap: theme('spacing.s'); + --bq-alert--title-body-gap: theme('spacing.s'); + + --bq-alert--background-info: theme('colors.ui.brand-alt'); + --bq-alert--background-success: theme('colors.ui.success-alt'); + --bq-alert--background-warning: theme('colors.ui.warning-alt'); + --bq-alert--background-error: theme('colors.ui.danger-alt'); + --bq-alert--background-default: theme('colors.ui.primary-alt'); + + --bq-alert--border-info: theme('colors.stroke.brand'); + --bq-alert--border-success: theme('colors.stroke.success'); + --bq-alert--border-warning: theme('colors.stroke.warning'); + --bq-alert--border-error: theme('colors.stroke.danger'); + --bq-alert--border-default: theme('colors.stroke.secondary'); + --bq-alert--border-width: 1px; + --bq-alert--border-style: solid; + + --bq-alert--icon-color-info: theme('colors.icon.brand'); + --bq-alert--icon-color-success: theme('colors.icon.success'); + --bq-alert--icon-color-warning: theme('colors.icon.warning'); + --bq-alert--icon-color-error: theme('colors.icon.danger'); + --bq-alert--icon-color-default: theme('colors.icon.primary'); + + --bq-alert--padding: theme('padding.s'); + + --bq-alert--min-width: 320px; +} diff --git a/packages/beeq/src/components/button/readme.md b/packages/beeq/src/components/button/readme.md index 4a02f83cd..69d57926e 100644 --- a/packages/beeq/src/components/button/readme.md +++ b/packages/beeq/src/components/button/readme.md @@ -49,6 +49,7 @@ Buttons are designed for users to take action on a page or a screen. ### Used by + - [bq-alert](../alert) - [bq-dialog](../dialog) - [bq-input](../input) - [bq-notification](../notification) @@ -62,6 +63,7 @@ Buttons are designed for users to take action on a page or a screen. ```mermaid graph TD; bq-button --> bq-icon + bq-alert --> bq-button bq-dialog --> bq-button bq-input --> bq-button bq-notification --> bq-button diff --git a/packages/beeq/src/components/icon/readme.md b/packages/beeq/src/components/icon/readme.md index 50aebeec8..b98b8b0e2 100644 --- a/packages/beeq/src/components/icon/readme.md +++ b/packages/beeq/src/components/icon/readme.md @@ -36,6 +36,7 @@ Icons are simplified images that graphically explain the meaning of an object on ### Used by + - [bq-alert](../alert) - [bq-button](../button) - [bq-dialog](../dialog) - [bq-input](../input) @@ -47,6 +48,7 @@ Icons are simplified images that graphically explain the meaning of an object on ### Graph ```mermaid graph TD; + bq-alert --> bq-icon bq-button --> bq-icon bq-dialog --> bq-icon bq-input --> bq-icon