- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
Menu
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
JSON View - Admin Components
Detail pages in the Medusa Admin show a JSON section to view the current page's details in JSON format.
To create a component that shows a JSON section in your customizations, create the file src/admin/components/json-view-section.tsx
with the following content:
1import {2 ArrowUpRightOnBox,3 Check,4 SquareTwoStack,5 TriangleDownMini,6 XMarkMini,7} from "@medusajs/icons"8import {9 Badge,10 Container,11 Drawer,12 Heading,13 IconButton,14 Kbd,15} from "@medusajs/ui"16import Primitive from "@uiw/react-json-view"17import { CSSProperties, MouseEvent, Suspense, useState } from "react"18 19type JsonViewSectionProps = {20 data: object21 title?: string22}23 24export const JsonViewSection = ({ data }: JsonViewSectionProps) => {25 const numberOfKeys = Object.keys(data).length26 27 return (28 <Container className="flex items-center justify-between px-6 py-4">29 <div className="flex items-center gap-x-4">30 <Heading level="h2">JSON</Heading>31 <Badge size="2xsmall" rounded="full">32 {numberOfKeys} keys33 </Badge>34 </div>35 <Drawer>36 <Drawer.Trigger asChild>37 <IconButton38 size="small"39 variant="transparent"40 className="text-ui-fg-muted hover:text-ui-fg-subtle"41 >42 <ArrowUpRightOnBox />43 </IconButton>44 </Drawer.Trigger>45 <Drawer.Content className="bg-ui-contrast-bg-base text-ui-code-fg-subtle !shadow-elevation-commandbar overflow-hidden border border-none max-md:inset-x-2 max-md:max-w-[calc(100%-16px)]">46 <div className="bg-ui-code-bg-base flex items-center justify-between px-6 py-4">47 <div className="flex items-center gap-x-4">48 <Drawer.Title asChild>49 <Heading className="text-ui-contrast-fg-primary">50 <span className="text-ui-fg-subtle">51 {numberOfKeys}52 </span>53 </Heading>54 </Drawer.Title>55 </div>56 <div className="flex items-center gap-x-2">57 <Kbd className="bg-ui-contrast-bg-subtle border-ui-contrast-border-base text-ui-contrast-fg-secondary">58 esc59 </Kbd>60 <Drawer.Close asChild>61 <IconButton62 size="small"63 variant="transparent"64 className="text-ui-contrast-fg-secondary hover:text-ui-contrast-fg-primary hover:bg-ui-contrast-bg-base-hover active:bg-ui-contrast-bg-base-pressed focus-visible:bg-ui-contrast-bg-base-hover focus-visible:shadow-borders-interactive-with-active"65 >66 <XMarkMini />67 </IconButton>68 </Drawer.Close>69 </div>70 </div>71 <Drawer.Body className="flex flex-1 flex-col overflow-hidden px-[5px] py-0 pb-[5px]">72 <div className="bg-ui-contrast-bg-subtle flex-1 overflow-auto rounded-b-[4px] rounded-t-lg p-3">73 <Suspense74 fallback={<div className="flex size-full flex-col"></div>}75 >76 <Primitive77 value={data}78 displayDataTypes={false}79 style={80 {81 "--w-rjv-font-family": "Roboto Mono, monospace",82 "--w-rjv-line-color": "var(--contrast-border-base)",83 "--w-rjv-curlybraces-color":84 "var(--contrast-fg-secondary)",85 "--w-rjv-brackets-color": "var(--contrast-fg-secondary)",86 "--w-rjv-key-string": "var(--contrast-fg-primary)",87 "--w-rjv-info-color": "var(--contrast-fg-secondary)",88 "--w-rjv-type-string-color": "var(--tag-green-icon)",89 "--w-rjv-quotes-string-color": "var(--tag-green-icon)",90 "--w-rjv-type-boolean-color": "var(--tag-orange-icon)",91 "--w-rjv-type-int-color": "var(--tag-orange-icon)",92 "--w-rjv-type-float-color": "var(--tag-orange-icon)",93 "--w-rjv-type-bigint-color": "var(--tag-orange-icon)",94 "--w-rjv-key-number": "var(--contrast-fg-secondary)",95 "--w-rjv-arrow-color": "var(--contrast-fg-secondary)",96 "--w-rjv-copied-color": "var(--contrast-fg-secondary)",97 "--w-rjv-copied-success-color":98 "var(--contrast-fg-primary)",99 "--w-rjv-colon-color": "var(--contrast-fg-primary)",100 "--w-rjv-ellipsis-color": "var(--contrast-fg-secondary)",101 } as CSSProperties102 }103 collapsed={1}104 >105 <Primitive.Quote render={() => <span />} />106 <Primitive.Null107 render={() => (108 <span className="text-ui-tag-red-icon">null</span>109 )}110 />111 <Primitive.Undefined112 render={() => (113 <span className="text-ui-tag-blue-icon">undefined</span>114 )}115 />116 <Primitive.CountInfo117 render={(_props, { value }) => {118 return (119 <span className="text-ui-contrast-fg-secondary ml-2">120 {Object.keys(value as object).length} items121 </span>122 )123 }}124 />125 <Primitive.Arrow>126 <TriangleDownMini className="text-ui-contrast-fg-secondary -ml-[0.5px]" />127 </Primitive.Arrow>128 <Primitive.Colon>129 <span className="mr-1">:</span>130 </Primitive.Colon>131 <Primitive.Copied132 render={({ style }, { value }) => {133 return <Copied style={style} value={value} />134 }}135 />136 </Primitive>137 </Suspense>138 </div>139 </Drawer.Body>140 </Drawer.Content>141 </Drawer>142 </Container>143 )144}145 146type CopiedProps = {147 style?: CSSProperties148 value: object | undefined149}150 151const Copied = ({ style, value }: CopiedProps) => {152 const [copied, setCopied] = useState(false)153 154 const handler = (e: MouseEvent<HTMLSpanElement>) => {155 e.stopPropagation()156 setCopied(true)157 158 if (typeof value === "string") {159 navigator.clipboard.writeText(value)160 } else {161 const json = JSON.stringify(value, null, 2)162 navigator.clipboard.writeText(json)163 }164 165 setTimeout(() => {166 setCopied(false)167 }, 2000)168 }169 170 const styl = { whiteSpace: "nowrap", width: "20px" }171 172 if (copied) {173 return (174 <span style={{ ...style, ...styl }}>175 <Check className="text-ui-contrast-fg-primary" />176 </span>177 )178 }179 180 return (181 <span style={{ ...style, ...styl }} onClick={handler}>182 <SquareTwoStack className="text-ui-contrast-fg-secondary" />183 </span>184 )185}
The JsonViewSection
component shows a section with the "JSON" title and a button to show the data as JSON in a drawer or side window.
The JsonViewSection
accepts a data
prop, which is the data to show as a JSON object in the drawer.
Example#
Use the JsonViewSection
component in any widget or UI route.
For example, create the widget src/admin/widgets/product-widget.tsx
with the following content:
1import { defineWidgetConfig } from "@medusajs/admin-sdk"2import { JsonViewSection } from "../components/json-view-section"3 4const ProductWidget = () => {5 return <JsonViewSection data={{6 name: "John",7 }} />8}9 10export const config = defineWidgetConfig({11 zone: "product.details.before",12})13 14export default ProductWidget
This shows the JSON section at the top of the product page, passing it the object { name: "John" }
.
Was this page helpful?