Skip to content

Commit

Permalink
feat: add articles blocks, code shared comp
Browse files Browse the repository at this point in the history
  • Loading branch information
TomatoVan committed Dec 22, 2023
1 parent 433b630 commit 8629867
Show file tree
Hide file tree
Showing 26 changed files with 363 additions and 97 deletions.
1 change: 1 addition & 0 deletions src/app/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ body {
padding: 20px 20px 20px 45px;
height: calc(100vh - var(--navbar-height));
overflow-y: auto;
width: 100%;
}
3 changes: 3 additions & 0 deletions src/app/styles/themes/dark.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
// skeleton
--skeleton-color: #1515ad;
--skeleton-shadow: #2b2be8;

// code
--code-bg: #1212a1;
}
3 changes: 3 additions & 0 deletions src/app/styles/themes/normal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
// skeleton
--skeleton-color: #fff;
--skeleton-shadow: rgba(0 0 0 / 20%);

// code
--code-bg: #fff;
}
7 changes: 7 additions & 0 deletions src/app/styles/themes/orange.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@
--secondary-color: #d01f0e;
--inverted-primary-color: #dbd5dc;
--inverted-secondary-color: #faf4fb;

// skeleton
--skeleton-color: #fff;
--skeleton-shadow: rgba(0 0 0 / 20%);

// code
--code-bg: #fff;
}
2 changes: 1 addition & 1 deletion src/entities/Article/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { ArticleDetails } from './ui/ArticleDetails/ArticleDetails';

export { articleDetailsActions, articleDetailsReducer } from './model/slice/articleDetailsSlice';
export type { Article } from './model/types/article';
export type { ArticleDetailsSchema } from './model/types/articleDetailsSchema';
49 changes: 49 additions & 0 deletions src/entities/Article/model/selectors/articleDetails.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { StateSchema } from 'app/providers/StoreProvider';
import {
getArticlesDetailsData,
getArticlesDetailsIsError,
getArticlesDetailsIsLoading,
} from './articleDetails';

describe('articleDetails.test', () => {
test('should return data', () => {
const data = {
id: '1',
title: 'subtitle',
};
const state: DeepPartial<StateSchema> = {
articleDetails: {
data,
},
};
expect(getArticlesDetailsData(state as StateSchema)).toEqual(data);
});
test('should work with empty state data', () => {
const state: DeepPartial<StateSchema> = {};
expect(getArticlesDetailsData(state as StateSchema)).toEqual(undefined);
});
test('should return error', () => {
const state: DeepPartial<StateSchema> = {
articleDetails: {
error: 'error',
},
};
expect(getArticlesDetailsIsError(state as StateSchema)).toEqual('error');
});
test('should work with empty state error', () => {
const state: DeepPartial<StateSchema> = {};
expect(getArticlesDetailsIsError(state as StateSchema)).toEqual(undefined);
});
test('should return isLoading', () => {
const state: DeepPartial<StateSchema> = {
articleDetails: {
isLoading: true,
},
};
expect(getArticlesDetailsIsLoading(state as StateSchema)).toEqual(true);
});
test('should work with empty state isLoading', () => {
const state: DeepPartial<StateSchema> = {};
expect(getArticlesDetailsIsLoading(state as StateSchema)).toEqual(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { memo } from 'react';
import { Code } from 'shared/ui/Code/Code';
import { ArticleCodeBlock } from '../../model/types/article';

interface ArticleCodeBlockComponentProps {
className?: string;
block?: ArticleCodeBlock;
}

export const ArticleCodeBlockComponent = memo(({ className, block }: ArticleCodeBlockComponentProps) => {
const { t } = useTranslation();
return (
<div className={classNames('', {}, [className])}>
{block?.code && <Code text={block?.code} />}
</div>
);
});

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { StoreDecorator } from 'shared/config/storybook/StoreDecorator/StoreDecorator';
import { Article } from 'entities/Article';
import { ArticleBlockType, ArticleType } from '../../model/types/article';
import { ArticleDetails } from './ArticleDetails';

export default {
title: 'shared/ArticleDetails',
title: 'entities/ArticleDetails',
component: ArticleDetails,
argTypes: {
backgroundColor: { control: 'color' },
Expand All @@ -13,5 +16,62 @@ export default {

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

const article: Article = {
id: '1',
title: 'Javascript news',
subtitle: 'Что нового в JS за 2022 год?',
img: 'https://teknotower.com/wp-content/uploads/2020/11/js.png',
views: 1022,
createdAt: '26.02.2022',
type: [ArticleType.IT],
blocks: [
{
id: '1',
type: ArticleBlockType.TEXT,
title: 'Заголовок этого блока',
paragraphs: [
'Программа, которую по традиции называют «Hello, world!», очень проста. Она выводит куда-либо фразу «Hello, world!», или другую подобную, средствами некоего языка.',
'JavaScript — это язык, программы на котором можно выполнять в разных средах. В нашем случае речь идёт о браузерах и о серверной платформе Node.js. Если до сих пор вы не написали ни строчки кода на JS и читаете этот текст в браузере, на настольном компьютере, это значит, что вы буквально в считанных секундах от своей первой JavaScript-программы.',
'Существуют и другие способы запуска JS-кода в браузере. Так, если говорить об обычном использовании программ на JavaScript, они загружаются в браузер для обеспечения работы веб-страниц. Как правило, код оформляют в виде отдельных файлов с расширением .js, которые подключают к веб-страницам, но программный код можно включать и непосредственно в код страницы. Всё это делается с помощью тега <script>. Когда браузер обнаруживает такой код, он выполняет его. Подробности о теге script можно посмотреть на сайте w3school.com. В частности, рассмотрим пример, демонстрирующий работу с веб-страницей средствами JavaScript, приведённый на этом ресурсе. Этот пример можно запустить и средствами данного ресурса (ищите кнопку Try it Yourself), но мы поступим немного иначе. А именно, создадим в каком-нибудь текстовом редакторе (например — в VS Code или в Notepad++) новый файл, который назовём hello.html, и добавим в него следующий код:',
],
},
{
id: '4',
type: ArticleBlockType.CODE,
code: '<!DOCTYPE html>\n<html>\n <body>\n <p id="hello"></p>\n\n <script>\n document.getElementById("hello").innerHTML = "Hello, world!";\n </script>\n </body>\n</html>;',
},
{
id: '5',
type: ArticleBlockType.TEXT,
title: 'Заголовок этого блока',
paragraphs: [
'Программа, которую по традиции называют «Hello, world!», очень проста. Она выводит куда-либо фразу «Hello, world!», или другую подобную, средствами некоего языка.',
'Существуют и другие способы запуска JS-кода в браузере. Так, если говорить об обычном использовании программ на JavaScript, они загружаются в браузер для обеспечения работы веб-страниц. Как правило, код оформляют в виде отдельных файлов с расширением .js, которые подключают к веб-страницам, но программный код можно включать и непосредственно в код страницы. Всё это делается с помощью тега <script>. Когда браузер обнаруживает такой код, он выполняет его. Подробности о теге script можно посмотреть на сайте w3school.com. В частности, рассмотрим пример, демонстрирующий работу с веб-страницей средствами JavaScript, приведённый на этом ресурсе. Этот пример можно запустить и средствами данного ресурса (ищите кнопку Try it Yourself), но мы поступим немного иначе. А именно, создадим в каком-нибудь текстовом редакторе (например — в VS Code или в Notepad++) новый файл, который назовём hello.html, и добавим в него следующий код:',
],
},
],
};

export const Normal = Template.bind({});
Normal.args = {};
Normal.decorators = [StoreDecorator({
articleDetails: {
data: article,
},
})];

export const Loading = Template.bind({});
Loading.args = {};
Loading.decorators = [StoreDecorator({
articleDetails: {
isLoading: true,
},
})];

export const Error = Template.bind({});
Error.args = {};
Error.decorators = [StoreDecorator({
articleDetails: {
error: 'error',
},
})];
53 changes: 42 additions & 11 deletions src/entities/Article/ui/ArticleDetails/ArticleDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import {
DynamicModuleLoader,
ReducersList,
} from 'shared/lib/components/DynamicModuleLoared/DynamicModuleLoared';
import { memo, useEffect } from 'react';
import { DynamicModuleLoader, ReducersList } from 'shared/lib/components/DynamicModuleLoared/DynamicModuleLoared';
import { memo, useCallback, useEffect } from 'react';
import { useAppDispatch } from 'shared/lib/hook/useAppDispatch/useAppDispatch';
import { useSelector } from 'react-redux';
import {
Expand All @@ -15,11 +12,11 @@ import { Avatar } from 'shared/ui/Avatar/Avatar';
import EyeIcon from 'shared/assets/icons/eye-20-20.svg';
import CalendarIcon from 'shared/assets/icons/calendar-20-20.svg';
import { Icon } from 'shared/ui/Icon/Icon';
import {
getArticlesDetailsData,
getArticlesDetailsIsError,
getArticlesDetailsIsLoading,
} from '../../model/selectors/articleDetails';
import { ArticleImageBlockComponent } from '../ArticleImageBlockComponent/ArticleImageBlockComponent';
import { ArticleTextBlockComponent } from '../ArticleTextBlockComponent/ArticleTextBlockComponent';
import { ArticleCodeBlockComponent } from '../ArticleCodeBlockComponent/ArticleCodeBlockComponent';
import { ArticleBlock, ArticleBlockType } from '../../model/types/article';
import { getArticlesDetailsData, getArticlesDetailsIsError, getArticlesDetailsIsLoading } from '../../model/selectors/articleDetails';
import { fetchArticleById } from '../../services/fetchArticleById/fetchArticleById';
import { articleDetailsReducer } from '../../model/slice/articleDetailsSlice';
import cls from './ArticleDetails.module.scss';
Expand All @@ -41,8 +38,41 @@ export const ArticleDetails = memo(({ className, id }: ArticleDetailsProps) => {
const isLoading = useSelector(getArticlesDetailsIsLoading);
const error = useSelector(getArticlesDetailsIsError);

const renderBlock = useCallback((block: ArticleBlock) => {
switch (block.type) {
case ArticleBlockType.CODE:
return (
<ArticleCodeBlockComponent
key={block.id}
className={cls.block}
block={block}
/>
);
case ArticleBlockType.IMAGE:
return (
<ArticleImageBlockComponent
key={block.id}
className={cls.block}
block={block}
/>
);
case ArticleBlockType.TEXT:
return (
<ArticleTextBlockComponent
key={block.id}
className={cls.block}
block={block}
/>
);
default:
return null;
}
}, []);

useEffect(() => {
dispatch(fetchArticleById(id));
if (__PROJECT__ !== 'storybook') {
dispatch(fetchArticleById(id));
}
}, [dispatch, id]);

let content;
Expand Down Expand Up @@ -90,6 +120,7 @@ export const ArticleDetails = memo(({ className, id }: ArticleDetailsProps) => {
<Icon Svg={CalendarIcon} className={cls.icon} />
<Text text={article?.createdAt} />
</div>
{article?.blocks?.map(renderBlock)}

</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.ArticleImageBlockComponent {

max-width: 100%;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { memo } from 'react';
import { ArticleImageBlock } from 'entities/Article/model/types/article';
import { Text, TextAlign } from 'shared/ui/Text/Text';
import cls from './ArticleImageBlockComponent.module.scss';

interface ArticleImageBlockComponentProps {
className?: string;
className?: string;
block?: ArticleImageBlock;
}

export const ArticleImageBlockComponent = ({ className }: ArticleImageBlockComponentProps) => {
const { t } = useTranslation();
return (
<div className={classNames(cls.ArticleImageBlockComponent, {}, [className])} />
);
};
export const ArticleImageBlockComponent = memo(({ className, block }: ArticleImageBlockComponentProps) => (
<div className={classNames(cls.ArticleImageBlockComponent, {}, [className])}>
<img alt={block?.title} src={block?.src} className={cls.img} />
{block?.title && (
<Text title={block?.title} className={cls.title} align={TextAlign.CENTER} />
)}
</div>
));
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.ArticleTextBlockComponent {
.title {
margin-bottom: 16px;
}

.text {
margin-top: 8px;
}

This file was deleted.

Loading

0 comments on commit 8629867

Please sign in to comment.