diff --git a/README.md b/README.md index b677a5611..89166e622 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Each layer does its bit to enforce and enhance accessibility. We consider this l - [`ebay-segmented-buttons`](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-segmented-buttons) - [`ebay-select`](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-select) - [`ebay-signal`](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-signal) +- [`ebay-skeleton`](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-skeleton) - [`ebay-snackbar-dialog`](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-snackbar-dialog) - [`ebay-split-button`](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-split-button) - [`ebay-star-rating`](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-star-rating) diff --git a/src/components/ebay-skeleton/README.md b/src/components/ebay-skeleton/README.md new file mode 100644 index 000000000..af866ed32 --- /dev/null +++ b/src/components/ebay-skeleton/README.md @@ -0,0 +1,15 @@ +

+ + ebay-skeleton + + + DS v1.0.0 [BETA] + +

+ +The Skeletons are simplified versions of layouts to indicate that information has not been fully loaded to improve the perceived performance. + +## Examples and Documentation + +- [Storybook](https://ebay.github.io/ebayui-core/?path=/docs/building-blocks-ebay-skeleton--documentation) +- [Code Examples](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-skeleton/examples) diff --git a/src/components/ebay-skeleton/components/ebay-skeleton-avatar/index.marko b/src/components/ebay-skeleton/components/ebay-skeleton-avatar/index.marko new file mode 100644 index 000000000..3dfb37aeb --- /dev/null +++ b/src/components/ebay-skeleton/components/ebay-skeleton-avatar/index.marko @@ -0,0 +1,15 @@ +import { processHtmlAttributes } from "../../../../common/html-attributes"; +import type { AttrClass } from "marko/tags-html"; +static var ignoredAttributes = ["as", "class"]; + +static interface SkeletonAvatarInput extends Omit, `on${string}`> { + as?: Marko.NativeTags; + class?: AttrClass; +} + +export interface Input extends WithNormalizedProps {} + +<${input.as || 'div'} + class=["skeleton__avatar", input.class] + ...processHtmlAttributes(input, ignoredAttributes) +/> diff --git a/src/components/ebay-skeleton/components/ebay-skeleton-button/index.marko b/src/components/ebay-skeleton/components/ebay-skeleton-button/index.marko new file mode 100644 index 000000000..97a6bec66 --- /dev/null +++ b/src/components/ebay-skeleton/components/ebay-skeleton-button/index.marko @@ -0,0 +1,18 @@ +import { processHtmlAttributes } from "../../../../common/html-attributes"; +import type { AttrClass } from "marko/tags-html"; +static var ignoredAttributes = ["as", "class", "size"]; + +static interface SkeletonButtonInput extends Omit, `on${string}`> { + as?: Marko.NativeTags; + class?: AttrClass; + size?: "small" | "large"; +} + +export interface Input extends WithNormalizedProps {} + +$ let sizeClass = input.size ? `skeleton__button--${input.size}` : ""; + +<${input.as || "div"} + class=["skeleton__button", sizeClass, input.class] + ...processHtmlAttributes(input, ignoredAttributes) +/> diff --git a/src/components/ebay-skeleton/components/ebay-skeleton-image/index.marko b/src/components/ebay-skeleton/components/ebay-skeleton-image/index.marko new file mode 100644 index 000000000..0367327be --- /dev/null +++ b/src/components/ebay-skeleton/components/ebay-skeleton-image/index.marko @@ -0,0 +1,15 @@ +import { processHtmlAttributes } from "../../../../common/html-attributes"; +import type { AttrClass } from "marko/tags-html"; +static var ignoredAttributes = ["as", "class"]; + +static interface SkeletonImageInput extends Omit, `on${string}`> { + as?: Marko.NativeTags; + class?: AttrClass; +} + +export interface Input extends WithNormalizedProps {} + +<${input.as || 'div'} + class=["skeleton__image", input.class] + ...processHtmlAttributes(input, ignoredAttributes) +/> diff --git a/src/components/ebay-skeleton/components/ebay-skeleton-text/index.marko b/src/components/ebay-skeleton/components/ebay-skeleton-text/index.marko new file mode 100644 index 000000000..616d2ec93 --- /dev/null +++ b/src/components/ebay-skeleton/components/ebay-skeleton-text/index.marko @@ -0,0 +1,24 @@ +import { processHtmlAttributes } from "../../../../common/html-attributes"; +import type { AttrClass } from "marko/tags-html"; +static var ignoredAttributes = ["as", "class", "size", "multiline"]; + +static interface SkeletonTextInput extends Omit, `on${string}`> { + as?: Marko.NativeTags; + class?: AttrClass; + size?: "small" | "large"; + multiline?: boolean; +} + +export interface Input extends WithNormalizedProps {} + +$ const { size, multiline } = input; + +<${input.as || "div"} + class=[ + "skeleton__text", + multiline && "skeleton__text--multiline", + size && size === "large" && `skeleton__text--large`, + input.class, + ] + ...processHtmlAttributes(input, ignoredAttributes) +/> diff --git a/src/components/ebay-skeleton/components/ebay-skeleton-textbox/index.marko b/src/components/ebay-skeleton/components/ebay-skeleton-textbox/index.marko new file mode 100644 index 000000000..1356e9205 --- /dev/null +++ b/src/components/ebay-skeleton/components/ebay-skeleton-textbox/index.marko @@ -0,0 +1,15 @@ +import { processHtmlAttributes } from "../../../../common/html-attributes"; +import type { AttrClass } from "marko/tags-html"; +static var ignoredAttributes = ["as", "class"]; + +static interface SkeletonTextBoxInput extends Omit, `on${string}`> { + as?: Marko.NativeTags; + class?: AttrClass; +} + +export interface Input extends WithNormalizedProps {} + +<${input.as || 'div'} + class=["skeleton__textbox", input.class] + ...processHtmlAttributes(input, ignoredAttributes) +/> diff --git a/src/components/ebay-skeleton/examples/avatar.marko b/src/components/ebay-skeleton/examples/avatar.marko new file mode 100644 index 000000000..0a3a683da --- /dev/null +++ b/src/components/ebay-skeleton/examples/avatar.marko @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ebay-skeleton/examples/button-small.marko b/src/components/ebay-skeleton/examples/button-small.marko new file mode 100644 index 000000000..77d361d5b --- /dev/null +++ b/src/components/ebay-skeleton/examples/button-small.marko @@ -0,0 +1,10 @@ + +
+ + + +
diff --git a/src/components/ebay-skeleton/examples/button.marko b/src/components/ebay-skeleton/examples/button.marko new file mode 100644 index 000000000..0e75ceebc --- /dev/null +++ b/src/components/ebay-skeleton/examples/button.marko @@ -0,0 +1,10 @@ + +
+ + + +
diff --git a/src/components/ebay-skeleton/examples/composite.marko b/src/components/ebay-skeleton/examples/composite.marko new file mode 100644 index 000000000..e9340c14a --- /dev/null +++ b/src/components/ebay-skeleton/examples/composite.marko @@ -0,0 +1,19 @@ + +
+ +
+ + +
+ +
+
diff --git a/src/components/ebay-skeleton/examples/image.marko b/src/components/ebay-skeleton/examples/image.marko new file mode 100644 index 000000000..25154beec --- /dev/null +++ b/src/components/ebay-skeleton/examples/image.marko @@ -0,0 +1,15 @@ + + +
+ + + +
diff --git a/src/components/ebay-skeleton/examples/text-multiline.marko b/src/components/ebay-skeleton/examples/text-multiline.marko new file mode 100644 index 000000000..ac6acb90e --- /dev/null +++ b/src/components/ebay-skeleton/examples/text-multiline.marko @@ -0,0 +1,10 @@ + +
+ + + +
diff --git a/src/components/ebay-skeleton/examples/text.marko b/src/components/ebay-skeleton/examples/text.marko new file mode 100644 index 000000000..34c8f703f --- /dev/null +++ b/src/components/ebay-skeleton/examples/text.marko @@ -0,0 +1,10 @@ + +
+ + + +
diff --git a/src/components/ebay-skeleton/examples/textbox.marko b/src/components/ebay-skeleton/examples/textbox.marko new file mode 100644 index 000000000..8285b3a89 --- /dev/null +++ b/src/components/ebay-skeleton/examples/textbox.marko @@ -0,0 +1,10 @@ + +
+ + + +
diff --git a/src/components/ebay-skeleton/examples/tile.marko b/src/components/ebay-skeleton/examples/tile.marko new file mode 100644 index 000000000..747630fc1 --- /dev/null +++ b/src/components/ebay-skeleton/examples/tile.marko @@ -0,0 +1,16 @@ + + +
+ + + + +
diff --git a/src/components/ebay-skeleton/examples/withContent.marko b/src/components/ebay-skeleton/examples/withContent.marko new file mode 100644 index 000000000..1e0cfb7e4 --- /dev/null +++ b/src/components/ebay-skeleton/examples/withContent.marko @@ -0,0 +1,38 @@ +class { + onCreate() { + this.state = { + isLoading: true, + data: null, + }; + } + async getData() { + return new Promise((resolve, reject) => { + setTimeout(() => resolve("Button"), 2000); + }); + } + async onMount() { + try { + this.state.data = await this.getData(); + } catch (err) { + this.state.error = err; + } finally { + this.state.isLoading = false; + } + } +} + + +
+ + + + + + + ${state.data} + +
diff --git a/src/components/ebay-skeleton/index.marko b/src/components/ebay-skeleton/index.marko new file mode 100644 index 000000000..042eaf55d --- /dev/null +++ b/src/components/ebay-skeleton/index.marko @@ -0,0 +1,17 @@ +import { processHtmlAttributes } from "../../common/html-attributes"; +static var ignoredAttributes = ["a11yText", "class"]; + +static interface SkeletonInput extends Omit, `on${string}`> { + "a11y-text"?: string; +} + +export interface Input extends WithNormalizedProps {} + + diff --git a/src/components/ebay-skeleton/marko-tag.json b/src/components/ebay-skeleton/marko-tag.json new file mode 100644 index 000000000..5343b269a --- /dev/null +++ b/src/components/ebay-skeleton/marko-tag.json @@ -0,0 +1,14 @@ +{ + "attribute-groups": ["html-attributes"], + "@*": { + "targetProperty": null, + "type": "expression" + }, + "@html-attributes": "expression", + "@role": "never", + "@size": { + "enum": ["small", "large"] + }, + "@multiline": "boolean", + "@a11yText": "string" +} diff --git a/src/components/ebay-skeleton/skeleton.stories.ts b/src/components/ebay-skeleton/skeleton.stories.ts new file mode 100644 index 000000000..ec9f998f6 --- /dev/null +++ b/src/components/ebay-skeleton/skeleton.stories.ts @@ -0,0 +1,125 @@ +import { tagToString } from "../../../.storybook/storybook-code-source"; +import { + addRenderBodies, + buildExtensionTemplate, +} from "../../../.storybook/utils"; +import Component from "./index.marko"; +import Readme from "./README.md"; +import avatarTemplate from "./examples/avatar.marko"; +import avatarCode from "./examples/avatar.marko?raw"; +import buttonTemplate from "./examples/button.marko"; +import buttonCode from "./examples/button.marko?raw"; +import buttonSmallTemplate from "./examples/button-small.marko"; +import buttonSmallCode from "./examples/button-small.marko?raw"; +import textTemplate from "./examples/text.marko"; +import textCode from "./examples/text.marko?raw"; +import textMultilineTemplate from "./examples/text-multiline.marko"; +import textMultilineCode from "./examples/text-multiline.marko?raw"; +import textboxTemplate from "./examples/textbox.marko"; +import textboxCode from "./examples/textbox.marko?raw"; +import imageTemplate from "./examples/image.marko"; +import imageCode from "./examples/image.marko?raw"; +import tileTemplate from "./examples/tile.marko"; +import tileCode from "./examples/tile.marko?raw"; +import withContentTemplate from "./examples/withContent.marko"; +import withContentCode from "./examples/withContent.marko?raw"; +import compositeTemplate from "./examples/composite.marko"; +import compositeCode from "./examples/composite.marko?raw"; + +const Template = (args) => ({ + input: addRenderBodies(args), +}); + +export default { + title: "building blocks/ebay-skeleton", + component: Component, + parameters: { + docs: { + description: { + component: Readme, + }, + }, + }, + + argTypes: { + renderBody: { + control: { type: "text" }, + }, + a11yText: { + control: { type: "text" }, + description: + "The localized accessibility text for the component. By default for english, Loading is used.", + table: { + defaultValue: { + summary: "Loading", + }, + }, + }, + size: { + control: { type: "select" }, + options: ["small", "large"], + table: { + defaultValue: { + summary: "default", + }, + }, + description: + "The Size of the component to render. Applicable for `ebay-skeleton-button` and `ebay-skeleton-text` only", + }, + multiline: { + control: { type: "boolean" }, + table: { + defaultValue: { + summary: false, + }, + }, + description: + "Boolean flag to make `ebay-skeleton-text` render more than one line", + }, + }, +}; + +export const Default = Template.bind({}); +Default.args = { + style: "width: 220px", + renderBody: `
`, +}; +Default.parameters = { + docs: { + source: { + code: tagToString("ebay-skeleton", Default.args), + }, + }, +}; + +export const Avatar = buildExtensionTemplate(avatarTemplate, avatarCode); + +export const Button = buildExtensionTemplate(buttonTemplate, buttonCode); + +export const ButtonSmall = buildExtensionTemplate( + buttonSmallTemplate, + buttonSmallCode, +); + +export const Text = buildExtensionTemplate(textTemplate, textCode); + +export const TextMultiLine = buildExtensionTemplate( + textMultilineTemplate, + textMultilineCode, +); + +export const TextBox = buildExtensionTemplate(textboxTemplate, textboxCode); + +export const Image = buildExtensionTemplate(imageTemplate, imageCode); + +export const Tile = buildExtensionTemplate(tileTemplate, tileCode); + +export const composite = buildExtensionTemplate( + compositeTemplate, + compositeCode, +); + +export const withContent = buildExtensionTemplate( + withContentTemplate, + withContentCode, +); diff --git a/src/components/ebay-skeleton/style.ts b/src/components/ebay-skeleton/style.ts new file mode 100644 index 000000000..32adaa1c5 --- /dev/null +++ b/src/components/ebay-skeleton/style.ts @@ -0,0 +1 @@ +import "@ebay/skin/skeleton"; diff --git a/src/components/ebay-skeleton/test/test.server.js b/src/components/ebay-skeleton/test/test.server.js new file mode 100644 index 000000000..15f6fb810 --- /dev/null +++ b/src/components/ebay-skeleton/test/test.server.js @@ -0,0 +1,31 @@ +import { use } from "chai"; +import { composeStories } from "@storybook/marko"; +import { snapshotHTML } from "../../../common/test-utils/snapshots"; +import * as stories from "../skeleton.stories"; + +const { Default, Avatar, Tile, Text, TextMultiLine } = composeStories(stories); +const htmlSnap = snapshotHTML(__dirname); + +use(require("chai-dom")); + +describe("skeleton", () => { + it("renders default skeleton", async () => { + await htmlSnap(Default); + }); + + it("renders avatar", async () => { + await htmlSnap(Avatar); + }); + + it("renders tile", async () => { + await htmlSnap(Tile); + }); + + it("renders text", async () => { + await htmlSnap(Text); + }); + + it("renders text multiLine", async () => { + await htmlSnap(TextMultiLine); + }); +});