docs: prep for v2 documentation (#6710)

This PR includes documentation that preps for v2 docs (but doesn't introduce new docs).

_Note: The number of file changes in the PR is due to find-and-replace within the `references` which is unavoidable. Let me know if I should move it to another PR._

## Changes

- Change Medusa version in base OAS used for v2.
- Fix to docblock generator related to not catching all path parameters.
- Added typedoc plugin that generates ER Diagrams, which will be used specifically for data model references in commerce modules.
- Changed OAS tool to output references in `www/apps/api-reference/specs-v2` directory when the `--v2` option is used.
- Added a version switcher to the API reference to switch between V1 and V2. This switcher is enabled by an environment variable, so it won't be visible/usable at the moment.
- Upgraded docusaurus to v3.0.1
- Added new Vale rules to ensure correct spelling of Medusa Admin and module names.
- Added new components to the `docs-ui` package that will be used in future documentation changes.
This commit is contained in:
Shahed Nasser
2024-03-18 07:47:35 +00:00
committed by GitHub
parent 56a6ec0227
commit bb87db8342
2008 changed files with 15716 additions and 10536 deletions
@@ -0,0 +1,264 @@
"use client"
import { useIsBrowser } from "@/hooks"
import { getLearningPath } from "@/utils/learning-paths"
import React, { createContext, useContext, useEffect, useState } from "react"
import { LearningPathFinishType } from "@/components/LearningPath/Finish"
import { useAnalytics } from "docs-ui"
import { usePathname, useRouter } from "next/navigation"
export type LearningPathType = {
name: string
label: string
description?: string
steps: LearningPathStepType[]
finish?: LearningPathFinishType
notificationId?: string
}
export type LearningPathStepType = {
title?: string
description?: string
descriptionJSX?: JSX.Element
path?: string
}
export type LearningPathContextType = {
path: LearningPathType | null
setPath: (value: LearningPathType) => void
currentStep: number
setCurrentStep: (value: number) => void
startPath: (path: LearningPathType) => void
updatePath: (data: Pick<LearningPathType, "notificationId">) => void
endPath: () => void
nextStep: () => void
hasNextStep: () => boolean
previousStep: () => void
hasPreviousStep: () => boolean
goToStep: (stepIndex: number) => void
isCurrentPath: () => boolean
goToCurrentPath: () => void
baseUrl?: string
}
type LearningPathProviderProps = {
children?: React.ReactNode
baseUrl?: string
}
const LearningPathContext = createContext<LearningPathContextType | null>(null)
export const LearningPathProvider: React.FC<LearningPathProviderProps> = ({
children,
baseUrl,
}) => {
const [path, setPath] = useState<LearningPathType | null>(null)
const [currentStep, setCurrentStep] = useState(-1)
const isBrowser = useIsBrowser()
const pathname = usePathname()
const router = useRouter()
const { track } = useAnalytics()
const startPath = (path: LearningPathType) => {
setPath(path)
setCurrentStep(-1)
if (isBrowser) {
localStorage.setItem(
"learning-path",
JSON.stringify({
pathName: path.name,
currentStep: -1,
})
)
}
track(`learning_path_${path.name}`, {
url: pathname,
state: `start`,
})
}
useEffect(() => {
if (path && currentStep === -1) {
nextStep()
}
}, [path])
const endPath = () => {
const didFinish = currentStep === (path?.steps.length || 0) - 1
const reachedIndex = currentStep === -1 ? 0 : currentStep
track(`learning_path_${path?.name}`, {
url: pathname,
state: !didFinish ? `closed` : `end`,
reachedStep:
path?.steps[reachedIndex]?.title ||
path?.steps[reachedIndex]?.description ||
path?.steps[reachedIndex]?.descriptionJSX ||
reachedIndex,
})
setPath(null)
setCurrentStep(-1)
if (isBrowser) {
localStorage.removeItem("learning-path")
}
}
const hasNextStep = () => currentStep !== (path?.steps.length || 0) - 1
const nextStep = () => {
if (!path || !hasNextStep()) {
return
}
const nextStepIndex = currentStep + 1
setCurrentStep(nextStepIndex)
const newPath = path.steps[nextStepIndex].path
if (isBrowser) {
localStorage.setItem(
"learning-path",
JSON.stringify({
pathName: path.name,
currentStep: nextStepIndex,
})
)
}
if (pathname !== newPath && newPath) {
router.push(newPath)
}
}
const hasPreviousStep = () => currentStep > 0
const previousStep = () => {
if (!path || !hasPreviousStep()) {
return
}
const previousStepIndex = currentStep - 1
setCurrentStep(previousStepIndex)
const newPath = path.steps[previousStepIndex].path
if (isBrowser) {
localStorage.setItem(
"learning-path",
JSON.stringify({
pathName: path.name,
currentStep: previousStepIndex,
})
)
}
if (pathname !== newPath && newPath) {
router.push(newPath)
}
}
const goToStep = (stepIndex: number) => {
if (!path || stepIndex >= path.steps.length) {
return
}
setCurrentStep(stepIndex)
const newPath = path.steps[stepIndex].path
if (isBrowser) {
localStorage.setItem(
"learning-path",
JSON.stringify({
pathName: path.name,
currentStep: stepIndex,
})
)
}
if (pathname !== newPath && newPath) {
router.push(newPath)
}
}
const isCurrentPath = () => {
if (!path || currentStep === -1) {
return false
}
return pathname === path.steps[currentStep].path
}
const goToCurrentPath = () => {
if (!path || currentStep === -1 || !path.steps[currentStep].path) {
return
}
router.push(path.steps[currentStep].path!)
}
const updatePath = (data: Pick<LearningPathType, "notificationId">) => {
if (!path) {
return
}
setPath({
...path,
...data,
})
}
const initPath = () => {
if (isBrowser) {
// give query parameters higher precedence over local storage
const queryPathName = new URLSearchParams(location.search).get("path")
const queryPath = queryPathName
? getLearningPath(queryPathName)
: undefined
if (queryPath) {
startPath(queryPath)
} else {
const storedPath = localStorage.getItem("learning-path")
if (storedPath) {
const storedPathParsed = JSON.parse(storedPath)
const currentPath = getLearningPath(storedPathParsed?.pathName)
if (currentPath) {
setPath(currentPath)
setCurrentStep(storedPathParsed?.currentStep || 0)
}
}
}
}
}
useEffect(() => {
if (isBrowser && !path) {
initPath()
}
}, [isBrowser])
return (
<LearningPathContext.Provider
value={{
path,
setPath,
currentStep,
setCurrentStep,
startPath,
updatePath,
endPath,
nextStep,
hasNextStep,
previousStep,
hasPreviousStep,
goToStep,
isCurrentPath,
goToCurrentPath,
baseUrl,
}}
>
{children}
</LearningPathContext.Provider>
)
}
export const useLearningPath = () => {
const context = useContext(LearningPathContext)
if (!context) {
throw new Error(
"useLearningPath must be used within a LearningPathProvider"
)
}
return context
}
@@ -0,0 +1,194 @@
"use client"
import React, {
createContext,
useContext,
useEffect,
useMemo,
useState,
} from "react"
import { useSidebar } from "../Sidebar"
import { usePrevious } from "@uidotdev/usehooks"
import { SidebarItemType } from "types"
export type Page = {
title: string
description?: string
link: string
}
export type PaginationContextType = {
nextPage?: Page
previousPage?: Page
}
export const PaginationContext = createContext<PaginationContextType | null>(
null
)
type SearchItemsResult = {
foundActive: boolean
prevItem?: SidebarItemType
nextItem?: SidebarItemType
}
export type PaginationProviderProps = {
children?: React.ReactNode
}
export const PaginationProvider = ({ children }: PaginationProviderProps) => {
const { items, activePath } = useSidebar()
const combinedItems = useMemo(() => [...items.top, ...items.bottom], [items])
const previousActivePath = usePrevious(activePath)
const [nextPage, setNextPage] = useState<Page | undefined>()
const [prevPage, setPrevPage] = useState<Page | undefined>()
const getFirstChild = (
item: SidebarItemType
): SidebarItemType | undefined => {
const children = getChildrenWithPages(item)
if (!children?.length) {
return undefined
}
return children[0].path ? children[0] : getFirstChild(children[0])
}
const getChildrenWithPages = (
item: SidebarItemType
): SidebarItemType[] | undefined => {
return item.children?.filter(
(childItem) =>
childItem.path !== undefined || getChildrenWithPages(childItem)?.length
)
}
const getPrevItem = (
items: SidebarItemType[],
index: number
): SidebarItemType | undefined => {
let foundItem: SidebarItemType | undefined
items
.slice(0, index)
.reverse()
.some((item) => {
if (item.children?.length) {
foundItem = getPrevItem(item.children, item.children.length)
} else if (item.path) {
foundItem = item
}
return foundItem !== undefined
})
return foundItem
}
const getNextItem = (
items: SidebarItemType[],
index: number
): SidebarItemType | undefined => {
let foundItem: SidebarItemType | undefined
items.slice(index + 1).some((item) => {
if (item.path) {
foundItem = item
} else if (item.children?.length) {
foundItem = getNextItem(item.children, -1)
}
return foundItem !== undefined
})
return foundItem
}
const searchItems = (currentItems: SidebarItemType[]): SearchItemsResult => {
const result: SearchItemsResult = {
foundActive: false,
}
result.foundActive = currentItems.some((item, index) => {
if (item.path === activePath) {
if (index !== 0) {
result.prevItem = getPrevItem(currentItems, index)
}
if (item.children?.length) {
result.nextItem = getFirstChild(item)
}
if (!result.nextItem && index !== currentItems.length - 1) {
result.nextItem = getNextItem(currentItems, index)
}
return true
}
if (item.children?.length) {
const childrenResult = searchItems(item.children)
if (childrenResult.foundActive) {
result.prevItem = childrenResult.prevItem
result.nextItem = childrenResult.nextItem
if (!result.prevItem) {
result.prevItem = item.path
? item
: getPrevItem(currentItems, index)
}
if (!result.nextItem && index !== currentItems.length - 1) {
result.nextItem = getNextItem(currentItems, index)
}
return true
}
}
return false
})
return result
}
useEffect(() => {
if (activePath !== previousActivePath) {
const result = searchItems(combinedItems)
setPrevPage(
result.prevItem
? {
title: result.prevItem.title,
link: result.prevItem.path || "",
}
: undefined
)
setNextPage(
result.nextItem
? {
title: result.nextItem.title,
link: result.nextItem.path || "",
}
: undefined
)
}
}, [activePath, previousActivePath])
return (
<PaginationContext.Provider
value={{
previousPage: prevPage,
nextPage,
}}
>
{children}
</PaginationContext.Provider>
)
}
export const usePagination = (): PaginationContextType => {
const context = useContext(PaginationContext)
if (!context) {
throw new Error("usePagination must be used inside a PaginationProvider")
}
return context
}
@@ -7,34 +7,30 @@ import React, {
useContext,
useEffect,
useReducer,
useRef,
useState,
} from "react"
import { usePathname } from "next/navigation"
import { getScrolledTop } from "../../utils"
import { useIsBrowser } from "../../hooks"
import { usePathname, useRouter } from "next/navigation"
import { getScrolledTop } from "@/utils"
import { useIsBrowser } from "@/hooks"
import {
SidebarItemSections,
SidebarItemType,
SidebarSectionItemsType,
} from "types"
export enum SidebarItemSections {
TOP = "top",
BOTTOM = "bottom",
MOBILE = "mobile",
export type CurrentItemsState = SidebarSectionItemsType & {
previousSidebar?: CurrentItemsState
}
export type SidebarItemType = {
path?: string
title: string
additionalElms?: React.ReactNode
children?: SidebarItemType[]
loaded?: boolean
isPathHref?: boolean
linkProps?: React.AllHTMLAttributes<HTMLAnchorElement>
}
export type SidebarSectionItemsType = {
[k in SidebarItemSections]: SidebarItemType[]
export type SidebarStyleOptions = {
disableActiveTransition?: boolean
noTitleStyling?: boolean
}
export type SidebarContextType = {
items: SidebarSectionItemsType
currentItems: CurrentItemsState | undefined
activePath: string | null
getActiveItem: () => SidebarItemType | undefined
setActivePath: (path: string | null) => void
@@ -61,7 +57,11 @@ export type SidebarContextType = {
isSidebarEmpty: () => boolean
desktopSidebarOpen: boolean
setDesktopSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>
}
staticSidebarItems?: boolean
shouldHandleHashChange: boolean
sidebarRef: React.RefObject<HTMLUListElement>
goBack: () => void
} & SidebarStyleOptions
export const SidebarContext = createContext<SidebarContextType | null>(null)
@@ -86,16 +86,21 @@ const findItem = (
item: Partial<SidebarItemType>,
checkChildren = true
): SidebarItemType | undefined => {
return section.find((i) => {
if (!item.path) {
return !i.path && i.title === item.title
} else {
return (
i.path === item.path ||
(checkChildren && i.children && findItem(i.children, item))
)
let foundItem: SidebarItemType | undefined
section.some((i) => {
if (
(!item.path && !i.path && i.title === item.title) ||
i.path === item.path
) {
foundItem = i
} else if (checkChildren && i.children) {
foundItem = findItem(i.children, item)
}
return foundItem !== undefined
})
return foundItem
}
export const reducer = (
@@ -160,7 +165,8 @@ export type SidebarProviderProps = {
shouldHandleHashChange?: boolean
shouldHandlePathChange?: boolean
scrollableElement?: Element | Window
}
staticSidebarItems?: boolean
} & SidebarStyleOptions
export const SidebarProvider = ({
children,
@@ -170,16 +176,25 @@ export const SidebarProvider = ({
shouldHandleHashChange = false,
shouldHandlePathChange = false,
scrollableElement,
staticSidebarItems = false,
disableActiveTransition = false,
noTitleStyling = false,
}: SidebarProviderProps) => {
const [items, dispatch] = useReducer(reducer, {
top: initialItems?.top || [],
bottom: initialItems?.bottom || [],
mobile: initialItems?.mobile || [],
})
const [currentItems, setCurrentItems] = useState<
CurrentItemsState | undefined
>()
const [activePath, setActivePath] = useState<string | null>("")
const [mobileSidebarOpen, setMobileSidebarOpen] = useState<boolean>(false)
const [desktopSidebarOpen, setDesktopSidebarOpen] = useState(true)
const sidebarRef = useRef<HTMLUListElement>(null)
const pathname = usePathname()
const router = useRouter()
const isBrowser = useIsBrowser()
const getResolvedScrollableElement = useCallback(() => {
return scrollableElement || window
@@ -229,9 +244,13 @@ export const SidebarProvider = ({
)
const isSidebarEmpty = useCallback((): boolean => {
return Object.values(items).every(
(sectionItems) => sectionItems.length === 0
)
return Object.values(items).every((sectionItems) => {
if (!Array.isArray(sectionItems)) {
return true
}
return sectionItems.length === 0
})
}, [items])
const init = () => {
@@ -241,6 +260,54 @@ export const SidebarProvider = ({
}
}
const getCurrentSidebar = useCallback(
(searchItems: SidebarItemType[]): SidebarItemType | undefined => {
let currentSidebar: SidebarItemType | undefined
searchItems.some((item) => {
if (item.isChildSidebar) {
if (isItemActive(item)) {
currentSidebar = item
} else if (item.children?.length) {
const childSidebar =
getCurrentSidebar(item.children) ||
findItem(item.children, { path: activePath || undefined })
if (childSidebar) {
currentSidebar = childSidebar.isChildSidebar ? childSidebar : item
}
}
} else if (item.children?.length) {
currentSidebar = getCurrentSidebar(item.children)
}
return currentSidebar !== undefined
})
return currentSidebar
},
[isItemActive, activePath]
)
const goBack = () => {
if (!currentItems) {
return
}
const previousSidebar = currentItems.previousSidebar || items
const backItem =
previousSidebar.top.find((item) => item.path && !item.isChildSidebar) ||
previousSidebar.bottom.find((item) => item.path && !item.isChildSidebar)
if (!backItem) {
return
}
setActivePath(backItem.path!)
setCurrentItems(currentItems.previousSidebar)
router.replace(backItem.path!)
}
useEffect(() => {
if (shouldHandleHashChange) {
init()
@@ -297,15 +364,50 @@ export const SidebarProvider = ({
}, [items, isLoading, setIsLoading])
useEffect(() => {
if (shouldHandlePathChange && pathname !== activePath) {
if (!shouldHandlePathChange) {
return
}
if (pathname !== activePath) {
setActivePath(pathname)
}
}, [shouldHandlePathChange, pathname])
useEffect(() => {
if (!activePath?.length) {
setCurrentItems(undefined)
return
}
const currentSidebar =
getCurrentSidebar(items.top) || getCurrentSidebar(items.bottom)
if (!currentSidebar) {
setCurrentItems(undefined)
return
}
if (
currentSidebar.isChildSidebar &&
currentSidebar.children &&
currentItems?.parentItem?.path !== currentSidebar.path
) {
const { children, ...parentItem } = currentSidebar
setCurrentItems({
top: children,
bottom: [],
mobile: items.mobile,
parentItem: parentItem,
previousSidebar: currentItems,
})
}
}, [getCurrentSidebar, activePath])
return (
<SidebarContext.Provider
value={{
items,
currentItems,
addItems,
activePath,
setActivePath,
@@ -317,6 +419,12 @@ export const SidebarProvider = ({
getActiveItem,
desktopSidebarOpen,
setDesktopSidebarOpen,
staticSidebarItems,
disableActiveTransition,
noTitleStyling,
shouldHandleHashChange,
sidebarRef,
goBack,
}}
>
{children}
@@ -1,10 +1,12 @@
export * from "./AiAssistant"
export * from "./Analytics"
export * from "./ColorMode"
export * from "./LearningPath"
export * from "./Mobile"
export * from "./Modal"
export * from "./Navbar"
export * from "./Notification"
export * from "./PageLoading"
export * from "./Pagination"
export * from "./Search"
export * from "./Sidebar"