-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Empty State): add new bq-empty-state component
- Loading branch information
Showing
10 changed files
with
449 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
packages/beeq/src/components/empty-state/__tests__/bq-empty-state.e2e.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { newE2EPage } from '@stencil/core/testing'; | ||
|
||
describe('bq-empty-state', () => { | ||
it('should render', async () => { | ||
const page = await newE2EPage({ | ||
html: '<bq-empty-state></bq-empty-state>', | ||
}); | ||
const element = await page.find('bq-empty-state'); | ||
|
||
expect(element).toHaveClass('hydrated'); | ||
}); | ||
|
||
it('should have shadow root', async () => { | ||
const page = await newE2EPage({ | ||
html: '<bq-empty-state></bq-empty-state>', | ||
}); | ||
const element = await page.find('bq-empty-state'); | ||
|
||
expect(element.shadowRoot).not.toBeNull(); | ||
}); | ||
|
||
it('should render a basic empty state', async () => { | ||
const page = await newE2EPage({ | ||
html: ` | ||
<bq-empty-state> | ||
Title | ||
<span slot="body">You have a basic empty state</span> | ||
</bq-empty-state> | ||
`, | ||
}); | ||
|
||
const element = await page.find('bq-empty-state >>> slot[name="body"]'); | ||
|
||
expect(element).not.toBeNull(); | ||
}); | ||
}); |
30 changes: 30 additions & 0 deletions
30
packages/beeq/src/components/empty-state/_storybook/bq-empty-state.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { ArgTypes, Title, Subtitle } from '@storybook/addon-docs'; | ||
|
||
<div className="bq-doc__wrapper" data-theme="light"> | ||
<div className="bq-doc__container"> | ||
<Title>Empty state</Title> | ||
|
||
An Empty State is a UI component that is displayed when a user interacts with an application or system and there | ||
is no data or content available to display. Empty States are common in applications that have dynamic or changing data, | ||
or when the user is in a state of no activity or interaction. | ||
|
||
<Subtitle>Usage</Subtitle> | ||
|
||
- Use clear and concise language to explain the context of the empty state, so that users can understand the reason why there is no data or content available. | ||
- Use visually appealing and positive design elements, such as illustrations, icons, or images, to keep users engaged and reduce frustration. | ||
- Consider the use of humor or lightheartedness, if appropriate, to maintain a positive and inviting experience for users. | ||
- Provide a clear and concise call-to-action that helps users navigate to other parts of the application or system, where they can find the data or content they need. | ||
- Test the empty state with real users to ensure that it is effective in communicating the message and providing the guidance needed. | ||
|
||
<Subtitle>👍 When to use</Subtitle> | ||
|
||
- When to display a helpful message or guidance to users when there is no data or content available. | ||
- When to provide a clear and concise way to inform users that there is no data or content available, so that they can understand the context and meaning of the empty state. | ||
- When to provide an inviting and positive visual experience, to maintain user engagement and reduce frustration. | ||
- When to include a call-to-action (CTA) that helps users navigate to other parts of the application or system, where they can find the data or content they need. | ||
|
||
<Title>Properties</Title> | ||
|
||
<ArgTypes of="bq-empty-state" /> | ||
</div> | ||
</div> |
77 changes: 77 additions & 0 deletions
77
packages/beeq/src/components/empty-state/_storybook/bq-empty-state.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import type { Args, Meta, StoryObj } from '@storybook/web-components'; | ||
import { html } from 'lit-html'; | ||
|
||
import mdx from './bq-empty-state.mdx'; | ||
import { EMPTY_STATE_SIZE } from '../bq-empty-state.types'; | ||
|
||
const meta: Meta = { | ||
title: 'Components/Empty state', | ||
component: 'bq-empty-state', | ||
parameters: { | ||
docs: { | ||
page: mdx, | ||
}, | ||
}, | ||
argTypes: { | ||
size: { control: 'select', options: [...EMPTY_STATE_SIZE] }, | ||
}, | ||
args: { | ||
size: 'medium', | ||
}, | ||
}; | ||
export default meta; | ||
|
||
type Story = StoryObj; | ||
|
||
const Template = (args: Args) => html` <bq-empty-state size=${args.size}> Title </bq-empty-state> `; | ||
|
||
const TemplateWithBody = (args: Args) => html` | ||
<div class="flex flex-row gap-20"> | ||
<bq-empty-state size=${args.size}> | ||
Title | ||
<span slot="body"> Description </span> | ||
</bq-empty-state> | ||
<bq-empty-state size=${args.size}> | ||
Title <span slot="body"> Description <a class="bq-link" href="https://example.com">Link</a> </span> | ||
</bq-empty-state> | ||
</div> | ||
`; | ||
|
||
const TemplateWithCTA = (args: Args) => html` | ||
<div class="flex flex-row gap-20"> | ||
<bq-empty-state size=${args.size}> | ||
Title <span slot="body"> Description <a class="bq-link" href="https://example.com">Link</a> </span> | ||
<div class="flex gap-xs" slot="footer"> | ||
<bq-button appearance="primary" size="small"> Button </bq-button> | ||
</div> | ||
</bq-empty-state> | ||
<bq-empty-state size=${args.size}> | ||
Title <span slot="body"> Description <a class="bq-link" href="https://example.com">Link</a> </span> | ||
<div class="flex gap-xs" slot="footer"> | ||
<bq-button size="small" variant="ghost"> Button </bq-button> | ||
</div> | ||
</bq-empty-state> | ||
<bq-empty-state size=${args.size}> | ||
Title <span slot="body"> Description <a class="bq-link" href="https://example.com">Link</a> </span> | ||
<div class="flex gap-xs" slot="footer"> | ||
<bq-button size="small" variant="ghost"> Button </bq-button> | ||
<bq-button appearance="primary" size="small"> Button </bq-button> | ||
</div> | ||
</bq-empty-state> | ||
</div> | ||
`; | ||
|
||
export const Default: Story = { | ||
render: Template, | ||
args: {}, | ||
}; | ||
|
||
export const WithBody: Story = { | ||
render: TemplateWithBody, | ||
args: {}, | ||
}; | ||
|
||
export const WithCallToAction: Story = { | ||
render: TemplateWithCTA, | ||
args: {}, | ||
}; |
117 changes: 117 additions & 0 deletions
117
packages/beeq/src/components/empty-state/bq-empty-state.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { Component, Element, h, Host, Prop, Watch } from '@stencil/core'; | ||
|
||
import { EMPTY_STATE_SIZE, SIZE_TO_VALUE_MAP, TEmptyStateSize } from './bq-empty-state.types'; | ||
import { validatePropValue } from '../../shared/utils'; | ||
|
||
/** | ||
* @part body - The container `<div>` that wraps the alert description content | ||
* @part footer - The container `<div>` that wraps the alert footer content | ||
* @part icon - The `<bq-icon>` element used to render a predefined icon size based on the empty state size (small, medium, large) | ||
* @part title - The container `<div>` that wraps the empty state title content | ||
* @part wrapper - The wrapper container `<div>` of the element inside the shadow DOM | ||
*/ | ||
|
||
@Component({ | ||
tag: 'bq-empty-state', | ||
styleUrl: './scss/bq-empty-state.scss', | ||
shadow: true, | ||
}) | ||
export class BqEmptyState { | ||
// Own Properties | ||
// ==================== | ||
|
||
// Reference to host HTML element | ||
// =================================== | ||
|
||
@Element() el!: HTMLBqEmptyStateElement; | ||
|
||
// State() variables | ||
// Inlined decorator, alphabetical order | ||
// ======================================= | ||
|
||
// Public Property API | ||
// ======================== | ||
|
||
/** The size of the empty state component */ | ||
@Prop({ reflect: true, mutable: true }) size: TEmptyStateSize = 'medium'; | ||
|
||
// Prop lifecycle events | ||
// ======================= | ||
|
||
@Watch('size') | ||
checkPropValues() { | ||
validatePropValue(EMPTY_STATE_SIZE, 'medium', this.el, 'size'); | ||
} | ||
|
||
// Events section | ||
// Requires JSDocs for public API documentation | ||
// ============================================== | ||
|
||
// Component lifecycle events | ||
// Ordered by their natural call order | ||
// ===================================== | ||
|
||
componentWillLoad() { | ||
this.checkPropValues(); | ||
} | ||
|
||
// 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. | ||
// =============================================== | ||
|
||
// Local methods | ||
// Internal business logic. | ||
// These methods cannot be called from the host element. | ||
// ======================================================= | ||
|
||
private get iconSize(): number { | ||
return SIZE_TO_VALUE_MAP[this.size] || SIZE_TO_VALUE_MAP.medium; | ||
} | ||
|
||
// render() function | ||
// Always the last one in the class. | ||
// =================================== | ||
|
||
render() { | ||
return ( | ||
<Host> | ||
<div class="bq-empty-state" part="wrapper"> | ||
<div class={{ [`bq-empty-state-icon-margin-bottom__${this.size}`]: true }}> | ||
<slot name="icon"> | ||
<bq-icon size={this.iconSize} name="database" part="icon" exportparts="base,svg" /> | ||
</slot> | ||
</div> | ||
<div | ||
class={{ | ||
'title-font font-bold leading-regular text-text-primary': true, | ||
[`bq-empty-state-title-font-size__${this.size}`]: true, | ||
[`bq-empty-state-title-margin-bottom__${this.size}`]: true, | ||
}} | ||
part="title" | ||
> | ||
<slot /> | ||
</div> | ||
<div | ||
class={{ | ||
'font-normal leading-regular': true, | ||
[`bq-empty-state-body-font-size__${this.size}`]: true, | ||
[`bq-empty-state-body-margin-bottom__${this.size}`]: true, | ||
}} | ||
part="body" | ||
> | ||
<slot name="body" /> | ||
</div> | ||
<div class={{ 'flex items-start gap-xs': true }} part="footer"> | ||
<slot name="footer" /> | ||
</div> | ||
</div> | ||
</Host> | ||
); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
packages/beeq/src/components/empty-state/bq-empty-state.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const EMPTY_STATE_SIZE = ['small', 'medium', 'large'] as const; | ||
export type TEmptyStateSize = (typeof EMPTY_STATE_SIZE)[number]; | ||
|
||
export const SIZE_TO_VALUE_MAP: Record<TEmptyStateSize, number> = { | ||
small: 40, | ||
medium: 80, | ||
large: 180, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# bq-empty-state | ||
|
||
|
||
|
||
<!-- Auto Generated Below --> | ||
|
||
|
||
## Properties | ||
|
||
| Property | Attribute | Description | Type | Default | | ||
| -------- | --------- | ------------------------------------- | -------------------------------- | ---------- | | ||
| `size` | `size` | The size of the empty state component | `"large" \| "medium" \| "small"` | `'medium'` | | ||
|
||
|
||
## Shadow Parts | ||
|
||
| Part | Description | | ||
| ----------- | ------------------------------------------------------------------------------------------------------------------ | | ||
| `"body"` | The container `<div>` that wraps the alert description content | | ||
| `"footer"` | The container `<div>` that wraps the alert footer content | | ||
| `"icon"` | The `<bq-icon>` element used to render a predefined icon size based on the empty state size (small, medium, large) | | ||
| `"title"` | The container `<div>` that wraps the empty state title content | | ||
| `"wrapper"` | The wrapper container `<div>` of the element inside the shadow DOM | | ||
|
||
|
||
## Dependencies | ||
|
||
### Depends on | ||
|
||
- [bq-icon](../icon) | ||
|
||
### Graph | ||
```mermaid | ||
graph TD; | ||
bq-empty-state --> bq-icon | ||
style bq-empty-state fill:#f9f,stroke:#333,stroke-width:4px | ||
``` | ||
|
||
---------------------------------------------- | ||
|
||
*Built with [StencilJS](https://stenciljs.com/)* |
Oops, something went wrong.