Skip to content

Commit

Permalink
feat(core): add shimmer effect component
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasstroeh6380 authored Feb 25, 2025
1 parent d0f2eb1 commit 6abbbfd
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 8 deletions.
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions packages/core-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,20 @@ export namespace Components {
*/
"alignment": 'vertical' | 'horizontal';
}
interface B2bShimmer {
/**
* The height of the shimmer effect in px.
*/
"height": number;
/**
* Whether the shimmer effect is shown or not.
*/
"loading": boolean;
/**
* The width of the shimmer effect im px.
*/
"width": number;
}
interface B2bSnackbar {
/**
* Text for the Call-to-Action link.
Expand Down Expand Up @@ -2087,6 +2101,12 @@ declare global {
prototype: HTMLB2bSeparatorElement;
new (): HTMLB2bSeparatorElement;
};
interface HTMLB2bShimmerElement extends Components.B2bShimmer, HTMLStencilElement {
}
var HTMLB2bShimmerElement: {
prototype: HTMLB2bShimmerElement;
new (): HTMLB2bShimmerElement;
};
interface HTMLB2bSnackbarElementEventMap {
"b2b-close": void;
"b2b-action-click": void;
Expand Down Expand Up @@ -2395,6 +2415,7 @@ declare global {
"b2b-scrollable-container": HTMLB2bScrollableContainerElement;
"b2b-search": HTMLB2bSearchElement;
"b2b-separator": HTMLB2bSeparatorElement;
"b2b-shimmer": HTMLB2bShimmerElement;
"b2b-snackbar": HTMLB2bSnackbarElement;
"b2b-spinner": HTMLB2bSpinnerElement;
"b2b-tab": HTMLB2bTabElement;
Expand Down Expand Up @@ -3520,6 +3541,20 @@ declare namespace LocalJSX {
*/
"alignment"?: 'vertical' | 'horizontal';
}
interface B2bShimmer {
/**
* The height of the shimmer effect in px.
*/
"height"?: number;
/**
* Whether the shimmer effect is shown or not.
*/
"loading"?: boolean;
/**
* The width of the shimmer effect im px.
*/
"width"?: number;
}
interface B2bSnackbar {
/**
* Text for the Call-to-Action link.
Expand Down Expand Up @@ -4005,6 +4040,7 @@ declare namespace LocalJSX {
"b2b-scrollable-container": B2bScrollableContainer;
"b2b-search": B2bSearch;
"b2b-separator": B2bSeparator;
"b2b-shimmer": B2bShimmer;
"b2b-snackbar": B2bSnackbar;
"b2b-spinner": B2bSpinner;
"b2b-tab": B2bTab;
Expand Down Expand Up @@ -4091,6 +4127,7 @@ declare module "@stencil/core" {
"b2b-scrollable-container": LocalJSX.B2bScrollableContainer & JSXBase.HTMLAttributes<HTMLB2bScrollableContainerElement>;
"b2b-search": LocalJSX.B2bSearch & JSXBase.HTMLAttributes<HTMLB2bSearchElement>;
"b2b-separator": LocalJSX.B2bSeparator & JSXBase.HTMLAttributes<HTMLB2bSeparatorElement>;
"b2b-shimmer": LocalJSX.B2bShimmer & JSXBase.HTMLAttributes<HTMLB2bShimmerElement>;
"b2b-snackbar": LocalJSX.B2bSnackbar & JSXBase.HTMLAttributes<HTMLB2bSnackbarElement>;
/**
* Spinner component to display loading indicator.
Expand Down
19 changes: 19 additions & 0 deletions packages/core-components/src/components/shimmer/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# b2b-shimmer



<!-- Auto Generated Below -->


## Properties

| Property | Attribute | Description | Type | Default |
| --------- | --------- | ------------------------------------------- | --------- | ----------- |
| `height` | `height` | The height of the shimmer effect in px. | `number` | `undefined` |
| `loading` | `loading` | Whether the shimmer effect is shown or not. | `boolean` | `undefined` |
| `width` | `width` | The width of the shimmer effect im px. | `number` | `undefined` |


----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
15 changes: 15 additions & 0 deletions packages/core-components/src/components/shimmer/shimmer.docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ArgsTable, PRIMARY_STORY, Primary, Meta } from '@storybook/blocks';

import * as ShimmerStories from './shimmer.stories';

<Meta of={ShimmerStories} />

# Shimmer

The shimmer effect is an animated placeholder shown while the actual content is being loaded.

## Attributes

<ArgsTable story={PRIMARY_STORY} />
Changes made to the attributes in the above table will reflect in the example below:
<Primary />
25 changes: 25 additions & 0 deletions packages/core-components/src/components/shimmer/shimmer.e2e.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { newE2EPage } from '@stencil/core/testing';

describe('B2B-Shimmer', () => {
let page;
beforeEach(async () => {
page = await newE2EPage();
await page.setContent(
'<b2b-shimmer loading="true" width="100" height="100"></b2b-shimmer>',
);
});

it('should render shimmer component', async () => {
const element = await page.find('b2b-shimmer');
expect(element).toBeDefined();
});

it('should show slot content if loading is false', async () => {
const page = await newE2EPage();
await page.setContent('<b2b-shimmer><h1>dummy content</h1></b2b-shimmer>');

const element = await page.find('h1');

expect(element).toEqualText('dummy content');
});
});
29 changes: 29 additions & 0 deletions packages/core-components/src/components/shimmer/shimmer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@use '../../global/b2b-styles';

.b2b-shimmer {
position: relative;
overflow: hidden;
border-radius: 3px;

&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--b2b-color-grey-150);
animation: b2b-shimmer 2s infinite;
}
}

@keyframes b2b-shimmer {
0%,
100% {
opacity: 1;
}

50% {
opacity: 0.5;
}
}
46 changes: 46 additions & 0 deletions packages/core-components/src/components/shimmer/shimmer.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { newSpecPage } from '@stencil/core/testing';
import { ShimmerComponent } from './shimmer';
import { h } from '@stencil/core';

describe('B2B-Shimmer', () => {
async function renderPage(template) {
return newSpecPage({
components: [ShimmerComponent],
template: () => template,
});
}

it('should render shimmer effect with width 100 and height 100 attributes', async () => {
const dummyContent = 'Dummy content';
const page = await renderPage(
<b2b-shimmer loading={true} width={100} height={100}>
{dummyContent}
</b2b-shimmer>,
);
expect(page.root).toEqualHtml(`
<b2b-shimmer>
<mock:shadow-root>
<div class="b2b-shimmer" style="width: 100px; height: 100px;"></div>
</mock:shadow-root>
${dummyContent}
</b2b-shimmer>
`);
});

it('should show slot content and hide shimmer effect', async () => {
const dummyContent = 'Dummy content';
const page = await renderPage(
<b2b-shimmer loading={false} width={100} height={100}>
{dummyContent}
</b2b-shimmer>,
);
expect(page.root).toEqualHtml(`
<b2b-shimmer>
<mock:shadow-root>
<slot></slot>
</mock:shadow-root>
${dummyContent}
</b2b-shimmer>
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit-html';
import { getArgTypes } from '../../docs/config/utils';

type Story = StoryObj;

const meta: Meta = {
title: 'Components/Status & Feedback/Shimmer',
component: 'b2b-shimmer',
args: {
loading: true,
width: 400,
height: 25,
},
argTypes: {
...getArgTypes('b2b-shimmer'),
},
render: ({ ...args }) => html`<b2b-shimmer
loading="${args.loading}"
width="${args.width}"
height="${args.height}">
This is the mean content which takes a while to load.
</b2b-shimmer>`,
};

export default meta;

export const story010Default: Story = {
name: 'Default',
args: {
...meta.args,
},
};
34 changes: 34 additions & 0 deletions packages/core-components/src/components/shimmer/shimmer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Component, Prop, h, Host } from '@stencil/core';

@Component({
tag: 'b2b-shimmer',
styleUrl: 'shimmer.scss',
shadow: true,
})
export class ShimmerComponent {
/** Whether the shimmer effect is shown or not. */
@Prop() loading: boolean;

/** The width of the shimmer effect im px. */
@Prop() width: number;

/** The height of the shimmer effect in px. */
@Prop() height: number;

render() {
const shimmerStyle = {
width: `${this.width}px`,
height: `${this.height}px`,
};

return (
<Host>
{this.loading ? (
<div class="b2b-shimmer" style={shimmerStyle} />
) : (
<slot />
)}
</Host>
);
}
}
32 changes: 32 additions & 0 deletions packages/core-components/src/docs/config/components-args.json
Original file line number Diff line number Diff line change
Expand Up @@ -3501,6 +3501,38 @@
"description": "The alignment of the separator. Per default it is horizontal."
}
},
"b2b-shimmer": {
"height": {
"table": {
"type": {
"summary": "number"
},
"defaultValue": {}
},
"description": "The height of the shimmer effect in px."
},
"loading": {
"control": {
"type": "boolean"
},
"table": {
"type": {
"summary": "boolean"
},
"defaultValue": {}
},
"description": "Whether the shimmer effect is shown or not."
},
"width": {
"table": {
"type": {
"summary": "number"
},
"defaultValue": {}
},
"description": "The width of the shimmer effect im px."
}
},
"b2b-snackbar": {
"actionLabel": {
"table": {
Expand Down
Loading

0 comments on commit 6abbbfd

Please sign in to comment.