-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from Quiddlee/feat/add_docs_section
Feat/add docs section
- Loading branch information
Showing
25 changed files
with
8,707 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import useSchemaExplorer from './lib/hooks/useSchemaExplorer'; | ||
import DocsModal from './ui/DocsModal'; | ||
import DocsOverlay from './ui/DocsOverlay'; | ||
|
||
type PropsType = { | ||
setIsDocsShown: React.Dispatch<React.SetStateAction<boolean>>; | ||
isShown: boolean; | ||
}; | ||
|
||
const DocsComp = ({ isShown, setIsDocsShown }: PropsType) => { | ||
const schemaExplorer = useSchemaExplorer(); | ||
return ( | ||
<DocsOverlay isShown={isShown} setIsDocsShown={setIsDocsShown} explorer={schemaExplorer}> | ||
<DocsModal setIsDocsShown={setIsDocsShown} explorer={schemaExplorer} /> | ||
</DocsOverlay> | ||
); | ||
}; | ||
|
||
export default DocsComp; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { SchemaTypeFieldOfType } from '@/shared/types'; | ||
|
||
function getTypeName(type: SchemaTypeFieldOfType) { | ||
if (type?.name) return type?.name; | ||
|
||
const modifiers: string[] = []; | ||
|
||
if (type?.kind === 'NON_NULL') modifiers.push('!'); | ||
if (type?.kind === 'LIST') modifiers.push('[]'); | ||
|
||
function getOfTypeName(argOfType: SchemaTypeFieldOfType): number | string { | ||
if (argOfType?.name) { | ||
return modifiers.push(argOfType.name); | ||
} | ||
if (argOfType?.kind === 'NON_NULL') modifiers.push('!'); | ||
if (argOfType?.kind === 'LIST') modifiers.push('[]'); | ||
|
||
return getOfTypeName(argOfType?.ofType as SchemaTypeFieldOfType); | ||
} | ||
|
||
getOfTypeName(type?.ofType as SchemaTypeFieldOfType); | ||
|
||
const output = modifiers.reduceRight((acc, curr) => { | ||
if (curr === '!') return `${acc}!`; | ||
if (curr === '[]') return `[${acc}]`; | ||
return acc + curr; | ||
}); | ||
|
||
return output; | ||
} | ||
|
||
export default getTypeName; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
function separateString(inputString: string) { | ||
const regex = /([^a-zA-Z]*)([a-zA-Z]+)([^a-zA-Z]*$)/; | ||
const matches = inputString.match(regex); | ||
|
||
if (matches) { | ||
const [, beforeLetters, letters, afterLetters] = matches; | ||
return [beforeLetters, letters, afterLetters]; | ||
} | ||
return ['', inputString, '']; | ||
} | ||
|
||
export default separateString; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { useState } from 'react'; | ||
|
||
function useSchemaExplorer() { | ||
const [typeNames, setTypeNames] = useState(['Docs']); | ||
|
||
function current() { | ||
return typeNames[typeNames.length - 1]; | ||
} | ||
function prev() { | ||
return typeNames[typeNames.length - 2]; | ||
} | ||
function next(elem: string) { | ||
setTypeNames((prevEl) => [...prevEl, elem]); | ||
} | ||
function back() { | ||
if (typeNames.length > 1) { | ||
setTypeNames((prevEl) => prevEl.filter((_, i) => i < prevEl.length - 1)); | ||
} | ||
} | ||
function isDocs() { | ||
return current() === 'Docs'; | ||
} | ||
function setInitState() { | ||
setTypeNames(['Docs']); | ||
} | ||
|
||
return { current, next, prev, isDocs, back, setInitState }; | ||
} | ||
|
||
export default useSchemaExplorer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { FC } from 'react'; | ||
|
||
import Icon from '@/shared/ui/Icon'; | ||
|
||
type PropsType = { onClick: () => void; title: string }; | ||
|
||
const BackDocsBtn: FC<PropsType> = ({ onClick, title }: PropsType) => { | ||
return ( | ||
<button type="button" onClick={onClick} className="flex items-center gap-1 rounded-full p-2 hover:bg-slate-500/30"> | ||
<Icon>arrow_back_ios</Icon> | ||
<span className="text-2xl">{title}</span> | ||
</button> | ||
); | ||
}; | ||
|
||
export default BackDocsBtn; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { FC } from 'react'; | ||
|
||
import Icon from '@/shared/ui/Icon'; | ||
import IconButton from '@/shared/ui/IconButton'; | ||
|
||
type PropsType = { onClick: () => void; className: string }; | ||
|
||
const CloseDocsBtn: FC<PropsType> = ({ onClick, className }) => { | ||
return ( | ||
<IconButton onClick={onClick} className={className}> | ||
<Icon>close</Icon> | ||
</IconButton> | ||
); | ||
}; | ||
|
||
export default CloseDocsBtn; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// AFTER LOGIC FOR SAVING AND FETCHING ENDPOINT SHEMA WILL BE ADDED - MUST REMOVE SCHEMA IMPORT AND REPLACE IT FOR DOWNLOADED SCHEMA IN FURTHER CODE | ||
|
||
import { Dispatch, SetStateAction } from 'react'; | ||
|
||
import { swapiSchema } from '@/shared/constants/schemaData'; | ||
import { DocsExplorerType, SchemaTypeObj } from '@/shared/types'; | ||
import CloseDocsBtn from '@components/DocsComp/ui/CloseDocsBtn'; | ||
|
||
import DocsRootComp from './DocsRootComp'; | ||
import DocsTypeComp from './DocsTypeComp'; | ||
|
||
type PropsType = { | ||
setIsDocsShown: Dispatch<SetStateAction<boolean>>; | ||
explorer: DocsExplorerType; | ||
}; | ||
|
||
const DocsModal = ({ setIsDocsShown, explorer }: PropsType) => { | ||
const content = explorer.isDocs() ? ( | ||
<DocsRootComp types={swapiSchema.data.__schema.types as SchemaTypeObj[]} explorer={explorer} /> | ||
) : ( | ||
<DocsTypeComp | ||
explorer={explorer} | ||
currType={swapiSchema.data.__schema.types.find((elem) => elem.name === explorer.current()) as SchemaTypeObj} | ||
/> | ||
); | ||
return ( | ||
<section className="relative z-20 h-[100dvh] w-[420px] cursor-auto rounded-r-[28px] bg-surface p-3"> | ||
<CloseDocsBtn | ||
onClick={() => { | ||
setIsDocsShown((prev) => !prev); | ||
explorer.setInitState(); | ||
}} | ||
className="absolute right-[20px] top-[20px] z-20" | ||
/> | ||
{content} | ||
</section> | ||
); | ||
}; | ||
|
||
export default DocsModal; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
type PropsType = { | ||
setIsDocsShown: React.Dispatch<React.SetStateAction<boolean>>; | ||
isShown: boolean; | ||
explorer: { | ||
current: () => string; | ||
next: (elem: string) => void; | ||
prev: () => string; | ||
isDocs: () => boolean; | ||
back: () => void; | ||
setInitState: () => void; | ||
}; | ||
children: JSX.Element; | ||
}; | ||
|
||
const DocsOverlay = ({ isShown, setIsDocsShown, explorer, children }: PropsType) => { | ||
function closeHandler(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) { | ||
if ((e.target as HTMLButtonElement).hasAttribute('data-overlay')) { | ||
setIsDocsShown((prev) => !prev); | ||
explorer.setInitState(); | ||
} | ||
} | ||
if (!isShown) { | ||
return null; | ||
} | ||
return ( | ||
<button | ||
data-testid="overlay" | ||
data-overlay | ||
type="button" | ||
onClick={(e) => closeHandler(e)} | ||
className="overlay absolute left-0 top-0 z-10 flex h-full w-full justify-start bg-black/60 " | ||
> | ||
{children} | ||
</button> | ||
); | ||
}; | ||
|
||
export default DocsOverlay; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import useScrollbar from '@/shared/lib/hooks/useScrollbar'; | ||
import { DocsExplorerType, SchemaTypeObj } from '@/shared/types'; | ||
|
||
const DocsRootComp = ({ types, explorer }: { types: SchemaTypeObj[]; explorer: DocsExplorerType }) => { | ||
function clinkHandler(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, typeName: string) { | ||
e.preventDefault(); | ||
explorer.next(typeName); | ||
} | ||
const allTypes = types | ||
.filter((type) => type.name[0] !== '_' && type.name[1] !== '_') | ||
.map((type, i) => { | ||
if (i > 0) { | ||
return ( | ||
<li key={type.name}> | ||
<a | ||
className="text-docs-link-text-color hover:underline" | ||
href={type.name} | ||
onClick={(e) => clinkHandler(e, type.name)} | ||
> | ||
{type.name} | ||
</a> | ||
</li> | ||
); | ||
} | ||
return null; | ||
}); | ||
const rootRef = useScrollbar<HTMLDivElement>(); | ||
return ( | ||
<div ref={rootRef} className="h-full"> | ||
<div className="rounded-[24px] bg-surface-container px-10 py-[56px] text-left text-on-surface sm:px-[56px]"> | ||
<h3 className="text-[57px] font-[500]">Docs</h3> | ||
<p className="text-md text-left">A GraphQL schema provides a root type for each kind of operation.</p> | ||
</div> | ||
<div className="mt-[56px] p-10 text-left font-[500] sm:px-[56px]"> | ||
<h4 className="text-[28px]">Root types:</h4> | ||
<p className="mt-4"> | ||
query: | ||
<a | ||
className="text-docs-link-text-color hover:underline" | ||
href={types[0].name} | ||
onClick={(e) => clinkHandler(e, types[0].name)} | ||
> | ||
{types[0].name} | ||
</a> | ||
</p> | ||
<h4 className="mt-[56px] text-[28px]">All schema types:</h4> | ||
<ul className="mt-4">{allTypes}</ul> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default DocsRootComp; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import useScrollbar from '@/shared/lib/hooks/useScrollbar'; | ||
import { DocsExplorerType, SchemaTypeObj } from '@/shared/types'; | ||
|
||
import BackDocsBtn from './BackDocsBtn'; | ||
import getTypeName from '../lib/helpers/getTypeName'; | ||
import separateString from '../lib/helpers/separateString'; | ||
|
||
const DocsTypeComp = ({ explorer, currType }: { explorer: DocsExplorerType; currType: SchemaTypeObj }) => { | ||
function clinkHandler(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, typeName: string) { | ||
e.preventDefault(); | ||
explorer.next(typeName); | ||
} | ||
|
||
const fields = currType?.fields?.map((field) => { | ||
const args = field.args.map((arg, i) => { | ||
const argTypeName = getTypeName(arg.type); | ||
const [before, link, after] = separateString(argTypeName); | ||
const separation = field.args.length > 1; | ||
const beforeSeparator = <br />; | ||
const afterSeparator = i >= field.args.length - 1 ? <br /> : null; | ||
return ( | ||
<> | ||
{separation && beforeSeparator} | ||
<span className={separation ? 'pl-3' : ''} key={argTypeName}> | ||
<span className="text-tertiary">{arg.name}</span>: | ||
{before} | ||
<a className="text-docs-link-text-color hover:underline" href={link} onClick={(e) => clinkHandler(e, link)}> | ||
{link} | ||
</a> | ||
{after} | ||
</span> | ||
{separation && afterSeparator} | ||
</> | ||
); | ||
}); | ||
const returnType = getTypeName(field.type); | ||
const [prevType, typeLink, afterType] = separateString(returnType); | ||
return ( | ||
<li key={field.name}> | ||
<span className="text-docs-field-text-color">{field.name}</span> | ||
{args.length > 0 && '('} | ||
{args} | ||
{args.length > 0 && ')'}: {prevType} | ||
<a | ||
className="text-docs-link-text-color hover:underline" | ||
href={typeLink} | ||
onClick={(e) => clinkHandler(e, typeLink)} | ||
> | ||
{typeLink} | ||
</a> | ||
{afterType} | ||
<br /> | ||
{field.description} | ||
</li> | ||
); | ||
}); | ||
const inputFields = currType?.inputFields?.map((field) => { | ||
return ( | ||
<li key={field.name}> | ||
{field.name}: | ||
<a | ||
className="text-docs-link-text-color hover:underline" | ||
href={field?.type?.name || '#'} | ||
onClick={(e) => clinkHandler(e, field?.type?.name as string)} | ||
> | ||
{field?.type?.name} | ||
</a> | ||
</li> | ||
); | ||
}); | ||
const isFields = fields || inputFields; | ||
const enumValues = currType?.enumValues?.map((value) => { | ||
return <li key={value.name}>{value.name}</li>; | ||
}); | ||
const possibleTypes = currType?.possibleTypes?.map((type) => { | ||
return ( | ||
<li key={type.name}> | ||
<a | ||
className="text-docs-link-text-color hover:underline" | ||
href={type.name} | ||
onClick={(e) => clinkHandler(e, type.name)} | ||
> | ||
{type.name} | ||
</a> | ||
</li> | ||
); | ||
}); | ||
const interfaces = currType?.interfaces?.map((inter) => { | ||
return ( | ||
<li key={inter.name}> | ||
<a | ||
className="text-docs-link-text-color hover:underline" | ||
href={inter.name} | ||
onClick={(e) => clinkHandler(e, inter.name)} | ||
> | ||
{inter.name} | ||
</a> | ||
</li> | ||
); | ||
}); | ||
const rootRef = useScrollbar<HTMLDivElement>(); | ||
return ( | ||
<div ref={rootRef} className="h-full"> | ||
<div className="p-10 py-[56px] text-left text-on-surface sm:px-[56px]"> | ||
<BackDocsBtn onClick={() => explorer.back()} title={explorer.prev()} /> | ||
<h2 className="mt-8 w-full text-3xl">{currType?.name}</h2> | ||
<p className="mt-8">{currType?.description}</p> | ||
{interfaces && interfaces?.length > 0 && <h3 className="text-xl">Implements:</h3>} | ||
<ul className="mt-8 flex flex-col gap-5">{interfaces}</ul> | ||
{isFields && <h3 className="text-xl">Fields:</h3>} | ||
<ul className="mt-8 flex flex-col gap-5">{fields}</ul> | ||
<ul className="mt-8 flex flex-col gap-5">{inputFields}</ul> | ||
{enumValues && <h3 className="text-xl">Enum values:</h3>} | ||
<ul className="mt-8 flex flex-col gap-5">{enumValues}</ul> | ||
{possibleTypes && <h3 className="text-xl">Implementations</h3>} | ||
<ul className="mt-8 flex flex-col gap-5">{possibleTypes}</ul> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default DocsTypeComp; |
Oops, something went wrong.