added reusable ui components: posts list and panel header w/ cta

This commit is contained in:
neil 2022-12-08 14:47:18 +08:00
parent 9173ef66b2
commit c05d3b0775
12 changed files with 123 additions and 222 deletions

View file

@ -0,0 +1,17 @@
<script lang="ts">
import '$appcss';
import { getAllPosts } from '$libs/api/mock';
import type { AirtablePost } from '@tea/ui/types';
import Posts from '@tea/ui/Posts/Posts.svelte';
import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte';
import { onMount } from 'svelte';
let courses: AirtablePost[] = [];
onMount(async () => {
courses = await getAllPosts('course');
});
</script>
<PanelHeader title="Essential Workshops" ctaLabel="View all" ctaLink="/" />
<Posts posts={courses} />

View file

@ -0,0 +1,17 @@
<script lang="ts">
import '$appcss';
import { getAllPosts } from '$libs/api/mock';
import type { AirtablePost } from '@tea/ui/types';
import Posts from '@tea/ui/Posts/Posts.svelte';
import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte';
import { onMount } from 'svelte';
let news: AirtablePost[] = [];
onMount(async () => {
news = await getAllPosts('news');
});
</script>
<PanelHeader title="Open-source News" ctaLabel="Read more articles >" ctaLink="/" />
<Posts posts={news} />

View file

@ -1,40 +0,0 @@
<script lang="ts">
import '$appcss';
import { getAllPosts } from '$libs/api/mock';
import type { AirtablePost } from '@tea/ui/types';
import { onMount } from 'svelte';
export let title: string;
export let type: string;
export let readMoreLink: string;
export let readMoreCta = 'Read more';
let posts: AirtablePost[] = [];
onMount(async () => {
// todo: filter by type
posts = await getAllPosts();
});
</script>
<header class="flex items-center justify-between border border-gray bg-black p-4 text-primary">
<span class="uppercase">{title}</span>
<a href={readMoreLink} class="font-sono text-sm underline">{readMoreCta}</a>
</header>
<ul class="flex flex-col bg-black">
{#each posts as article}
<li class="border border-t-0 border-gray p-4">
<article class="flex border border-gray">
<figure class="w-1/3">
<img src={article.thumb_image_url} alt={article.title} />
</figure>
<section class="p-4 font-sono">
<h1 class="text-xl text-primary">{article.title}</h1>
<p class="my-4 text-sm">{article.short_description}</p>
<a href={article.link} class="text-sm text-primary underline">Read more ...</a>
</section>
</article>
</li>
{/each}
</ul>

View file

@ -6,7 +6,7 @@
import FeaturedPackages from '$components/FeaturedPackages/FeaturedPackages.svelte'; import FeaturedPackages from '$components/FeaturedPackages/FeaturedPackages.svelte';
import GettingStarted from '$components/GettingStarted/GettingStarted.svelte'; import GettingStarted from '$components/GettingStarted/GettingStarted.svelte';
import TopPackages from '$components/TopPackages/TopPackages.svelte'; import TopPackages from '$components/TopPackages/TopPackages.svelte';
import PostThumbs from '$components/PostThumbs/PostThumbs.svelte'; import News from '$components/News/News.svelte';
backLink.set(''); backLink.set('');
</script> </script>
@ -22,12 +22,7 @@
<TopPackages /> <TopPackages />
</section> </section>
<section class="mt-8"> <section class="mt-8">
<PostThumbs <News />
title="open-source news"
readMoreCta="Read more articles >"
readMoreLink="/"
type="news"
/>
</section> </section>
</div> </div>

View file

@ -2,7 +2,7 @@
import '$appcss'; import '$appcss';
import PageHeader from '$components/PageHeader/PageHeader.svelte'; import PageHeader from '$components/PageHeader/PageHeader.svelte';
import FeaturedCourses from '$components/FeaturedCourses/FeaturedCourses.svelte'; import FeaturedCourses from '$components/FeaturedCourses/FeaturedCourses.svelte';
import PostThumbs from '$components/PostThumbs/PostThumbs.svelte'; import EssentialWorkshops from '$components/EssentialWorkshops/EssentialWorkshops.svelte';
import { backLink } from '$libs/stores'; import { backLink } from '$libs/stores';
backLink.set('/'); backLink.set('/');
</script> </script>
@ -15,11 +15,6 @@
</section> </section>
<section class="mt-8"> <section class="mt-8">
<PostThumbs <EssentialWorkshops />
type="course"
title="ESSENTIAL WORKSHOPS"
readMoreCta="View all >"
readMoreLink="/"
/>
</section> </section>
</div> </div>

View file

@ -1,27 +0,0 @@
import { within, userEvent } from '@storybook/testing-library';
import Page from './Page.svelte';
export default {
title: 'Example/Page',
component: Page,
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/7.0/svelte/configure/story-layout
layout: 'fullscreen'
}
};
export const LoggedOut = {};
// More on interaction testing: https://storybook.js.org/docs/7.0/svelte/writing-tests/interaction-testing
export const LoggedIn = {
render: (args) => ({
Component: Page,
props: args
}),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const loginButton = await canvas.getByRole('button', {
name: /Log in/i
});
await userEvent.click(loginButton);
}
};

View file

@ -1,70 +0,0 @@
<script type="ts">
import './page.css';
import Header from '../Header/Header.svelte';
let user = null;
</script>
<article>
<Header
{user}
on:login={() => (user = { name: 'Jane Doe' })}
on:logout={() => (user = null)}
on:createAccount={() => (user = { name: 'Jane Doe' })}
/>
<section>
<h2>Pages in Storybook</h2>
<p>
We recommend building UIs with a
<a
href="https://blog.hichroma.com/component-driven-development-ce1109d56c8e"
target="_blank"
rel="noopener noreferrer"
>
<strong>component-driven</strong>
</a>
process starting with atomic components and ending with pages.
</p>
<p>
Render pages with mock data. This makes it easy to build and review page states without
needing to navigate to them in your app. Here are some handy patterns for managing page data
in Storybook:
</p>
<ul>
<li>
Use a higher-level connected component. Storybook helps you compose such data from the
"args" of child component stories
</li>
<li>
Assemble data in the page component from your services. You can mock these services out
using Storybook.
</li>
</ul>
<p>
Get a guided tutorial on component-driven development at
<a href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
Storybook tutorials
</a>
. Read more in the
<a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">docs</a>
.
</p>
<div class="tip-wrapper">
<span class="tip">Tip</span>
Adjust the width of the canvas with the
<svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path
d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0
01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0
010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
id="a"
fill="#999"
/>
</g>
</svg>
Viewports addon in the toolbar
</div>
</section>
</article>

View file

@ -1,71 +0,0 @@
@import '../app.css';
section {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 24px;
padding: 48px 20px;
margin: 0 auto;
max-width: 600px;
color: #333;
}
section h2 {
font-weight: 900;
font-size: 32px;
line-height: 1;
margin: 0 0 4px;
display: inline-block;
vertical-align: top;
}
section p {
margin: 1em 0;
}
section a {
text-decoration: none;
color: #1ea7fd;
}
section ul {
padding-left: 30px;
margin: 1em 0;
}
section li {
margin-bottom: 8px;
}
section .tip {
display: inline-block;
border-radius: 1em;
font-size: 11px;
line-height: 12px;
font-weight: 700;
background: #e7fdd8;
color: #66bf3c;
padding: 4px 12px;
margin-right: 10px;
vertical-align: top;
}
section .tip-wrapper {
font-size: 13px;
line-height: 20px;
margin-top: 40px;
margin-bottom: 40px;
}
section .tip-wrapper svg {
display: inline-block;
height: 12px;
width: 12px;
margin-right: 4px;
vertical-align: top;
margin-top: 3px;
}
section .tip-wrapper svg path {
fill: #1ea7fd;
}

View file

@ -0,0 +1,19 @@
import PanelHeader from './PanelHeader.svelte';
export default {
title: 'Example/PanelHeader',
component: PanelHeader,
render: (props) => ({
Component: PanelHeader,
props
})
};
// More on interaction testing: https://storybook.js.org/docs/7.0/svelte/writing-tests/interaction-testing
export const Example = {
args: {
title: 'Open-Source News',
ctaLabel: 'Read More News >',
ctaLink: '/'
}
};

View file

@ -0,0 +1,10 @@
<script lang="ts">
export let title: string;
export let ctaLabel: string;
export let ctaLink: string;
</script>
<header class="flex items-center justify-between border border-gray bg-black p-4 text-primary">
<span class="uppercase">{title}</span>
<a href={ctaLink} class="font-sono text-sm underline">{ctaLabel}</a>
</header>

View file

@ -0,0 +1,33 @@
import Posts from './Posts.svelte';
export default {
title: 'Example/Posts',
component: Posts,
render: ({ posts }) => ({
Component: Posts,
props: {
posts
}
})
};
// More on interaction testing: https://storybook.js.org/docs/7.0/svelte/writing-tests/interaction-testing
export const Example = {
args: {
posts: [
{
airtable_record_id: 'a',
link: 'https://google.com',
title: 'Tea Inc releases game changing api!',
sub_title: 'lorem ipsum dolor sit amet',
short_description: 'lorem ipsum dolor sit amet',
thumb_image_url: '/images/bored-ape.png',
thumb_image_name: 'borred-api.png',
created_at: new Date(),
updated_at: new Date(),
published_at: new Date(),
tags: ['news']
}
]
}
};

View file

@ -0,0 +1,23 @@
<script lang="ts">
import '../app.css';
import type { AirtablePost } from '../types';
export let posts: AirtablePost[] = [];
</script>
<ul class="flex flex-col bg-black">
{#each posts as article}
<li class="border border-t-0 border-gray p-4">
<article class="flex border border-gray">
<figure class="w-1/3">
<img src={article.thumb_image_url} alt={article.title} />
</figure>
<section class="p-4 font-sono">
<h1 class="text-xl text-primary">{article.title}</h1>
<p class="my-4 text-sm">{article.short_description}</p>
<a href={article.link} class="text-sm text-primary underline">Read more ...</a>
</section>
</article>
</li>
{/each}
</ul>