Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add card component #43

Merged
merged 4 commits into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/components/Card/Card.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
import Svg from "@cxa/astro-inline-svg";
import { Icon } from "astro-icon/components";

import "../../styles/global.css";
import { generateQRCode } from "../../libs/qrcode.ts";

export type Props = { url: string };
const { url } = Astro.props;

const { hostname } = new URL(url);
const qrCode: string = await generateQRCode(url);
---

<a
href={url}
target="_blank"
rel="noopener noreferrer"
aria-label={`Visit ${hostname}`}
>
<Svg id="qr" raw={qrCode} />
<div>
<Icon id="icon" name="mingcute:idcard-line" />
<span aria-hidden="true">{hostname}</span>
</div>
</a>

<style>
a {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.3rem;
text-decoration: none;

&:hover div,
&:focus-visible div {
border-color: color-mix(in srgb, var(--foreground) 60%, transparent);
}
}

#qr {
path {
stroke: var(--foreground);
}
width: 6rem;
height: auto;
}

div {
display: flex;
justify-content: center;
align-items: center;
gap: 0.3rem;
padding: 0 0.3rem;
color: var(--foreground);
border-bottom: 2px solid
color-mix(in srgb, var(--foreground) 30%, transparent);
transition: border-color 0.3s ease-in-out;
}

#icon {
width: 1rem;
height: auto;
}

span {
font-family: "LINE Seed JP", sans-serif;
font-size: 0.75rem;
font-style: normal;
font-weight: normal;
line-height: normal;
}
</style>
10 changes: 10 additions & 0 deletions src/components/Card/Card.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Card from "./Card.astro";
import * as stories from "./story.ts";

export default {
title: "Card",
component: Card,
};

export const Default = { args: stories.Default };
export const SubDomain = { args: stories.SubDomain };
31 changes: 31 additions & 0 deletions src/components/Card/Card.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, expect, test } from "vitest";

import Card from "./Card.astro";
import type { Props } from "./Card.astro";
import * as stories from "./story.ts";

describe("Card", () => {
describe("Valid URL", () => {
for (const [name, props] of Object.entries(stories)) {
test(name, async () => {
const container: AstroContainer = await AstroContainer.create();
const result: string = await container.renderToString(Card, {
props,
});

expect(result).toMatchSnapshot();
});
}
});

test("Invalid URL", async () => {
const container: AstroContainer = await AstroContainer.create();

await expect(
container.renderToString(Card, {
props: { url: "not-valid-url" } satisfies Props,
}),
).rejects.toThrow();
});
});
5 changes: 5 additions & 0 deletions src/components/Card/__snapshots__/Card.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Card > Valid URL > Default 1`] = `"<a href="https://example.com" target="_blank" rel="noopener noreferrer" aria-label="Visit example.com" data-astro-cid-nzteqis6> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" shape-rendering="crispEdges" id="qr" data-astro-cid-nzteqis6><path stroke="#000000" d="M0 0.5h7m4 0h3m2 0h1m1 0h7M0 1.5h1m5 0h1m3 0h1m2 0h4m1 0h1m5 0h1M0 2.5h1m1 0h3m1 0h1m1 0h2m1 0h1m2 0h1m3 0h1m1 0h3m1 0h1M0 3.5h1m1 0h3m1 0h1m1 0h1m4 0h3m2 0h1m1 0h3m1 0h1M0 4.5h1m1 0h3m1 0h1m1 0h3m2 0h1m2 0h1m1 0h1m1 0h3m1 0h1M0 5.5h1m5 0h1m1 0h1m2 0h1m2 0h2m2 0h1m5 0h1M0 6.5h7m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h7M8 7.5h1m5 0h1m1 0h1M0 8.5h1m1 0h5m5 0h1m5 0h5M1 9.5h1m2 0h2m2 0h1m1 0h2m1 0h1m3 0h1m1 0h1m3 0h1M0 10.5h5m1 0h1m1 0h2m3 0h4m2 0h1m1 0h1m1 0h2M0 11.5h2m1 0h3m2 0h1m1 0h2m1 0h1m1 0h2m1 0h2m4 0h1M1 12.5h3m2 0h1m4 0h2m1 0h2m1 0h2m1 0h1m1 0h3M0 13.5h5m3 0h1m1 0h1m5 0h1m2 0h1m1 0h1m1 0h1M0 14.5h1m5 0h2m2 0h3m2 0h1m2 0h4m1 0h2M0 15.5h1m2 0h1m3 0h1m3 0h1m2 0h7m3 0h1M0 16.5h1m1 0h1m2 0h2m1 0h4m4 0h5m1 0h1M8 17.5h2m2 0h5m3 0h2M0 18.5h7m6 0h2m1 0h1m1 0h1m1 0h1m1 0h3M0 19.5h1m5 0h1m1 0h2m2 0h2m2 0h1m3 0h2m1 0h1M0 20.5h1m1 0h3m1 0h1m1 0h3m1 0h1m1 0h7m1 0h1m1 0h1M0 21.5h1m1 0h3m1 0h1m1 0h1m6 0h1m1 0h2m1 0h5M0 22.5h1m1 0h3m1 0h1m1 0h5m2 0h1m5 0h2m1 0h1M0 23.5h1m5 0h1m4 0h1m2 0h1m1 0h2m1 0h3m2 0h1M0 24.5h7m1 0h2m1 0h1m5 0h8"></path></svg> <div data-astro-cid-nzteqis6> <svg width="1em" height="1em" id="icon" data-astro-cid-nzteqis6 data-icon="mingcute:idcard-line"> <symbol id="ai:mingcute:idcard-line" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M4 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 2h16v12H4zm6.25 3.75a1.75 1.75 0 1 1-3.5 0a1.75 1.75 0 0 1 3.5 0M5 14.5A2.5 2.5 0 0 1 7.5 12h2a2.5 2.5 0 0 1 2.5 2.5v.5a1 1 0 1 1-2 0v-.5a.5.5 0 0 0-.5-.5h-2a.5.5 0 0 0-.5.5v.5a1 1 0 1 1-2 0zm8-3.5a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1m1 2a1 1 0 1 0 0 2h4a1 1 0 1 0 0-2z"/></g></symbol><use href="#ai:mingcute:idcard-line"></use> </svg> <span aria-hidden="true" data-astro-cid-nzteqis6>example.com</span> </div> </a> "`;

exports[`Card > Valid URL > SubDomain 1`] = `"<a href="https://www.example.com" target="_blank" rel="noopener noreferrer" aria-label="Visit www.example.com" data-astro-cid-nzteqis6> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" shape-rendering="crispEdges" id="qr" data-astro-cid-nzteqis6><path stroke="#000000" d="M0 0.5h7m5 0h2m2 0h1m1 0h7M0 1.5h1m5 0h1m2 0h3m2 0h2m2 0h1m5 0h1M0 2.5h1m1 0h3m1 0h1m1 0h1m1 0h3m2 0h1m2 0h1m1 0h3m1 0h1M0 3.5h1m1 0h3m1 0h1m1 0h2m1 0h1m1 0h4m1 0h1m1 0h3m1 0h1M0 4.5h1m1 0h3m1 0h1m1 0h1m2 0h2m3 0h1m1 0h1m1 0h3m1 0h1M0 5.5h1m5 0h1m1 0h1m2 0h2m1 0h3m1 0h1m5 0h1M0 6.5h7m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h7M8 7.5h1m2 0h1m2 0h3M0 8.5h1m1 0h5m3 0h1m1 0h2m4 0h5M0 9.5h3m4 0h5m1 0h1m2 0h2m1 0h1m3 0h1M0 10.5h1m1 0h2m2 0h1m1 0h2m1 0h11m1 0h2M0 11.5h2m1 0h2m3 0h1m2 0h1m3 0h1m1 0h1m1 0h1m4 0h1M2 12.5h1m3 0h1m3 0h1m1 0h4m1 0h4m1 0h3M0 13.5h2m3 0h1m4 0h1m5 0h2m1 0h1m1 0h1m1 0h1M0 14.5h1m2 0h1m1 0h2m3 0h4m1 0h1m1 0h5m1 0h2M0 15.5h1m2 0h2m4 0h1m1 0h1m2 0h2m1 0h1m1 0h2m3 0h1M0 16.5h1m4 0h4m1 0h2m1 0h2m1 0h5m1 0h1M8 17.5h1m3 0h2m2 0h1m3 0h2M0 18.5h7m3 0h1m2 0h2m1 0h1m1 0h1m1 0h1m1 0h3M0 19.5h1m5 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m3 0h2m1 0h1M0 20.5h1m1 0h3m1 0h1m1 0h1m3 0h9m1 0h3M0 21.5h1m1 0h3m1 0h1m1 0h3m2 0h1m4 0h1m1 0h5M0 22.5h1m1 0h3m1 0h1m1 0h2m1 0h2m1 0h1m6 0h2m1 0h1M0 23.5h1m5 0h1m2 0h1m1 0h1m2 0h4m1 0h3m2 0h1M0 24.5h7m1 0h2m1 0h2m6 0h6"></path></svg> <div data-astro-cid-nzteqis6> <svg width="1em" height="1em" id="icon" data-astro-cid-nzteqis6 data-icon="mingcute:idcard-line"> <symbol id="ai:mingcute:idcard-line" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M4 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 2h16v12H4zm6.25 3.75a1.75 1.75 0 1 1-3.5 0a1.75 1.75 0 0 1 3.5 0M5 14.5A2.5 2.5 0 0 1 7.5 12h2a2.5 2.5 0 0 1 2.5 2.5v.5a1 1 0 1 1-2 0v-.5a.5.5 0 0 0-.5-.5h-2a.5.5 0 0 0-.5.5v.5a1 1 0 1 1-2 0zm8-3.5a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1m1 2a1 1 0 1 0 0 2h4a1 1 0 1 0 0-2z"/></g></symbol><use href="#ai:mingcute:idcard-line"></use> </svg> <span aria-hidden="true" data-astro-cid-nzteqis6>www.example.com</span> </div> </a> "`;
4 changes: 4 additions & 0 deletions src/components/Card/story.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { Props } from "./Card.astro";

export const Default: Props = { url: "https://example.com" };
export const SubDomain: Props = { url: "https://www.example.com" };