diff --git a/src/components/DocsComp/DocsComp.tsx b/src/components/DocsComp/DocsComp.tsx index 0b870c5..7deabe9 100644 --- a/src/components/DocsComp/DocsComp.tsx +++ b/src/components/DocsComp/DocsComp.tsx @@ -1,17 +1,23 @@ +import { lazy, Suspense } from 'react'; + import useSchemaExplorer from './lib/hooks/useSchemaExplorer'; -import DocsModal from './ui/DocsModal'; import DocsOverlay from './ui/DocsOverlay'; +import SuspenseFallback from './ui/SuspenseFallback'; type PropsType = { setIsDocsShown: React.Dispatch>; isShown: boolean; }; +const DocsModal = lazy(() => import('./ui/DocsModal')); + const DocsComp = ({ isShown, setIsDocsShown }: PropsType) => { const schemaExplorer = useSchemaExplorer(); return ( - + }> + + ); }; diff --git a/src/components/DocsComp/lib/helpers/getEndpointSchema.ts b/src/components/DocsComp/lib/helpers/getEndpointSchema.ts index d6890a0..a4854a3 100644 --- a/src/components/DocsComp/lib/helpers/getEndpointSchema.ts +++ b/src/components/DocsComp/lib/helpers/getEndpointSchema.ts @@ -1,22 +1,31 @@ -import { SetStateAction } from 'react'; +import { Dispatch, SetStateAction } from 'react'; import introspectionQuery from '@/shared/constants/introspectionQuery'; import { DocsSchemaType } from '@/shared/types'; export default async function getEndpointSchema( endpoint: string, - setter: (value: SetStateAction) => void, + schemaSetter: (value: SetStateAction) => void, + loadingSetter: Dispatch>, ) { - const response = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(introspectionQuery), - }); - if (response.ok && response.status === 200) { - const res = await response.json(); - return setter(res.data.__schema); + try { + loadingSetter(true); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(introspectionQuery), + }); + if (response.ok && response.status === 200) { + const res = await response.json(); + loadingSetter(false); + return schemaSetter(res.data.__schema); + } + loadingSetter(false); + return schemaSetter(null); + } catch (e) { + loadingSetter(false); + return schemaSetter(null); } - return setter(null); } diff --git a/src/components/DocsComp/ui/DocsLoader.tsx b/src/components/DocsComp/ui/DocsLoader.tsx new file mode 100644 index 0000000..bf0a144 --- /dev/null +++ b/src/components/DocsComp/ui/DocsLoader.tsx @@ -0,0 +1,15 @@ +import DocsModalLayout from '@/layouts/DocsModalLayout'; +import Spinner from '@/shared/ui/Spinner'; + +const DocsLoader = () => { + return ( + +
+ +

We are loading your docs...

+
+
+ ); +}; + +export default DocsLoader; diff --git a/src/components/DocsComp/ui/DocsModal.tsx b/src/components/DocsComp/ui/DocsModal.tsx index f6c9f98..142d07c 100644 --- a/src/components/DocsComp/ui/DocsModal.tsx +++ b/src/components/DocsComp/ui/DocsModal.tsx @@ -1,12 +1,14 @@ -import { Dispatch, SetStateAction, useEffect } from 'react'; +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import DocsModalLayout from '@/layouts/DocsModalLayout'; import { useAppContext } from '@/shared/Context/hooks'; import { DocsExplorerType, SchemaTypeObj } from '@/shared/types'; import CloseDocsBtn from '@components/DocsComp/ui/CloseDocsBtn'; +import DocsLoader from './DocsLoader'; import DocsRootComp from './DocsRootComp'; import DocsTypeComp from './DocsTypeComp'; -import SchemaFallaback from './schemaFallback'; +import SchemaFallbackUi from './SchemaFallbackUi'; import getEndpointSchema from '../lib/helpers/getEndpointSchema'; type PropsType = { @@ -16,11 +18,14 @@ type PropsType = { const DocsModal = ({ setIsDocsShown, explorer }: PropsType) => { const { currEndpoint, setEndpointSchema, endpointSchema } = useAppContext(); + const [isLoading, setIsLoading] = useState(false); + useEffect(() => { - getEndpointSchema(currEndpoint, setEndpointSchema); + getEndpointSchema(currEndpoint, setEndpointSchema, setIsLoading); }, [currEndpoint, setEndpointSchema]); - if (!endpointSchema) return ; + if (isLoading) return ; + if (!endpointSchema) return ; const content = explorer.isDocs() ? ( @@ -32,7 +37,7 @@ const DocsModal = ({ setIsDocsShown, explorer }: PropsType) => { ); return ( -
+ { setIsDocsShown((prev) => !prev); @@ -41,7 +46,7 @@ const DocsModal = ({ setIsDocsShown, explorer }: PropsType) => { className="absolute right-[20px] top-[20px] z-20" /> {content} -
+ ); }; diff --git a/src/components/DocsComp/ui/SchemaFallbackUi.tsx b/src/components/DocsComp/ui/SchemaFallbackUi.tsx new file mode 100644 index 0000000..1924f5e --- /dev/null +++ b/src/components/DocsComp/ui/SchemaFallbackUi.tsx @@ -0,0 +1,27 @@ +import { FC, SetStateAction } from 'react'; + +import DocsModalLayout from '@/layouts/DocsModalLayout'; + +import CloseDocsBtn from './CloseDocsBtn'; + +type PropsType = { + closeModal: (value: SetStateAction) => void; +}; + +const SchemaFallbackUi: FC = ({ closeModal }) => { + return ( + + { + closeModal((prev) => !prev); + }} + className="absolute right-[20px] top-[20px] z-20" + /> +
+

There is no schema at provided endpoint :(

+
+
+ ); +}; + +export default SchemaFallbackUi; diff --git a/src/components/DocsComp/ui/SuspenseFallback.tsx b/src/components/DocsComp/ui/SuspenseFallback.tsx new file mode 100644 index 0000000..9b6affc --- /dev/null +++ b/src/components/DocsComp/ui/SuspenseFallback.tsx @@ -0,0 +1,15 @@ +import DocsModalLayout from '@/layouts/DocsModalLayout'; +import Spinner from '@/shared/ui/Spinner'; + +const SuspenseFallback = () => { + return ( + +
+ +

Soon here will be docs section...

+
+
+ ); +}; + +export default SuspenseFallback; diff --git a/src/components/DocsComp/ui/schemaFallback.tsx b/src/components/DocsComp/ui/schemaFallback.tsx deleted file mode 100644 index d811091..0000000 --- a/src/components/DocsComp/ui/schemaFallback.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { FC, SetStateAction } from 'react'; - -import CloseDocsBtn from './CloseDocsBtn'; - -type PropsType = { - closeModal: (value: SetStateAction) => void; -}; - -const SchemaFallaback: FC = ({ closeModal }) => { - return ( -
- { - closeModal((prev) => !prev); - }} - className="absolute right-[20px] top-[20px] z-20" - /> -

There is no schema at provided endpoint :(

-
- ); -}; - -export default SchemaFallaback; diff --git a/src/layouts/DocsModalLayout.tsx b/src/layouts/DocsModalLayout.tsx new file mode 100644 index 0000000..8296160 --- /dev/null +++ b/src/layouts/DocsModalLayout.tsx @@ -0,0 +1,11 @@ +type PropsType = { children: JSX.Element | JSX.Element[] }; + +const DocsModalLayout = ({ children }: PropsType) => { + return ( +
+ {children} +
+ ); +}; + +export default DocsModalLayout; diff --git a/src/shared/ui/Spinner.tsx b/src/shared/ui/Spinner.tsx new file mode 100644 index 0000000..e5718ea --- /dev/null +++ b/src/shared/ui/Spinner.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { createComponent } from '@lit/react'; +import { MdCircularProgress } from '@material/web/progress/circular-progress'; + +const Spinner = createComponent({ + react: React, + tagName: 'md-circular-progress', + elementClass: MdCircularProgress, +}); + +export default Spinner; diff --git a/src/test/docsComponent/DocsComp.test.tsx b/src/test/docsComponent/DocsComp.test.tsx index 1b2b92e..0d66710 100644 --- a/src/test/docsComponent/DocsComp.test.tsx +++ b/src/test/docsComponent/DocsComp.test.tsx @@ -13,6 +13,9 @@ describe('Testing for docs component', () => { await act(async () => { fireEvent.click(showDocsBtn); }); + await new Promise((resolve) => { + setTimeout(() => resolve('done'), 1000); + }); expect(await screen.findByTestId('overlay')).toBeInTheDocument(); expect(await screen.findByText('Docs')).toBeInTheDocument(); expect(