diff --git a/.storybook/main.js b/.storybook/main.js
index c41d607..1c28e9f 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -3,11 +3,13 @@ const setRootPath = (_path) => path.resolve(process.cwd(), _path);
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
+ staticDirs: ['../public'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
+ 'storybook-addon-next-router',
],
framework: '@storybook/react',
diff --git a/.storybook/preview.js b/.storybook/preview.js
index f478437..91af775 100644
--- a/.storybook/preview.js
+++ b/.storybook/preview.js
@@ -1,19 +1,27 @@
import ko from 'axe-core/locales/ko.json';
import React from 'react';
+import { RouterContext } from 'next/dist/shared/lib/router-context';
import { GlobalStyle } from 'styles/GlobalStyle';
import { ThemeProvider } from '@emotion/react';
import { theme } from 'theme/theme';
+import * as NextImage from 'next/image';
+import { StoreProvider } from 'store';
export const decorators = [
(Story) => (
-
-
+
+
+
+
),
];
export const parameters = {
+ nextRouter: {
+ Provider: RouterContext.Provider,
+ },
a11y: {
config: { locale: ko },
},
@@ -25,3 +33,17 @@ export const parameters = {
},
},
};
+
+const OriginalNextImage = NextImage.default;
+
+Object.defineProperty(NextImage, 'default', {
+ configurable: true,
+ value: (props) => (
+
+ ),
+});
diff --git a/package.json b/package.json
index 7a82f72..264b1b5 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"firebase-tools": "^10.3.0",
"prettier": "^2.5.1",
"redux-logger": "^3.0.6",
+ "storybook-addon-next-router": "^3.1.1",
"typescript": "4.6.2"
}
}
diff --git a/public/images/default.jpg b/public/images/default.jpg
new file mode 100644
index 0000000..7c76e09
Binary files /dev/null and b/public/images/default.jpg differ
diff --git a/public/images/food-image-temp.jpg b/public/images/food-image-temp.jpg
new file mode 100644
index 0000000..6969f2c
Binary files /dev/null and b/public/images/food-image-temp.jpg differ
diff --git a/public/images/no-image.jpg b/public/images/no-image.jpg
new file mode 100644
index 0000000..9f3cbc1
Binary files /dev/null and b/public/images/no-image.jpg differ
diff --git a/src/assets/images/default.jpg b/src/assets/images/default.jpg
new file mode 100644
index 0000000..7c76e09
Binary files /dev/null and b/src/assets/images/default.jpg differ
diff --git a/src/assets/images/food-image-temp.jpg b/src/assets/images/food-image-temp.jpg
new file mode 100644
index 0000000..6969f2c
Binary files /dev/null and b/src/assets/images/food-image-temp.jpg differ
diff --git a/src/assets/images/no-image.jpg b/src/assets/images/no-image.jpg
new file mode 100644
index 0000000..9f3cbc1
Binary files /dev/null and b/src/assets/images/no-image.jpg differ
diff --git a/src/components/Card/Card.stories.tsx b/src/components/Card/Card.stories.tsx
new file mode 100644
index 0000000..6138256
--- /dev/null
+++ b/src/components/Card/Card.stories.tsx
@@ -0,0 +1,26 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { Card } from './Card';
+
+export default {
+ title: 'Card',
+ component: Card,
+ args: {
+ id: 1,
+ type: 'wide',
+ background: 'white',
+ hasSummary: true,
+ headingPosition: 'bottomLeft',
+ title: 'hi',
+ },
+ parameters: {
+ nextRouter: {
+ query: {
+ id: 1,
+ },
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => ;
+
+export const Default = Template.bind({});
diff --git a/src/components/Card/Card.styled.tsx b/src/components/Card/Card.styled.tsx
new file mode 100644
index 0000000..31fab7a
--- /dev/null
+++ b/src/components/Card/Card.styled.tsx
@@ -0,0 +1,161 @@
+import { css } from '@emotion/react';
+import styled from '@emotion/styled';
+import { media, pxToRem } from 'utils';
+import { CardContainerProps, CardFigcaptionProps, CardFigureImgProps } from './Card.types';
+
+const inlineBlock = css`
+ text-align: center;
+ width: 100%;
+ ${media.desktop} {
+ display: inline-block;
+ }
+`;
+
+const typeCss = {
+ wide: css`
+ width: 100%;
+ height: 50vw;
+ object-fit: cover;
+
+ ${media.desktop} {
+ width: 100%;
+ height: 250px;
+ }
+ `,
+ square: css`
+ width: 100%;
+
+ ${media.mobile} {
+ height: 50vw;
+ }
+ ${media.desktop} {
+ height: rem(200px);
+ @media (max-width: 1547px) {
+ height: 20vw;
+ }
+
+ @media (max-width: 1100px) {
+ height: 25vw;
+ }
+ }
+ object-fit: cover;
+ `,
+
+ smallSquare: css`
+ width: 100%;
+ aspect-ratio: 1 / 1;
+ object-fit: cover;
+ ${media.mobile} {
+ aspect-ratio: 16 / 9;
+ }
+
+ @media (max-width: 1056px) {
+ aspect-ratio: 16 / 9;
+ }
+ `,
+};
+
+const headingPositionCss = {
+ bottomLeft: css`
+ font-size: ${pxToRem(20)};
+ left: 0;
+ `,
+ bottomCenter: css`
+ font-size: ${pxToRem(20)};
+ text-align: center;
+ `,
+ topLeft: css`
+ font-size: ${pxToRem(24)};
+ color: orange;
+ order: -1;
+ `,
+};
+
+export const CardLink = styled.a`
+ width: 100%;
+ border: none;
+ background-color: transparent;
+ text-align: left;
+ ${media.mobile} {
+ width: 100%;
+ }
+`;
+
+export const CardContainer = styled.div`
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ padding: ${pxToRem(16)};
+ position: relative;
+ cursor: pointer;
+
+ background: ${({ $background }) => ($background === 'white' ? 'white' : 'none')};
+ box-shadow: ${({ $background }) => $background === 'white' && '0px 4px 4px rgba(0, 0, 0, 0.25);'};
+
+ ${({ $type }) => $type === 'square' && inlineBlock}
+`;
+
+export const CardFigureImgContainer = styled.div`
+ ${({ $type }) => typeCss[$type]}
+`;
+
+export const CardFigcaption = styled.figcaption`
+ font-size: ${pxToRem(18)};
+ font-weight: bold;
+ padding: ${pxToRem(16)} 0;
+ width: 100%;
+
+ ${({ $headingPosition }) => headingPositionCss[$headingPosition]}
+`;
+
+export const CardSummary = styled.div`
+ line-height: ${pxToRem(24)};
+ ${media.desktop} {
+ display: -webkit-box;
+ word-wrap: break-word;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ &:after {
+ content: '';
+ color: black;
+ position: absolute;
+ display: block;
+ width: 100%;
+ height: 30%;
+ bottom: 0;
+ left: 0;
+ background: linear-gradient(to top, #fff 50%, transparent);
+ }
+ }
+ ${media.mobile} {
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ word-wrap: break-word;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+`;
+
+export const CardSummaryText = styled.p`
+ margin-top: 0;
+ margin-bottom: ${pxToRem(8)};
+`;
+
+export const CardButton = styled.button`
+ ${media.mobile} {
+ display: none;
+ }
+
+ padding: ${pxToRem(8)} 0;
+ font-size: ${pxToRem(24)};
+ z-index: 1;
+ margin: 0;
+
+ display: flex;
+ gap: 10px;
+ border: none;
+ background: none;
+ justify-content: center;
+`;
diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx
new file mode 100644
index 0000000..172d09b
--- /dev/null
+++ b/src/components/Card/Card.tsx
@@ -0,0 +1,58 @@
+import Image from 'next/image';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import { useState } from 'react';
+import { HiCursorClick } from 'react-icons/hi';
+import { excludeTags } from 'utils';
+import {
+ CardButton,
+ CardContainer,
+ CardFigcaption,
+ CardFigureImgContainer,
+ CardLink,
+ CardSummary,
+ CardSummaryText,
+} from './Card.styled';
+import { CardProps } from './Card.types';
+
+export const Card = ({
+ id = 0,
+ type,
+ background,
+ hasSummary,
+ headingPosition,
+ // image,
+ imgSrc = '/images/no-image.jpg',
+ title,
+ summary = '',
+}: CardProps): JSX.Element => {
+ return (
+
+
+
+
+
+
+
+ {title}
+
+ {hasSummary && (
+ <>
+
+ {excludeTags(summary)
+ .split('. ')
+ .map((text, index, texts) => (
+ {text + (index < texts.length - 1 ? '.' : '')}
+ ))}
+
+
+ Click for more
+
+
+ >
+ )}
+
+
+
+ );
+};
diff --git a/src/components/Card/Card.types.ts b/src/components/Card/Card.types.ts
new file mode 100644
index 0000000..ae87b28
--- /dev/null
+++ b/src/components/Card/Card.types.ts
@@ -0,0 +1,27 @@
+type CardType = 'wide' | 'square' | 'smallSquare';
+type CardBackgroundType = 'white' | 'none';
+type CardHeadingPositionType = 'bottomLeft' | 'bottomCenter' | 'topLeft';
+
+export interface CardProps {
+ id: string | number;
+ type: CardType;
+ background: CardBackgroundType;
+ hasSummary: boolean;
+ headingPosition: CardHeadingPositionType;
+ imgSrc: string;
+ title: string;
+ summary?: string;
+}
+
+export interface CardContainerProps {
+ $type: CardType;
+ $background: CardBackgroundType;
+}
+
+export interface CardFigureImgProps {
+ $type: CardType;
+}
+
+export interface CardFigcaptionProps {
+ $headingPosition: CardHeadingPositionType;
+}
diff --git a/src/components/CookingInfo/CookingInfo.tsx b/src/components/CookingInfo/CookingInfo.tsx
index 3b59083..bc014b6 100644
--- a/src/components/CookingInfo/CookingInfo.tsx
+++ b/src/components/CookingInfo/CookingInfo.tsx
@@ -9,7 +9,7 @@ export const CookingInfo = ({ time = 0, count = 0 }: CookingInfoProps): JSX.Elem
Cooking Time
- {time} miuntes
+ {time} minutes
diff --git a/src/components/HotRecipes/HotRecipes.stories.tsx b/src/components/HotRecipes/HotRecipes.stories.tsx
new file mode 100644
index 0000000..c599e91
--- /dev/null
+++ b/src/components/HotRecipes/HotRecipes.stories.tsx
@@ -0,0 +1,11 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { HotRecipes } from './HotRecipes';
+
+export default {
+ title: 'HotRecipes',
+ component: HotRecipes,
+} as ComponentMeta;
+
+const Template: ComponentStory = () => ;
+
+export const Default = Template.bind({});
diff --git a/src/components/HotRecipes/HotRecipes.styled.tsx b/src/components/HotRecipes/HotRecipes.styled.tsx
new file mode 100644
index 0000000..d6741ec
--- /dev/null
+++ b/src/components/HotRecipes/HotRecipes.styled.tsx
@@ -0,0 +1,55 @@
+import styled from '@emotion/styled';
+import { Heading } from '..';
+import { media, pxToRem } from 'utils';
+
+export const HotRecipesSection = styled.section`
+ ${media.desktop} {
+ flex-grow: 2;
+ padding-top: ${pxToRem(20)};
+ height: 80vh;
+ }
+`;
+
+export const HotRecipesHeader = styled(Heading)`
+ ${media.mobile} {
+ margin-top: ${pxToRem(40)};
+ }
+
+ ${media.desktop} {
+ padding-left: ${pxToRem(16)};
+ }
+
+ @media (min-width: 1546px) {
+ padding-left: ${pxToRem(28)};
+ }
+`;
+
+export const HotRecipesCardList = styled.ul`
+ display: flex;
+ flex-direction: column;
+ ${media.mobile} {
+ margin: 0;
+ }
+ ${media.desktop} {
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+ height: 99%;
+ overflow: auto;
+ }
+`;
+
+export const HotRecipesCardItem = styled.li`
+ text-align: center;
+ width: ${pxToRem(290)};
+
+ @media (max-width: 1547px) {
+ width: 50%;
+ text-align: center;
+ }
+
+ @media (max-width: 1100px) {
+ width: 100%;
+ text-align: center;
+ }
+`;
diff --git a/src/components/HotRecipes/HotRecipes.tsx b/src/components/HotRecipes/HotRecipes.tsx
new file mode 100644
index 0000000..031e998
--- /dev/null
+++ b/src/components/HotRecipes/HotRecipes.tsx
@@ -0,0 +1,37 @@
+import { SkeletonCard, Card } from '..';
+import { useHotRecipes } from 'hooks';
+import { HotRecipesCardItem, HotRecipesCardList, HotRecipesHeader, HotRecipesSection } from './HotRecipes.styled';
+
+export const HotRecipes = () => {
+ const { hotRecipes, loading } = useHotRecipes();
+
+ //TODO: customAPi 완료후 여기에 타입 달아주기
+ const renderCards = ({ recipeId, image, title }) => {
+ if (loading) {
+ return ;
+ } else {
+ return (
+
+ );
+ }
+ };
+
+ return (
+
+ Hot Recipes
+
+ {hotRecipes.map((recipe, idx) => {
+ return {renderCards(recipe)};
+ })}
+
+
+ );
+};
diff --git a/src/components/RandomRecipe/RandomRecipe.stories.tsx b/src/components/RandomRecipe/RandomRecipe.stories.tsx
new file mode 100644
index 0000000..d66ec47
--- /dev/null
+++ b/src/components/RandomRecipe/RandomRecipe.stories.tsx
@@ -0,0 +1,11 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { RandomRecipe } from './RandomRecipe';
+
+export default {
+ title: 'RandomRecipe',
+ component: RandomRecipe,
+} as ComponentMeta;
+
+const Template: ComponentStory = () => ;
+
+export const Default = Template.bind({});
diff --git a/src/components/RandomRecipe/RandomRecipe.styled.tsx b/src/components/RandomRecipe/RandomRecipe.styled.tsx
new file mode 100644
index 0000000..8dfad1d
--- /dev/null
+++ b/src/components/RandomRecipe/RandomRecipe.styled.tsx
@@ -0,0 +1,51 @@
+import styled from '@emotion/styled';
+import { Button } from '../';
+import { media, pxToRem } from 'utils';
+import { GiPerspectiveDiceSixFacesRandom } from 'react-icons/gi';
+
+export const RandomRecipeSection = styled.section`
+ flex-grow: 1;
+ position: relative;
+ font-family: inherit;
+
+ ${media.desktop} {
+ height: 80vh;
+ max-width: 30%;
+ min-width: ${pxToRem(512)};
+ padding-top: ${pxToRem(20)};
+ margin: 0 40px;
+ }
+`;
+
+export const RandomRecipeButton = styled(Button)`
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: ${pxToRem(16)};
+
+ ${media.mobile} {
+ margin: ${pxToRem(8)} 0 ${pxToRem(16)} ${pxToRem(16)};
+ font-size: ${pxToRem(12)};
+ padding: ${pxToRem(1)} ${pxToRem(6)};
+ }
+
+ ${media.desktop} {
+ margin: ${pxToRem(36)} 0 ${pxToRem(36)} ${pxToRem(36)};
+ padding: ${pxToRem(6)} ${pxToRem(18)};
+ }
+`;
+
+export const RandomDiceIcon = styled(GiPerspectiveDiceSixFacesRandom)`
+ font-size: 25px;
+`;
+
+export const RandomRecipeCardWrapper = styled.div`
+ margin-top: ${pxToRem(16)};
+
+ ${media.desktop} {
+ height: 70vh;
+ }
+`;
diff --git a/src/components/RandomRecipe/RandomRecipe.tsx b/src/components/RandomRecipe/RandomRecipe.tsx
new file mode 100644
index 0000000..e1f4a98
--- /dev/null
+++ b/src/components/RandomRecipe/RandomRecipe.tsx
@@ -0,0 +1,47 @@
+import { useRandomRecipe } from 'hooks';
+import { RandomRecipe as RandomRecipeType } from 'store/services/types/queries';
+import { Heading, SkeletonCard, Card } from '..';
+import {
+ RandomDiceIcon,
+ RandomRecipeButton,
+ RandomRecipeCardWrapper,
+ RandomRecipeSection,
+} from './RandomRecipe.styled';
+
+export const RandomRecipe = (): JSX.Element => {
+ const { recipe, error, isLoading, handleClick } = useRandomRecipe();
+
+ console.log(recipe);
+ console.log(error);
+
+ const renderCard = () => {
+ if (isLoading) {
+ return ;
+ } else {
+ const { id, title, summary, image } = recipe as RandomRecipeType;
+ return (
+
+ );
+ }
+ };
+
+ return (
+
+ Random Recipe
+
+
+ REROLL
+
+ {renderCard()}
+
+ );
+};
diff --git a/src/components/SkeletonCard/SkeletonCard.styled.tsx b/src/components/SkeletonCard/SkeletonCard.styled.tsx
index dd6dfb1..80bd7f8 100644
--- a/src/components/SkeletonCard/SkeletonCard.styled.tsx
+++ b/src/components/SkeletonCard/SkeletonCard.styled.tsx
@@ -27,22 +27,23 @@ const inlineBlock = css`
justify-content: center;
`;
-const squareType = css`
- width: ${pxToRem(200)};
- height: ${pxToRem(200)};
- background: gray;
-`;
-
-const wideType = css`
- width: 100%;
- height: 50vw;
- background: gray;
-
- ${media.desktop} {
+const typeCss = {
+ square: css`
+ width: ${pxToRem(200)};
+ height: ${pxToRem(200)};
+ background: gray;
+ `,
+ wide: css`
width: 100%;
- height: 250px;
- }
-`;
+ height: 50vw;
+ background: gray;
+
+ ${media.desktop} {
+ width: 100%;
+ height: 250px;
+ }
+ `,
+};
const hasSummaryTrue = css`
display: block;
@@ -60,19 +61,20 @@ const hasSummaryFalse = css`
display: none;
`;
-const bottomLeft = css`
- background: gray;
- left: 0;
-`;
-const bottomCenter = css`
- background: gray;
- align-self: center;
-`;
-
-const topLeft = css`
- background: gray;
- order: -1;
-`;
+const headingPositionCss = {
+ bottomLeft: css`
+ background: gray;
+ left: 0;
+ `,
+ bottomCenter: css`
+ background: gray;
+ align-self: center;
+ `,
+ topLeft: css`
+ background: gray;
+ order: -1;
+ `,
+};
export const SkeletonContainer = styled.div`
${(props) => props.$type === 'square' && inlineBlock}
@@ -91,7 +93,7 @@ export const SkeletonCardWrapper = styled.div`
`;
export const SkeletonImage = styled.div`
- ${(props) => (props.$type === 'square' ? squareType : wideType)}
+ ${({ $type }) => typeCss[$type]}
overflow: hidden;
position: relative;
@@ -101,12 +103,7 @@ export const SkeletonImage = styled.div`
`;
export const SkeletonTitle = styled.div`
- ${(props) =>
- props.$headingPosition === 'topLeft'
- ? topLeft
- : props.$headingPosition === 'bottomCenter'
- ? bottomCenter
- : bottomLeft}
+ ${({ $headingPosition }) => headingPositionCss[$headingPosition]}
margin: ${pxToRem(16)} 0 !important;
width: 70%;
@@ -122,5 +119,5 @@ export const SkeletonTitle = styled.div`
`;
export const SkeletonSummary = styled.div`
- ${(props) => (props.$hasSummary ? hasSummaryTrue : hasSummaryFalse)}
+ ${({ $hasSummary }) => ($hasSummary ? hasSummaryTrue : hasSummaryFalse)}
`;
diff --git a/src/components/index.ts b/src/components/index.ts
index e05baaf..46add14 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -11,3 +11,6 @@ export * from './Header/Header';
export * from './EmptyPage/EmptyPage';
export * from './SearchForm/SearchForm';
export * from './SkeletonCard/SkeletonCard';
+export * from './Card/Card';
+export * from './RandomRecipe/RandomRecipe';
+export * from './HotRecipes/HotRecipes';
diff --git a/src/hooks/.keep b/src/hooks/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
new file mode 100644
index 0000000..3604ec7
--- /dev/null
+++ b/src/hooks/index.ts
@@ -0,0 +1,3 @@
+export * from './usePageNum';
+export * from './useRandomRecipe';
+export * from './useHotRecipes';
diff --git a/src/hooks/useHotRecipes.ts b/src/hooks/useHotRecipes.ts
new file mode 100644
index 0000000..7e1d9c8
--- /dev/null
+++ b/src/hooks/useHotRecipes.ts
@@ -0,0 +1,18 @@
+import { useEffect, useState } from 'react';
+
+export const useHotRecipes = () => {
+ const [hotRecipes, setHotRecipes] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ // 이 부분 custom API 완료후 적용!
+ // useEffect(() => {
+ // (async () => {
+ // setHotRecipes(await getHotRecipes());
+ // })();
+ // setTimeout(() => {
+ // setLoading(false);
+ // }, 1200);
+ // }, []);
+
+ return { hotRecipes, loading };
+};
diff --git a/src/hooks/useRandomRecipe.ts b/src/hooks/useRandomRecipe.ts
new file mode 100644
index 0000000..b1ea182
--- /dev/null
+++ b/src/hooks/useRandomRecipe.ts
@@ -0,0 +1,36 @@
+import { useCallback, useEffect, useState } from 'react';
+import { useGetRandomRecipeQuery } from 'store/services';
+import _ from 'lodash';
+import { RandomRecipe } from 'store/services/types/queries';
+
+export const useRandomRecipe = () => {
+ const [savedRecipe, setSavedRecipe] = useState({});
+ const [recipe, setRecipe] = useState({});
+
+ const { data, error, isLoading } = useGetRandomRecipeQuery(2);
+
+ useEffect(() => {
+ if (data) {
+ const { recipes } = data;
+ setRecipe(recipes[0]);
+ setSavedRecipe(recipes[1]);
+ }
+ }, []);
+
+ const getRecipe = useCallback(() => {
+ setRecipe(savedRecipe);
+ const { data } = useGetRandomRecipeQuery(1);
+ if (data) {
+ const { recipes } = data;
+ setSavedRecipe(recipes[0]);
+ }
+ }, []);
+
+ const handleClick = () => {
+ _.throttle(() => {
+ getRecipe();
+ }, 300);
+ };
+
+ return { recipe, error, isLoading, handleClick };
+};
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index ce52bf1..4a874c8 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -4,8 +4,12 @@ import Head from 'next/head';
import Image from 'next/image';
const Home: NextPage = () => {
- const { data, error, isLoading } = useGetRandomRecipeQuery(1);
- console.log(data);
+ const {
+ data: { recipes },
+ error,
+ isLoading,
+ } = useGetRandomRecipeQuery(1);
+ console.log(recipes);
console.log(error);
console.log(isLoading);
diff --git a/src/store/services/index.ts b/src/store/services/index.ts
index 06de4c9..dd05900 100644
--- a/src/store/services/index.ts
+++ b/src/store/services/index.ts
@@ -16,7 +16,6 @@ export const twoSpoonApi = createApi({
endpoints: (builder) => ({
getRandomRecipe: builder.query({
query: (number = 1) => `recipes/random?number=${number}`,
- // transformResponse: (response: { data: RandomRecipeQuery }) => response.data,
}),
}),
});
diff --git a/src/utils/dom.ts b/src/utils/dom.ts
index 97ac82b..53e994e 100644
--- a/src/utils/dom.ts
+++ b/src/utils/dom.ts
@@ -1,4 +1,6 @@
export const $ = (selector: string) => {
- const element = document.querySelector(selector);
- return element as T;
+ if (typeof window !== 'undefined') {
+ const element = document.querySelector(selector);
+ return element as T;
+ }
};
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 96ee6ae..b601cfd 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,3 +1,4 @@
export * from './style';
export * from './dom';
+export * from './misc';
export * from './tabbable';
diff --git a/src/utils/misc.ts b/src/utils/misc.ts
new file mode 100644
index 0000000..2093923
--- /dev/null
+++ b/src/utils/misc.ts
@@ -0,0 +1 @@
+export const excludeTags = (content: string) => content.replace(/<[^>]*>/g, '');
diff --git a/yarn.lock b/yarn.lock
index dd675ba..29d45e3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12661,6 +12661,13 @@ store2@^2.12.0:
resolved "https://registry.yarnpkg.com/store2/-/store2-2.13.2.tgz#01ad8802ca5b445b9c316b55e72645c13a3cd7e3"
integrity sha512-CMtO2Uneg3SAz/d6fZ/6qbqqQHi2ynq6/KzMD/26gTkiEShCcpqFfTHgOxsE0egAq6SX3FmN4CeSqn8BzXQkJg==
+storybook-addon-next-router@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/storybook-addon-next-router/-/storybook-addon-next-router-3.1.1.tgz#46623ca36b450745c3517f5cdc4bf30fa4a4a930"
+ integrity sha512-Z14dED37vNXkN7+VY80HhF9itGReWoBAlKREHEk2By/dW7zSSqcSyXYV4bDMXIMAFYHMaA1svcBC1idVG8FhAw==
+ dependencies:
+ tslib "^2.3.0"
+
stream-browserify@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"