Skip to content

Commit

Permalink
feat(AuthorSidebar): add Collection
Browse files Browse the repository at this point in the history
  • Loading branch information
wlliaml committed Jan 9, 2024
1 parent 90ea777 commit 5fda3b8
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/common/enums/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum TEST_ID {
DIGEST_ARTICLE_FEED_FOOTER_PIN = 'digest/article/feed/footer/pin',
DIGEST_ARTICLE_NOTICE = 'digest/article/notice',
DIGEST_ARTICLE_SIDEBAR = 'digest/article/sidebar',
DIGEST_ARTICLE_AUTHOR_SIDEBAR = 'digest/article/author-sidebar',
DIGEST_ARTICLE_TITLE = 'digest/article/title',
DIGEST_ARTICLE_PUBLISHED = 'digest/article/published',
DIGEST_ARTICLE_PUBLISHED_READER_COUNT = 'digest/article/published/reader-count',
Expand Down
1 change: 1 addition & 0 deletions src/common/utils/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ type ArticleFeedType =
| 'wallet'
| 'related_donations'
| 'circle_detail'
| 'article_detail_author-sidebar-collection'

type CollectionFeedType =
| 'user_collection'
Expand Down
18 changes: 18 additions & 0 deletions src/components/ArticleDigest/AuthorSidebar/AuthorSidebar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it } from 'vitest'

import { TEST_ID } from '~/common/enums'
import { render, screen } from '~/common/utils/test'
import { ArticleDigestAuthorSidebar } from '~/components'
import { MOCK_ARTILCE } from '~/stories/mocks'

describe('<ArticleDigest.Sidebar>', () => {
it('should render an ArticleDigest.Sidebar', () => {
render(<ArticleDigestAuthorSidebar article={MOCK_ARTILCE} />)

const $digest = screen.getByTestId(TEST_ID.DIGEST_ARTICLE_AUTHOR_SIDEBAR)
expect($digest).toBeInTheDocument()

const $title = screen.getByRole('heading', { name: MOCK_ARTILCE.title })
expect($title).toBeInTheDocument()
})
})
15 changes: 15 additions & 0 deletions src/components/ArticleDigest/AuthorSidebar/Placeholder/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import styles from './styles.module.css'

const Placeholder = () => {
return (
<section className={styles.container}>
<section className={styles.title}>
<section className={styles.text}></section>
<section className={styles.text}></section>
</section>
<section className={styles.image}></section>
</section>
)
}

export default Placeholder
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.container {
@mixin flex-start-space-between;

margin: var(--spacing-tight) 0;

& .title {
display: flex;
flex-direction: column;
gap: var(--spacing-base-tight);

& .text {
width: 9.375rem;
height: 1rem;
background-color: var(--color-grey-lighter);
}
}

& .image {
width: 2.75rem;
height: 2.75rem;
background-color: var(--color-grey-lighter);
border-radius: 0.25rem;
}
}
91 changes: 91 additions & 0 deletions src/components/ArticleDigest/AuthorSidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import classNames from 'classnames'
import gql from 'graphql-tag'

import { TEST_ID } from '~/common/enums'
import { capitalizeFirstLetter, toPath } from '~/common/utils'
import { LinkWrapper, ResponsiveImage } from '~/components'
import { ArticleDigestAuthorSidebarArticleFragment } from '~/gql/graphql'

import { ArticleDigestTitle, ArticleDigestTitleTextSize } from '../Title'
import Placeholder from './Placeholder'
import styles from './styles.module.css'

export type ArticleDigestAuthorSidebarProps = {
article: ArticleDigestAuthorSidebarArticleFragment
collectionId?: string
titleTextSize?: ArticleDigestTitleTextSize
titleColor?: 'greyDarker' | 'black'
}

const fragments = {
article: gql`
fragment ArticleDigestAuthorSidebarArticle on Article {
id
articleState: state
title
slug
mediaHash
cover
...ArticleDigestTitleArticle
}
${ArticleDigestTitle.fragments.article}
`,
}

export const ArticleDigestAuthorSidebar = ({
article,
collectionId,

titleTextSize = 'mdS',
titleColor = 'greyDarker',
}: ArticleDigestAuthorSidebarProps) => {
const { articleState: state } = article
const isBanned = state === 'banned'
const cover = !isBanned ? article.cover : null
const containerClasses = classNames({
[styles.container]: true,
[styles.hasCover]: !!cover,
})
const path = toPath({
page: 'articleDetail',
article,
collectionId,
})

const headerClasses = classNames({
[styles[`textColor${capitalizeFirstLetter(titleColor)}`]]: !!titleColor,
})

return (
<section
className={containerClasses}
data-test-id={TEST_ID.DIGEST_ARTICLE_AUTHOR_SIDEBAR}
>
<header className={headerClasses}>
<ArticleDigestTitle
article={article}
textSize={titleTextSize}
collectionId={collectionId}
textWeight="normal"
is="h3"
/>
</header>

{cover && (
<LinkWrapper {...path} disabled={isBanned}>
<aside className={styles.cover}>
<ResponsiveImage
url={cover}
width={44}
height={44}
disableAnimation={true}
/>
</aside>
</LinkWrapper>
)}
</section>
)
}

ArticleDigestAuthorSidebar.Placeholder = Placeholder
ArticleDigestAuthorSidebar.fragments = fragments
47 changes: 47 additions & 0 deletions src/components/ArticleDigest/AuthorSidebar/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.container {
margin-top: var(--spacing-tight);
margin-bottom: var(--spacing-tight);

@mixin flex-start-space-between;

& header {
flex-grow: 1;

& h3 {
line-height: 1.375rem;
}

&.textColorBlack {
& a {
color: var(--color-black);
}
}

&.textColorGreyDarker {
& a {
color: var(--color-grey-darker);
}
}
}

&.hasCover {
position: relative;
min-height: 2.75rem;
padding-right: calc(2.75rem + var(--spacing-loose));

& .cover {
position: absolute;
top: 0;
right: 0;
display: initial;
width: 2.75rem;
height: 2.75rem;

& img {
@mixin object-fit-cover;

border-radius: var(--spacing-xx-tight);
}
}
}
}
1 change: 1 addition & 0 deletions src/components/ArticleDigest/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './Archived'
export * from './AuthorSidebar'
export * from './Card'
export * from './Dropdown'
export * from './Feed'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
.container {
display: flex;
gap: var(--spacing-loose);
align-items: center;
align-items: start;
margin-top: var(--spacing-x-loose);

& > a {
align-self: center;
}

& .info {
@mixin flex-start-center;

flex-direction: column;
gap: var(--spacing-xx-tight);
color: var(--color-black);

& .displayName {
font-size: var(--font-size-sm);
Expand Down
7 changes: 4 additions & 3 deletions src/views/ArticleDetail/AuthorSidebar/Collection/gql.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import gql from 'graphql-tag'

import { ArticleDigestSidebar } from '~/components'

export const AUTHOR_SIDEBAR_COLLECTION = gql`
query AuthorSidebarCollection($id: ID!, $after: String) {
node(input: { id: $id }) {
Expand All @@ -17,13 +19,12 @@ export const AUTHOR_SIDEBAR_COLLECTION = gql`
edges {
cursor
node {
id
title
cover
...ArticleDigestSidebarArticle
}
}
}
}
}
}
${ArticleDigestSidebar.fragments.article}
`
67 changes: 59 additions & 8 deletions src/views/ArticleDetail/AuthorSidebar/Collection/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import { toPath } from '~/common/utils'
import { LinkWrapper, QueryError, Throw404, usePublicQuery } from '~/components'
import { analytics, mergeConnections, toPath } from '~/common/utils'
import {
ArticleDigestAuthorSidebar,
InfiniteScroll,
LinkWrapper,
List,
QueryError,
Throw404,
usePublicQuery,
} from '~/components'
import {
ArticleDetailPublicQuery,
AuthorSidebarCollectionQuery,
} from '~/gql/graphql'

import { FeedPlaceholder } from '../Placeholder'
import {
ArticleDigestAuthorSidebarFeedPlaceholder,
FeedPlaceholder,
} from '../Placeholder'
import { AUTHOR_SIDEBAR_COLLECTION } from './gql'
import styles from './styles.module.css'

Expand All @@ -18,12 +29,10 @@ export const Collection = ({ article, collectionId }: CollectionProps) => {
/**
* Data Fetching
*/
const { data, loading, error } = usePublicQuery<AuthorSidebarCollectionQuery>(
AUTHOR_SIDEBAR_COLLECTION,
{
const { data, loading, error, fetchMore } =
usePublicQuery<AuthorSidebarCollectionQuery>(AUTHOR_SIDEBAR_COLLECTION, {
variables: { id: collectionId },
}
)
})
const collection = data?.node!

/**
Expand All @@ -41,6 +50,28 @@ export const Collection = ({ article, collectionId }: CollectionProps) => {
return <Throw404 />
}

// pagination
const connectionPath = 'node.articles'
const { edges, pageInfo } = collection?.articles || {}

// load next page
const loadMore = async () => {
analytics.trackEvent('load_more', {
type: 'article_detail_author-sidebar-collection',
location: edges?.length || 0,
})

await fetchMore({
variables: { after: pageInfo?.endCursor },
updateQuery: (previousResult, { fetchMoreResult }) =>
mergeConnections({
oldData: previousResult,
newData: fetchMoreResult,
path: connectionPath,
}),
})
}

const collectionDetailPath = toPath({
page: 'collectionDetail',
userName: article?.author.userName || '',
Expand All @@ -61,6 +92,26 @@ export const Collection = ({ article, collectionId }: CollectionProps) => {
</section>
</LinkWrapper>
)}
<section className={styles.feed}>
<InfiniteScroll
hasNextPage={pageInfo?.hasNextPage}
loadMore={loadMore}
loader={<ArticleDigestAuthorSidebarFeedPlaceholder />}
>
<List>
{edges?.map(({ node, cursor }, i) => (
<List.Item key={cursor}>
<ArticleDigestAuthorSidebar
article={node}
titleTextSize="sm"
collectionId={collectionId}
titleColor={node.id === article?.id ? 'black' : 'greyDarker'}
/>
</List.Item>
))}
</List>
</InfiniteScroll>
</section>
</section>
)
}
10 changes: 10 additions & 0 deletions src/views/ArticleDetail/AuthorSidebar/Collection/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,14 @@
font-size: var(--font-size-xs);
color: var(--color-grey-darker);
}

& .feed {
@mixin border-top-grey;

max-height: 18.5rem;
padding-top: var(--spacing-tight);
padding-bottom: var(--spacing-x-loose);
overflow-y: auto;
border-top-style: dashed;
}
}
Loading

0 comments on commit 5fda3b8

Please sign in to comment.