Skip to content

Commit

Permalink
feat: redesign articles list in burger view
Browse files Browse the repository at this point in the history
  • Loading branch information
sashtje committed Oct 12, 2023
1 parent 7a5be70 commit b76b3d2
Show file tree
Hide file tree
Showing 12 changed files with 458 additions and 124 deletions.
8 changes: 4 additions & 4 deletions src/entities/Article/ui/ArticleList/ArticleList.module.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.articleList {
padding-top: 30px;
}

.BIG {
.card {
margin-bottom: 30px;
}

.row {
margin-bottom: 30px;
}
}

.SMALL {
Expand Down
115 changes: 24 additions & 91 deletions src/entities/Article/ui/ArticleListItem/ArticleListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import { HTMLAttributeAnchorTarget, memo } from 'react';
import { useTranslation } from 'react-i18next';

import { classNames } from '@/shared/lib/classNames';
import { Text } from '@/shared/ui/deprecated/Text';
import { Card } from '@/shared/ui/deprecated/Card';
import { Avatar } from '@/shared/ui/deprecated/Avatar';
import { Button } from '@/shared/ui/deprecated/Button';
import { Icon } from '@/shared/ui/deprecated/Icon';
import EyeIcon from '@/shared/assets/icons/eye-20-20.svg';
import { AppLink } from '@/shared/ui/deprecated/AppLink';
import { getRouteArticlesDetails } from '@/shared/const/router';
import { AppImage } from '@/shared/ui/redesigned/AppImage';
import { Skeleton } from '@/shared/ui/deprecated/Skeleton';
import { ToggleFeatures } from '@/shared/lib/features';

import { ArticleBlockType, ArticleView } from '../../model/consts/consts';
import { Article, ArticleTextBlock } from '../../model/types/article';
import cls from './ArticleListItem.module.scss';
import { ArticleTextBlockComponent } from '../../ui/ArticleTextBlockComponent/ArticleTextBlockComponent';
import { ArticleListItemRedesigned } from './ArticleListItemRedesigned/ArticleListItemRedesigned';
import { ArticleListItemDeprecated } from './ArticleListItemDeprecated/ArticleListItemDeprecated';
import { ArticleView } from '../../model/consts/consts';
import { Article } from '../../model/types/article';

interface ArticleListItemProps {
className?: string;
Expand All @@ -28,82 +17,26 @@ interface ArticleListItemProps {
export const ArticleListItem = memo((props: ArticleListItemProps) => {
const { className, article, view, target } = props;

const { t } = useTranslation('articles');

const types = <Text className={cls.types} text={article.type.join(', ')} />;
const views = (
<>
<Text className={cls.views} text={String(article.views)} />
<Icon Svg={EyeIcon} />
</>
);

if (view === ArticleView.BIG) {
const textBlock = article.blocks.find(
(block) => block.type === ArticleBlockType.TEXT,
) as ArticleTextBlock;

return (
<div
data-testid="ArticleListItem"
className={classNames(cls.articleListItem, {}, [className, cls[view]])}
>
<Card className={cls.card}>
<div className={cls.header}>
<Avatar size={30} src={article.user.avatar} alt={article.user.username} />
<Text text={article.user.username} className={cls.username} />
<Text text={article.createdAt} className={cls.date} />
</div>

<Text title={article.title} className={cls.title} />
{types}
<AppImage
fallback={<Skeleton width="100%" height={250} />}
src={article.img}
alt={article.title}
className={cls.img}
/>
{!!textBlock && <ArticleTextBlockComponent block={textBlock} className={cls.textBlock} />}

<div className={cls.footer}>
<AppLink target={target} to={getRouteArticlesDetails(article.id)}>
<Button>{t('Читать далее')}</Button>
</AppLink>

{views}
</div>
</Card>
</div>
);
}

return (
<AppLink
data-testid="ArticleListItem"
target={target}
to={getRouteArticlesDetails(article.id)}
className={classNames(cls.articleListItem, {}, [className, cls[view]])}
>
<Card className={cls.card}>
<div className={cls.imageWrapper}>
<AppImage
fallback={<Skeleton width={200} height={200} />}
className={cls.img}
src={article.img}
alt={article.title}
/>

<Text className={cls.date} text={article.createdAt} />
</div>

<div className={cls.infoWrapper}>
{types}
{views}
</div>

<Text className={cls.title} text={article.title} />
</Card>
</AppLink>
<ToggleFeatures
feature="isAppRedesigned"
on={
<ArticleListItemRedesigned
className={className}
article={article}
view={view}
target={target}
/>
}
off={
<ArticleListItemDeprecated
className={className}
article={article}
view={view}
target={target}
/>
}
/>
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react';

import { ArticleView } from '../../model/consts/consts';
import { article } from '../../mocks/data';
import { ArticleListItem } from './ArticleListItem';
import { ArticleListItem } from './ArticleListItemDeprecated';

export default {
title: 'entities/Article/ArticleListItem',
title: 'entities/Article/ArticleListItemRedesigned',
component: ArticleListItem,
argTypes: {
backgroundColor: { control: 'color' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { HTMLAttributeAnchorTarget, memo } from 'react';
import { useTranslation } from 'react-i18next';

import { classNames } from '@/shared/lib/classNames';
import { Text } from '@/shared/ui/deprecated/Text';
import { Card } from '@/shared/ui/deprecated/Card';
import { Avatar } from '@/shared/ui/deprecated/Avatar';
import { Button } from '@/shared/ui/deprecated/Button';
import { Icon } from '@/shared/ui/deprecated/Icon';
import EyeIcon from '@/shared/assets/icons/eye-20-20.svg';
import { AppLink } from '@/shared/ui/deprecated/AppLink';
import { getRouteArticlesDetails } from '@/shared/const/router';
import { AppImage } from '@/shared/ui/redesigned/AppImage';
import { Skeleton } from '@/shared/ui/deprecated/Skeleton';

import { ArticleBlockType, ArticleView } from '../../../model/consts/consts';
import { Article, ArticleTextBlock } from '../../../model/types/article';
import cls from './ArticleListItemDeprecated.module.scss';
import { ArticleTextBlockComponent } from '../../../ui/ArticleTextBlockComponent/ArticleTextBlockComponent';

interface ArticleListItemProps {
className?: string;
article: Article;
view: ArticleView;
target?: HTMLAttributeAnchorTarget;
}

export const ArticleListItemDeprecated = memo((props: ArticleListItemProps) => {
const { className, article, view, target } = props;

const { t } = useTranslation('articles');

const types = <Text className={cls.types} text={article.type.join(', ')} />;
const views = (
<>
<Text className={cls.views} text={String(article.views)} />
<Icon Svg={EyeIcon} />
</>
);

if (view === ArticleView.BIG) {
const textBlock = article.blocks.find(
(block) => block.type === ArticleBlockType.TEXT,
) as ArticleTextBlock;

return (
<div
data-testid="ArticleListItemRedesigned"
className={classNames(cls.articleListItem, {}, [className, cls[view]])}
>
<Card className={cls.card}>
<div className={cls.header}>
<Avatar size={30} src={article.user.avatar} alt={article.user.username} />
<Text text={article.user.username} className={cls.username} />
<Text text={article.createdAt} className={cls.date} />
</div>

<Text title={article.title} className={cls.title} />
{types}
<AppImage
fallback={<Skeleton width="100%" height={250} />}
src={article.img}
alt={article.title}
className={cls.img}
/>
{!!textBlock && <ArticleTextBlockComponent block={textBlock} className={cls.textBlock} />}

<div className={cls.footer}>
<AppLink target={target} to={getRouteArticlesDetails(article.id)}>
<Button>{t('Читать далее')}</Button>
</AppLink>

{views}
</div>
</Card>
</div>
);
}

return (
<AppLink
data-testid="ArticleListItemRedesigned"
target={target}
to={getRouteArticlesDetails(article.id)}
className={classNames(cls.articleListItem, {}, [className, cls[view]])}
>
<Card className={cls.card}>
<div className={cls.imageWrapper}>
<AppImage
fallback={<Skeleton width={200} height={200} />}
className={cls.img}
src={article.img}
alt={article.title}
/>

<Text className={cls.date} text={article.createdAt} />
</div>

<div className={cls.infoWrapper}>
{types}
{views}
</div>

<Text className={cls.title} text={article.title} />
</Card>
</AppLink>
);
});

ArticleListItemDeprecated.displayName = 'ArticleListItemDeprecated';
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.BIG {
.img {
width: 100%;
max-height: 250px;
object-fit: cover;
}

.textBlock {
max-height: 72px;
overflow-y: hidden;
}
}

.SMALL {
width: 230px;
transition: 0.2s;
cursor: pointer;

&:hover {
opacity: 0.8;
transform: scale(1.01);

.date {
display: block;
}
}

.imageWrapper {
width: 200px;
height: 200px;
position: relative;
}

.img {
width: 200px;
height: 200px;
object-fit: cover;
}

.infoWrapper {
display: flex;
align-items: center;
margin-top: 8px;
}

.types p {
width: 115px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.views {
margin-left: auto;
margin-right: 8px;
}

.title {
margin-top: 8px;

p {
width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { ComponentMeta, ComponentStory } from '@storybook/react';

import { ArticleView } from '../../../model/consts/consts';
import { article } from '../../../mocks/data';
import { ArticleListItemRedesigned } from './ArticleListItemRedesigned';

export default {
title: 'entities/Article/redesigned/ArticleListItemRedesigned',
component: ArticleListItemRedesigned,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof ArticleListItemRedesigned>;

const Template: ComponentStory<typeof ArticleListItemRedesigned> = (args) => (
<ArticleListItemRedesigned {...args} />
);

export const NormalSmall = Template.bind({});
NormalSmall.args = {
article,
view: ArticleView.SMALL,
};

export const NormalBig = Template.bind({});
NormalBig.args = {
article,
view: ArticleView.BIG,
};
Loading

0 comments on commit b76b3d2

Please sign in to comment.