docs: make code blocks collapsible (#7606)

* added collapsible code feature

* fixed side shadow

* fix build errors

* change design

* make code blocks collapsible
This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-06-05 10:28:41 +03:00
1 parent dc087bf310
commit 4a6327e497
44 files changed
+756 -387

No files matched your search

@@ -72,7 +72,7 @@ For example:
export const highlights = [["18", "parseCorsOrigins", "A utility function that parses the CORS configurations in `medusa-config.js`"]] export const highlights = [["18", "parseCorsOrigins", "A utility function that parses the CORS configurations in `medusa-config.js`"]]
```ts title="src/api/middlewares.ts" highlights={highlights} ```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-7" expandButtonLabel="Show Imports"
import { import {
ConfigModule, ConfigModule,
MiddlewaresConfig, MiddlewaresConfig,
@@ -57,7 +57,7 @@ You can access the logged-in customer’s ID in all API routes starting with `/s
For example: For example:
```ts title="src/api/store/customers/me/custom/route.ts" highlights={[["16", "", "Access the logged-in customer's ID."]]} ```ts title="src/api/store/customers/me/custom/route.ts" highlights={[["16", "", "Access the logged-in customer's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports"
import type { import type {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
@@ -90,7 +90,7 @@ You can access the logged-in admin user’s ID in all API Routes starting with `
For example: For example:
```ts title="src/api/admin/custom/route.ts" highlights={[["16", "req.user.userId", "Access the logged-in admin user's ID."]]} ```ts title="src/api/admin/custom/route.ts" highlights={[["16", "req.user.userId", "Access the logged-in admin user's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports"
import type { import type {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
@@ -125,12 +125,11 @@ To protect custom API Routes that don’t start with `/store/customers/me` or `/
For example: For example:
export const highlights = [ export const highlights = [
["11", "authenticate", "Only authenticated admin users can access routes starting with `/custom/admin`"], ["8", "authenticate", "Only authenticated admin users can access routes starting with `/custom/admin`"],
["17", "authenticate", "Only authenticated customers can access routes starting with `/custom/customers`"] ["14", "authenticate", "Only authenticated customers can access routes starting with `/custom/customers`"]
] ]
```ts title="src/api/middlewares.ts" highlights={highlights} ```ts title="src/api/middlewares.ts" highlights={highlights}
// TODO update import
import { MiddlewaresConfig, authenticate } from "@medusajs/medusa" import { MiddlewaresConfig, authenticate } from "@medusajs/medusa"
export const config: MiddlewaresConfig = { export const config: MiddlewaresConfig = {
@@ -35,7 +35,7 @@ For building relationships between data models in different modules, refer to th
The following example showcase a data model with common definitions: The following example showcase a data model with common definitions:
```ts ```ts collapsibleLines="1-10" expandButtonLabel="Show Imports"
import { import {
Entity, Entity,
Enum, Enum,
@@ -20,7 +20,7 @@ When retrieving or listing records of that data model, records having their `del
To create a soft-deletable model, first, add the following filter decorator to the data model class: To create a soft-deletable model, first, add the following filter decorator to the data model class:
```ts title="src/module/hello/models/my-soft-deletable.ts" highlights={[["7"]]} ```ts title="src/module/hello/models/my-soft-deletable.ts" highlights={[["7"]]} collapsibleLines="1-7" expandButtonLabel="Show Imports"
// other imports... // other imports...
import { Entity, Filter } from "@mikro-orm/core" import { Entity, Filter } from "@mikro-orm/core"
import { DALUtils } from "@medusajs/utils" import { DALUtils } from "@medusajs/utils"
@@ -20,10 +20,10 @@ export const highlights = [
["9", '"data" in data ? data.data.id : data.id', "The payload data is either in `data.data` or directly in `data`."] ["9", '"data" in data ? data.data.id : data.id', "The payload data is either in `data.data` or directly in `data`."]
] ]
```ts title="src/subscribers/product-created.ts" highlights={highlights} ```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-5" expandButtonLabel="Show Imports"
import { import type {
SubscriberArgs, SubscriberArgs,
type SubscriberConfig, SubscriberConfig,
} from "@medusajs/medusa" } from "@medusajs/medusa"
export default async function productCreateHandler({ export default async function productCreateHandler({
@@ -48,7 +48,7 @@ When the loader function is created outside a module, it receives the Medusa con
For example: For example:
```ts title="src/loaders/hello-world.ts" ```ts title="src/loaders/hello-world.ts" collapsibleLines="1-5" expandButtonLabel="Show Imports"
import { MedusaContainer } from "@medusajs/medusa" import { MedusaContainer } from "@medusajs/medusa"
import { IProductModuleService } from "@medusajs/types" import { IProductModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { ModuleRegistrationName } from "@medusajs/modules-sdk"
@@ -40,7 +40,7 @@ The Medusa application resolves these relationships while maintaining isolation
Consider you’re creating a data model that adds custom fields associated with a product: Consider you’re creating a data model that adds custom fields associated with a product:
```ts title="src/modules/hello/models/custom-product-data.ts" highlights={[["17"]]} ```ts title="src/modules/hello/models/custom-product-data.ts" highlights={[["17"]]} collapsibleLines="1-8" expandButtonLabel="Show Imports"
import { BaseEntity } from "@medusajs/utils" import { BaseEntity } from "@medusajs/utils"
import { import {
Entity, Entity,
@@ -83,7 +83,7 @@ export const relationshipsHighlight = [
["42", "foreignKey", "The name of the field in your data models referencing the other module’s model."], ["42", "foreignKey", "The name of the field in your data models referencing the other module’s model."],
] ]
```ts title="src/modules/hello/service.ts" highlights={relationshipsHighlight} ```ts title="src/modules/hello/service.ts" highlights={relationshipsHighlight} collapsibleLines="1-6" expandButtonLabel="Show Imports"
// other imports... // other imports...
import { MyCustom } from "./models/custom-product-data" import { MyCustom } from "./models/custom-product-data"
import { CustomProductData } from "./models/custom-product-data" import { CustomProductData } from "./models/custom-product-data"
@@ -12,18 +12,17 @@ The remote link is a class with utility methods to manage links defined by the l
For example: For example:
```ts ```ts collapsibleLines="1-9" expandButtonLabel="Show Imports"
import { import {
MedusaRequest, MedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import {
ModuleRegistrationName,
RemoteLink,
} from "@medusajs/modules-sdk"
import { import {
ContainerRegistrationKeys, ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
import {
RemoteLink,
} from "@medusajs/modules-sdk"
export async function POST( export async function POST(
req: MedusaRequest, req: MedusaRequest,
@@ -28,7 +28,7 @@ export const exampleHighlights = [
["27", "remoteQuery", "Run the query using the remote query."] ["27", "remoteQuery", "Run the query using the remote query."]
] ]
```ts title="src/api/store/query/route.ts" highlights={exampleHighlights} apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/query" ```ts title="src/api/store/query/route.ts" highlights={exampleHighlights} apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/query" collapsibleLines="1-12" expandButtonLabel="Show Imports"
import { import {
MedusaRequest, MedusaRequest,
MedusaResponse, MedusaResponse,
@@ -210,7 +210,7 @@ The remote query function alternatively accepts a string with GraphQL syntax as
### Basic GraphQL usage ### Basic GraphQL usage
```ts title="src/api/store/query/route.ts" apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/query" ```ts title="src/api/store/query/route.ts" apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/query" collapsibleLines="1-10" expandButtonLabel="Show Imports"
import { import {
MedusaRequest, MedusaRequest,
MedusaResponse, MedusaResponse,
@@ -146,7 +146,7 @@ export const typeArgsHighlights = [
["27", "AllModelsDTO", "The expected input/output type of the generated methods of every data model."], ["27", "AllModelsDTO", "The expected input/output type of the generated methods of every data model."],
] ]
```ts title="src/modules/hello/service.ts" highlights={typeArgsHighlights} ```ts title="src/modules/hello/service.ts" highlights={typeArgsHighlights} collapsibleLines="1-22" expandButtonLabel="Show More"
import { ModulesSdkUtils } from "@medusajs/utils" import { ModulesSdkUtils } from "@medusajs/utils"
import { MyCustom } from "./models/my-custom" import { MyCustom } from "./models/my-custom"
@@ -19,7 +19,7 @@ export const highlights = [
["14", "throwOnError", "Specify that errors occuring during the workflow's execution should be returned, not thrown."], ["14", "throwOnError", "Specify that errors occuring during the workflow's execution should be returned, not thrown."],
] ]
```ts title="src/api/store/workflows/route.ts" highlights={highlights} ```ts title="src/api/store/workflows/route.ts" highlights={highlights} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import type { import type {
MedusaRequest, MedusaRequest,
MedusaResponse, MedusaResponse,
@@ -46,7 +46,7 @@ export const updateProductHighlights = [
["39", "", "Revert the product’s data using the `previousProductData` passed from the step to the compensation function."] ["39", "", "Revert the product’s data using the `previousProductData` passed from the step to the compensation function."]
] ]
```ts title="src/workflows/update-product-erp/steps/update-product.ts" highlights={updateProductHighlights} ```ts title="src/workflows/update-product-erp/steps/update-product.ts" highlights={updateProductHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports"
import { import {
createStep, createStep,
StepResponse, StepResponse,
@@ -145,7 +145,7 @@ export const updateErpHighlights = [
["37", "updateProductErpData", "Revert the product's data in the ERP system to its previous state using the `previousErpData`."] ["37", "updateProductErpData", "Revert the product's data in the ERP system to its previous state using the `previousErpData`."]
] ]
```ts title="src/workflows/update-product-erp/steps/update-erp.ts" highlights={updateErpHighlights} ```ts title="src/workflows/update-product-erp/steps/update-erp.ts" highlights={updateErpHighlights} collapsibleLines="1-8" expandButtonLabel="Show Imports"
import { import {
createStep, createStep,
StepResponse, StepResponse,
@@ -208,7 +208,7 @@ With the steps ready, you'll create the workflow that runs these steps to update
Change the content of `src/workflows/update-product-erp/index.ts` to the following: Change the content of `src/workflows/update-product-erp/index.ts` to the following:
```ts title="src/workflows/update-product-erp/index.ts" ```ts title="src/workflows/update-product-erp/index.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
import { createWorkflow } from "@medusajs/workflows-sdk" import { createWorkflow } from "@medusajs/workflows-sdk"
import { UpdateProductDTO, ProductDTO } from "@medusajs/types" import { UpdateProductDTO, ProductDTO } from "@medusajs/types"
import updateProduct from "./steps/update-product" import updateProduct from "./steps/update-product"
@@ -12,7 +12,7 @@ Errors can occur in a workflow. To avoid data inconsistency, define a function t
For example: For example:
```ts title="src/workflows/hello-world.ts" highlights={[["16"], ["17"], ["18"]]} ```ts title="src/workflows/hello-world.ts" highlights={[["16"], ["17"], ["18"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
// other imports... // other imports...
import { import {
createStep, createStep,
@@ -52,7 +52,7 @@ const step2 = createStep(
2. Use the steps in a workflow. For example: 2. Use the steps in a workflow. For example:
```ts title="src/workflows/hello-world.ts" ```ts title="src/workflows/hello-world.ts" collapsibleLines="1-7" expandButtonLabel="Show Imports"
import { import {
// other imports... // other imports...
createWorkflow, createWorkflow,
@@ -81,7 +81,7 @@ export default myWorkflow
3. Execute the workflow from a resource, such as an API route: 3. Execute the workflow from a resource, such as an API route:
```ts title="src/api/store/workflow/route.ts" ```ts title="src/api/store/workflow/route.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
import type { import type {
MedusaRequest, MedusaRequest,
MedusaResponse, MedusaResponse,
@@ -22,7 +22,7 @@ A workflow is considered long-running if at least one step has its `async` confi
For example, consider the following workflow and steps: For example, consider the following workflow and steps:
```ts title="src/workflows/hello-world.ts" highlights={[["13"]]} ```ts title="src/workflows/hello-world.ts" highlights={[["13"]]} collapsibleLines="1-10" expandButtonLabel="Show More"
import { import {
createStep, createStep,
createWorkflow, createWorkflow,
@@ -88,7 +88,7 @@ export const highlights = [
["24", "subscribe", "Subscribe to status changes of the workflow execution."], ["24", "subscribe", "Subscribe to status changes of the workflow execution."],
] ]
```ts title="src/api/store/workflows/route.ts" highlights={highlights} ```ts title="src/api/store/workflows/route.ts" highlights={highlights} collapsibleLines="1-11" expandButtonLabel="Show Imports"
import type { import type {
MedusaRequest, MedusaRequest,
MedusaResponse, MedusaResponse,
@@ -19,7 +19,7 @@ export const highlights = [
["23", "parallelize", "Run the steps passed as parameters in parallel."], ["23", "parallelize", "Run the steps passed as parameters in parallel."],
] ]
```ts highlights={highlights} ```ts highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports"
import { import {
createWorkflow, createWorkflow,
parallelize, parallelize,
@@ -12,7 +12,7 @@ By default, when an error occurs in a step, the step and the workflow fail, and
You can configure the step to retry on failure. The `createStep` function can accept a configuration object instead of the step’s name as a first parameter: You can configure the step to retry on failure. The `createStep` function can accept a configuration object instead of the step’s name as a first parameter:
```ts title="src/workflows/hello-world.ts" highlights={[["10"]]} ```ts title="src/workflows/hello-world.ts" highlights={[["10"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import { import {
createStep, createStep,
StepResponse, StepResponse,
@@ -20,7 +20,7 @@ Timeout doesn't stop the execution of a running step. The timeout only affects t
For example: For example:
```ts title="src/workflows/hello-world.ts" highlights={[["22"]]} ```ts title="src/workflows/hello-world.ts" highlights={[["22"]]} collapsibleLines="1-16" expandButtonLabel="Show More"
import { import {
createStep, createStep,
createWorkflow, createWorkflow,
@@ -1,77 +0,0 @@
export const metadata = {
title: `${pageNumber} Services`,
}
# {metadata.title}
In this chapter, you'll learn what services are and how to create a service.
## What is a Service?
A service is a class that holds methods related to a business logic or commerce functionality. For example, the `ProductService` holds methods to retrieve and manage products.
---
## How to Create a Service
Services are created in a TypeScript or JavaScript file under the `src/services` directory of your Medusa application.
For example, to create a `HelloWorld` service, create the file `src/services/hello-world.ts` with the following content:
```ts title="src/services/hello-world.ts"
class HelloWorld {
// TODO add methods
}
export default HelloWorld
```
<Note title="Tip">
A service's file name must be the kebab-case name of the service without the word `Service`.
</Note>
---
## When to Use
<Note title="Use services when" type="success">
- You're implementing custom commerce functionality to be reused across the Medusa application.
- You're integrating a third-party service, such as an ERP system.
</Note>
---
## Resolve Resources
In a service's constructor, you can resolve other services or resources of your Medusa application using the Medusa container.
For example, you can resolve the `IProductModuleService` to use it in your methods:
```ts title="src/services/hello-world.ts" highlights={[["10"]]}
import { IProductModuleService } from "@medusajs/types"
type InjectedDependencies = {
productModuleService: IProductModuleService
}
class HelloWorldService {
protected productModuleService: IProductModuleService
constructor({ productModuleService }: InjectedDependencies) {
this.productModuleService = productModuleService
}
async getMessage(): Promise<string> {
const [, count] =
await this.productModuleService.listAndCount()
return `You have ${count} products`
}
}
export default HelloWorldService
```
@@ -20,7 +20,7 @@ export const highlights = [
["21", "data", "The data to pass to the template defined in the third-party service."] ["21", "data", "The data to pass to the template defined in the third-party service."]
] ]
```ts title="src/subscribers/product-created.ts" highlights={highlights} ```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-7" expandButtonLabel="Show Imports"
import type { import type {
SubscriberArgs, SubscriberArgs,
SubscriberConfig, SubscriberConfig,
@@ -146,7 +146,7 @@ export const highlights = [
["21", "data", "The data to pass to the template defined in SendGrid."] ["21", "data", "The data to pass to the template defined in SendGrid."]
] ]
```ts title="src/subscribers/product-created.ts" highlights={highlights} ```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-7" expandButtonLabel="Show Imports"
import type { import type {
SubscriberArgs, SubscriberArgs,
SubscriberConfig, SubscriberConfig,
@@ -119,7 +119,7 @@ In this guide, you’ll find common examples of how you can use the API Key Modu
<CodeTabs groupId="app-type"> <CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa"> <CodeTab label="Medusa API Router" value="medusa">
```ts ```ts collapsibleLines="1-9" expandButtonLabel="Show Imports"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse MedusaResponse
@@ -257,7 +257,7 @@ In this guide, you’ll find common examples of how you can use the API Key Modu
<CodeTabs groupId="app-type"> <CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa"> <CodeTab label="Medusa API Router" value="medusa">
```ts ```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse MedusaResponse
@@ -19,7 +19,7 @@ This example uses the [jsonwebtoken NPM package](https://www.npmjs.com/package/j
<CodeTabs groupId="app-type"> <CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa"> <CodeTab label="Medusa API Router" value="medusa">
```ts ```ts collapsibleLines="1-10" expandButtonLabel="Show Imports"
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa" import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
import { import {
IAuthModuleService, IAuthModuleService,
@@ -122,7 +122,7 @@ This example uses the [jsonwebtoken NPM package](https://www.npmjs.com/package/j
<CodeTabs groupId="app-type"> <CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa"> <CodeTab label="Medusa API Router" value="medusa">
```ts ```ts collapsibleLines="1-10" expandButtonLabel="Show Imports"
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa" import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
import { import {
IAuthModuleService, IAuthModuleService,
@@ -171,7 +171,7 @@ This example uses the [jsonwebtoken NPM package](https://www.npmjs.com/package/j
</CodeTab> </CodeTab>
<CodeTab label="Next.js App Router" value="nextjs"> <CodeTab label="Next.js App Router" value="nextjs">
```ts ```ts collapsibleLines="1-7" expandButtonLabel="Show Imports"
import { NextResponse } from "next/server" import { NextResponse } from "next/server"
import { import {
@@ -41,7 +41,7 @@ Learn more about actions in the [Promotion Module’s documentation](../../promo
</Note> </Note>
```ts ```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
import { import {
ComputeActionAdjustmentLine, ComputeActionAdjustmentLine,
ComputeActionItemLine, ComputeActionItemLine,
@@ -101,7 +101,7 @@ The `computeActions` method accepts the existing adjustments of line items and s
Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the cart’s line item and the shipping method’s adjustments. Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the cart’s line item and the shipping method’s adjustments.
```ts ```ts collapsibleLines="1-9" expandButtonLabel="Show Imports"
import { import {
AddItemAdjustmentAction, AddItemAdjustmentAction,
AddShippingMethodAdjustment, AddShippingMethodAdjustment,
@@ -41,7 +41,7 @@ Learn more about actions in the [Promotion Module’s documentation](../../promo
</Note> </Note>
```ts ```ts collapsibleLines="1-10" expandButtonLabel="Show Imports"
import { import {
ComputeActionAdjustmentLine, ComputeActionAdjustmentLine,
ComputeActionItemLine, ComputeActionItemLine,
@@ -103,7 +103,7 @@ The `computeActions` method accepts the existing adjustments of line items and s
Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the order’s line items and the shipping method’s adjustments. Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the order’s line items and the shipping method’s adjustments.
```ts ```ts collapsibleLines="1-9" expandButtonLabel="Show Imports"
import { import {
AddItemAdjustmentAction, AddItemAdjustmentAction,
AddShippingMethodAdjustment, AddShippingMethodAdjustment,
@@ -323,7 +323,7 @@ In this document, you’ll find common examples of how you can use the Pricing M
<CodeTabs groupId="app-type"> <CodeTabs groupId="app-type">
<CodeTab value="medusa" label="Medusa API Router"> <CodeTab value="medusa" label="Medusa API Router">
```ts ```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa" import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
import { import {
IPricingModuleService, IPricingModuleService,
+55 -61
View File
@@ -1,4 +1,4 @@
import { Table, LegacyCodeTabs } from "docs-ui" import { Table, CodeTabs, CodeTab } from "docs-ui"
export const metadata = { export const metadata = {
title: `Medusa JS Client`, title: `Medusa JS Client`,
@@ -267,13 +267,11 @@ Once the user or customer is authenticated successfully, the Medusa client autom
For example: For example:
<LegacyCodeTabs tabs={[ <CodeTabs group="client-authentication-type">
{ <CodeTab label="Admin User" value="user">
label: "Admin User",
value: "user", ```ts
code: { import Medusa from "@medusajs/medusa-js"
lang: "ts",
source: `import Medusa from "@medusajs/medusa-js"
const medusa = new Medusa({ const medusa = new Medusa({
baseUrl: MEDUSA_BACKEND_URL, baseUrl: MEDUSA_BACKEND_URL,
maxRetries: 3, maxRetries: 3,
@@ -282,15 +280,14 @@ For example:
email: "user@example.com", email: "user@example.com",
password: "supersecret", password: "supersecret",
}) })
// send authenticated requests now` // send authenticated requests now
} ```
},
{ </CodeTab>
label: "Customer", <CodeTab label="Customer" value="customer">
value: "customer",
code: { ```ts
lang: "ts", import Medusa from "@medusajs/medusa-js"
source: `import Medusa from "@medusajs/medusa-js"
const medusa = new Medusa({ const medusa = new Medusa({
baseUrl: MEDUSA_BACKEND_URL, baseUrl: MEDUSA_BACKEND_URL,
maxRetries: 3, maxRetries: 3,
@@ -299,10 +296,11 @@ For example:
email: "user@example.com", email: "user@example.com",
password: "supersecret", password: "supersecret",
}) })
// send authenticated requests now` // send authenticated requests now
} ```
}
]} group="client-authentication-type" /> </CodeTab>
</CodeTabs>
### Cookie Session ID ### Cookie Session ID
@@ -314,13 +312,11 @@ Once the user or customer is authenticated successfully, the Medusa client sets
For example: For example:
<LegacyCodeTabs tabs={[ <CodeTabs group="client-authentication-type">
{ <CodeTab label="Admin User" value="user">
label: "Admin User",
value: "user", ```ts
code: { import Medusa from "@medusajs/medusa-js"
lang: "ts",
source: `import Medusa from "@medusajs/medusa-js"
const medusa = new Medusa({ const medusa = new Medusa({
baseUrl: MEDUSA_BACKEND_URL, baseUrl: MEDUSA_BACKEND_URL,
maxRetries: 3, maxRetries: 3,
@@ -329,15 +325,14 @@ For example:
email: "user@example.com", email: "user@example.com",
password: "supersecret", password: "supersecret",
}) })
// send authenticated requests now` // send authenticated requests now
} ```
},
{ </CodeTab>
label: "Customer", <CodeTab label="Customer" value="customer">
value: "customer",
code: { ```ts
lang: "ts", import Medusa from "@medusajs/medusa-js"
source: `import Medusa from "@medusajs/medusa-js"
const medusa = new Medusa({ const medusa = new Medusa({
baseUrl: MEDUSA_BACKEND_URL, baseUrl: MEDUSA_BACKEND_URL,
maxRetries: 3, maxRetries: 3,
@@ -346,10 +341,11 @@ For example:
email: "user@example.com", email: "user@example.com",
password: "user@example.com", password: "user@example.com",
}) })
// send authenticated requests now` // send authenticated requests now
} ```
}
]} group="client-authentication-type" /> </CodeTab>
</CodeTabs>
### API Key ### API Key
@@ -357,13 +353,11 @@ You can authenticate admin users with a personal API Token.
If a user doesn't have a personal API token, create one with the [admin.users.update](/references/js-client/AdminUsersResource#update) method or the [Update User API route](https://docs.medusajs.com/api/admin#users_postusersuser): If a user doesn't have a personal API token, create one with the [admin.users.update](/references/js-client/AdminUsersResource#update) method or the [Update User API route](https://docs.medusajs.com/api/admin#users_postusersuser):
<LegacyCodeTabs tabs={[ <CodeTabs group="client-type">
{ <CodeTab label="JS Client" value="js-client">
label: "JS Client",
value: "js-client", ```ts
code: { import Medusa from "@medusajs/medusa-js"
lang: "ts",
source: `import Medusa from "@medusajs/medusa-js"
const medusa = new Medusa({ const medusa = new Medusa({
baseUrl: MEDUSA_BACKEND_URL, baseUrl: MEDUSA_BACKEND_URL,
maxRetries: 3, maxRetries: 3,
@@ -374,23 +368,23 @@ If a user doesn't have a personal API token, create one with the [admin.users.up
}) })
.then(({ user }) => { .then(({ user }) => {
console.log(user.api_token) console.log(user.api_token)
})` })
} ```
},
{ </CodeTab>
label: "cURL", <CodeTab label="cURL" value="curl">
value: "curl",
code: { ```bash
lang: "bash", curl -L -X POST '<BACKEND_URL>/admin/users/<USER_ID>' \\
source: `curl -L -X POST '<BACKEND_URL>/admin/users/<USER_ID>' \\
-H 'Cookie: connect.sid={sid}' \\ -H 'Cookie: connect.sid={sid}' \\
-H 'Content-Type: application/json' \\ -H 'Content-Type: application/json' \\
--data-raw '{ --data-raw '{
"api_token": "{api_token}" "api_token": "{api_token}"
}'` }'
} ```
}
]} group="client-type" /> </CodeTab>
</CodeTabs>
Then, initialize the Medusa client passing it the `apiKey` option: Then, initialize the Medusa client passing it the `apiKey` option:
+19 -21
View File
@@ -1,4 +1,4 @@
import { Table, LegacyCodeTabs } from "docs-ui" import { Table, CodeTabs, CodeTab } from "docs-ui"
export const metadata = { export const metadata = {
title: `Medusa React`, title: `Medusa React`,
@@ -216,13 +216,11 @@ There are two ways to authenticate an admin user:
For example: For example:
<LegacyCodeTabs tabs={[ <CodeTabs group="admin-auth">
{ <CodeTab label="useAdminLogin hook" value="useadminlogin">
value: "useadminlogin",
label: "useAdminLogin hook", ```ts
code: { import React from "react"
lang: "ts",
source: `import React from "react"
import { useAdminLogin } from "medusa-react" import { useAdminLogin } from "medusa-react"
const Login = () => { const Login = () => {
@@ -244,15 +242,14 @@ For example:
// ... // ...
} }
export default Login` export default Login
} ```
},
{ </CodeTab>
value: "apikey", <CodeTab label="apikey Option" value="apikey">
label: "apikey Option",
code: { ```tsx
lang: "tsx", import { MedusaProvider } from "medusa-react"
source: `import { MedusaProvider } from "medusa-react"
import Storefront from "./Storefront" import Storefront from "./Storefront"
import { QueryClient } from "@tanstack/react-query" import { QueryClient } from "@tanstack/react-query"
import React from "react" import React from "react"
@@ -271,10 +268,11 @@ For example:
) )
} }
export default App` export default App
} ```
}
]} group="admin-auth" /> </CodeTab>
</CodeTabs>
### Customer Authentication ### Customer Authentication
@@ -1,4 +1,4 @@
import { LegacyCodeTabs } from "docs-ui" import { CodeTabs, CodeTab } from "docs-ui"
If you install the Medusa CLI tool with Yarn, then try to use the CLI tool but get the error: If you install the Medusa CLI tool with Yarn, then try to use the CLI tool but get the error:
@@ -8,28 +8,23 @@ command not found: medusa
You have to add Yarn’s install location to the PATH variable: You have to add Yarn’s install location to the PATH variable:
<LegacyCodeTabs <CodeTabs group="operating-systems">
tabs={[ <CodeTab label="MacOS / Linux" value="unix">
{
label: "MacOS / Linux", ```plain
value: "unix", export PATH="$(yarn global bin):$PATH
code: { ```
lang: "bash",
source: `export PATH="$(yarn global bin):$PATH"` </CodeTab>
} <CodeTab label="Windows" value="windows">
},
{ ```bash
label: "Windows", # MAKE SURE TO INCLUDE %path%
value: "windows",
code: {
lang: "bash",
source: `# MAKE SURE TO INCLUDE %path%
setx path "%path%;c:\\users\\YOURUSERNAME\\appdata\\local\\yarnbin" setx path "%path%;c:\\users\\YOURUSERNAME\\appdata\\local\\yarnbin"
# YOURUSERNAME is your account username` # YOURUSERNAME is your account username
} ```
}
]} </CodeTab>
group="operating-systems" </CodeTabs>
/>
You can learn more in [Yarn’s documentation](https://classic.yarnpkg.com/en/docs/cli/global#adding-the-install-location-to-your-path). You can learn more in [Yarn’s documentation](https://classic.yarnpkg.com/en/docs/cli/global#adding-the-install-location-to-your-path).
@@ -1,26 +1,24 @@
import { LegacyCodeTabs } from "docs-ui" import { CodeTabs, CodeTab } from "docs-ui"
This error can occur while installing any of Medusa's projects (for example, Next.js Starter Template). There is no specific cause to this error. This error can occur while installing any of Medusa's projects (for example, Next.js Starter Template). There is no specific cause to this error.
One way to resolve it is by removing the `node_modules` directory in the project and re-installing the dependencies: One way to resolve it is by removing the `node_modules` directory in the project and re-installing the dependencies:
<LegacyCodeTabs tabs={[ <CodeTabs group="operating-systems">
{ <CodeTab label="MacOS / Linux" value="unix">
label: "MacOS / Linux",
value: "unix", ```bash
code: { rm -rf node_modules
lang: "bash", yarn install
source: `rm -rf node_modules ```
yarn install`
} </CodeTab>
}, <CodeTab label="Windows" value="windows">
{
label: "Windows", ```bash
value: "windows", rd /s /q node_modules
code: { yarn install
lang: "bash", ```
source: `rd /s /q node_modules
yarn install` </CodeTab>
} </CodeTabs>
}
]} group="operating-systems" />
+12 -16
View File
@@ -227,6 +227,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/architectural-modules/workflow-engine/redis/page.mdx", "filePath": "/www/apps/resources/app/architectural-modules/workflow-engine/redis/page.mdx",
"pathname": "/architectural-modules/workflow-engine/redis" "pathname": "/architectural-modules/workflow-engine/redis"
}, },
{
"filePath": "/www/apps/resources/app/commerce-modules/api-key/concepts/page.mdx",
"pathname": "/commerce-modules/api-key/concepts"
},
{ {
"filePath": "/www/apps/resources/app/commerce-modules/api-key/events/_events-table/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/api-key/events/_events-table/page.mdx",
"pathname": "/commerce-modules/api-key/events/_events-table" "pathname": "/commerce-modules/api-key/events/_events-table"
@@ -247,10 +251,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/api-key/relations-to-other-modules/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/api-key/relations-to-other-modules/page.mdx",
"pathname": "/commerce-modules/api-key/relations-to-other-modules" "pathname": "/commerce-modules/api-key/relations-to-other-modules"
}, },
{
"filePath": "/www/apps/resources/app/commerce-modules/api-key/tokens/page.mdx",
"pathname": "/commerce-modules/api-key/tokens"
},
{ {
"filePath": "/www/apps/resources/app/commerce-modules/auth/auth-flows/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/auth/auth-flows/page.mdx",
"pathname": "/commerce-modules/auth/auth-flows" "pathname": "/commerce-modules/auth/auth-flows"
@@ -331,6 +331,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/currency/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/currency/page.mdx",
"pathname": "/commerce-modules/currency" "pathname": "/commerce-modules/currency"
}, },
{
"filePath": "/www/apps/resources/app/commerce-modules/currency/relations-to-other-modules/page.mdx",
"pathname": "/commerce-modules/currency/relations-to-other-modules"
},
{ {
"filePath": "/www/apps/resources/app/commerce-modules/customer/customer-accounts/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/customer/customer-accounts/page.mdx",
"pathname": "/commerce-modules/customer/customer-accounts" "pathname": "/commerce-modules/customer/customer-accounts"
@@ -687,6 +691,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/store/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/store/page.mdx",
"pathname": "/commerce-modules/store" "pathname": "/commerce-modules/store"
}, },
{
"filePath": "/www/apps/resources/app/commerce-modules/store/relations-to-other-modules/page.mdx",
"pathname": "/commerce-modules/store/relations-to-other-modules"
},
{ {
"filePath": "/www/apps/resources/app/commerce-modules/tax/events/_events-table/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/tax/events/_events-table/page.mdx",
"pathname": "/commerce-modules/tax/events/_events-table" "pathname": "/commerce-modules/tax/events/_events-table"
@@ -743,10 +751,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/user/user-creation-flows/page.mdx", "filePath": "/www/apps/resources/app/commerce-modules/user/user-creation-flows/page.mdx",
"pathname": "/commerce-modules/user/user-creation-flows" "pathname": "/commerce-modules/user/user-creation-flows"
}, },
{
"filePath": "/www/apps/resources/app/configurations/_medusa-admin/page.mdx",
"pathname": "/configurations/_medusa-admin"
},
{ {
"filePath": "/www/apps/resources/app/contribution-guidelines/_admin-translations/page.mdx", "filePath": "/www/apps/resources/app/contribution-guidelines/_admin-translations/page.mdx",
"pathname": "/contribution-guidelines/_admin-translations" "pathname": "/contribution-guidelines/_admin-translations"
@@ -931,14 +935,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/troubleshooting/_transaction-promise-all/page.mdx", "filePath": "/www/apps/resources/app/troubleshooting/_transaction-promise-all/page.mdx",
"pathname": "/troubleshooting/_transaction-promise-all" "pathname": "/troubleshooting/_transaction-promise-all"
}, },
{
"filePath": "/www/apps/resources/app/troubleshooting/admin-sign-in/page.mdx",
"pathname": "/troubleshooting/admin-sign-in"
},
{
"filePath": "/www/apps/resources/app/troubleshooting/admin-webpack-build-error/page.mdx",
"pathname": "/troubleshooting/admin-webpack-build-error"
},
{ {
"filePath": "/www/apps/resources/app/troubleshooting/cors-errors/page.mdx", "filePath": "/www/apps/resources/app/troubleshooting/cors-errors/page.mdx",
"pathname": "/troubleshooting/cors-errors" "pathname": "/troubleshooting/cors-errors"
+31 -32
View File
@@ -37,8 +37,8 @@ export const generatedSidebar = [
{ {
"loaded": true, "loaded": true,
"isPathHref": true, "isPathHref": true,
"path": "/commerce-modules/api-key/tokens", "path": "/commerce-modules/api-key/concepts",
"title": "Tokens", "title": "API Key Concepts",
"children": [] "children": []
}, },
{ {
@@ -863,6 +863,20 @@ export const generatedSidebar = [
"title": "Examples", "title": "Examples",
"children": [] "children": []
}, },
{
"loaded": true,
"isPathHref": true,
"title": "Concepts",
"children": [
{
"loaded": true,
"isPathHref": true,
"path": "/commerce-modules/currency/relations-to-other-modules",
"title": "Relation to Modules",
"children": []
}
]
},
{ {
"loaded": true, "loaded": true,
"isPathHref": true, "isPathHref": true,
@@ -5068,6 +5082,20 @@ export const generatedSidebar = [
"title": "Examples", "title": "Examples",
"children": [] "children": []
}, },
{
"loaded": true,
"isPathHref": true,
"title": "Concepts",
"children": [
{
"loaded": true,
"isPathHref": true,
"path": "/commerce-modules/store/relations-to-other-modules",
"title": "Relation to Modules",
"children": []
}
]
},
{ {
"loaded": true, "loaded": true,
"isPathHref": true, "isPathHref": true,
@@ -5236,7 +5264,7 @@ export const generatedSidebar = [
"loaded": true, "loaded": true,
"isPathHref": true, "isPathHref": true,
"path": "/commerce-modules/tax/tax-calculation-with-provider", "path": "/commerce-modules/tax/tax-calculation-with-provider",
"title": "Tax Calculation", "title": "Tax Calculation and Providers",
"children": [] "children": []
} }
] ]
@@ -5246,13 +5274,6 @@ export const generatedSidebar = [
"isPathHref": true, "isPathHref": true,
"title": "References", "title": "References",
"children": [ "children": [
{
"loaded": true,
"isPathHref": true,
"path": "/references/tax/provider",
"title": "Tax Provider Reference",
"children": []
},
{ {
"loaded": true, "loaded": true,
"isPathHref": true, "isPathHref": true,
@@ -7144,28 +7165,6 @@ export const generatedSidebar = [
"children": [] "children": []
} }
] ]
},
{
"loaded": true,
"isPathHref": true,
"title": "Medusa Admin",
"hasTitleStyling": true,
"children": [
{
"loaded": true,
"isPathHref": true,
"path": "/troubleshooting/admin-sign-in",
"title": "Signing In",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"path": "/troubleshooting/admin-custom-hooks-error",
"title": "Custom Hooks Error",
"children": []
}
]
} }
] ]
} }
+4 -3
View File
@@ -2,7 +2,7 @@
import { import {
SearchProvider as UiSearchProvider, SearchProvider as UiSearchProvider,
AiAssistantCommandIcon, AiAssistantIcon,
AiAssistantProvider, AiAssistantProvider,
searchFiltersV1, searchFiltersV1,
} from "docs-ui" } from "docs-ui"
@@ -48,7 +48,7 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
commands={[ commands={[
{ {
name: "ai-assistant", name: "ai-assistant",
icon: <AiAssistantCommandIcon />, icon: <AiAssistantIcon />,
component: ( component: (
<AiAssistantProvider <AiAssistantProvider
apiUrl={process.env.NEXT_PUBLIC_AI_ASSISTANT_URL || "temp"} apiUrl={process.env.NEXT_PUBLIC_AI_ASSISTANT_URL || "temp"}
@@ -61,8 +61,9 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
), ),
title: "AI Assistant", title: "AI Assistant",
badge: { badge: {
variant: "purple", variant: "blue",
children: "Beta", children: "Beta",
badgeType: "shaded",
}, },
}, },
]} ]}
@@ -12,6 +12,9 @@ export type CodeBlockActionsProps = {
source: string source: string
isSingleLine?: boolean isSingleLine?: boolean
inHeader: boolean inHeader: boolean
showGradientBg?: boolean
inInnerCode?: boolean
isCollapsed: boolean
canShowApiTesting?: boolean canShowApiTesting?: boolean
blockStyle: CodeBlockStyle blockStyle: CodeBlockStyle
onApiTesting: React.Dispatch<React.SetStateAction<boolean>> onApiTesting: React.Dispatch<React.SetStateAction<boolean>>
@@ -22,6 +25,9 @@ export type CodeBlockActionsProps = {
export const CodeBlockActions = ({ export const CodeBlockActions = ({
source, source,
inHeader, inHeader,
showGradientBg = true,
inInnerCode = false,
isCollapsed,
isSingleLine = false, isSingleLine = false,
canShowApiTesting = false, canShowApiTesting = false,
blockStyle, blockStyle,
@@ -45,14 +51,35 @@ export const CodeBlockActions = ({
return ( return (
<div <div
className={clsx( className={clsx(
"absolute hidden md:flex md:justify-end", "absolute hidden md:block",
"xs:rounded xs:absolute xs:right-0 xs:top-0 xs:w-[calc(10%+24px)] xs:h-full xs:bg-transparent", "xs:rounded xs:absolute xs:right-0 xs:top-0 xs:w-[calc(17%+10px)] xs:h-full"
!inHeader && [ )}
"md:right-docs_0.5", >
isSingleLine && "md:top-docs_0.25", <div
!isSingleLine && "md:top-docs_0.5", className={clsx(
!inHeader &&
showGradientBg && [
inInnerCode &&
"xs:bg-subtle-code-fade-right-to-left dark:xs:bg-subtle-code-fade-right-to-left-dark",
!inInnerCode &&
"xs:bg-base-code-fade-right-to-left dark:xs:bg-base-code-fade-right-to-left-dark",
], ],
inHeader && "md:right-docs_1 md:top-docs_0.5" (inHeader || !showGradientBg) && "xs:bg-transparent",
"z-[9] w-full h-full absolute top-0 left-0"
)}
/>
<div
className={clsx(
"md:flex md:justify-end z-[11] relative",
!inHeader && [
"md:pr-docs_0.5",
isCollapsed && "md:pt-docs_2.5",
!isCollapsed && [
isSingleLine && "md:pt-docs_0.25",
!isSingleLine && "md:pt-docs_0.5",
],
],
inHeader && "md:pr-docs_1 md:pt-docs_0.5"
)} )}
> >
{canShowApiTesting && ( {canShowApiTesting && (
@@ -105,5 +132,6 @@ export const CodeBlockActions = ({
/> />
)} )}
</div> </div>
</div>
) )
} }
@@ -0,0 +1,57 @@
"use client"
import clsx from "clsx"
import React from "react"
import { CollapsibleReturn } from "../../../../hooks"
import { Button } from "@medusajs/ui"
export type CodeBlockCollapsibleButtonProps = {
type: "start" | "end"
expandButtonLabel?: string
className?: string
} & Omit<CollapsibleReturn, "getCollapsibleElms">
export const CodeBlockCollapsibleButton = ({
type,
expandButtonLabel = "Show more",
collapsed,
setCollapsed,
className,
}: CodeBlockCollapsibleButtonProps) => {
if (!collapsed) {
return <></>
}
return (
<>
{type === "start" && (
<Button
className={clsx(
"font-base w-full p-docs_0.5 !shadow-none z-10",
"bg-medusa-contrast-button hover:bg-medusa-contrast-button-hover",
"txt-compact-xsmall text-medusa-contrast-fg-secondary",
type === "start" && "rounded-t-docs_DEFAULT rounded-b-none",
className
)}
onClick={() => setCollapsed(false)}
>
{expandButtonLabel}
</Button>
)}
{type === "end" && (
<Button
className={clsx(
"font-base w-full p-docs_0.5 !shadow-none z-10",
"bg-medusa-contrast-button hover:bg-medusa-contrast-button-hover",
"txt-compact-xsmall text-medusa-contrast-fg-secondary",
"rounded-t-none rounded-b-docs_DEFAULT",
className
)}
onClick={() => setCollapsed(false)}
>
{expandButtonLabel}
</Button>
)}
</>
)
}
@@ -0,0 +1,53 @@
import clsx from "clsx"
import React from "react"
import { CollapsibleReturn } from "../../../../hooks"
export type CodeBlockCollapsibleFadeProps = {
type: "start" | "end"
hasHeader?: boolean
} & Pick<CollapsibleReturn, "collapsed">
export const CodeBlockCollapsibleFade = ({
type,
hasHeader = false,
collapsed,
}: CodeBlockCollapsibleFadeProps) => {
if (!collapsed) {
return <></>
}
return (
<span
className={clsx(
"absolute flex flex-col z-10",
hasHeader && "left-[6px] w-[calc(100%-12px)]",
!hasHeader && "w-full left-0",
type === "start" && [
hasHeader && "top-[44px]",
!hasHeader && "top-[36px]",
],
type === "end" && [
hasHeader && "bottom-[44px]",
!hasHeader && "bottom-[36px]",
]
)}
>
{type === "end" && (
<span
className={clsx(
"w-full h-[56px]",
"bg-code-fade-bottom-to-top dark:bg-code-fade-bottom-to-top-dark"
)}
/>
)}
{type === "start" && (
<span
className={clsx(
"w-full h-[56px]",
"bg-code-fade-top-to-bottom dark:bg-code-fade-top-to-bottom-dark"
)}
/>
)}
</span>
)
}
@@ -0,0 +1,29 @@
"use client"
import React, { useMemo } from "react"
import { CollapsibleReturn } from "../../../../hooks"
export type CodeBlockCollapsibleLinesProps = {
children: React.ReactNode
type: "start" | "end"
} & Omit<CollapsibleReturn, "setCollapsed">
export const CodeBlockCollapsibleLines = ({
children,
type,
collapsed,
getCollapsibleElms,
}: CodeBlockCollapsibleLinesProps) => {
const shownChildren: React.ReactNode = useMemo(() => {
const isStart = type === "start"
return (
<>
{collapsed && Array.isArray(children)
? children.slice(isStart ? -2 : 0, isStart ? undefined : 2)
: children}
</>
)
}, [children, collapsed])
return getCollapsibleElms(shownChildren)
}
@@ -22,6 +22,7 @@ type CodeBlockLineProps = {
lineNumber: number lineNumber: number
showLineNumber: boolean showLineNumber: boolean
lineNumberColorClassName: string lineNumberColorClassName: string
lineNumberBgClassName: string
noLineNumbers?: boolean noLineNumbers?: boolean
} & Pick<RenderProps, "getLineProps" | "getTokenProps"> } & Pick<RenderProps, "getLineProps" | "getTokenProps">
@@ -33,6 +34,7 @@ export const CodeBlockLine = ({
getTokenProps, getTokenProps,
showLineNumber, showLineNumber,
lineNumberColorClassName, lineNumberColorClassName,
lineNumberBgClassName,
}: CodeBlockLineProps) => { }: CodeBlockLineProps) => {
const lineProps = getLineProps({ line, key: lineNumber }) const lineProps = getLineProps({ line, key: lineNumber })
@@ -241,7 +243,8 @@ export const CodeBlockLine = ({
className={clsx( className={clsx(
"mr-docs_1 table-cell select-none", "mr-docs_1 table-cell select-none",
"sticky left-0 w-[1%] px-docs_1 text-right", "sticky left-0 w-[1%] px-docs_1 text-right",
lineNumberColorClassName lineNumberColorClassName,
lineNumberBgClassName
)} )}
> >
{lineNumber + 1} {lineNumber + 1}
@@ -1,15 +1,19 @@
"use client" "use client"
import React, { useMemo, useState } from "react" import React, { useEffect, useMemo, useRef, useState } from "react"
import clsx from "clsx" import clsx from "clsx"
import { HighlightProps, Highlight, themes } from "prism-react-renderer" import { Highlight, HighlightProps, themes, Token } from "prism-react-renderer"
import { ApiRunner } from "@/components" import { ApiRunner } from "@/components"
import { useColorMode } from "@/providers" import { useColorMode } from "@/providers"
import { CodeBlockHeader, CodeBlockHeaderMeta } from "./Header" import { CodeBlockHeader, CodeBlockHeaderMeta } from "./Header"
import { CodeBlockLine } from "./Line" import { CodeBlockLine } from "./Line"
import { ApiAuthType, ApiDataOptions, ApiMethod } from "types" import { ApiAuthType, ApiDataOptions, ApiMethod } from "types"
import { CSSTransition } from "react-transition-group" import { CSSTransition } from "react-transition-group"
import { useCollapsibleCodeLines } from "../.."
import { HighlightProps as CollapsibleHighlightProps } from "@/hooks"
import { CodeBlockActions, CodeBlockActionsProps } from "./Actions" import { CodeBlockActions, CodeBlockActionsProps } from "./Actions"
import { CodeBlockCollapsibleButton } from "./Collapsible/Button"
import { CodeBlockCollapsibleFade } from "./Collapsible/Fade"
export type Highlight = { export type Highlight = {
line: number line: number
@@ -32,6 +36,8 @@ export type CodeBlockMetaFields = {
noCopy?: boolean noCopy?: boolean
noReport?: boolean noReport?: boolean
noLineNumbers?: boolean noLineNumbers?: boolean
collapsibleLines?: string
expandButtonLabel?: string
} & CodeBlockHeaderMeta } & CodeBlockHeaderMeta
export type CodeBlockStyle = "loud" | "subtle" export type CodeBlockStyle = "loud" | "subtle"
@@ -60,6 +66,8 @@ export const CodeBlock = ({
noReport = false, noReport = false,
noLineNumbers = false, noLineNumbers = false,
children, children,
collapsibleLines,
expandButtonLabel,
...rest ...rest
}: CodeBlockProps) => { }: CodeBlockProps) => {
if (!source && typeof children === "string") { if (!source && typeof children === "string") {
@@ -68,6 +76,9 @@ export const CodeBlock = ({
const { colorMode } = useColorMode() const { colorMode } = useColorMode()
const [showTesting, setShowTesting] = useState(false) const [showTesting, setShowTesting] = useState(false)
const codeContainerRef = useRef<HTMLDivElement>(null)
const codeRef = useRef<HTMLElement>(null)
const [scrollable, setScrollable] = useState(false)
const hasInnerCodeBlock = useMemo( const hasInnerCodeBlock = useMemo(
() => hasTabs || title.length > 0, () => hasTabs || title.length > 0,
[hasTabs, title] [hasTabs, title]
@@ -107,7 +118,7 @@ export const CodeBlock = ({
const borderColor = useMemo( const borderColor = useMemo(
() => () =>
clsx( clsx(
blockStyle === "loud" && "border-transparent", blockStyle === "loud" && "border-0",
blockStyle === "subtle" && [ blockStyle === "subtle" && [
colorMode === "light" && "border-medusa-border-base", colorMode === "light" && "border-medusa-border-base",
colorMode === "dark" && "border-medusa-code-border", colorMode === "dark" && "border-medusa-code-border",
@@ -158,10 +169,6 @@ export const CodeBlock = ({
return lowerLang === "json" ? "plain" : lowerLang return lowerLang === "json" ? "plain" : lowerLang
}, [lang]) }, [lang])
if (!source.length) {
return <></>
}
const transformedHighlights: Highlight[] = highlights const transformedHighlights: Highlight[] = highlights
.filter((highlight) => highlight.length !== 0) .filter((highlight) => highlight.length !== 0)
.map((highlight) => ({ .map((highlight) => ({
@@ -170,13 +177,79 @@ export const CodeBlock = ({
tooltipText: highlight.length >= 3 ? highlight[2] : undefined, tooltipText: highlight.length >= 3 ? highlight[2] : undefined,
})) }))
const actionsProps: Omit<CodeBlockActionsProps, "inHeader"> = { const getLines = (
tokens: Token[][],
highlightProps: CollapsibleHighlightProps,
lineNumberOffset = 0
) =>
tokens.map((line, i) => {
const offsettedLineNumber = i + lineNumberOffset
const highlightedLines = transformedHighlights.filter(
(highlight) => highlight.line - 1 === offsettedLineNumber
)
return (
<CodeBlockLine
line={line}
lineNumber={offsettedLineNumber}
highlights={highlightedLines}
showLineNumber={!noLineNumbers && tokens.length > 1}
key={offsettedLineNumber}
lineNumberColorClassName={lineNumbersColor}
lineNumberBgClassName={innerBgColor}
{...highlightProps}
/>
)
})
const {
getCollapsedLinesElm,
getNonCollapsedLinesElm,
type: collapsibleType,
...collapsibleResult
} = useCollapsibleCodeLines({
collapsibleLinesStr: collapsibleLines,
getLines,
})
useEffect(() => {
if (!codeContainerRef.current || !codeRef.current) {
return
}
setScrollable(
codeContainerRef.current.scrollWidth < codeRef.current.clientWidth
)
}, [codeContainerRef.current, codeRef.current])
const actionsProps: Omit<CodeBlockActionsProps, "inHeader"> = useMemo(
() => ({
source, source,
canShowApiTesting, canShowApiTesting,
onApiTesting: setShowTesting, onApiTesting: setShowTesting,
blockStyle, blockStyle,
noReport, noReport,
noCopy, noCopy,
isCollapsed: collapsibleType !== undefined && collapsibleResult.collapsed,
inInnerCode: hasInnerCodeBlock,
showGradientBg: scrollable,
}),
[
source,
canShowApiTesting,
setShowTesting,
blockStyle,
noReport,
noCopy,
collapsibleType,
collapsibleResult,
hasInnerCodeBlock,
scrollable,
]
)
if (!source.length) {
return <></>
} }
return ( return (
@@ -233,12 +306,30 @@ export const CodeBlock = ({
tokens, tokens,
...rest ...rest
}) => ( }) => (
<div className={clsx(innerBorderClasses, innerBgColor)}> <div
className={clsx(innerBorderClasses, innerBgColor, "relative")}
ref={codeContainerRef}
>
{collapsibleType === "start" && (
<>
<CodeBlockCollapsibleButton
type={collapsibleType}
expandButtonLabel={expandButtonLabel}
className={innerBorderClasses}
{...collapsibleResult}
/>
<CodeBlockCollapsibleFade
type={collapsibleType}
collapsed={collapsibleResult.collapsed}
hasHeader={hasInnerCodeBlock}
/>
</>
)}
<pre <pre
style={{ ...style, fontStretch: "100%" }} style={{ ...style, fontStretch: "100%" }}
className={clsx( className={clsx(
"relative !my-0 break-words bg-transparent !outline-none", "relative !my-0 break-words bg-transparent !outline-none",
"overflow-auto break-words p-0", "overflow-auto break-words p-0 pr-docs_0.25",
"rounded-docs_DEFAULT", "rounded-docs_DEFAULT",
!hasInnerCodeBlock && !hasInnerCodeBlock &&
tokens.length <= 1 && tokens.length <= 1 &&
@@ -253,23 +344,21 @@ export const CodeBlock = ({
tokens.length > 1 && "py-docs_0.75", tokens.length > 1 && "py-docs_0.75",
tokens.length <= 1 && "!py-[6px] px-docs_0.5" tokens.length <= 1 && "!py-[6px] px-docs_0.5"
)} )}
ref={codeRef}
> >
{tokens.map((line, i) => { {collapsibleType === "start" &&
const highlightedLines = transformedHighlights.filter( getCollapsedLinesElm({
(highlight) => highlight.line - 1 === i tokens,
) highlightProps: rest,
})}
return ( {getNonCollapsedLinesElm({
<CodeBlockLine tokens,
line={line} highlightProps: rest,
lineNumber={i} })}
highlights={highlightedLines} {collapsibleType === "end" &&
showLineNumber={!noLineNumbers && tokens.length > 1} getCollapsedLinesElm({
key={i} tokens,
lineNumberColorClassName={lineNumbersColor} highlightProps: rest,
{...rest}
/>
)
})} })}
</code> </code>
</pre> </pre>
@@ -280,6 +369,21 @@ export const CodeBlock = ({
isSingleLine={tokens.length <= 1} isSingleLine={tokens.length <= 1}
/> />
)} )}
{collapsibleType === "end" && (
<>
<CodeBlockCollapsibleFade
type={collapsibleType}
collapsed={collapsibleResult.collapsed}
hasHeader={hasInnerCodeBlock}
/>
<CodeBlockCollapsibleButton
type={collapsibleType}
expandButtonLabel={expandButtonLabel}
className={innerBorderClasses}
{...collapsibleResult}
/>
</>
)}
</div> </div>
)} )}
</Highlight> </Highlight>
@@ -3,8 +3,8 @@
import React, { Suspense, cloneElement, useRef, useState } from "react" import React, { Suspense, cloneElement, useRef, useState } from "react"
import { Loading } from "@/components" import { Loading } from "@/components"
import clsx from "clsx" import clsx from "clsx"
import { CSSTransition } from "react-transition-group"
import { DetailsSummary } from "./Summary" import { DetailsSummary } from "./Summary"
import { useCollapsible } from "../../hooks"
export type DetailsProps = { export type DetailsProps = {
openInitial?: boolean openInitial?: boolean
@@ -22,7 +22,11 @@ export const Details = ({
...props ...props
}: DetailsProps) => { }: DetailsProps) => {
const [open, setOpen] = useState(openInitial) const [open, setOpen] = useState(openInitial)
const [showContent, setShowContent] = useState(openInitial) const { getCollapsibleElms, setCollapsed } = useCollapsible({
initialValue: !openInitial,
heightAnimation,
onClose: () => setOpen(false),
})
const ref = useRef<HTMLDetailsElement>(null) const ref = useRef<HTMLDetailsElement>(null)
const handleToggle = (e: React.MouseEvent<HTMLElement>) => { const handleToggle = (e: React.MouseEvent<HTMLElement>) => {
@@ -36,10 +40,10 @@ export const Details = ({
return return
} }
if (open) { if (open) {
setShowContent(false) setCollapsed(true)
} else { } else {
setOpen(true) setOpen(true)
setShowContent(true) setCollapsed(false)
} }
} }
@@ -77,56 +81,11 @@ export const Details = ({
open, open,
onClick: handleToggle, onClick: handleToggle,
})} })}
<CSSTransition {getCollapsibleElms(
unmountOnExit
in={showContent}
timeout={150}
onEnter={(node: HTMLElement) => {
if (heightAnimation) {
node.classList.add("transition-[height]")
node.style.height = `0px`
} else {
node.classList.add(
"!mb-docs_2",
"!mt-0",
"translate-y-docs_1",
"transition-transform"
)
}
}}
onEntering={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `${node.scrollHeight}px`
}
}}
onEntered={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `auto`
}
}}
onExit={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `${node.scrollHeight}px`
} else {
node.classList.add("transition-transform", "!-translate-y-docs_1")
setTimeout(() => {
setOpen(false)
}, 100)
}
}}
onExiting={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `0px`
setTimeout(() => {
setOpen(false)
}, 100)
}
}}
>
<Suspense fallback={<Loading className="!mb-docs_2 !mt-0" />}> <Suspense fallback={<Loading className="!mb-docs_2 !mt-0" />}>
{children} {children}
</Suspense> </Suspense>
</CSSTransition> )}
</details> </details>
) )
} }
+2
View File
@@ -1,3 +1,5 @@
export * from "./use-collapsible"
export * from "./use-collapsible-code-lines"
export * from "./use-copy" export * from "./use-copy"
export * from "./use-current-learning-path" export * from "./use-current-learning-path"
export * from "./use-is-browser" export * from "./use-is-browser"
@@ -0,0 +1,136 @@
"use client"
import {
LineInputProps,
LineOutputProps,
Token,
TokenInputProps,
TokenOutputProps,
} from "prism-react-renderer"
import React, { useCallback, useMemo } from "react"
import { CodeBlockCollapsibleLines } from "../../components/CodeBlock/Collapsible/Lines"
import { useCollapsible } from "../use-collapsible"
export type HighlightProps = {
getLineProps: (input: LineInputProps) => LineOutputProps
getTokenProps: (input: TokenInputProps) => TokenOutputProps
}
export type CollapsibleCodeLines = {
collapsibleLinesStr?: string
getLines: (
token: Token[][],
highlightProps: HighlightProps,
lineNumberOffset?: number
) => React.ReactNode
}
export type CollapsedCodeLinesPosition = "start" | "end"
export const useCollapsibleCodeLines = ({
collapsibleLinesStr,
getLines,
}: CollapsibleCodeLines) => {
const collapsedRange:
| {
start: number
end: number
}
| undefined = useMemo(() => {
if (!collapsibleLinesStr) {
return
}
const splitCollapsedLines = collapsibleLinesStr
.split("-")
.map((lineNumber) => parseInt(lineNumber))
if (
splitCollapsedLines.length !== 2 ||
(splitCollapsedLines[0] !== 1 && splitCollapsedLines[1] < 2)
) {
return
}
return {
start: splitCollapsedLines[0],
end: splitCollapsedLines[1],
}
}, [collapsibleLinesStr])
const type: CollapsedCodeLinesPosition | undefined = useMemo(() => {
if (!collapsedRange) {
return undefined
}
return collapsedRange.start === 1 ? "start" : "end"
}, [collapsedRange])
const collapsibleHookResult = useCollapsible({
unmountOnExit: false,
translateEnabled: false,
heightAnimation: true,
})
const getCollapsedLinesElm = useCallback(
({
tokens,
highlightProps,
}: {
tokens: Token[][]
highlightProps: HighlightProps
}) => {
if (!collapsedRange || !type) {
return <></>
}
const startIndex =
type === "start" ? collapsedRange.start - 1 : collapsedRange.start
const lines = tokens.slice(
startIndex,
Math.min(collapsedRange.end, tokens.length)
)
return (
<CodeBlockCollapsibleLines {...collapsibleHookResult} type={type}>
{getLines(lines, highlightProps, startIndex)}
</CodeBlockCollapsibleLines>
)
},
[collapsedRange, collapsibleHookResult]
)
const getNonCollapsedLinesElm = useCallback(
({
tokens,
highlightProps,
}: {
tokens: Token[][]
highlightProps: HighlightProps
}) => {
if (!collapsedRange) {
return getLines(tokens, highlightProps)
}
const isCollapseBeginning = collapsedRange.start === 1
const lines = tokens.slice(
isCollapseBeginning ? collapsedRange.end : 0,
isCollapseBeginning ? undefined : collapsedRange.start
)
return getLines(
lines,
highlightProps,
isCollapseBeginning ? collapsedRange.end : 0
)
},
[collapsedRange, collapsibleHookResult]
)
return {
getCollapsedLinesElm,
getNonCollapsedLinesElm,
type,
...collapsibleHookResult,
}
}
@@ -0,0 +1,85 @@
"use client"
import React, { useState } from "react"
import { CSSTransition } from "react-transition-group"
export type CollapsibleProps = {
initialValue?: boolean
heightAnimation?: boolean
translateEnabled?: boolean
onClose?: () => void
unmountOnExit?: boolean
}
export type CollapsibleReturn = {
getCollapsibleElms: (children: React.ReactNode) => React.JSX.Element
collapsed: boolean
setCollapsed: React.Dispatch<React.SetStateAction<boolean>>
}
export const useCollapsible = ({
initialValue = true,
heightAnimation = false,
translateEnabled = true,
onClose,
unmountOnExit = true,
}: CollapsibleProps): CollapsibleReturn => {
const [collapsed, setCollapsed] = useState(initialValue)
const getCollapsibleElms = (children: React.ReactNode) => (
<CSSTransition
unmountOnExit={unmountOnExit}
in={!collapsed}
timeout={150}
onEnter={(node: HTMLElement) => {
if (heightAnimation) {
node.classList.add("transition-[height]")
node.style.height = `0px`
} else {
node.classList.add("!mb-docs_2", "!mt-0")
if (translateEnabled) {
node.classList.add("translate-y-docs_1", "transition-transform")
}
}
}}
onEntering={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `${node.scrollHeight}px`
}
}}
onEntered={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `auto`
}
}}
onExit={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `${node.scrollHeight}px`
} else {
if (translateEnabled) {
node.classList.add("transition-transform", "!-translate-y-docs_1")
}
setTimeout(() => {
onClose?.()
}, 100)
}
}}
onExiting={(node: HTMLElement) => {
if (heightAnimation) {
node.style.height = `0px`
setTimeout(() => {
onClose?.()
}, 100)
}
}}
>
{children}
</CSSTransition>
)
return {
getCollapsibleElms,
collapsed,
setCollapsed,
}
}
+12 -1
View File
@@ -176,6 +176,11 @@ module.exports = {
top: "var(--docs-contrast-border-top)", top: "var(--docs-contrast-border-top)",
bot: "var(--docs-contrast-border-bot)", bot: "var(--docs-contrast-border-bot)",
}, },
// not in UI but necessary for show more button
button: {
DEFAULT: "#3D3D3F",
hover: "#505052",
},
}, },
}, },
/* docs defaults */ /* docs defaults */
@@ -257,7 +262,13 @@ module.exports = {
}, },
backgroundImage: { backgroundImage: {
"code-fade-top-to-bottom": `linear-gradient(180deg, #27272A 0%, rgba(39, 39, 42, 0.00) 100%)`, "code-fade-top-to-bottom": `linear-gradient(180deg, #27272A 0%, rgba(39, 39, 42, 0.00) 100%)`,
"code-fade-bottom-to-top": `linear-gradient(180deg, #27272A 0%, rgba(39, 39, 42, 0.00) 100%)`, "code-fade-bottom-to-top": `linear-gradient(180deg, rgba(39, 39, 42, 0.00) 0%, #27272A 100%)`,
"base-code-fade-right-to-left": `linear-gradient(90deg, #18181b7d, #18181B)`,
"subtle-code-fade-right-to-left": `linear-gradient(90deg, #27272aa3, #27272A)`,
"code-fade-top-to-bottom-dark": `linear-gradient(180deg, #2F2F32 0%, rgba(47, 47, 50, 0.00) 100%)`,
"code-fade-bottom-to-top-dark": `linear-gradient(180deg, rgba(47, 47, 50, 0.00) 0%, #2F2F32 100%)`,
"base-code-fade-right-to-left-dark": `linear-gradient(90deg, #27272aa3, #27272A)`,
"subtle-code-fade-right-to-left-dark": `linear-gradient(90deg, #30303380, #303033)`,
}, },
screens: { screens: {
xs: "576px", xs: "576px",
+1 -1
View File
@@ -196,7 +196,7 @@ const dark = {
"--docs-contrast-bg-base": "rgba(39, 39, 42, 1)", "--docs-contrast-bg-base": "rgba(39, 39, 42, 1)",
"--docs-contrast-bg-base-pressed": "rgba(113, 113, 122, 1)", "--docs-contrast-bg-base-pressed": "rgba(113, 113, 122, 1)",
"--docs-contrast-bg-base-hover": "rgba(82, 82, 91, 1)", "--docs-contrast-bg-base-hover": "rgba(82, 82, 91, 1)",
"--docs-contrast-bg-subtle": "rgba(255, 255, 255, 0.04)", "--docs-contrast-bg-subtle": "rgba(47, 47, 50, 1)",
"--docs-contrast-bg-highlight": "rgba(82, 82, 91, 1)", "--docs-contrast-bg-highlight": "rgba(82, 82, 91, 1)",
"--docs-contrast-bg-alpha": "rgba(63, 63, 70, 0.9)", "--docs-contrast-bg-alpha": "rgba(63, 63, 70, 0.9)",
"--docs-contrast-fg-primary": "rgba(250, 250, 250, 1)", "--docs-contrast-fg-primary": "rgba(250, 250, 250, 1)",