Skip to content

Commit

Permalink
Merge pull request #4938 from thematters/feat/new-tag-component
Browse files Browse the repository at this point in the history
Feat/new tag component
  • Loading branch information
Kechicode authored Nov 18, 2024
2 parents 718d553 + 4b8721d commit ba5ab05
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 175 deletions.
2 changes: 1 addition & 1 deletion src/common/enums/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export enum TEST_ID {
DIGEST_USER_VERBOSE = 'digest/user/verbose',
DIGEST_TAG_CONCISE = 'digest/tag/concise',
DIGEST_TAG_FEED = 'digest/tag/feed',
DIGEST_TAG_FEED_COVER = 'digest/tag/feed/cover',
DIGEST_TAG_FEED_NUM_ARTICLES = 'digest/tag/feed/num-articles',
DIGEST_TAG_RICH = 'digest/tag/rich',
DIGEST_TAG_SIDEBAR = 'digest/tag/sidebar',
DIGRET_CIRCLE_PLAIN = 'digest/circle/plain',
Expand Down
25 changes: 6 additions & 19 deletions src/components/TagDigest/Feed/Feed.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import mockRouter from 'next-router-mock'
import { describe, expect, it } from 'vitest'

import { TEST_ID } from '~/common/enums'
import { abbr } from '~/common/utils/number/abbr'
import { render, screen } from '~/common/utils/test'
import { TagDigest } from '~/components'
import { MOCK_TAG } from '~/stories/mocks'
Expand All @@ -18,24 +19,10 @@ describe('<TagDigest.Feed>', () => {
const $name = screen.getAllByText(MOCK_TAG.content)[0] // duplicated items in the mock
expect($name).toBeInTheDocument()

const $articleCount = screen.getByText(MOCK_TAG.numArticles)
expect($articleCount).toBeInTheDocument()

const $authorCount = screen.getByText(MOCK_TAG.numAuthors)
expect($authorCount).toBeInTheDocument()

const $cover = screen.getByTestId(TEST_ID.DIGEST_TAG_FEED_COVER)
expect($cover).toBeInTheDocument()
mockRouter.push('/')
$cover.click()
expect(mockRouter.asPath).toContain(MOCK_TAG.slug)

const $articleList = screen.getByRole('list')
expect($articleList).toBeInTheDocument()
const $articleListItems = screen.getAllByRole('listitem')
expect($articleListItems.length).toBe(4)
mockRouter.push('/')
$articleListItems[0].click()
expect(mockRouter.asPath).toContain(MOCK_TAG.articles.edges[0].node.slug)
const $numArticles = screen.getByTestId(
TEST_ID.DIGEST_TAG_FEED_NUM_ARTICLES
)
expect($numArticles).toBeInTheDocument()
expect($numArticles).toHaveTextContent(`(${abbr(MOCK_TAG.numArticles, 2)})`)
})
})
118 changes: 17 additions & 101 deletions src/components/TagDigest/Feed/index.tsx
Original file line number Diff line number Diff line change
@@ -1,133 +1,49 @@
import gql from 'graphql-tag'
import Link from 'next/link'

import { ReactComponent as IconDraft } from '@/public/static/icons/24px/draft.svg'
import { ReactComponent as IconUser } from '@/public/static/icons/24px/user.svg'
import IMAGE_TAG_COVER from '@/public/static/images/tag-cover.png'
import { TEST_ID } from '~/common/enums'
import { captureClicks, numAbbr, toPath } from '~/common/utils'
import {
Card,
CardProps,
Icon,
PlainTag,
ResponsiveImage,
TextIcon,
} from '~/components'
import { toPath } from '~/common/utils'
import { abbr } from '~/common/utils/number/abbr'
import { TagDigestFeedTagFragment } from '~/gql/graphql'

import styles from './styles.module.css'

export type TagDigestFeedProps = {
tag: TagDigestFeedTagFragment
} & CardProps
onClick?: () => void
}

const fragments = {
tag: gql`
fragment TagDigestFeedTag on Tag {
id
content
cover
numArticles
numAuthors
articles(input: { first: 3 }) {
edges {
cursor
node {
id
title
slug
shortHash
author {
id
userName
}
}
}
}
}
`,
}

const Feed = ({ tag, ...cardProps }: TagDigestFeedProps) => {
const Feed = ({ tag, onClick }: TagDigestFeedProps) => {
const path = toPath({
page: 'tagDetail',
tag,
})

const articles = tag.articles.edges
const numArticles = abbr(tag.numArticles, 2)

return (
<Card
{...path}
spacing={[8, 8]}
bgColor="none"
bgActiveColor="none"
borderRadius="xtight"
testId={TEST_ID.DIGEST_TAG_FEED}
{...cardProps}
>
<section className={styles.container}>
<header className={styles.header}>
<PlainTag
tag={tag}
textIconProps={{ color: 'black', weight: 'medium', size: 14 }}
/>

<section className={styles.nums}>
<TextIcon
icon={<Icon icon={IconUser} color="greyDark" size={12} />}
size={12}
spacing={4}
color="greyDark"
>
{numAbbr(tag.numAuthors)}
</TextIcon>

<TextIcon
icon={<Icon icon={IconDraft} color="greyDark" size={12} />}
size={12}
spacing={4}
color="greyDark"
>
{numAbbr(tag.numArticles)}
</TextIcon>
</section>
</header>

<section className={styles.content}>
<ul className={styles.articles}>
{articles?.map(({ node, cursor }) => (
<li key={node.id}>
<Link
{...toPath({ page: 'articleDetail', article: node })}
legacyBehavior
>
<a className={styles.title} onClick={captureClicks}>
{node.title}
</a>
</Link>
</li>
))}
</ul>

<section
className={styles.cover}
data-test-id={TEST_ID.DIGEST_TAG_FEED_COVER}
>
<Link {...path} legacyBehavior>
<a>
<ResponsiveImage
url={tag.cover || IMAGE_TAG_COVER.src}
width={144}
height={144}
/>
</a>
</Link>
</section>
</section>
</section>
</Card>
<Link {...path} legacyBehavior onClick={onClick}>
<a className={styles.tag} data-test-id={TEST_ID.DIGEST_TAG_FEED}>
<span className={styles.name}>{tag.content}</span>&nbsp;
<span
className={styles.nums}
data-test-id={TEST_ID.DIGEST_TAG_FEED_NUM_ARTICLES}
>
({numArticles})
</span>
</a>
</Link>
)
}

Expand Down
56 changes: 10 additions & 46 deletions src/components/TagDigest/Feed/styles.module.css
Original file line number Diff line number Diff line change
@@ -1,53 +1,17 @@
.container {
position: relative;
}

.header {
@mixin flex-center-space-between;
}

.nums {
display: inline-flex;
flex-shrink: 0;
.tag {
@mixin transition;

& > * {
margin-left: var(--sp8);
}
}
display: flex;

.content {
@mixin flex-start-space-between;

margin-top: var(--sp8);
}

.cover {
position: relative;
display: inline-flex;
flex-shrink: 0;
width: 4.5rem;
height: 4.5rem;
margin-left: var(--sp8);

& img {
@mixin object-fit-cover;

background-color: var(--color-grey-lighter);
border-radius: 0.5rem;
}
}

.articles {
& .title {
& .name {
@mixin line-clamp;

font-size: var(--text13);
line-height: 1.5rem;
color: var(--color-grey-dark);
font-size: var(--text16);
line-height: 1.5rem; /* 24px */
}

&:hover,
&:focus {
color: var(--color-black);
}
& .nums {
color: var(--color-grey);
word-break: keep-all;
}
}
9 changes: 9 additions & 0 deletions src/stories/components/TagDigest/Feed.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,12 @@ export const Default = Template.bind({})
Default.args = {
tag: MOCK_TAG as any,
}

export const LongName = Template.bind({})
LongName.args = {
tag: {
...MOCK_TAG,
content:
'Matters.Town 是立足去中心化生態建立的,一個代碼開源、創作者自治的寫作社區。',
} as any,
}
9 changes: 1 addition & 8 deletions src/views/Tags/Feed.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _get from 'lodash/get'

import { analytics, mergeConnections, toPath } from '~/common/utils'
import { analytics, mergeConnections } from '~/common/utils'
import {
EmptyTag,
InfiniteScroll,
Expand Down Expand Up @@ -75,13 +75,6 @@ const Feed = ({ type }: Props) => {
<li key={tag.id} className={styles.listItem}>
<TagDigest.Feed
tag={tag}
spacing={[8, 0]}
href={
toPath({
page: 'tagDetail',
tag, // : node,
}).href
}
onClick={() =>
analytics.trackEvent('click_feed', {
type: trackingType,
Expand Down

0 comments on commit ba5ab05

Please sign in to comment.