Skip to content

Commit

Permalink
explorer: add AppAnchorButton component
Browse files Browse the repository at this point in the history
  • Loading branch information
deuch13 committed May 24, 2024
1 parent 147aaa9 commit d0d44d1
Show file tree
Hide file tree
Showing 9 changed files with 420 additions and 1 deletion.
66 changes: 66 additions & 0 deletions explorer/src/lib/components/__tests__/AppAnchorButton.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render } from "@testing-library/svelte";
import { base } from "$app/paths";

import { AppAnchorButton } from "..";

describe("AppAnchorButton", () => {
const baseProps = {
className: "foo bar",
href: "/setup",
id: "some-id",
};

afterEach(cleanup);

it("should render an `AnchorButton` with the base path prepended to the `href` attribute, if the `href` represents an absolute URL", () => {
const { container, getByRole, rerender } = render(
AppAnchorButton,
baseProps
);
const anchorA = getByRole("link");

expect(container.firstChild).toMatchSnapshot();
expect(anchorA).toHaveAttribute("href", `${base}${baseProps.href}`);
expect(anchorA).toHaveClass("foo bar");
expect(anchorA).toHaveAttribute("id", baseProps.id);

rerender({ ...baseProps, href: "/" });

const anchorB = getByRole("link");

expect(anchorB).toHaveAttribute("href", `${base}/`);
expect(anchorB).toHaveClass("foo bar");
expect(anchorB).toHaveAttribute("id", baseProps.id);
});

it("should leave the `AnchorButton` as it is if the `href` points to a relative path", () => {
const { getByRole } = render(AppAnchorButton, {
...baseProps,
href: "foo/bar",
});

expect(getByRole("link")).toHaveAttribute("href", "foo/bar");
});

it("should leave the `AnchorButton` as it is if the `href` points to an external URL", () => {
const href = "http://example.com";
const { getByRole } = render(AppAnchorButton, { ...baseProps, href });

expect(getByRole("link")).toHaveAttribute("href", href);
});

it("should forward the `onclick` event to the `AnchorButton`", async () => {
const handler = vi.fn();
const { component, getByRole } = render(AppAnchorButton, {
...baseProps,
href: "#",
});

component.$on("click", handler);

await fireEvent.click(getByRole("link"));

expect(handler).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`AppAnchorButton > should render an \`AnchorButton\` with the base path prepended to the \`href\` attribute, if the \`href\` represents an absolute URL 1`] = `
<div>
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal foo bar"
href="/some-base-path/setup"
id="some-id"
>
</a>
<!--&lt;Anchor&gt;-->
<!--&lt;AnchorButton&gt;-->
<!--&lt;AppAnchorButton&gt;-->
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<svelte:options immutable={true} />

<script>
import { AnchorButton } from "$lib/dusk/components";
import { addBasePath } from "$lib/navigation";
/** @type {string} */
export let href;
</script>

<AnchorButton {...$$restProps} href={addBasePath(href)} on:click />
3 changes: 2 additions & 1 deletion explorer/src/lib/components/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export { default as DataAlert } from "./data-alert/DataAlert.svelte";
export { default as AppAnchor } from "./app-anchor/AppAnchor.svelte";
export { default as AppAnchorButton } from "./app-anchor-button/AppAnchorButton.svelte";
export { default as AppImage } from "./app-image/AppImage.svelte";
export { default as BlockDetails } from "./block-details/BlockDetails.svelte";
export { default as BlocksCard } from "./blocks-card/BlocksCard.svelte";
export { default as BlocksList } from "./blocks-list/BlocksList.svelte";
export { default as BlocksTable } from "./blocks-table/BlocksTable.svelte";
export { default as DataAlert } from "./data-alert/DataAlert.svelte";
export { default as DataCard } from "./data-card/DataCard.svelte";
export { default as DataGuard } from "./data-guard/DataGuard.svelte";
export { default as DetailList } from "./detail-list/DetailList.svelte";
Expand Down
88 changes: 88 additions & 0 deletions explorer/src/lib/dusk/components/__tests__/AnchorButton.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// @ts-nocheck

import { afterEach, describe, expect, it } from "vitest";
import { cleanup, render } from "@testing-library/svelte";
import { mdiFolderOutline } from "@mdi/js";

import { AnchorButton } from "..";

describe("AnchorButton", () => {
const baseProps = {
href: "/some-url",
text: "some text",
};
const baseOptions = {
props: baseProps,
target: document.body,
};

afterEach(cleanup);

it("should render the AnchorButton component", () => {
const { container } = render(AnchorButton, baseOptions);

expect(container.firstChild).toMatchSnapshot();
});

it("should add a disabled class if the related property is `true`", () => {
const props = {
...baseProps,
disabled: true,
};
const { container } = render(AnchorButton, { ...baseOptions, props });

expect(container.firstChild).toMatchSnapshot();
});

it("should pass additional class names and attributes to the rendered element", () => {
const props = {
...baseProps,
className: "foo bar",
id: "some-id",
};
const { container } = render(AnchorButton, { ...baseOptions, props });

expect(container.firstChild).toMatchSnapshot();
});

it("should render a AnchorButton without a text", () => {
const props = {
...baseProps,
text: "",
};
const { container } = render(AnchorButton, { ...baseOptions, props });

expect(container.firstChild).toMatchSnapshot();
});

it("should be able to render a AnchorButton with an icon and text", () => {
["after", "before"].forEach((position) => {
const props = {
...baseProps,
icon: {
path: mdiFolderOutline,
position,
},
};
const { container } = render(AnchorButton, { ...baseOptions, props });

expect(container.firstChild).toMatchSnapshot();
});
});

it("should be able to render a AnchorButton with an icon only", () => {
["after", "before"].forEach((position) => {
const props = {
...baseProps,
icon: {
path: mdiFolderOutline,
position,
},
text: "",
};
const { container } = render(AnchorButton, { ...baseOptions, props });

expect(container.firstChild).toMatchSnapshot();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`AnchorButton > should add a disabled class if the related property is \`true\` 1`] = `
<a
aria-disabled="true"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal dusk-anchor-button--disabled"
href="/some-url"
tabindex="-1"
>
<span
class="dusk-anchor-button__text"
>
some text
</span>
</a>
`;

exports[`AnchorButton > should be able to render a AnchorButton with an icon and text 1`] = `
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal dusk-icon-button--labeled"
href="/some-url"
>
<span
class="dusk-anchor-button__text"
>
some text
</span>
<svg
class="dusk-icon dusk-icon--size--normal dusk-anchor-button__icon"
role="graphics-symbol"
viewBox="0 0 24 24"
>
<path
d="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z"
/>
</svg>
<!--&lt;Icon&gt;-->
</a>
`;

exports[`AnchorButton > should be able to render a AnchorButton with an icon and text 2`] = `
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal dusk-icon-button--labeled"
href="/some-url"
>
<span
class="dusk-anchor-button__text"
>
some text
</span>
<svg
class="dusk-icon dusk-icon--size--normal dusk-anchor-button__icon"
role="graphics-symbol"
viewBox="0 0 24 24"
>
<path
d="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z"
/>
</svg>
<!--&lt;Icon&gt;-->
</a>
`;

exports[`AnchorButton > should be able to render a AnchorButton with an icon only 1`] = `
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal dusk-icon-button"
href="/some-url"
>
<svg
class="dusk-icon dusk-icon--size--normal dusk-anchor-button__icon"
role="graphics-symbol"
viewBox="0 0 24 24"
>
<path
d="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z"
/>
</svg>
<!--&lt;Icon&gt;-->
</a>
`;

exports[`AnchorButton > should be able to render a AnchorButton with an icon only 2`] = `
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal dusk-icon-button"
href="/some-url"
>
<svg
class="dusk-icon dusk-icon--size--normal dusk-anchor-button__icon"
role="graphics-symbol"
viewBox="0 0 24 24"
>
<path
d="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z"
/>
</svg>
<!--&lt;Icon&gt;-->
</a>
`;

exports[`AnchorButton > should pass additional class names and attributes to the rendered element 1`] = `
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal foo bar"
href="/some-url"
id="some-id"
>
<span
class="dusk-anchor-button__text"
>
some text
</span>
</a>
`;

exports[`AnchorButton > should render a AnchorButton without a text 1`] = `
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal"
href="/some-url"
>
</a>
`;

exports[`AnchorButton > should render the AnchorButton component 1`] = `
<a
aria-disabled="false"
class="dusk-anchor dusk-anchor-button dusk-anchor-button--variant--primary dusk-anchor-button--size--normal"
href="/some-url"
>
<span
class="dusk-anchor-button__text"
>
some text
</span>
</a>
`;
10 changes: 10 additions & 0 deletions explorer/src/lib/dusk/components/anchor-button/AnchorButton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.dusk-anchor-button {
appearance: button;
text-decoration: none;
text-align: center;
}

.dusk-anchor-button.dusk-anchor-button--disabled {
cursor: default;
pointer-events: none;
}
Loading

0 comments on commit d0d44d1

Please sign in to comment.