@ably/ui 18.3.2 → 18.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/Accordion.js +1 -1
- package/core/Accordion.js.map +1 -1
- package/core/CodeSnippet/CodeSnippet.test.js +1 -1
- package/core/CodeSnippet/CodeSnippet.test.js.map +1 -1
- package/core/CodeSnippet/PlainCodeView.js +1 -1
- package/core/CodeSnippet/PlainCodeView.js.map +1 -1
- package/core/CodeSnippet/PlainCodeView.test.js +2 -0
- package/core/CodeSnippet/PlainCodeView.test.js.map +1 -0
- package/core/CodeSnippet/copyToClipboard.js +2 -0
- package/core/CodeSnippet/copyToClipboard.js.map +1 -0
- package/core/CodeSnippet.js +1 -1
- package/core/CodeSnippet.js.map +1 -1
- package/core/DropdownMenu.js +1 -1
- package/core/DropdownMenu.js.map +1 -1
- package/core/FeaturedLink.js +1 -1
- package/core/FeaturedLink.js.map +1 -1
- package/core/Flyout.js +1 -1
- package/core/Flyout.js.map +1 -1
- package/core/Header/HeaderLinks.js +1 -1
- package/core/Header/HeaderLinks.js.map +1 -1
- package/core/Header.js +1 -1
- package/core/Header.js.map +1 -1
- package/core/Icon/types.js.map +1 -1
- package/core/Icon.js.map +1 -1
- package/core/Meganav/MeganavMobile.js +1 -1
- package/core/Meganav/MeganavMobile.js.map +1 -1
- package/core/Meganav.js +1 -1
- package/core/Meganav.js.map +1 -1
- package/core/TabMenu.js +1 -1
- package/core/TabMenu.js.map +1 -1
- package/core/styles/buttons.css +4 -4
- package/core/styles/legacy-buttons.css +5 -5
- package/index.d.ts +29 -0
- package/package.json +26 -25
package/core/Accordion.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React,{useMemo,useState,forwardRef}from"react";import{AccordionContent,AccordionItem,AccordionTrigger,Accordion as RadixAccordion}from"@radix-ui/react-accordion";import Icon from"./Icon";import{themeClasses,isNonTransparentTheme,isStaticTheme}from"./Accordion/utils";import cn from"./utils/cn";const AccordionRow=({name,heading,children,rowIcon,options,toggleIcons,theme,index,onClick,openRowValues,rowInteractive=true})=>{const{selectable,sticky}=options||{};const rowKey=`accordion-item-${index}`;const isOpen=openRowValues.includes(rowKey);const{text,bg,hoverBg,selectableBg,selectableText,border,toggleIconColor}=themeClasses[theme];const textClass=selectable&&isOpen&&selectableText||text;const renderHeading=()=>{if(heading){if(typeof heading==="function"){return heading(index,isOpen)}return heading}return React.createElement("span",null,name)};return React.createElement(AccordionItem,{value:rowKey,className:cn({[`${border}`]:border&&!options?.hideBorders,[`${options?.selectedItemCSS}`]:options?.selectedItemCSS&&isOpen})},React.createElement(AccordionTrigger,{onClick:onClick,className:cn({"flex w-full group/accordion-trigger py-4 ui-text-p1 font-
|
|
1
|
+
import React,{useMemo,useState,forwardRef}from"react";import{AccordionContent,AccordionItem,AccordionTrigger,Accordion as RadixAccordion}from"@radix-ui/react-accordion";import Icon from"./Icon";import{themeClasses,isNonTransparentTheme,isStaticTheme}from"./Accordion/utils";import cn from"./utils/cn";const AccordionRow=({name,heading,children,rowIcon,options,toggleIcons,theme,index,onClick,openRowValues,rowInteractive=true})=>{const{selectable,sticky}=options||{};const rowKey=`accordion-item-${index}`;const isOpen=openRowValues.includes(rowKey);const{text,bg,hoverBg,selectableBg,selectableText,border,toggleIconColor}=themeClasses[theme];const textClass=selectable&&isOpen&&selectableText||text;const renderHeading=()=>{if(heading){if(typeof heading==="function"){return heading(index,isOpen)}return heading}return React.createElement("span",null,name)};return React.createElement(AccordionItem,{value:rowKey,className:cn({[`${border}`]:border&&!options?.hideBorders,[`${options?.selectedItemCSS}`]:options?.selectedItemCSS&&isOpen})},React.createElement(AccordionTrigger,{onClick:onClick,className:cn({"flex w-full group/accordion-trigger py-4 ui-text-p1 font-semibold text-left items-center gap-3 transition-colors focus:outline-none":true,"px-4 mb-4 rounded-lg":isNonTransparentTheme(theme),"px-0 rounded-none":!isNonTransparentTheme(theme),"pointer-events-none focus-visible:outline-none":isStaticTheme(theme),"focus-base":!isStaticTheme(theme),"sticky top-0":sticky,[`${bg} ${hoverBg} ${text}`]:!(selectable&&isOpen),[`${selectableBg} ${selectableText}`]:selectable&&isOpen,[options?.headerCSS??""]:options?.headerCSS,[options?.selectedHeaderCSS??""]:options?.selectedHeaderCSS&&isOpen})},rowIcon?React.createElement(Icon,{name:typeof rowIcon==="object"?rowIcon.name:rowIcon,color:textClass,additionalCSS:typeof rowIcon==="object"&&rowIcon.css?rowIcon.css:"",size:options?.rowIconSize??"32px"}):null,renderHeading(),!selectable&&!isStaticTheme(theme)&&rowInteractive?React.createElement("span",{className:"flex-1 justify-end flex items-center"},React.createElement(Icon,{name:isOpen?toggleIcons.open.name:toggleIcons.closed.name,color:toggleIconColor,additionalCSS:isOpen?typeof toggleIcons.open==="object"&&toggleIcons.open.css||"":typeof toggleIcons.closed==="object"&&toggleIcons.closed.css||"",size:options?.iconSize??"16px"})):null),rowInteractive&&React.createElement(AccordionContent,{className:cn({"ui-text-p2 overflow-hidden transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down":true,[options?.contentCSS??""]:options?.contentCSS})},React.createElement("div",{className:"pb-4"},children)))};const Accordion=forwardRef(({data,theme="transparent",icons={closed:{name:"icon-gui-plus-outline"},open:{name:"icon-gui-minus-outline"}},options,...props},ref)=>{const openIndexes=useMemo(()=>{const indexValues=data.map((_,i)=>`accordion-item-${i}`);return options?.fullyOpen?indexValues:indexValues.filter((_,index)=>options?.defaultOpenIndexes?.includes(index))},[data,options?.fullyOpen,options?.defaultOpenIndexes]);const[openRowValues,setOpenRowValues]=useState(openIndexes);const[prevOpenIndexes,setPrevOpenIndexes]=useState(openIndexes);if(prevOpenIndexes!==openIndexes){setPrevOpenIndexes(openIndexes);setOpenRowValues(openIndexes)}const innerAccordion=data.map((item,index)=>React.createElement(AccordionRow,{key:item.name,name:item.name,heading:item.heading,rowIcon:item.icon,toggleIcons:icons,theme:theme,options:options,index:index,onClick:()=>{item.onClick?.(index)},openRowValues:openRowValues,rowInteractive:item.interactive},item.content));return React.createElement("div",{ref:ref,...props},options?.autoClose?React.createElement(RadixAccordion,{type:"single",collapsible:true,value:openRowValues[0],onValueChange:values=>setOpenRowValues([values])},innerAccordion):React.createElement(RadixAccordion,{type:"multiple",value:openRowValues,onValueChange:values=>setOpenRowValues(values)},innerAccordion))});Accordion.displayName="Accordion";export default Accordion;
|
|
2
2
|
//# sourceMappingURL=Accordion.js.map
|
package/core/Accordion.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/Accordion.tsx"],"sourcesContent":["import React, { ReactNode, useMemo, useState, forwardRef } from \"react\";\nimport {\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n Accordion as RadixAccordion,\n} from \"@radix-ui/react-accordion\";\n\nimport Icon from \"./Icon\";\nimport type { IconName } from \"./Icon/types\";\nimport type {\n AccordionData,\n AccordionIcon,\n AccordionIcons,\n AccordionOptions,\n AccordionTheme,\n} from \"./Accordion/types\";\nimport {\n themeClasses,\n isNonTransparentTheme,\n isStaticTheme,\n} from \"./Accordion/utils\";\nimport cn from \"./utils/cn\";\n\ntype AccordionRowProps = {\n children: ReactNode;\n name: string;\n heading?: ReactNode | ((index: number, isOpen: boolean) => ReactNode);\n rowIcon?: IconName | AccordionIcon;\n theme: AccordionTheme;\n toggleIcons: AccordionIcons;\n options?: AccordionOptions;\n index: number;\n onClick: () => void;\n openRowValues: string[];\n rowInteractive?: boolean;\n};\n\nexport type AccordionProps = {\n /**\n * The data for the accordion items.\n */\n data: AccordionData[];\n\n /**\n * Icons for the accordion toggle.\n */\n icons?: AccordionIcons;\n\n /**\n * Theme for the accordion.\n */\n theme?: AccordionTheme;\n\n /**\n * Options for the accordion behavior.\n */\n options?: AccordionOptions;\n} & React.HTMLAttributes<HTMLDivElement>;\n\nconst AccordionRow = ({\n name,\n heading,\n children,\n rowIcon,\n options,\n toggleIcons,\n theme,\n index,\n onClick,\n openRowValues,\n rowInteractive = true,\n}: AccordionRowProps) => {\n const { selectable, sticky } = options || {};\n const rowKey = `accordion-item-${index}`;\n const isOpen = openRowValues.includes(rowKey);\n\n const {\n text,\n bg,\n hoverBg,\n selectableBg,\n selectableText,\n border,\n toggleIconColor,\n } = themeClasses[theme];\n\n const textClass = (selectable && isOpen && selectableText) || text;\n\n // Render custom heading or fallback to name\n const renderHeading = () => {\n if (heading) {\n if (typeof heading === \"function\") {\n return heading(index, isOpen);\n }\n return heading;\n }\n return <span>{name}</span>;\n };\n\n return (\n <AccordionItem\n value={rowKey}\n className={cn({\n [`${border}`]: border && !options?.hideBorders,\n [`${options?.selectedItemCSS}`]: options?.selectedItemCSS && isOpen,\n })}\n >\n <AccordionTrigger\n onClick={onClick}\n className={cn({\n \"flex w-full group/accordion-trigger py-4 ui-text-p1 font-bold text-left items-center gap-3 transition-colors focus:outline-none\": true,\n \"px-4 mb-4 rounded-lg\": isNonTransparentTheme(theme),\n \"px-0 rounded-none\": !isNonTransparentTheme(theme),\n \"pointer-events-none focus-visible:outline-none\":\n isStaticTheme(theme),\n \"focus-base\": !isStaticTheme(theme),\n \"sticky top-0\": sticky,\n [`${bg} ${hoverBg} ${text}`]: !(selectable && isOpen),\n [`${selectableBg} ${selectableText}`]: selectable && isOpen,\n [options?.headerCSS ?? \"\"]: options?.headerCSS,\n [options?.selectedHeaderCSS ?? \"\"]:\n options?.selectedHeaderCSS && isOpen,\n })}\n >\n {rowIcon ? (\n <Icon\n name={typeof rowIcon === \"object\" ? rowIcon.name : rowIcon}\n color={textClass}\n additionalCSS={\n typeof rowIcon === \"object\" && rowIcon.css ? rowIcon.css : \"\"\n }\n size={options?.rowIconSize ?? \"32px\"}\n />\n ) : null}\n {renderHeading()}\n {!selectable && !isStaticTheme(theme) && rowInteractive ? (\n <span className=\"flex-1 justify-end flex items-center\">\n <Icon\n name={isOpen ? toggleIcons.open.name : toggleIcons.closed.name}\n color={toggleIconColor}\n additionalCSS={\n isOpen\n ? (typeof toggleIcons.open === \"object\" &&\n toggleIcons.open.css) ||\n \"\"\n : (typeof toggleIcons.closed === \"object\" &&\n toggleIcons.closed.css) ||\n \"\"\n }\n size={options?.iconSize ?? \"16px\"}\n />\n </span>\n ) : null}\n </AccordionTrigger>\n {rowInteractive && (\n <AccordionContent\n className={cn({\n \"ui-text-p2 overflow-hidden transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down\": true,\n [options?.contentCSS ?? \"\"]: options?.contentCSS,\n })}\n >\n <div className=\"pb-4\">{children}</div>\n </AccordionContent>\n )}\n </AccordionItem>\n );\n};\n\nconst Accordion = forwardRef<HTMLDivElement, AccordionProps>(\n (\n {\n data,\n theme = \"transparent\",\n icons = {\n closed: { name: \"icon-gui-plus-outline\" },\n open: { name: \"icon-gui-minus-outline\" },\n },\n options,\n ...props\n },\n ref,\n ) => {\n const openIndexes = useMemo(() => {\n const indexValues = data.map((_, i) => `accordion-item-${i}`);\n return options?.fullyOpen\n ? indexValues\n : indexValues.filter((_, index) =>\n options?.defaultOpenIndexes?.includes(index),\n );\n }, [data, options?.fullyOpen, options?.defaultOpenIndexes]);\n\n const [openRowValues, setOpenRowValues] = useState<string[]>(openIndexes);\n const [prevOpenIndexes, setPrevOpenIndexes] = useState(openIndexes);\n if (prevOpenIndexes !== openIndexes) {\n setPrevOpenIndexes(openIndexes);\n setOpenRowValues(openIndexes);\n }\n\n const innerAccordion = data.map((item, index) => (\n <AccordionRow\n key={item.name}\n name={item.name}\n heading={item.heading}\n rowIcon={item.icon}\n toggleIcons={icons}\n theme={theme}\n options={options}\n index={index}\n onClick={() => {\n item.onClick?.(index);\n }}\n openRowValues={openRowValues}\n rowInteractive={item.interactive}\n >\n {item.content}\n </AccordionRow>\n ));\n\n return (\n <div ref={ref} {...props}>\n {options?.autoClose ? (\n <RadixAccordion\n type=\"single\"\n collapsible\n value={openRowValues[0]}\n onValueChange={(values) => setOpenRowValues([values])}\n >\n {innerAccordion}\n </RadixAccordion>\n ) : (\n <RadixAccordion\n type=\"multiple\"\n value={openRowValues}\n onValueChange={(values) => setOpenRowValues(values)}\n >\n {innerAccordion}\n </RadixAccordion>\n )}\n </div>\n );\n },\n);\n\nAccordion.displayName = \"Accordion\";\n\nexport default Accordion;\n"],"names":["React","useMemo","useState","forwardRef","AccordionContent","AccordionItem","AccordionTrigger","Accordion","RadixAccordion","Icon","themeClasses","isNonTransparentTheme","isStaticTheme","cn","AccordionRow","name","heading","children","rowIcon","options","toggleIcons","theme","index","onClick","openRowValues","rowInteractive","selectable","sticky","rowKey","isOpen","includes","text","bg","hoverBg","selectableBg","selectableText","border","toggleIconColor","textClass","renderHeading","span","value","className","hideBorders","selectedItemCSS","headerCSS","selectedHeaderCSS","color","additionalCSS","css","size","rowIconSize","open","closed","iconSize","contentCSS","div","data","icons","props","ref","openIndexes","indexValues","map","_","i","fullyOpen","filter","defaultOpenIndexes","setOpenRowValues","prevOpenIndexes","setPrevOpenIndexes","innerAccordion","item","key","icon","interactive","content","autoClose","type","collapsible","onValueChange","values","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/Accordion.tsx"],"sourcesContent":["import React, { ReactNode, useMemo, useState, forwardRef } from \"react\";\nimport {\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n Accordion as RadixAccordion,\n} from \"@radix-ui/react-accordion\";\n\nimport Icon from \"./Icon\";\nimport type { IconName } from \"./Icon/types\";\nimport type {\n AccordionData,\n AccordionIcon,\n AccordionIcons,\n AccordionOptions,\n AccordionTheme,\n} from \"./Accordion/types\";\nimport {\n themeClasses,\n isNonTransparentTheme,\n isStaticTheme,\n} from \"./Accordion/utils\";\nimport cn from \"./utils/cn\";\n\ntype AccordionRowProps = {\n children: ReactNode;\n name: string;\n heading?: ReactNode | ((index: number, isOpen: boolean) => ReactNode);\n rowIcon?: IconName | AccordionIcon;\n theme: AccordionTheme;\n toggleIcons: AccordionIcons;\n options?: AccordionOptions;\n index: number;\n onClick: () => void;\n openRowValues: string[];\n rowInteractive?: boolean;\n};\n\nexport type AccordionProps = {\n /**\n * The data for the accordion items.\n */\n data: AccordionData[];\n\n /**\n * Icons for the accordion toggle.\n */\n icons?: AccordionIcons;\n\n /**\n * Theme for the accordion.\n */\n theme?: AccordionTheme;\n\n /**\n * Options for the accordion behavior.\n */\n options?: AccordionOptions;\n} & React.HTMLAttributes<HTMLDivElement>;\n\nconst AccordionRow = ({\n name,\n heading,\n children,\n rowIcon,\n options,\n toggleIcons,\n theme,\n index,\n onClick,\n openRowValues,\n rowInteractive = true,\n}: AccordionRowProps) => {\n const { selectable, sticky } = options || {};\n const rowKey = `accordion-item-${index}`;\n const isOpen = openRowValues.includes(rowKey);\n\n const {\n text,\n bg,\n hoverBg,\n selectableBg,\n selectableText,\n border,\n toggleIconColor,\n } = themeClasses[theme];\n\n const textClass = (selectable && isOpen && selectableText) || text;\n\n // Render custom heading or fallback to name\n const renderHeading = () => {\n if (heading) {\n if (typeof heading === \"function\") {\n return heading(index, isOpen);\n }\n return heading;\n }\n return <span>{name}</span>;\n };\n\n return (\n <AccordionItem\n value={rowKey}\n className={cn({\n [`${border}`]: border && !options?.hideBorders,\n [`${options?.selectedItemCSS}`]: options?.selectedItemCSS && isOpen,\n })}\n >\n <AccordionTrigger\n onClick={onClick}\n className={cn({\n \"flex w-full group/accordion-trigger py-4 ui-text-p1 font-semibold text-left items-center gap-3 transition-colors focus:outline-none\": true,\n \"px-4 mb-4 rounded-lg\": isNonTransparentTheme(theme),\n \"px-0 rounded-none\": !isNonTransparentTheme(theme),\n \"pointer-events-none focus-visible:outline-none\":\n isStaticTheme(theme),\n \"focus-base\": !isStaticTheme(theme),\n \"sticky top-0\": sticky,\n [`${bg} ${hoverBg} ${text}`]: !(selectable && isOpen),\n [`${selectableBg} ${selectableText}`]: selectable && isOpen,\n [options?.headerCSS ?? \"\"]: options?.headerCSS,\n [options?.selectedHeaderCSS ?? \"\"]:\n options?.selectedHeaderCSS && isOpen,\n })}\n >\n {rowIcon ? (\n <Icon\n name={typeof rowIcon === \"object\" ? rowIcon.name : rowIcon}\n color={textClass}\n additionalCSS={\n typeof rowIcon === \"object\" && rowIcon.css ? rowIcon.css : \"\"\n }\n size={options?.rowIconSize ?? \"32px\"}\n />\n ) : null}\n {renderHeading()}\n {!selectable && !isStaticTheme(theme) && rowInteractive ? (\n <span className=\"flex-1 justify-end flex items-center\">\n <Icon\n name={isOpen ? toggleIcons.open.name : toggleIcons.closed.name}\n color={toggleIconColor}\n additionalCSS={\n isOpen\n ? (typeof toggleIcons.open === \"object\" &&\n toggleIcons.open.css) ||\n \"\"\n : (typeof toggleIcons.closed === \"object\" &&\n toggleIcons.closed.css) ||\n \"\"\n }\n size={options?.iconSize ?? \"16px\"}\n />\n </span>\n ) : null}\n </AccordionTrigger>\n {rowInteractive && (\n <AccordionContent\n className={cn({\n \"ui-text-p2 overflow-hidden transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down\": true,\n [options?.contentCSS ?? \"\"]: options?.contentCSS,\n })}\n >\n <div className=\"pb-4\">{children}</div>\n </AccordionContent>\n )}\n </AccordionItem>\n );\n};\n\nconst Accordion = forwardRef<HTMLDivElement, AccordionProps>(\n (\n {\n data,\n theme = \"transparent\",\n icons = {\n closed: { name: \"icon-gui-plus-outline\" },\n open: { name: \"icon-gui-minus-outline\" },\n },\n options,\n ...props\n },\n ref,\n ) => {\n const openIndexes = useMemo(() => {\n const indexValues = data.map((_, i) => `accordion-item-${i}`);\n return options?.fullyOpen\n ? indexValues\n : indexValues.filter((_, index) =>\n options?.defaultOpenIndexes?.includes(index),\n );\n }, [data, options?.fullyOpen, options?.defaultOpenIndexes]);\n\n const [openRowValues, setOpenRowValues] = useState<string[]>(openIndexes);\n const [prevOpenIndexes, setPrevOpenIndexes] = useState(openIndexes);\n if (prevOpenIndexes !== openIndexes) {\n setPrevOpenIndexes(openIndexes);\n setOpenRowValues(openIndexes);\n }\n\n const innerAccordion = data.map((item, index) => (\n <AccordionRow\n key={item.name}\n name={item.name}\n heading={item.heading}\n rowIcon={item.icon}\n toggleIcons={icons}\n theme={theme}\n options={options}\n index={index}\n onClick={() => {\n item.onClick?.(index);\n }}\n openRowValues={openRowValues}\n rowInteractive={item.interactive}\n >\n {item.content}\n </AccordionRow>\n ));\n\n return (\n <div ref={ref} {...props}>\n {options?.autoClose ? (\n <RadixAccordion\n type=\"single\"\n collapsible\n value={openRowValues[0]}\n onValueChange={(values) => setOpenRowValues([values])}\n >\n {innerAccordion}\n </RadixAccordion>\n ) : (\n <RadixAccordion\n type=\"multiple\"\n value={openRowValues}\n onValueChange={(values) => setOpenRowValues(values)}\n >\n {innerAccordion}\n </RadixAccordion>\n )}\n </div>\n );\n },\n);\n\nAccordion.displayName = \"Accordion\";\n\nexport default Accordion;\n"],"names":["React","useMemo","useState","forwardRef","AccordionContent","AccordionItem","AccordionTrigger","Accordion","RadixAccordion","Icon","themeClasses","isNonTransparentTheme","isStaticTheme","cn","AccordionRow","name","heading","children","rowIcon","options","toggleIcons","theme","index","onClick","openRowValues","rowInteractive","selectable","sticky","rowKey","isOpen","includes","text","bg","hoverBg","selectableBg","selectableText","border","toggleIconColor","textClass","renderHeading","span","value","className","hideBorders","selectedItemCSS","headerCSS","selectedHeaderCSS","color","additionalCSS","css","size","rowIconSize","open","closed","iconSize","contentCSS","div","data","icons","props","ref","openIndexes","indexValues","map","_","i","fullyOpen","filter","defaultOpenIndexes","setOpenRowValues","prevOpenIndexes","setPrevOpenIndexes","innerAccordion","item","key","icon","interactive","content","autoClose","type","collapsible","onValueChange","values","displayName"],"mappings":"AAAA,OAAOA,OAAoBC,OAAO,CAAEC,QAAQ,CAAEC,UAAU,KAAQ,OAAQ,AACxE,QACEC,gBAAgB,CAChBC,aAAa,CACbC,gBAAgB,CAChBC,aAAaC,cAAc,KACtB,2BAA4B,AAEnC,QAAOC,SAAU,QAAS,AAS1B,QACEC,YAAY,CACZC,qBAAqB,CACrBC,aAAa,KACR,mBAAoB,AAC3B,QAAOC,OAAQ,YAAa,CAsC5B,MAAMC,aAAe,CAAC,CACpBC,IAAI,CACJC,OAAO,CACPC,QAAQ,CACRC,OAAO,CACPC,OAAO,CACPC,WAAW,CACXC,KAAK,CACLC,KAAK,CACLC,OAAO,CACPC,aAAa,CACbC,eAAiB,IAAI,CACH,IAClB,KAAM,CAAEC,UAAU,CAAEC,MAAM,CAAE,CAAGR,SAAW,CAAC,EAC3C,MAAMS,OAAS,CAAC,eAAe,EAAEN,MAAM,CAAC,CACxC,MAAMO,OAASL,cAAcM,QAAQ,CAACF,QAEtC,KAAM,CACJG,IAAI,CACJC,EAAE,CACFC,OAAO,CACPC,YAAY,CACZC,cAAc,CACdC,MAAM,CACNC,eAAe,CAChB,CAAG3B,YAAY,CAACW,MAAM,CAEvB,MAAMiB,UAAY,AAACZ,YAAcG,QAAUM,gBAAmBJ,KAG9D,MAAMQ,cAAgB,KACpB,GAAIvB,QAAS,CACX,GAAI,OAAOA,UAAY,WAAY,CACjC,OAAOA,QAAQM,MAAOO,OACxB,CACA,OAAOb,OACT,CACA,OAAO,oBAACwB,YAAMzB,KAChB,EAEA,OACE,oBAACV,eACCoC,MAAOb,OACPc,UAAW7B,GAAG,CACZ,CAAC,CAAC,EAAEuB,OAAO,CAAC,CAAC,CAAEA,QAAU,CAACjB,SAASwB,YACnC,CAAC,CAAC,EAAExB,SAASyB,gBAAgB,CAAC,CAAC,CAAEzB,SAASyB,iBAAmBf,MAC/D,IAEA,oBAACvB,kBACCiB,QAASA,QACTmB,UAAW7B,GAAG,CACZ,sIAAuI,KACvI,uBAAwBF,sBAAsBU,OAC9C,oBAAqB,CAACV,sBAAsBU,OAC5C,iDACET,cAAcS,OAChB,aAAc,CAACT,cAAcS,OAC7B,eAAgBM,OAChB,CAAC,CAAC,EAAEK,GAAG,CAAC,EAAEC,QAAQ,CAAC,EAAEF,KAAK,CAAC,CAAC,CAAE,CAAEL,CAAAA,YAAcG,MAAK,EACnD,CAAC,CAAC,EAAEK,aAAa,CAAC,EAAEC,eAAe,CAAC,CAAC,CAAET,YAAcG,OACrD,CAACV,SAAS0B,WAAa,GAAG,CAAE1B,SAAS0B,UACrC,CAAC1B,SAAS2B,mBAAqB,GAAG,CAChC3B,SAAS2B,mBAAqBjB,MAClC,IAECX,QACC,oBAACT,MACCM,KAAM,OAAOG,UAAY,SAAWA,QAAQH,IAAI,CAAGG,QACnD6B,MAAOT,UACPU,cACE,OAAO9B,UAAY,UAAYA,QAAQ+B,GAAG,CAAG/B,QAAQ+B,GAAG,CAAG,GAE7DC,KAAM/B,SAASgC,aAAe,SAE9B,KACHZ,gBACA,CAACb,YAAc,CAACd,cAAcS,QAAUI,eACvC,oBAACe,QAAKE,UAAU,wCACd,oBAACjC,MACCM,KAAMc,OAAST,YAAYgC,IAAI,CAACrC,IAAI,CAAGK,YAAYiC,MAAM,CAACtC,IAAI,CAC9DgC,MAAOV,gBACPW,cACEnB,OACI,AAAC,OAAOT,YAAYgC,IAAI,GAAK,UAC3BhC,YAAYgC,IAAI,CAACH,GAAG,EACtB,GACA,AAAC,OAAO7B,YAAYiC,MAAM,GAAK,UAC7BjC,YAAYiC,MAAM,CAACJ,GAAG,EACxB,GAENC,KAAM/B,SAASmC,UAAY,UAG7B,MAEL7B,gBACC,oBAACrB,kBACCsC,UAAW7B,GAAG,CACZ,8HAA+H,KAC/H,CAACM,SAASoC,YAAc,GAAG,CAAEpC,SAASoC,UACxC,IAEA,oBAACC,OAAId,UAAU,QAAQzB,WAKjC,EAEA,MAAMV,UAAYJ,WAChB,CACE,CACEsD,IAAI,CACJpC,MAAQ,aAAa,CACrBqC,MAAQ,CACNL,OAAQ,CAAEtC,KAAM,uBAAwB,EACxCqC,KAAM,CAAErC,KAAM,wBAAyB,CACzC,CAAC,CACDI,OAAO,CACP,GAAGwC,MACJ,CACDC,OAEA,MAAMC,YAAc5D,QAAQ,KAC1B,MAAM6D,YAAcL,KAAKM,GAAG,CAAC,CAACC,EAAGC,IAAM,CAAC,eAAe,EAAEA,EAAE,CAAC,EAC5D,OAAO9C,SAAS+C,UACZJ,YACAA,YAAYK,MAAM,CAAC,CAACH,EAAG1C,QACrBH,SAASiD,oBAAoBtC,SAASR,OAE9C,EAAG,CAACmC,KAAMtC,SAAS+C,UAAW/C,SAASiD,mBAAmB,EAE1D,KAAM,CAAC5C,cAAe6C,iBAAiB,CAAGnE,SAAmB2D,aAC7D,KAAM,CAACS,gBAAiBC,mBAAmB,CAAGrE,SAAS2D,aACvD,GAAIS,kBAAoBT,YAAa,CACnCU,mBAAmBV,aACnBQ,iBAAiBR,YACnB,CAEA,MAAMW,eAAiBf,KAAKM,GAAG,CAAC,CAACU,KAAMnD,QACrC,oBAACR,cACC4D,IAAKD,KAAK1D,IAAI,CACdA,KAAM0D,KAAK1D,IAAI,CACfC,QAASyD,KAAKzD,OAAO,CACrBE,QAASuD,KAAKE,IAAI,CAClBvD,YAAasC,MACbrC,MAAOA,MACPF,QAASA,QACTG,MAAOA,MACPC,QAAS,KACPkD,KAAKlD,OAAO,GAAGD,MACjB,EACAE,cAAeA,cACfC,eAAgBgD,KAAKG,WAAW,EAE/BH,KAAKI,OAAO,GAIjB,OACE,oBAACrB,OAAII,IAAKA,IAAM,GAAGD,KAAK,EACrBxC,SAAS2D,UACR,oBAACtE,gBACCuE,KAAK,SACLC,YAAAA,KACAvC,MAAOjB,aAAa,CAAC,EAAE,CACvByD,cAAe,AAACC,QAAWb,iBAAiB,CAACa,OAAO,GAEnDV,gBAGH,oBAAChE,gBACCuE,KAAK,WACLtC,MAAOjB,cACPyD,cAAe,AAACC,QAAWb,iBAAiBa,SAE3CV,gBAKX,EAGFjE,CAAAA,UAAU4E,WAAW,CAAG,WAExB,gBAAe5E,SAAU"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React from"react";import{describe,expect,it,vi,afterEach}from"vitest";import{render,screen,within,cleanup}from"@testing-library/react";import CodeSnippet from"../CodeSnippet";afterEach(cleanup);vi.mock("../Code",()=>({default:({snippet,language})=>React.createElement("div",{"data-testid":"code-block","data-language":language},snippet)}));vi.mock("../Icon",()=>({default:({name})=>React.createElement("span",{"data-testid":"icon","data-icon":name})}));vi.mock("../SegmentedControl",()=>({default:({children,onClick,active})=>React.createElement("button",{"data-testid":"sdk-toggle","data-active":active,onClick:onClick},children)}));vi.mock("./LanguageSelector",()=>({default:({languages,activeLanguage,onLanguageChange})=>React.createElement("div",{"data-testid":"language-selector","data-active":activeLanguage},languages.map(lang=>React.createElement("button",{key:lang,"data-testid":`lang-${lang}`,onClick:()=>onLanguageChange(lang)},lang)))}));vi.mock("./CopyButton",()=>({default:()=>React.createElement("button",{"data-testid":"copy-button"},"Copy")}));vi.mock("./ApiKeySelector",()=>({default:()=>React.createElement("div",{"data-testid":"api-key-selector"})}));vi.mock("./PlainCodeView",()=>({default:({content,language})=>React.createElement("div",{"data-testid":"plain-code-view","data-language":language},content)}));const makeSnippet=(lang,content)=>React.createElement("pre",null,React.createElement("code",{className:`language-${lang}`},content));describe("CodeSnippet",()=>{describe("basic rendering",()=>{it("renders a single language snippet",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},makeSnippet("javascript","console.log('hello')")));expect(screen.getByTestId("code-block").textContent).toBe("console.log('hello')")});it("renders a language label for a single non-fixed language",()=>{const{container}=render(React.createElement(CodeSnippet,{lang:"javascript"},makeSnippet("javascript","const x = 1;")));const labelSpans=container.querySelectorAll(".ui-text-label4");const labels=Array.from(labelSpans).map(el=>el.textContent);expect(labels).toContain("JavaScript")});it("renders the language selector when multiple languages are available",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},makeSnippet("javascript","const x = 1;"),makeSnippet("python","x = 1")));expect(screen.getByTestId("language-selector")).toBeTruthy()})});describe("realtime/rest SDK types",()=>{it("shows SDK selector for realtime/rest prefixed languages",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime"},makeSnippet("realtime_javascript","// realtime code"),makeSnippet("rest_javascript","// rest code")));const toggles=screen.getAllByTestId("sdk-toggle");expect(toggles).toHaveLength(2);expect(toggles[0].textContent).toBe("Realtime");expect(toggles[1].textContent).toBe("REST")});it("shows single SDK label when only one SDK type present",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime"},makeSnippet("realtime_javascript","// realtime only")));const toggles=screen.getAllByTestId("sdk-toggle");expect(toggles[0].textContent).toBe("Realtime")});it("resolves to the correct SDK when only one type is available",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime"},makeSnippet("rest_javascript","// rest only")));expect(screen.getByTestId("code-block").textContent).toBe("// rest only")})});describe("client/agent SDK types",()=>{it("does not show SDK selector for client/agent prefixed languages",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"client",fixed:true},makeSnippet("client_javascript","// client code"),makeSnippet("agent_python","// agent code")));expect(screen.queryAllByTestId("sdk-toggle")).toHaveLength(0)});it("renders client_ prefixed snippet with fixed mode and sdk=client",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"client",fixed:true},makeSnippet("client_javascript","// client JS"),makeSnippet("agent_python","// agent python")));expect(screen.getByTestId("code-block").textContent).toBe("// client JS")});it("renders agent_ prefixed snippet with fixed mode and sdk=agent",()=>{render(React.createElement(CodeSnippet,{lang:"python",sdk:"agent",fixed:true},makeSnippet("client_javascript","// client JS"),makeSnippet("agent_python","// agent python")));expect(screen.getByTestId("code-block").textContent).toBe("// agent python")});it("falls back to first language with matching prefix when exact match not found",()=>{render(React.createElement(CodeSnippet,{lang:"ruby",sdk:"client",fixed:true},makeSnippet("client_javascript","// client JS"),makeSnippet("client_typescript","// client TS")));expect(screen.getByTestId("code-block").textContent).toBe("// client JS")})});describe("fixed mode",()=>{it("shows a read-only language label in fixed mode",()=>{const{container}=render(React.createElement(CodeSnippet,{lang:"javascript",fixed:true},makeSnippet("javascript","const x = 1;")));const labelSpans=container.querySelectorAll(".ui-text-label4");const labels=Array.from(labelSpans).map(el=>el.textContent);expect(labels).toContain("JavaScript");expect(screen.queryByTestId("language-selector")).toBeNull()});it("shows correct label for client_ prefixed language in fixed mode",()=>{const{container}=render(React.createElement(CodeSnippet,{lang:"python",sdk:"client",fixed:true},makeSnippet("client_python","# client python")));const labelSpans=container.querySelectorAll(".ui-text-label4");const labels=Array.from(labelSpans).map(el=>el.textContent);expect(labels).toContain("Python")});it("hides language selector in fixed mode even with multiple languages",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",fixed:true},makeSnippet("javascript","const x = 1;"),makeSnippet("python","x = 1")));expect(screen.queryByTestId("language-selector")).toBeNull()})});describe("header row",()=>{it("renders a header row when headerRow is true",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",headerRow:true,title:"My Code"},makeSnippet("javascript","const x = 1;")));expect(screen.getByText("My Code")).toBeTruthy()})});describe("plain command mode",()=>{it("renders shell commands in plain mode",()=>{render(React.createElement(CodeSnippet,{lang:"shell"},makeSnippet("shell","npm install")));expect(screen.getByTestId("plain-code-view").textContent).toBe("npm install")});it("renders text commands in plain mode",()=>{render(React.createElement(CodeSnippet,{lang:"text"},makeSnippet("text","Hello world")));expect(screen.getByTestId("plain-code-view").textContent).toBe("Hello world")})});describe("onChange callback",()=>{it("passes stripped language and sdk type on language change",()=>{const onChange=vi.fn();render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime",onChange:onChange},makeSnippet("realtime_javascript","// js code"),makeSnippet("realtime_python","# python code")));const pyButton=screen.getByTestId("lang-realtime_python");pyButton.click();expect(onChange).toHaveBeenCalledWith("python","realtime")});it("passes undefined sdk when no SDK type",()=>{const onChange=vi.fn();render(React.createElement(CodeSnippet,{lang:"javascript",onChange:onChange},makeSnippet("javascript","// js"),makeSnippet("python","# py")));const pyButton=screen.getByTestId("lang-python");pyButton.click();expect(onChange).toHaveBeenCalledWith("python",undefined)})});describe("missing language snippet",()=>{it("shows a message when active language has no snippet",()=>{render(React.createElement(CodeSnippet,{lang:"ruby"},makeSnippet("javascript","const x = 1;"),makeSnippet("python","x = 1")));expect(screen.getByText(/currently viewing the Ruby docs/)).toBeTruthy()})});describe("findCodeElement handling",()=>{it("extracts code from pre with a single code child",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},React.createElement("pre",null,React.createElement("code",{className:"language-javascript"},"console.log('single')"))));expect(screen.getByTestId("code-block").textContent).toBe("console.log('single')")});it("extracts code from pre with multiple children (array)",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},React.createElement("pre",null,"\n",React.createElement("code",{className:"language-javascript"},"console.log('array')"),"\n")));expect(screen.getByTestId("code-block").textContent).toBe("console.log('array')")});it("handles multiple snippets where pre has array children",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},React.createElement("pre",null,"\n",React.createElement("code",{className:"language-javascript"},"// js code")),React.createElement("pre",null,"\n",React.createElement("code",{className:"language-python"},"# py code"))));expect(screen.getByTestId("language-selector")).toBeTruthy();expect(screen.getByTestId("code-block").textContent).toBe("// js code")})});describe("JSON-only snippets",()=>{it("always shows JSON content regardless of selected language",()=>{render(React.createElement(CodeSnippet,{lang:"ruby"},makeSnippet("json",'{"key": "value"}')));expect(screen.getByTestId("code-block").textContent).toBe('{"key": "value"}')})});describe("language ordering",()=>{it("respects custom language ordering",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",languageOrdering:["python","typescript","javascript"]},makeSnippet("javascript","// js"),makeSnippet("typescript","// ts"),makeSnippet("python","# py")));const selector=screen.getByTestId("language-selector");const buttons=within(selector).getAllByRole("button");expect(buttons[0].textContent).toBe("python");expect(buttons[1].textContent).toBe("typescript");expect(buttons[2].textContent).toBe("javascript")})})});
|
|
1
|
+
import React from"react";import{describe,expect,it,vi,afterEach}from"vitest";import{render,screen,within,cleanup,fireEvent,waitFor}from"@testing-library/react";import CodeSnippet from"../CodeSnippet";afterEach(cleanup);vi.mock("../Code",()=>({default:({snippet,language})=>React.createElement("div",{"data-testid":"code-block","data-language":language},snippet)}));vi.mock("../Icon",()=>({default:({name})=>React.createElement("span",{"data-testid":"icon","data-icon":name})}));vi.mock("../SegmentedControl",()=>({default:({children,onClick,active})=>React.createElement("button",{"data-testid":"sdk-toggle","data-active":active,onClick:onClick},children)}));vi.mock("./LanguageSelector",()=>({default:({languages,activeLanguage,onLanguageChange})=>React.createElement("div",{"data-testid":"language-selector","data-active":activeLanguage},languages.map(lang=>React.createElement("button",{key:lang,"data-testid":`lang-${lang}`,onClick:()=>onLanguageChange(lang)},lang)))}));vi.mock("./CopyButton",()=>({default:({onCopy})=>React.createElement("button",{"data-testid":"copy-button",onClick:onCopy},"Copy")}));vi.mock("./ApiKeySelector",()=>({default:()=>React.createElement("div",{"data-testid":"api-key-selector"})}));const plainCodeViewProps=vi.hoisted(()=>[]);vi.mock("./PlainCodeView",()=>({default:props=>{plainCodeViewProps.push(props);return React.createElement("div",{"data-testid":"plain-code-view","data-language":props.language},props.content)}}));const makeSnippet=(lang,content)=>React.createElement("pre",null,React.createElement("code",{className:`language-${lang}`},content));describe("CodeSnippet",()=>{describe("basic rendering",()=>{it("renders a single language snippet",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},makeSnippet("javascript","console.log('hello')")));expect(screen.getByTestId("code-block").textContent).toBe("console.log('hello')")});it("renders a language label for a single non-fixed language",()=>{const{container}=render(React.createElement(CodeSnippet,{lang:"javascript"},makeSnippet("javascript","const x = 1;")));const labelSpans=container.querySelectorAll(".ui-text-label4");const labels=Array.from(labelSpans).map(el=>el.textContent);expect(labels).toContain("JavaScript")});it("renders the language selector when multiple languages are available",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},makeSnippet("javascript","const x = 1;"),makeSnippet("python","x = 1")));expect(screen.getByTestId("language-selector")).toBeTruthy()})});describe("realtime/rest SDK types",()=>{it("shows SDK selector for realtime/rest prefixed languages",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime"},makeSnippet("realtime_javascript","// realtime code"),makeSnippet("rest_javascript","// rest code")));const toggles=screen.getAllByTestId("sdk-toggle");expect(toggles).toHaveLength(2);expect(toggles[0].textContent).toBe("Realtime");expect(toggles[1].textContent).toBe("REST")});it("shows single SDK label when only one SDK type present",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime"},makeSnippet("realtime_javascript","// realtime only")));const toggles=screen.getAllByTestId("sdk-toggle");expect(toggles[0].textContent).toBe("Realtime")});it("resolves to the correct SDK when only one type is available",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime"},makeSnippet("rest_javascript","// rest only")));expect(screen.getByTestId("code-block").textContent).toBe("// rest only")})});describe("client/agent SDK types",()=>{it("does not show SDK selector for client/agent prefixed languages",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"client",fixed:true},makeSnippet("client_javascript","// client code"),makeSnippet("agent_python","// agent code")));expect(screen.queryAllByTestId("sdk-toggle")).toHaveLength(0)});it("renders client_ prefixed snippet with fixed mode and sdk=client",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"client",fixed:true},makeSnippet("client_javascript","// client JS"),makeSnippet("agent_python","// agent python")));expect(screen.getByTestId("code-block").textContent).toBe("// client JS")});it("renders agent_ prefixed snippet with fixed mode and sdk=agent",()=>{render(React.createElement(CodeSnippet,{lang:"python",sdk:"agent",fixed:true},makeSnippet("client_javascript","// client JS"),makeSnippet("agent_python","// agent python")));expect(screen.getByTestId("code-block").textContent).toBe("// agent python")});it("falls back to first language with matching prefix when exact match not found",()=>{render(React.createElement(CodeSnippet,{lang:"ruby",sdk:"client",fixed:true},makeSnippet("client_javascript","// client JS"),makeSnippet("client_typescript","// client TS")));expect(screen.getByTestId("code-block").textContent).toBe("// client JS")})});describe("fixed mode",()=>{it("shows a read-only language label in fixed mode",()=>{const{container}=render(React.createElement(CodeSnippet,{lang:"javascript",fixed:true},makeSnippet("javascript","const x = 1;")));const labelSpans=container.querySelectorAll(".ui-text-label4");const labels=Array.from(labelSpans).map(el=>el.textContent);expect(labels).toContain("JavaScript");expect(screen.queryByTestId("language-selector")).toBeNull()});it("shows correct label for client_ prefixed language in fixed mode",()=>{const{container}=render(React.createElement(CodeSnippet,{lang:"python",sdk:"client",fixed:true},makeSnippet("client_python","# client python")));const labelSpans=container.querySelectorAll(".ui-text-label4");const labels=Array.from(labelSpans).map(el=>el.textContent);expect(labels).toContain("Python")});it("hides language selector in fixed mode even with multiple languages",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",fixed:true},makeSnippet("javascript","const x = 1;"),makeSnippet("python","x = 1")));expect(screen.queryByTestId("language-selector")).toBeNull()})});describe("header row",()=>{it("renders a header row when headerRow is true",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",headerRow:true,title:"My Code"},makeSnippet("javascript","const x = 1;")));expect(screen.getByText("My Code")).toBeTruthy()})});describe("plain command mode",()=>{it("renders shell commands in plain mode",()=>{render(React.createElement(CodeSnippet,{lang:"shell"},makeSnippet("shell","npm install")));expect(screen.getByTestId("plain-code-view").textContent).toBe("npm install")});it("renders text commands in plain mode",()=>{render(React.createElement(CodeSnippet,{lang:"text"},makeSnippet("text","Hello world")));expect(screen.getByTestId("plain-code-view").textContent).toBe("Hello world")})});describe("onChange callback",()=>{it("passes stripped language and sdk type on language change",()=>{const onChange=vi.fn();render(React.createElement(CodeSnippet,{lang:"javascript",sdk:"realtime",onChange:onChange},makeSnippet("realtime_javascript","// js code"),makeSnippet("realtime_python","# python code")));const pyButton=screen.getByTestId("lang-realtime_python");pyButton.click();expect(onChange).toHaveBeenCalledWith("python","realtime")});it("passes undefined sdk when no SDK type",()=>{const onChange=vi.fn();render(React.createElement(CodeSnippet,{lang:"javascript",onChange:onChange},makeSnippet("javascript","// js"),makeSnippet("python","# py")));const pyButton=screen.getByTestId("lang-python");pyButton.click();expect(onChange).toHaveBeenCalledWith("python",undefined)})});describe("missing language snippet",()=>{it("shows a message when active language has no snippet",()=>{render(React.createElement(CodeSnippet,{lang:"ruby"},makeSnippet("javascript","const x = 1;"),makeSnippet("python","x = 1")));expect(screen.getByText(/currently viewing the Ruby docs/)).toBeTruthy()})});describe("findCodeElement handling",()=>{it("extracts code from pre with a single code child",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},React.createElement("pre",null,React.createElement("code",{className:"language-javascript"},"console.log('single')"))));expect(screen.getByTestId("code-block").textContent).toBe("console.log('single')")});it("extracts code from pre with multiple children (array)",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},React.createElement("pre",null,"\n",React.createElement("code",{className:"language-javascript"},"console.log('array')"),"\n")));expect(screen.getByTestId("code-block").textContent).toBe("console.log('array')")});it("handles multiple snippets where pre has array children",()=>{render(React.createElement(CodeSnippet,{lang:"javascript"},React.createElement("pre",null,"\n",React.createElement("code",{className:"language-javascript"},"// js code")),React.createElement("pre",null,"\n",React.createElement("code",{className:"language-python"},"# py code"))));expect(screen.getByTestId("language-selector")).toBeTruthy();expect(screen.getByTestId("code-block").textContent).toBe("// js code")})});describe("JSON-only snippets",()=>{it("always shows JSON content regardless of selected language",()=>{render(React.createElement(CodeSnippet,{lang:"ruby"},makeSnippet("json",'{"key": "value"}')));expect(screen.getByTestId("code-block").textContent).toBe('{"key": "value"}')})});describe("copy callback",()=>{afterEach(()=>vi.unstubAllGlobals());const apiKeys=[{app:"Sports Live",keys:[{name:"root",key:"test.abc:secret"}]}];const hoverCodeBody=()=>{const body=screen.getByTestId("code-block").parentElement;if(!body)throw new Error("no code body to hover");fireEvent.mouseEnter(body)};it("reports the copied text with the API key substituted unmasked",async()=>{const writeText=vi.fn().mockResolvedValue(undefined);vi.stubGlobal("navigator",{clipboard:{writeText}});const onCopy=vi.fn();render(React.createElement(CodeSnippet,{lang:"javascript",apiKeys:apiKeys,onCopy:onCopy},makeSnippet("javascript","new Ably.Realtime('{{API_KEY}}')")));expect(screen.getByTestId("code-block").textContent).toBe("new Ably.Realtime('test.abc:*****')");hoverCodeBody();fireEvent.click(screen.getByTestId("copy-button"));await waitFor(()=>expect(onCopy).toHaveBeenCalledTimes(1));expect(onCopy).toHaveBeenCalledWith("new Ably.Realtime('test.abc:secret')");expect(writeText).toHaveBeenCalledWith("new Ably.Realtime('test.abc:secret')")});it("does not report a copy the clipboard rejected",async()=>{const writeText=vi.fn().mockRejectedValue(new Error("denied"));vi.stubGlobal("navigator",{clipboard:{writeText}});const warn=vi.spyOn(console,"warn").mockImplementation(()=>{});const onCopy=vi.fn();render(React.createElement(CodeSnippet,{lang:"javascript",apiKeys:apiKeys,onCopy:onCopy},makeSnippet("javascript","new Ably.Realtime('{{API_KEY}}')")));hoverCodeBody();fireEvent.click(screen.getByTestId("copy-button"));await waitFor(()=>expect(warn).toHaveBeenCalled());expect(onCopy).not.toHaveBeenCalled();warn.mockRestore()});it("does not fire until the copy control is used",()=>{vi.stubGlobal("navigator",{clipboard:{writeText:vi.fn().mockResolvedValue(undefined)}});const onCopy=vi.fn();render(React.createElement(CodeSnippet,{lang:"javascript",onCopy:onCopy},makeSnippet("javascript","const x = 1;")));hoverCodeBody();expect(onCopy).not.toHaveBeenCalled()});it("hands the callback to the plain-command view",()=>{plainCodeViewProps.length=0;const onCopy=vi.fn();render(React.createElement(CodeSnippet,{lang:"shell",onCopy:onCopy},makeSnippet("shell","npx @ably/cli init")));expect(screen.getByTestId("plain-code-view")).toBeTruthy();expect(plainCodeViewProps[plainCodeViewProps.length-1].onCopy).toBe(onCopy)})});describe("language ordering",()=>{it("respects custom language ordering",()=>{render(React.createElement(CodeSnippet,{lang:"javascript",languageOrdering:["python","typescript","javascript"]},makeSnippet("javascript","// js"),makeSnippet("typescript","// ts"),makeSnippet("python","# py")));const selector=screen.getByTestId("language-selector");const buttons=within(selector).getAllByRole("button");expect(buttons[0].textContent).toBe("python");expect(buttons[1].textContent).toBe("typescript");expect(buttons[2].textContent).toBe("javascript")})})});
|
|
2
2
|
//# sourceMappingURL=CodeSnippet.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/CodeSnippet/CodeSnippet.test.tsx"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport React from \"react\";\nimport { describe, expect, it, vi, afterEach } from \"vitest\";\nimport { render, screen, within, cleanup } from \"@testing-library/react\";\n\nimport CodeSnippet from \"../CodeSnippet\";\n\nafterEach(cleanup);\n\n// Mock child components to avoid complex rendering dependencies\nvi.mock(\"../Code\", () => ({\n default: ({ snippet, language }: { snippet: string; language: string }) => (\n <div data-testid=\"code-block\" data-language={language}>\n {snippet}\n </div>\n ),\n}));\n\nvi.mock(\"../Icon\", () => ({\n default: ({ name }: { name: string }) => (\n <span data-testid=\"icon\" data-icon={name} />\n ),\n}));\n\nvi.mock(\"../SegmentedControl\", () => ({\n default: ({\n children,\n onClick,\n active,\n }: {\n children: React.ReactNode;\n onClick: () => void;\n active: boolean;\n }) => (\n <button data-testid=\"sdk-toggle\" data-active={active} onClick={onClick}>\n {children}\n </button>\n ),\n}));\n\nvi.mock(\"./LanguageSelector\", () => ({\n default: ({\n languages,\n activeLanguage,\n onLanguageChange,\n }: {\n languages: string[];\n activeLanguage: string;\n onLanguageChange: (lang: string) => void;\n }) => (\n <div data-testid=\"language-selector\" data-active={activeLanguage}>\n {languages.map((lang) => (\n <button\n key={lang}\n data-testid={`lang-${lang}`}\n onClick={() => onLanguageChange(lang)}\n >\n {lang}\n </button>\n ))}\n </div>\n ),\n}));\n\nvi.mock(\"./CopyButton\", () => ({\n default: () => <button data-testid=\"copy-button\">Copy</button>,\n}));\n\nvi.mock(\"./ApiKeySelector\", () => ({\n default: () => <div data-testid=\"api-key-selector\" />,\n}));\n\nvi.mock(\"./PlainCodeView\", () => ({\n default: ({ content, language }: { content: string; language: string }) => (\n <div data-testid=\"plain-code-view\" data-language={language}>\n {content}\n </div>\n ),\n}));\n\nconst makeSnippet = (lang: string, content: string) => (\n <pre>\n <code className={`language-${lang}`}>{content}</code>\n </pre>\n);\n\ndescribe(\"CodeSnippet\", () => {\n describe(\"basic rendering\", () => {\n it(\"renders a single language snippet\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n {makeSnippet(\"javascript\", \"console.log('hello')\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"console.log('hello')\",\n );\n });\n\n it(\"renders a language label for a single non-fixed language\", () => {\n const { container } = render(\n <CodeSnippet lang=\"javascript\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n </CodeSnippet>,\n );\n const labelSpans = container.querySelectorAll(\".ui-text-label4\");\n const labels = Array.from(labelSpans).map((el) => el.textContent);\n expect(labels).toContain(\"JavaScript\");\n });\n\n it(\"renders the language selector when multiple languages are available\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n {makeSnippet(\"python\", \"x = 1\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"language-selector\")).toBeTruthy();\n });\n });\n\n describe(\"realtime/rest SDK types\", () => {\n it(\"shows SDK selector for realtime/rest prefixed languages\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\">\n {makeSnippet(\"realtime_javascript\", \"// realtime code\")}\n {makeSnippet(\"rest_javascript\", \"// rest code\")}\n </CodeSnippet>,\n );\n const toggles = screen.getAllByTestId(\"sdk-toggle\");\n expect(toggles).toHaveLength(2);\n expect(toggles[0].textContent).toBe(\"Realtime\");\n expect(toggles[1].textContent).toBe(\"REST\");\n });\n\n it(\"shows single SDK label when only one SDK type present\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\">\n {makeSnippet(\"realtime_javascript\", \"// realtime only\")}\n </CodeSnippet>,\n );\n const toggles = screen.getAllByTestId(\"sdk-toggle\");\n expect(toggles[0].textContent).toBe(\"Realtime\");\n });\n\n it(\"resolves to the correct SDK when only one type is available\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\">\n {makeSnippet(\"rest_javascript\", \"// rest only\")}\n </CodeSnippet>,\n );\n // Should fall back to rest since that's the only type available\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// rest only\");\n });\n });\n\n describe(\"client/agent SDK types\", () => {\n it(\"does not show SDK selector for client/agent prefixed languages\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"client\" fixed>\n {makeSnippet(\"client_javascript\", \"// client code\")}\n {makeSnippet(\"agent_python\", \"// agent code\")}\n </CodeSnippet>,\n );\n expect(screen.queryAllByTestId(\"sdk-toggle\")).toHaveLength(0);\n });\n\n it(\"renders client_ prefixed snippet with fixed mode and sdk=client\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"client\" fixed>\n {makeSnippet(\"client_javascript\", \"// client JS\")}\n {makeSnippet(\"agent_python\", \"// agent python\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// client JS\");\n });\n\n it(\"renders agent_ prefixed snippet with fixed mode and sdk=agent\", () => {\n render(\n <CodeSnippet lang=\"python\" sdk=\"agent\" fixed>\n {makeSnippet(\"client_javascript\", \"// client JS\")}\n {makeSnippet(\"agent_python\", \"// agent python\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"// agent python\",\n );\n });\n\n it(\"falls back to first language with matching prefix when exact match not found\", () => {\n render(\n <CodeSnippet lang=\"ruby\" sdk=\"client\" fixed>\n {makeSnippet(\"client_javascript\", \"// client JS\")}\n {makeSnippet(\"client_typescript\", \"// client TS\")}\n </CodeSnippet>,\n );\n // Ruby doesn't exist with client_ prefix, should fall back to first client_ language\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// client JS\");\n });\n });\n\n describe(\"fixed mode\", () => {\n it(\"shows a read-only language label in fixed mode\", () => {\n const { container } = render(\n <CodeSnippet lang=\"javascript\" fixed>\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n </CodeSnippet>,\n );\n const labelSpans = container.querySelectorAll(\".ui-text-label4\");\n const labels = Array.from(labelSpans).map((el) => el.textContent);\n expect(labels).toContain(\"JavaScript\");\n expect(screen.queryByTestId(\"language-selector\")).toBeNull();\n });\n\n it(\"shows correct label for client_ prefixed language in fixed mode\", () => {\n const { container } = render(\n <CodeSnippet lang=\"python\" sdk=\"client\" fixed>\n {makeSnippet(\"client_python\", \"# client python\")}\n </CodeSnippet>,\n );\n const labelSpans = container.querySelectorAll(\".ui-text-label4\");\n const labels = Array.from(labelSpans).map((el) => el.textContent);\n expect(labels).toContain(\"Python\");\n });\n\n it(\"hides language selector in fixed mode even with multiple languages\", () => {\n render(\n <CodeSnippet lang=\"javascript\" fixed>\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n {makeSnippet(\"python\", \"x = 1\")}\n </CodeSnippet>,\n );\n expect(screen.queryByTestId(\"language-selector\")).toBeNull();\n });\n });\n\n describe(\"header row\", () => {\n it(\"renders a header row when headerRow is true\", () => {\n render(\n <CodeSnippet lang=\"javascript\" headerRow title=\"My Code\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n </CodeSnippet>,\n );\n expect(screen.getByText(\"My Code\")).toBeTruthy();\n });\n });\n\n describe(\"plain command mode\", () => {\n it(\"renders shell commands in plain mode\", () => {\n render(\n <CodeSnippet lang=\"shell\">\n {makeSnippet(\"shell\", \"npm install\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"plain-code-view\").textContent).toBe(\n \"npm install\",\n );\n });\n\n it(\"renders text commands in plain mode\", () => {\n render(\n <CodeSnippet lang=\"text\">\n {makeSnippet(\"text\", \"Hello world\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"plain-code-view\").textContent).toBe(\n \"Hello world\",\n );\n });\n });\n\n describe(\"onChange callback\", () => {\n it(\"passes stripped language and sdk type on language change\", () => {\n const onChange = vi.fn();\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\" onChange={onChange}>\n {makeSnippet(\"realtime_javascript\", \"// js code\")}\n {makeSnippet(\"realtime_python\", \"# python code\")}\n </CodeSnippet>,\n );\n const pyButton = screen.getByTestId(\"lang-realtime_python\");\n pyButton.click();\n expect(onChange).toHaveBeenCalledWith(\"python\", \"realtime\");\n });\n\n it(\"passes undefined sdk when no SDK type\", () => {\n const onChange = vi.fn();\n render(\n <CodeSnippet lang=\"javascript\" onChange={onChange}>\n {makeSnippet(\"javascript\", \"// js\")}\n {makeSnippet(\"python\", \"# py\")}\n </CodeSnippet>,\n );\n const pyButton = screen.getByTestId(\"lang-python\");\n pyButton.click();\n expect(onChange).toHaveBeenCalledWith(\"python\", undefined);\n });\n });\n\n describe(\"missing language snippet\", () => {\n it(\"shows a message when active language has no snippet\", () => {\n render(\n <CodeSnippet lang=\"ruby\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n {makeSnippet(\"python\", \"x = 1\")}\n </CodeSnippet>,\n );\n expect(screen.getByText(/currently viewing the Ruby docs/)).toBeTruthy();\n });\n });\n\n describe(\"findCodeElement handling\", () => {\n it(\"extracts code from pre with a single code child\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n <pre>\n <code className=\"language-javascript\">\n console.log('single')\n </code>\n </pre>\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"console.log('single')\",\n );\n });\n\n it(\"extracts code from pre with multiple children (array)\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n <pre>\n {\"\\n\"}\n <code className=\"language-javascript\">\n console.log('array')\n </code>\n {\"\\n\"}\n </pre>\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"console.log('array')\",\n );\n });\n\n it(\"handles multiple snippets where pre has array children\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n <pre>\n {\"\\n\"}\n <code className=\"language-javascript\">{\"// js code\"}</code>\n </pre>\n <pre>\n {\"\\n\"}\n <code className=\"language-python\">{\"# py code\"}</code>\n </pre>\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"language-selector\")).toBeTruthy();\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// js code\");\n });\n });\n\n describe(\"JSON-only snippets\", () => {\n it(\"always shows JSON content regardless of selected language\", () => {\n render(\n <CodeSnippet lang=\"ruby\">\n {makeSnippet(\"json\", '{\"key\": \"value\"}')}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n '{\"key\": \"value\"}',\n );\n });\n });\n\n describe(\"language ordering\", () => {\n it(\"respects custom language ordering\", () => {\n render(\n <CodeSnippet\n lang=\"javascript\"\n languageOrdering={[\"python\", \"typescript\", \"javascript\"]}\n >\n {makeSnippet(\"javascript\", \"// js\")}\n {makeSnippet(\"typescript\", \"// ts\")}\n {makeSnippet(\"python\", \"# py\")}\n </CodeSnippet>,\n );\n const selector = screen.getByTestId(\"language-selector\");\n const buttons = within(selector).getAllByRole(\"button\");\n expect(buttons[0].textContent).toBe(\"python\");\n expect(buttons[1].textContent).toBe(\"typescript\");\n expect(buttons[2].textContent).toBe(\"javascript\");\n });\n });\n});\n"],"names":["React","describe","expect","it","vi","afterEach","render","screen","within","cleanup","CodeSnippet","mock","default","snippet","language","div","data-testid","data-language","name","span","data-icon","children","onClick","active","button","data-active","languages","activeLanguage","onLanguageChange","map","lang","key","content","makeSnippet","pre","code","className","getByTestId","textContent","toBe","container","labelSpans","querySelectorAll","labels","Array","from","el","toContain","toBeTruthy","sdk","toggles","getAllByTestId","toHaveLength","fixed","queryAllByTestId","queryByTestId","toBeNull","headerRow","title","getByText","onChange","fn","pyButton","click","toHaveBeenCalledWith","undefined","languageOrdering","selector","buttons","getAllByRole"],"mappings":"AAIA,OAAOA,UAAW,OAAQ,AAC1B,QAASC,QAAQ,CAAEC,MAAM,CAAEC,EAAE,CAAEC,EAAE,CAAEC,SAAS,KAAQ,QAAS,AAC7D,QAASC,MAAM,CAAEC,MAAM,CAAEC,MAAM,CAAEC,OAAO,KAAQ,wBAAyB,AAEzE,QAAOC,gBAAiB,gBAAiB,CAEzCL,UAAUI,SAGVL,GAAGO,IAAI,CAAC,UAAW,IAAO,CAAA,CACxBC,QAAS,CAAC,CAAEC,OAAO,CAAEC,QAAQ,CAAyC,GACpE,oBAACC,OAAIC,cAAY,aAAaC,gBAAeH,UAC1CD,QAGP,CAAA,GAEAT,GAAGO,IAAI,CAAC,UAAW,IAAO,CAAA,CACxBC,QAAS,CAAC,CAAEM,IAAI,CAAoB,GAClC,oBAACC,QAAKH,cAAY,OAAOI,YAAWF,MAExC,CAAA,GAEAd,GAAGO,IAAI,CAAC,sBAAuB,IAAO,CAAA,CACpCC,QAAS,CAAC,CACRS,QAAQ,CACRC,OAAO,CACPC,MAAM,CAKP,GACC,oBAACC,UAAOR,cAAY,aAAaS,cAAaF,OAAQD,QAASA,SAC5DD,SAGP,CAAA,GAEAjB,GAAGO,IAAI,CAAC,qBAAsB,IAAO,CAAA,CACnCC,QAAS,CAAC,CACRc,SAAS,CACTC,cAAc,CACdC,gBAAgB,CAKjB,GACC,oBAACb,OAAIC,cAAY,oBAAoBS,cAAaE,gBAC/CD,UAAUG,GAAG,CAAC,AAACC,MACd,oBAACN,UACCO,IAAKD,KACLd,cAAa,CAAC,KAAK,EAAEc,KAAK,CAAC,CAC3BR,QAAS,IAAMM,iBAAiBE,OAE/BA,OAKX,CAAA,GAEA1B,GAAGO,IAAI,CAAC,eAAgB,IAAO,CAAA,CAC7BC,QAAS,IAAM,oBAACY,UAAOR,cAAY,eAAc,OACnD,CAAA,GAEAZ,GAAGO,IAAI,CAAC,mBAAoB,IAAO,CAAA,CACjCC,QAAS,IAAM,oBAACG,OAAIC,cAAY,oBAClC,CAAA,GAEAZ,GAAGO,IAAI,CAAC,kBAAmB,IAAO,CAAA,CAChCC,QAAS,CAAC,CAAEoB,OAAO,CAAElB,QAAQ,CAAyC,GACpE,oBAACC,OAAIC,cAAY,kBAAkBC,gBAAeH,UAC/CkB,QAGP,CAAA,GAEA,MAAMC,YAAc,CAACH,KAAcE,UACjC,oBAACE,WACC,oBAACC,QAAKC,UAAW,CAAC,SAAS,EAAEN,KAAK,CAAC,EAAGE,UAI1C/B,SAAS,cAAe,KACtBA,SAAS,kBAAmB,KAC1BE,GAAG,oCAAqC,KACtCG,OACE,oBAACI,aAAYoB,KAAK,cACfG,YAAY,aAAc,0BAG/B/B,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CACvD,uBAEJ,GAEApC,GAAG,2DAA4D,KAC7D,KAAM,CAAEqC,SAAS,CAAE,CAAGlC,OACpB,oBAACI,aAAYoB,KAAK,cACfG,YAAY,aAAc,kBAG/B,MAAMQ,WAAaD,UAAUE,gBAAgB,CAAC,mBAC9C,MAAMC,OAASC,MAAMC,IAAI,CAACJ,YAAYZ,GAAG,CAAC,AAACiB,IAAOA,GAAGR,WAAW,EAChEpC,OAAOyC,QAAQI,SAAS,CAAC,aAC3B,GAEA5C,GAAG,sEAAuE,KACxEG,OACE,oBAACI,aAAYoB,KAAK,cACfG,YAAY,aAAc,gBAC1BA,YAAY,SAAU,WAG3B/B,OAAOK,OAAO8B,WAAW,CAAC,sBAAsBW,UAAU,EAC5D,EACF,GAEA/C,SAAS,0BAA2B,KAClCE,GAAG,0DAA2D,KAC5DG,OACE,oBAACI,aAAYoB,KAAK,aAAamB,IAAI,YAChChB,YAAY,sBAAuB,oBACnCA,YAAY,kBAAmB,kBAGpC,MAAMiB,QAAU3C,OAAO4C,cAAc,CAAC,cACtCjD,OAAOgD,SAASE,YAAY,CAAC,GAC7BlD,OAAOgD,OAAO,CAAC,EAAE,CAACZ,WAAW,EAAEC,IAAI,CAAC,YACpCrC,OAAOgD,OAAO,CAAC,EAAE,CAACZ,WAAW,EAAEC,IAAI,CAAC,OACtC,GAEApC,GAAG,wDAAyD,KAC1DG,OACE,oBAACI,aAAYoB,KAAK,aAAamB,IAAI,YAChChB,YAAY,sBAAuB,sBAGxC,MAAMiB,QAAU3C,OAAO4C,cAAc,CAAC,cACtCjD,OAAOgD,OAAO,CAAC,EAAE,CAACZ,WAAW,EAAEC,IAAI,CAAC,WACtC,GAEApC,GAAG,8DAA+D,KAChEG,OACE,oBAACI,aAAYoB,KAAK,aAAamB,IAAI,YAChChB,YAAY,kBAAmB,kBAIpC/B,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CAAC,eAC5D,EACF,GAEAtC,SAAS,yBAA0B,KACjCE,GAAG,iEAAkE,KACnEG,OACE,oBAACI,aAAYoB,KAAK,aAAamB,IAAI,SAASI,MAAAA,MACzCpB,YAAY,oBAAqB,kBACjCA,YAAY,eAAgB,mBAGjC/B,OAAOK,OAAO+C,gBAAgB,CAAC,eAAeF,YAAY,CAAC,EAC7D,GAEAjD,GAAG,kEAAmE,KACpEG,OACE,oBAACI,aAAYoB,KAAK,aAAamB,IAAI,SAASI,MAAAA,MACzCpB,YAAY,oBAAqB,gBACjCA,YAAY,eAAgB,qBAGjC/B,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CAAC,eAC5D,GAEApC,GAAG,gEAAiE,KAClEG,OACE,oBAACI,aAAYoB,KAAK,SAASmB,IAAI,QAAQI,MAAAA,MACpCpB,YAAY,oBAAqB,gBACjCA,YAAY,eAAgB,qBAGjC/B,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CACvD,kBAEJ,GAEApC,GAAG,+EAAgF,KACjFG,OACE,oBAACI,aAAYoB,KAAK,OAAOmB,IAAI,SAASI,MAAAA,MACnCpB,YAAY,oBAAqB,gBACjCA,YAAY,oBAAqB,kBAItC/B,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CAAC,eAC5D,EACF,GAEAtC,SAAS,aAAc,KACrBE,GAAG,iDAAkD,KACnD,KAAM,CAAEqC,SAAS,CAAE,CAAGlC,OACpB,oBAACI,aAAYoB,KAAK,aAAauB,MAAAA,MAC5BpB,YAAY,aAAc,kBAG/B,MAAMQ,WAAaD,UAAUE,gBAAgB,CAAC,mBAC9C,MAAMC,OAASC,MAAMC,IAAI,CAACJ,YAAYZ,GAAG,CAAC,AAACiB,IAAOA,GAAGR,WAAW,EAChEpC,OAAOyC,QAAQI,SAAS,CAAC,cACzB7C,OAAOK,OAAOgD,aAAa,CAAC,sBAAsBC,QAAQ,EAC5D,GAEArD,GAAG,kEAAmE,KACpE,KAAM,CAAEqC,SAAS,CAAE,CAAGlC,OACpB,oBAACI,aAAYoB,KAAK,SAASmB,IAAI,SAASI,MAAAA,MACrCpB,YAAY,gBAAiB,qBAGlC,MAAMQ,WAAaD,UAAUE,gBAAgB,CAAC,mBAC9C,MAAMC,OAASC,MAAMC,IAAI,CAACJ,YAAYZ,GAAG,CAAC,AAACiB,IAAOA,GAAGR,WAAW,EAChEpC,OAAOyC,QAAQI,SAAS,CAAC,SAC3B,GAEA5C,GAAG,qEAAsE,KACvEG,OACE,oBAACI,aAAYoB,KAAK,aAAauB,MAAAA,MAC5BpB,YAAY,aAAc,gBAC1BA,YAAY,SAAU,WAG3B/B,OAAOK,OAAOgD,aAAa,CAAC,sBAAsBC,QAAQ,EAC5D,EACF,GAEAvD,SAAS,aAAc,KACrBE,GAAG,8CAA+C,KAChDG,OACE,oBAACI,aAAYoB,KAAK,aAAa2B,UAAAA,KAAUC,MAAM,WAC5CzB,YAAY,aAAc,kBAG/B/B,OAAOK,OAAOoD,SAAS,CAAC,YAAYX,UAAU,EAChD,EACF,GAEA/C,SAAS,qBAAsB,KAC7BE,GAAG,uCAAwC,KACzCG,OACE,oBAACI,aAAYoB,KAAK,SACfG,YAAY,QAAS,iBAG1B/B,OAAOK,OAAO8B,WAAW,CAAC,mBAAmBC,WAAW,EAAEC,IAAI,CAC5D,cAEJ,GAEApC,GAAG,sCAAuC,KACxCG,OACE,oBAACI,aAAYoB,KAAK,QACfG,YAAY,OAAQ,iBAGzB/B,OAAOK,OAAO8B,WAAW,CAAC,mBAAmBC,WAAW,EAAEC,IAAI,CAC5D,cAEJ,EACF,GAEAtC,SAAS,oBAAqB,KAC5BE,GAAG,2DAA4D,KAC7D,MAAMyD,SAAWxD,GAAGyD,EAAE,GACtBvD,OACE,oBAACI,aAAYoB,KAAK,aAAamB,IAAI,WAAWW,SAAUA,UACrD3B,YAAY,sBAAuB,cACnCA,YAAY,kBAAmB,mBAGpC,MAAM6B,SAAWvD,OAAO8B,WAAW,CAAC,wBACpCyB,SAASC,KAAK,GACd7D,OAAO0D,UAAUI,oBAAoB,CAAC,SAAU,WAClD,GAEA7D,GAAG,wCAAyC,KAC1C,MAAMyD,SAAWxD,GAAGyD,EAAE,GACtBvD,OACE,oBAACI,aAAYoB,KAAK,aAAa8B,SAAUA,UACtC3B,YAAY,aAAc,SAC1BA,YAAY,SAAU,UAG3B,MAAM6B,SAAWvD,OAAO8B,WAAW,CAAC,eACpCyB,SAASC,KAAK,GACd7D,OAAO0D,UAAUI,oBAAoB,CAAC,SAAUC,UAClD,EACF,GAEAhE,SAAS,2BAA4B,KACnCE,GAAG,sDAAuD,KACxDG,OACE,oBAACI,aAAYoB,KAAK,QACfG,YAAY,aAAc,gBAC1BA,YAAY,SAAU,WAG3B/B,OAAOK,OAAOoD,SAAS,CAAC,oCAAoCX,UAAU,EACxE,EACF,GAEA/C,SAAS,2BAA4B,KACnCE,GAAG,kDAAmD,KACpDG,OACE,oBAACI,aAAYoB,KAAK,cAChB,oBAACI,WACC,oBAACC,QAAKC,UAAU,uBAAsB,4BAM5ClC,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CACvD,wBAEJ,GAEApC,GAAG,wDAAyD,KAC1DG,OACE,oBAACI,aAAYoB,KAAK,cAChB,oBAACI,WACE,KACD,oBAACC,QAAKC,UAAU,uBAAsB,wBAGrC,QAIPlC,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CACvD,uBAEJ,GAEApC,GAAG,yDAA0D,KAC3DG,OACE,oBAACI,aAAYoB,KAAK,cAChB,oBAACI,WACE,KACD,oBAACC,QAAKC,UAAU,uBAAuB,eAEzC,oBAACF,WACE,KACD,oBAACC,QAAKC,UAAU,mBAAmB,gBAIzClC,OAAOK,OAAO8B,WAAW,CAAC,sBAAsBW,UAAU,GAC1D9C,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CAAC,aAC5D,EACF,GAEAtC,SAAS,qBAAsB,KAC7BE,GAAG,4DAA6D,KAC9DG,OACE,oBAACI,aAAYoB,KAAK,QACfG,YAAY,OAAQ,sBAGzB/B,OAAOK,OAAO8B,WAAW,CAAC,cAAcC,WAAW,EAAEC,IAAI,CACvD,mBAEJ,EACF,GAEAtC,SAAS,oBAAqB,KAC5BE,GAAG,oCAAqC,KACtCG,OACE,oBAACI,aACCoB,KAAK,aACLoC,iBAAkB,CAAC,SAAU,aAAc,aAAa,EAEvDjC,YAAY,aAAc,SAC1BA,YAAY,aAAc,SAC1BA,YAAY,SAAU,UAG3B,MAAMkC,SAAW5D,OAAO8B,WAAW,CAAC,qBACpC,MAAM+B,QAAU5D,OAAO2D,UAAUE,YAAY,CAAC,UAC9CnE,OAAOkE,OAAO,CAAC,EAAE,CAAC9B,WAAW,EAAEC,IAAI,CAAC,UACpCrC,OAAOkE,OAAO,CAAC,EAAE,CAAC9B,WAAW,EAAEC,IAAI,CAAC,cACpCrC,OAAOkE,OAAO,CAAC,EAAE,CAAC9B,WAAW,EAAEC,IAAI,CAAC,aACtC,EACF,EACF"}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/CodeSnippet/CodeSnippet.test.tsx"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport React from \"react\";\nimport { describe, expect, it, vi, afterEach } from \"vitest\";\nimport {\n render,\n screen,\n within,\n cleanup,\n fireEvent,\n waitFor,\n} from \"@testing-library/react\";\n\nimport CodeSnippet from \"../CodeSnippet\";\n\nafterEach(cleanup);\n\n// Mock child components to avoid complex rendering dependencies\nvi.mock(\"../Code\", () => ({\n default: ({ snippet, language }: { snippet: string; language: string }) => (\n <div data-testid=\"code-block\" data-language={language}>\n {snippet}\n </div>\n ),\n}));\n\nvi.mock(\"../Icon\", () => ({\n default: ({ name }: { name: string }) => (\n <span data-testid=\"icon\" data-icon={name} />\n ),\n}));\n\nvi.mock(\"../SegmentedControl\", () => ({\n default: ({\n children,\n onClick,\n active,\n }: {\n children: React.ReactNode;\n onClick: () => void;\n active: boolean;\n }) => (\n <button data-testid=\"sdk-toggle\" data-active={active} onClick={onClick}>\n {children}\n </button>\n ),\n}));\n\nvi.mock(\"./LanguageSelector\", () => ({\n default: ({\n languages,\n activeLanguage,\n onLanguageChange,\n }: {\n languages: string[];\n activeLanguage: string;\n onLanguageChange: (lang: string) => void;\n }) => (\n <div data-testid=\"language-selector\" data-active={activeLanguage}>\n {languages.map((lang) => (\n <button\n key={lang}\n data-testid={`lang-${lang}`}\n onClick={() => onLanguageChange(lang)}\n >\n {lang}\n </button>\n ))}\n </div>\n ),\n}));\n\nvi.mock(\"./CopyButton\", () => ({\n default: ({ onCopy }: { onCopy: () => void }) => (\n <button data-testid=\"copy-button\" onClick={onCopy}>\n Copy\n </button>\n ),\n}));\n\nvi.mock(\"./ApiKeySelector\", () => ({\n default: () => <div data-testid=\"api-key-selector\" />,\n}));\n\n// Records the props PlainCodeView is handed, so the plain-command path can\n// assert what CodeSnippet passes down. The path's own behaviour is covered by\n// PlainCodeView.test.tsx against the real component.\nconst plainCodeViewProps = vi.hoisted(\n () => [] as { content: string; onCopy?: (text: string) => void }[],\n);\n\nvi.mock(\"./PlainCodeView\", () => ({\n default: (props: {\n content: string;\n language: string;\n onCopy?: (text: string) => void;\n }) => {\n plainCodeViewProps.push(props);\n return (\n <div data-testid=\"plain-code-view\" data-language={props.language}>\n {props.content}\n </div>\n );\n },\n}));\n\nconst makeSnippet = (lang: string, content: string) => (\n <pre>\n <code className={`language-${lang}`}>{content}</code>\n </pre>\n);\n\ndescribe(\"CodeSnippet\", () => {\n describe(\"basic rendering\", () => {\n it(\"renders a single language snippet\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n {makeSnippet(\"javascript\", \"console.log('hello')\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"console.log('hello')\",\n );\n });\n\n it(\"renders a language label for a single non-fixed language\", () => {\n const { container } = render(\n <CodeSnippet lang=\"javascript\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n </CodeSnippet>,\n );\n const labelSpans = container.querySelectorAll(\".ui-text-label4\");\n const labels = Array.from(labelSpans).map((el) => el.textContent);\n expect(labels).toContain(\"JavaScript\");\n });\n\n it(\"renders the language selector when multiple languages are available\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n {makeSnippet(\"python\", \"x = 1\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"language-selector\")).toBeTruthy();\n });\n });\n\n describe(\"realtime/rest SDK types\", () => {\n it(\"shows SDK selector for realtime/rest prefixed languages\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\">\n {makeSnippet(\"realtime_javascript\", \"// realtime code\")}\n {makeSnippet(\"rest_javascript\", \"// rest code\")}\n </CodeSnippet>,\n );\n const toggles = screen.getAllByTestId(\"sdk-toggle\");\n expect(toggles).toHaveLength(2);\n expect(toggles[0].textContent).toBe(\"Realtime\");\n expect(toggles[1].textContent).toBe(\"REST\");\n });\n\n it(\"shows single SDK label when only one SDK type present\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\">\n {makeSnippet(\"realtime_javascript\", \"// realtime only\")}\n </CodeSnippet>,\n );\n const toggles = screen.getAllByTestId(\"sdk-toggle\");\n expect(toggles[0].textContent).toBe(\"Realtime\");\n });\n\n it(\"resolves to the correct SDK when only one type is available\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\">\n {makeSnippet(\"rest_javascript\", \"// rest only\")}\n </CodeSnippet>,\n );\n // Should fall back to rest since that's the only type available\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// rest only\");\n });\n });\n\n describe(\"client/agent SDK types\", () => {\n it(\"does not show SDK selector for client/agent prefixed languages\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"client\" fixed>\n {makeSnippet(\"client_javascript\", \"// client code\")}\n {makeSnippet(\"agent_python\", \"// agent code\")}\n </CodeSnippet>,\n );\n expect(screen.queryAllByTestId(\"sdk-toggle\")).toHaveLength(0);\n });\n\n it(\"renders client_ prefixed snippet with fixed mode and sdk=client\", () => {\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"client\" fixed>\n {makeSnippet(\"client_javascript\", \"// client JS\")}\n {makeSnippet(\"agent_python\", \"// agent python\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// client JS\");\n });\n\n it(\"renders agent_ prefixed snippet with fixed mode and sdk=agent\", () => {\n render(\n <CodeSnippet lang=\"python\" sdk=\"agent\" fixed>\n {makeSnippet(\"client_javascript\", \"// client JS\")}\n {makeSnippet(\"agent_python\", \"// agent python\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"// agent python\",\n );\n });\n\n it(\"falls back to first language with matching prefix when exact match not found\", () => {\n render(\n <CodeSnippet lang=\"ruby\" sdk=\"client\" fixed>\n {makeSnippet(\"client_javascript\", \"// client JS\")}\n {makeSnippet(\"client_typescript\", \"// client TS\")}\n </CodeSnippet>,\n );\n // Ruby doesn't exist with client_ prefix, should fall back to first client_ language\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// client JS\");\n });\n });\n\n describe(\"fixed mode\", () => {\n it(\"shows a read-only language label in fixed mode\", () => {\n const { container } = render(\n <CodeSnippet lang=\"javascript\" fixed>\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n </CodeSnippet>,\n );\n const labelSpans = container.querySelectorAll(\".ui-text-label4\");\n const labels = Array.from(labelSpans).map((el) => el.textContent);\n expect(labels).toContain(\"JavaScript\");\n expect(screen.queryByTestId(\"language-selector\")).toBeNull();\n });\n\n it(\"shows correct label for client_ prefixed language in fixed mode\", () => {\n const { container } = render(\n <CodeSnippet lang=\"python\" sdk=\"client\" fixed>\n {makeSnippet(\"client_python\", \"# client python\")}\n </CodeSnippet>,\n );\n const labelSpans = container.querySelectorAll(\".ui-text-label4\");\n const labels = Array.from(labelSpans).map((el) => el.textContent);\n expect(labels).toContain(\"Python\");\n });\n\n it(\"hides language selector in fixed mode even with multiple languages\", () => {\n render(\n <CodeSnippet lang=\"javascript\" fixed>\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n {makeSnippet(\"python\", \"x = 1\")}\n </CodeSnippet>,\n );\n expect(screen.queryByTestId(\"language-selector\")).toBeNull();\n });\n });\n\n describe(\"header row\", () => {\n it(\"renders a header row when headerRow is true\", () => {\n render(\n <CodeSnippet lang=\"javascript\" headerRow title=\"My Code\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n </CodeSnippet>,\n );\n expect(screen.getByText(\"My Code\")).toBeTruthy();\n });\n });\n\n describe(\"plain command mode\", () => {\n it(\"renders shell commands in plain mode\", () => {\n render(\n <CodeSnippet lang=\"shell\">\n {makeSnippet(\"shell\", \"npm install\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"plain-code-view\").textContent).toBe(\n \"npm install\",\n );\n });\n\n it(\"renders text commands in plain mode\", () => {\n render(\n <CodeSnippet lang=\"text\">\n {makeSnippet(\"text\", \"Hello world\")}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"plain-code-view\").textContent).toBe(\n \"Hello world\",\n );\n });\n });\n\n describe(\"onChange callback\", () => {\n it(\"passes stripped language and sdk type on language change\", () => {\n const onChange = vi.fn();\n render(\n <CodeSnippet lang=\"javascript\" sdk=\"realtime\" onChange={onChange}>\n {makeSnippet(\"realtime_javascript\", \"// js code\")}\n {makeSnippet(\"realtime_python\", \"# python code\")}\n </CodeSnippet>,\n );\n const pyButton = screen.getByTestId(\"lang-realtime_python\");\n pyButton.click();\n expect(onChange).toHaveBeenCalledWith(\"python\", \"realtime\");\n });\n\n it(\"passes undefined sdk when no SDK type\", () => {\n const onChange = vi.fn();\n render(\n <CodeSnippet lang=\"javascript\" onChange={onChange}>\n {makeSnippet(\"javascript\", \"// js\")}\n {makeSnippet(\"python\", \"# py\")}\n </CodeSnippet>,\n );\n const pyButton = screen.getByTestId(\"lang-python\");\n pyButton.click();\n expect(onChange).toHaveBeenCalledWith(\"python\", undefined);\n });\n });\n\n describe(\"missing language snippet\", () => {\n it(\"shows a message when active language has no snippet\", () => {\n render(\n <CodeSnippet lang=\"ruby\">\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n {makeSnippet(\"python\", \"x = 1\")}\n </CodeSnippet>,\n );\n expect(screen.getByText(/currently viewing the Ruby docs/)).toBeTruthy();\n });\n });\n\n describe(\"findCodeElement handling\", () => {\n it(\"extracts code from pre with a single code child\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n <pre>\n <code className=\"language-javascript\">\n console.log('single')\n </code>\n </pre>\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"console.log('single')\",\n );\n });\n\n it(\"extracts code from pre with multiple children (array)\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n <pre>\n {\"\\n\"}\n <code className=\"language-javascript\">\n console.log('array')\n </code>\n {\"\\n\"}\n </pre>\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"console.log('array')\",\n );\n });\n\n it(\"handles multiple snippets where pre has array children\", () => {\n render(\n <CodeSnippet lang=\"javascript\">\n <pre>\n {\"\\n\"}\n <code className=\"language-javascript\">{\"// js code\"}</code>\n </pre>\n <pre>\n {\"\\n\"}\n <code className=\"language-python\">{\"# py code\"}</code>\n </pre>\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"language-selector\")).toBeTruthy();\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\"// js code\");\n });\n });\n\n describe(\"JSON-only snippets\", () => {\n it(\"always shows JSON content regardless of selected language\", () => {\n render(\n <CodeSnippet lang=\"ruby\">\n {makeSnippet(\"json\", '{\"key\": \"value\"}')}\n </CodeSnippet>,\n );\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n '{\"key\": \"value\"}',\n );\n });\n });\n\n describe(\"copy callback\", () => {\n // jsdom has no clipboard, so each case stubs navigator wholesale.\n afterEach(() => vi.unstubAllGlobals());\n\n const apiKeys = [\n { app: \"Sports Live\", keys: [{ name: \"root\", key: \"test.abc:secret\" }] },\n ];\n\n const hoverCodeBody = () => {\n const body = screen.getByTestId(\"code-block\").parentElement;\n if (!body) throw new Error(\"no code body to hover\");\n fireEvent.mouseEnter(body);\n };\n\n it(\"reports the copied text with the API key substituted unmasked\", async () => {\n const writeText = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"navigator\", { clipboard: { writeText } });\n const onCopy = vi.fn();\n\n render(\n <CodeSnippet lang=\"javascript\" apiKeys={apiKeys} onCopy={onCopy}>\n {makeSnippet(\"javascript\", \"new Ably.Realtime('{{API_KEY}}')\")}\n </CodeSnippet>,\n );\n\n // The rendered snippet keeps the key masked; only the copy carries it.\n expect(screen.getByTestId(\"code-block\").textContent).toBe(\n \"new Ably.Realtime('test.abc:*****')\",\n );\n\n hoverCodeBody();\n fireEvent.click(screen.getByTestId(\"copy-button\"));\n\n await waitFor(() => expect(onCopy).toHaveBeenCalledTimes(1));\n expect(onCopy).toHaveBeenCalledWith(\n \"new Ably.Realtime('test.abc:secret')\",\n );\n expect(writeText).toHaveBeenCalledWith(\n \"new Ably.Realtime('test.abc:secret')\",\n );\n });\n\n it(\"does not report a copy the clipboard rejected\", async () => {\n const writeText = vi.fn().mockRejectedValue(new Error(\"denied\"));\n vi.stubGlobal(\"navigator\", { clipboard: { writeText } });\n const warn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n const onCopy = vi.fn();\n\n render(\n <CodeSnippet lang=\"javascript\" apiKeys={apiKeys} onCopy={onCopy}>\n {makeSnippet(\"javascript\", \"new Ably.Realtime('{{API_KEY}}')\")}\n </CodeSnippet>,\n );\n\n hoverCodeBody();\n fireEvent.click(screen.getByTestId(\"copy-button\"));\n\n await waitFor(() => expect(warn).toHaveBeenCalled());\n expect(onCopy).not.toHaveBeenCalled();\n warn.mockRestore();\n });\n\n it(\"does not fire until the copy control is used\", () => {\n vi.stubGlobal(\"navigator\", {\n clipboard: { writeText: vi.fn().mockResolvedValue(undefined) },\n });\n const onCopy = vi.fn();\n\n render(\n <CodeSnippet lang=\"javascript\" onCopy={onCopy}>\n {makeSnippet(\"javascript\", \"const x = 1;\")}\n </CodeSnippet>,\n );\n hoverCodeBody();\n\n expect(onCopy).not.toHaveBeenCalled();\n });\n\n it(\"hands the callback to the plain-command view\", () => {\n plainCodeViewProps.length = 0;\n const onCopy = vi.fn();\n\n render(\n <CodeSnippet lang=\"shell\" onCopy={onCopy}>\n {makeSnippet(\"shell\", \"npx @ably/cli init\")}\n </CodeSnippet>,\n );\n\n expect(screen.getByTestId(\"plain-code-view\")).toBeTruthy();\n expect(plainCodeViewProps[plainCodeViewProps.length - 1].onCopy).toBe(\n onCopy,\n );\n });\n });\n\n describe(\"language ordering\", () => {\n it(\"respects custom language ordering\", () => {\n render(\n <CodeSnippet\n lang=\"javascript\"\n languageOrdering={[\"python\", \"typescript\", \"javascript\"]}\n >\n {makeSnippet(\"javascript\", \"// js\")}\n {makeSnippet(\"typescript\", \"// ts\")}\n {makeSnippet(\"python\", \"# py\")}\n </CodeSnippet>,\n );\n const selector = screen.getByTestId(\"language-selector\");\n const buttons = within(selector).getAllByRole(\"button\");\n expect(buttons[0].textContent).toBe(\"python\");\n expect(buttons[1].textContent).toBe(\"typescript\");\n expect(buttons[2].textContent).toBe(\"javascript\");\n });\n });\n});\n"],"names":["React","describe","expect","it","vi","afterEach","render","screen","within","cleanup","fireEvent","waitFor","CodeSnippet","mock","default","snippet","language","div","data-testid","data-language","name","span","data-icon","children","onClick","active","button","data-active","languages","activeLanguage","onLanguageChange","map","lang","key","onCopy","plainCodeViewProps","hoisted","props","push","content","makeSnippet","pre","code","className","getByTestId","textContent","toBe","container","labelSpans","querySelectorAll","labels","Array","from","el","toContain","toBeTruthy","sdk","toggles","getAllByTestId","toHaveLength","fixed","queryAllByTestId","queryByTestId","toBeNull","headerRow","title","getByText","onChange","fn","pyButton","click","toHaveBeenCalledWith","undefined","unstubAllGlobals","apiKeys","app","keys","hoverCodeBody","body","parentElement","Error","mouseEnter","writeText","mockResolvedValue","stubGlobal","clipboard","toHaveBeenCalledTimes","mockRejectedValue","warn","spyOn","console","mockImplementation","toHaveBeenCalled","not","mockRestore","length","languageOrdering","selector","buttons","getAllByRole"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React,{useRef,useState}from"react";import Icon from"../Icon";import Code from"../Code";import cn from"../utils/cn";import CopyButton from"./CopyButton";const PlainCodeView=({content,className,language,icon})=>{const codeRef=useRef(null);const[isHovering,setIsHovering]=useState(false);return React.createElement("div",{className:cn("rounded-lg overflow-hidden bg-neutral-000 dark:bg-neutral-1300 border border-neutral-300 dark:border-neutral-1000 relative flex items-center",language==="shell"?"min-h-[3.375rem]":"min-h-12",className),onMouseEnter:()=>setIsHovering(true),onMouseLeave:()=>setIsHovering(false),onFocus:()=>setIsHovering(true),onBlur:()=>setIsHovering(false),tabIndex:0,role:"button","aria-label":"Focusable code view area",ref:codeRef},icon&&React.createElement("div",{className:"absolute top-2 left-2 z-10"},React.createElement("div",{className:"w-9 h-9 rounded-lg flex items-center justify-center bg-neutral-200 dark:bg-neutral-1100"},React.createElement(Icon,{name:icon,size:"20px",color:"text-neutral-1300 dark:text-neutral-000"}))),React.createElement(Code,{language:language,snippet:content,additionalCSS:cn("w-full bg-neutral-000 text-neutral-1300 dark:bg-neutral-1300 dark:text-neutral-200 px-4 py-2",icon&&"pl-14"),showLines:false}),isHovering&&React.createElement(CopyButton,{onCopy:()=>
|
|
1
|
+
import React,{useRef,useState}from"react";import Icon from"../Icon";import Code from"../Code";import cn from"../utils/cn";import CopyButton from"./CopyButton";import copyToClipboard from"./copyToClipboard";const PlainCodeView=({content,className,language,icon,onCopy})=>{const codeRef=useRef(null);const[isHovering,setIsHovering]=useState(false);return React.createElement("div",{className:cn("rounded-lg overflow-hidden bg-neutral-000 dark:bg-neutral-1300 border border-neutral-300 dark:border-neutral-1000 relative flex items-center",language==="shell"?"min-h-[3.375rem]":"min-h-12",className),onMouseEnter:()=>setIsHovering(true),onMouseLeave:()=>setIsHovering(false),onFocus:()=>setIsHovering(true),onBlur:()=>setIsHovering(false),tabIndex:0,role:"button","aria-label":"Focusable code view area",ref:codeRef},icon&&React.createElement("div",{className:"absolute top-2 left-2 z-10"},React.createElement("div",{className:"w-9 h-9 rounded-lg flex items-center justify-center bg-neutral-200 dark:bg-neutral-1100"},React.createElement(Icon,{name:icon,size:"20px",color:"text-neutral-1300 dark:text-neutral-000"}))),React.createElement(Code,{language:language,snippet:content,additionalCSS:cn("w-full bg-neutral-000 text-neutral-1300 dark:bg-neutral-1300 dark:text-neutral-200 px-4 py-2",icon&&"pl-14"),showLines:false}),isHovering&&React.createElement(CopyButton,{onCopy:()=>{copyToClipboard(content,onCopy)}}))};export default PlainCodeView;
|
|
2
2
|
//# sourceMappingURL=PlainCodeView.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/CodeSnippet/PlainCodeView.tsx"],"sourcesContent":["import React, { useRef, useState } from \"react\";\nimport Icon from \"../Icon\";\nimport Code from \"../Code\";\nimport cn from \"../utils/cn\";\nimport CopyButton from \"./CopyButton\";\nimport { IconName } from \"../Icon/types\";\n\ntype PlainCodeViewProps = {\n content: string;\n language: string;\n icon: IconName | null;\n className?: string;\n};\n\nconst PlainCodeView: React.FC<PlainCodeViewProps> = ({\n content,\n className,\n language,\n icon,\n}) => {\n const codeRef = useRef<HTMLDivElement>(null);\n const [isHovering, setIsHovering] = useState(false);\n\n return (\n <div\n className={cn(\n \"rounded-lg overflow-hidden bg-neutral-000 dark:bg-neutral-1300 border border-neutral-300 dark:border-neutral-1000 relative flex items-center\",\n language === \"shell\" ? \"min-h-[3.375rem]\" : \"min-h-12\",\n className,\n )}\n onMouseEnter={() => setIsHovering(true)}\n onMouseLeave={() => setIsHovering(false)}\n onFocus={() => setIsHovering(true)}\n onBlur={() => setIsHovering(false)}\n tabIndex={0}\n role=\"button\"\n aria-label=\"Focusable code view area\"\n ref={codeRef}\n >\n {icon && (\n <div className=\"absolute top-2 left-2 z-10\">\n <div className=\"w-9 h-9 rounded-lg flex items-center justify-center bg-neutral-200 dark:bg-neutral-1100\">\n <Icon\n name={icon}\n size=\"20px\"\n color=\"text-neutral-1300 dark:text-neutral-000\"\n />\n </div>\n </div>\n )}\n\n <Code\n language={language}\n snippet={content}\n additionalCSS={cn(\n \"w-full bg-neutral-000 text-neutral-1300 dark:bg-neutral-1300 dark:text-neutral-200 px-4 py-2\",\n icon && \"pl-14\",\n )}\n showLines={false}\n />\n\n {isHovering && (\n <CopyButton
|
|
1
|
+
{"version":3,"sources":["../../../src/core/CodeSnippet/PlainCodeView.tsx"],"sourcesContent":["import React, { useRef, useState } from \"react\";\nimport Icon from \"../Icon\";\nimport Code from \"../Code\";\nimport cn from \"../utils/cn\";\nimport CopyButton from \"./CopyButton\";\nimport copyToClipboard from \"./copyToClipboard\";\nimport { IconName } from \"../Icon/types\";\n\ntype PlainCodeViewProps = {\n content: string;\n language: string;\n icon: IconName | null;\n className?: string;\n /**\n * Fired with the copied text once the copy control is used and the text has\n * actually reached the clipboard.\n */\n onCopy?: (text: string) => void;\n};\n\nconst PlainCodeView: React.FC<PlainCodeViewProps> = ({\n content,\n className,\n language,\n icon,\n onCopy,\n}) => {\n const codeRef = useRef<HTMLDivElement>(null);\n const [isHovering, setIsHovering] = useState(false);\n\n return (\n <div\n className={cn(\n \"rounded-lg overflow-hidden bg-neutral-000 dark:bg-neutral-1300 border border-neutral-300 dark:border-neutral-1000 relative flex items-center\",\n language === \"shell\" ? \"min-h-[3.375rem]\" : \"min-h-12\",\n className,\n )}\n onMouseEnter={() => setIsHovering(true)}\n onMouseLeave={() => setIsHovering(false)}\n onFocus={() => setIsHovering(true)}\n onBlur={() => setIsHovering(false)}\n tabIndex={0}\n role=\"button\"\n aria-label=\"Focusable code view area\"\n ref={codeRef}\n >\n {icon && (\n <div className=\"absolute top-2 left-2 z-10\">\n <div className=\"w-9 h-9 rounded-lg flex items-center justify-center bg-neutral-200 dark:bg-neutral-1100\">\n <Icon\n name={icon}\n size=\"20px\"\n color=\"text-neutral-1300 dark:text-neutral-000\"\n />\n </div>\n </div>\n )}\n\n <Code\n language={language}\n snippet={content}\n additionalCSS={cn(\n \"w-full bg-neutral-000 text-neutral-1300 dark:bg-neutral-1300 dark:text-neutral-200 px-4 py-2\",\n icon && \"pl-14\",\n )}\n showLines={false}\n />\n\n {isHovering && (\n <CopyButton\n onCopy={() => {\n copyToClipboard(content, onCopy);\n }}\n />\n )}\n </div>\n );\n};\n\nexport default PlainCodeView;\n"],"names":["React","useRef","useState","Icon","Code","cn","CopyButton","copyToClipboard","PlainCodeView","content","className","language","icon","onCopy","codeRef","isHovering","setIsHovering","div","onMouseEnter","onMouseLeave","onFocus","onBlur","tabIndex","role","aria-label","ref","name","size","color","snippet","additionalCSS","showLines"],"mappings":"AAAA,OAAOA,OAASC,MAAM,CAAEC,QAAQ,KAAQ,OAAQ,AAChD,QAAOC,SAAU,SAAU,AAC3B,QAAOC,SAAU,SAAU,AAC3B,QAAOC,OAAQ,aAAc,AAC7B,QAAOC,eAAgB,cAAe,AACtC,QAAOC,oBAAqB,mBAAoB,CAehD,MAAMC,cAA8C,CAAC,CACnDC,OAAO,CACPC,SAAS,CACTC,QAAQ,CACRC,IAAI,CACJC,MAAM,CACP,IACC,MAAMC,QAAUb,OAAuB,MACvC,KAAM,CAACc,WAAYC,cAAc,CAAGd,SAAS,OAE7C,OACE,oBAACe,OACCP,UAAWL,GACT,+IACAM,WAAa,QAAU,mBAAqB,WAC5CD,WAEFQ,aAAc,IAAMF,cAAc,MAClCG,aAAc,IAAMH,cAAc,OAClCI,QAAS,IAAMJ,cAAc,MAC7BK,OAAQ,IAAML,cAAc,OAC5BM,SAAU,EACVC,KAAK,SACLC,aAAW,2BACXC,IAAKX,SAEJF,MACC,oBAACK,OAAIP,UAAU,8BACb,oBAACO,OAAIP,UAAU,2FACb,oBAACP,MACCuB,KAAMd,KACNe,KAAK,OACLC,MAAM,8CAMd,oBAACxB,MACCO,SAAUA,SACVkB,QAASpB,QACTqB,cAAezB,GACb,+FACAO,MAAQ,SAEVmB,UAAW,QAGZhB,YACC,oBAACT,YACCO,OAAQ,KACNN,gBAAgBE,QAASI,OAC3B,IAKV,CAEA,gBAAeL,aAAc"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import React from"react";import{describe,expect,it,vi,afterEach}from"vitest";import{render,screen,cleanup,fireEvent,waitFor}from"@testing-library/react";import PlainCodeView from"./PlainCodeView";afterEach(()=>{cleanup();vi.unstubAllGlobals()});vi.mock("../Code",()=>({default:({snippet})=>React.createElement("div",{"data-testid":"code-block"},snippet)}));vi.mock("../Icon",()=>({default:({name})=>React.createElement("span",{"data-testid":"icon","data-icon":name})}));vi.mock("./CopyButton",()=>({default:({onCopy})=>React.createElement("button",{"data-testid":"copy-button",onClick:onCopy},"Copy")}));describe("PlainCodeView",()=>{const copy=()=>{fireEvent.mouseEnter(screen.getByRole("button",{name:/code view/i}));fireEvent.click(screen.getByTestId("copy-button"))};it("writes the content to the clipboard and reports it",async()=>{const writeText=vi.fn().mockResolvedValue(undefined);vi.stubGlobal("navigator",{clipboard:{writeText}});const onCopy=vi.fn();render(React.createElement(PlainCodeView,{content:"npx @ably/cli init",language:"shell",icon:null,onCopy:onCopy}));copy();expect(writeText).toHaveBeenCalledWith("npx @ably/cli init");await waitFor(()=>expect(onCopy).toHaveBeenCalledTimes(1));expect(onCopy).toHaveBeenCalledWith("npx @ably/cli init")});it("does not report a copy the clipboard rejected",async()=>{const writeText=vi.fn().mockRejectedValue(new Error("denied"));vi.stubGlobal("navigator",{clipboard:{writeText}});const warn=vi.spyOn(console,"warn").mockImplementation(()=>{});const onCopy=vi.fn();render(React.createElement(PlainCodeView,{content:"npx @ably/cli init",language:"shell",icon:null,onCopy:onCopy}));copy();await waitFor(()=>expect(warn).toHaveBeenCalled());expect(onCopy).not.toHaveBeenCalled();warn.mockRestore()});it("still copies when no callback is given",()=>{const writeText=vi.fn().mockResolvedValue(undefined);vi.stubGlobal("navigator",{clipboard:{writeText}});render(React.createElement(PlainCodeView,{content:"ably channels list",language:"shell",icon:null}));copy();expect(writeText).toHaveBeenCalledWith("ably channels list")})});
|
|
2
|
+
//# sourceMappingURL=PlainCodeView.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/CodeSnippet/PlainCodeView.test.tsx"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport React from \"react\";\nimport { describe, expect, it, vi, afterEach } from \"vitest\";\nimport {\n render,\n screen,\n cleanup,\n fireEvent,\n waitFor,\n} from \"@testing-library/react\";\n\nimport PlainCodeView from \"./PlainCodeView\";\n\nafterEach(() => {\n cleanup();\n vi.unstubAllGlobals();\n});\n\nvi.mock(\"../Code\", () => ({\n default: ({ snippet }: { snippet: string }) => (\n <div data-testid=\"code-block\">{snippet}</div>\n ),\n}));\n\nvi.mock(\"../Icon\", () => ({\n default: ({ name }: { name: string }) => (\n <span data-testid=\"icon\" data-icon={name} />\n ),\n}));\n\nvi.mock(\"./CopyButton\", () => ({\n default: ({ onCopy }: { onCopy: () => void }) => (\n <button data-testid=\"copy-button\" onClick={onCopy}>\n Copy\n </button>\n ),\n}));\n\ndescribe(\"PlainCodeView\", () => {\n // The copy control only mounts on hover, so every case has to hover first.\n const copy = () => {\n fireEvent.mouseEnter(screen.getByRole(\"button\", { name: /code view/i }));\n fireEvent.click(screen.getByTestId(\"copy-button\"));\n };\n\n it(\"writes the content to the clipboard and reports it\", async () => {\n const writeText = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"navigator\", { clipboard: { writeText } });\n const onCopy = vi.fn();\n\n render(\n <PlainCodeView\n content=\"npx @ably/cli init\"\n language=\"shell\"\n icon={null}\n onCopy={onCopy}\n />,\n );\n copy();\n\n expect(writeText).toHaveBeenCalledWith(\"npx @ably/cli init\");\n await waitFor(() => expect(onCopy).toHaveBeenCalledTimes(1));\n expect(onCopy).toHaveBeenCalledWith(\"npx @ably/cli init\");\n });\n\n it(\"does not report a copy the clipboard rejected\", async () => {\n const writeText = vi.fn().mockRejectedValue(new Error(\"denied\"));\n vi.stubGlobal(\"navigator\", { clipboard: { writeText } });\n const warn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n const onCopy = vi.fn();\n\n render(\n <PlainCodeView\n content=\"npx @ably/cli init\"\n language=\"shell\"\n icon={null}\n onCopy={onCopy}\n />,\n );\n copy();\n\n await waitFor(() => expect(warn).toHaveBeenCalled());\n expect(onCopy).not.toHaveBeenCalled();\n warn.mockRestore();\n });\n\n it(\"still copies when no callback is given\", () => {\n const writeText = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"navigator\", { clipboard: { writeText } });\n\n render(\n <PlainCodeView\n content=\"ably channels list\"\n language=\"shell\"\n icon={null}\n />,\n );\n\n copy();\n\n expect(writeText).toHaveBeenCalledWith(\"ably channels list\");\n });\n});\n"],"names":["React","describe","expect","it","vi","afterEach","render","screen","cleanup","fireEvent","waitFor","PlainCodeView","unstubAllGlobals","mock","default","snippet","div","data-testid","name","span","data-icon","onCopy","button","onClick","copy","mouseEnter","getByRole","click","getByTestId","writeText","fn","mockResolvedValue","undefined","stubGlobal","clipboard","content","language","icon","toHaveBeenCalledWith","toHaveBeenCalledTimes","mockRejectedValue","Error","warn","spyOn","console","mockImplementation","toHaveBeenCalled","not","mockRestore"],"mappings":"AAIA,OAAOA,UAAW,OAAQ,AAC1B,QAASC,QAAQ,CAAEC,MAAM,CAAEC,EAAE,CAAEC,EAAE,CAAEC,SAAS,KAAQ,QAAS,AAC7D,QACEC,MAAM,CACNC,MAAM,CACNC,OAAO,CACPC,SAAS,CACTC,OAAO,KACF,wBAAyB,AAEhC,QAAOC,kBAAmB,iBAAkB,CAE5CN,UAAU,KACRG,UACAJ,GAAGQ,gBAAgB,EACrB,GAEAR,GAAGS,IAAI,CAAC,UAAW,IAAO,CAAA,CACxBC,QAAS,CAAC,CAAEC,OAAO,CAAuB,GACxC,oBAACC,OAAIC,cAAY,cAAcF,QAEnC,CAAA,GAEAX,GAAGS,IAAI,CAAC,UAAW,IAAO,CAAA,CACxBC,QAAS,CAAC,CAAEI,IAAI,CAAoB,GAClC,oBAACC,QAAKF,cAAY,OAAOG,YAAWF,MAExC,CAAA,GAEAd,GAAGS,IAAI,CAAC,eAAgB,IAAO,CAAA,CAC7BC,QAAS,CAAC,CAAEO,MAAM,CAA0B,GAC1C,oBAACC,UAAOL,cAAY,cAAcM,QAASF,QAAQ,OAIvD,CAAA,GAEApB,SAAS,gBAAiB,KAExB,MAAMuB,KAAO,KACXf,UAAUgB,UAAU,CAAClB,OAAOmB,SAAS,CAAC,SAAU,CAAER,KAAM,YAAa,IACrET,UAAUkB,KAAK,CAACpB,OAAOqB,WAAW,CAAC,eACrC,EAEAzB,GAAG,qDAAsD,UACvD,MAAM0B,UAAYzB,GAAG0B,EAAE,GAAGC,iBAAiB,CAACC,WAC5C5B,GAAG6B,UAAU,CAAC,YAAa,CAAEC,UAAW,CAAEL,SAAU,CAAE,GACtD,MAAMR,OAASjB,GAAG0B,EAAE,GAEpBxB,OACE,oBAACK,eACCwB,QAAQ,qBACRC,SAAS,QACTC,KAAM,KACNhB,OAAQA,UAGZG,OAEAtB,OAAO2B,WAAWS,oBAAoB,CAAC,qBACvC,OAAM5B,QAAQ,IAAMR,OAAOmB,QAAQkB,qBAAqB,CAAC,IACzDrC,OAAOmB,QAAQiB,oBAAoB,CAAC,qBACtC,GAEAnC,GAAG,gDAAiD,UAClD,MAAM0B,UAAYzB,GAAG0B,EAAE,GAAGU,iBAAiB,CAAC,IAAIC,MAAM,WACtDrC,GAAG6B,UAAU,CAAC,YAAa,CAAEC,UAAW,CAAEL,SAAU,CAAE,GACtD,MAAMa,KAAOtC,GAAGuC,KAAK,CAACC,QAAS,QAAQC,kBAAkB,CAAC,KAAO,GACjE,MAAMxB,OAASjB,GAAG0B,EAAE,GAEpBxB,OACE,oBAACK,eACCwB,QAAQ,qBACRC,SAAS,QACTC,KAAM,KACNhB,OAAQA,UAGZG,MAEA,OAAMd,QAAQ,IAAMR,OAAOwC,MAAMI,gBAAgB,IACjD5C,OAAOmB,QAAQ0B,GAAG,CAACD,gBAAgB,GACnCJ,KAAKM,WAAW,EAClB,GAEA7C,GAAG,yCAA0C,KAC3C,MAAM0B,UAAYzB,GAAG0B,EAAE,GAAGC,iBAAiB,CAACC,WAC5C5B,GAAG6B,UAAU,CAAC,YAAa,CAAEC,UAAW,CAAEL,SAAU,CAAE,GAEtDvB,OACE,oBAACK,eACCwB,QAAQ,qBACRC,SAAS,QACTC,KAAM,QAIVb,OAEAtB,OAAO2B,WAAWS,oBAAoB,CAAC,qBACzC,EACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/CodeSnippet/copyToClipboard.ts"],"sourcesContent":["/**\n * Writes `text` to the clipboard and only then reports it to `onCopy`. A failed\n * write (denied permission, or a non-secure context where `navigator.clipboard`\n * is absent) leaves nothing on the clipboard, so callers — which use `onCopy` to\n * record that the user took the snippet — must not be told a copy happened.\n */\nconst copyToClipboard = async (\n text: string,\n onCopy?: (text: string) => void,\n): Promise<void> => {\n try {\n await navigator.clipboard.writeText(text);\n } catch (error) {\n console.warn(\"Could not write the snippet to the clipboard:\", error);\n return;\n }\n\n onCopy?.(text);\n};\n\nexport default copyToClipboard;\n"],"names":["copyToClipboard","text","onCopy","navigator","clipboard","writeText","error","console","warn"],"mappings":"AAMA,MAAMA,gBAAkB,MACtBC,KACAC,UAEA,GAAI,CACF,MAAMC,UAAUC,SAAS,CAACC,SAAS,CAACJ,KACtC,CAAE,MAAOK,MAAO,CACdC,QAAQC,IAAI,CAAC,gDAAiDF,OAC9D,MACF,CAEAJ,SAASD,KACX,CAEA,gBAAeD,eAAgB"}
|
package/core/CodeSnippet.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import React,{useState,useEffect,Children,isValidElement,useRef,useCallback,useMemo}from"react";import Code from"./Code";import cn from"./utils/cn";import{parseLineHighlights}from"./utils/syntax-highlighter";import Icon from"./Icon";import{getLanguageInfo,stripSdkType,SDK_PREFIXES}from"./CodeSnippet/languages";import LanguageSelector from"./CodeSnippet/LanguageSelector";import ApiKeySelector from"./CodeSnippet/ApiKeySelector";import PlainCodeView from"./CodeSnippet/PlainCodeView";import CopyButton from"./CodeSnippet/CopyButton";import SegmentedControl from"./SegmentedControl";const substituteApiKey=(content,apiKey,mask=true)=>{return content.replace(/\{\{API_KEY\}\}/g,mask?`${apiKey.split(":")[0]}:*****`:apiKey)};const CodeSnippet=({fixed=false,headerRow=false,title="Code",children,className,lang,onChange,apiKeys,sdk,showCodeLines=true,languageOrdering,wrapCode=false})=>{const codeRef=useRef(null);const[selectedApiKey,setSelectedApiKey]=useState(()=>apiKeys?.[0]?.keys?.[0]?.key??"");const[prevApiKeys,setPrevApiKeys]=useState(apiKeys);if(prevApiKeys!==apiKeys){setPrevApiKeys(apiKeys);if(!selectedApiKey&&apiKeys&&apiKeys.length>0){setSelectedApiKey(apiKeys[0].keys?.[0]?.key??"")}}useEffect(()=>{const element=codeRef.current;if(!element)return;const unmaskRenderedApiKey=(content,apiKey)=>{return content.replace(/(['"]?)([^:'"]+):\*{5}\1/g,`$1${apiKey}$1`)};const handleCopy=event=>{const selection=window.getSelection();if(!selection||selection.rangeCount===0)return;const selectedText=selection.toString();if(!selectedText)return;const range=selection.getRangeAt(0);if(!element.contains(range.commonAncestorContainer))return;const modifiedText=unmaskRenderedApiKey(selectedText,selectedApiKey);event.clipboardData?.setData("text/plain",modifiedText);event.preventDefault()};document.addEventListener("copy",handleCopy);return()=>{document.removeEventListener("copy",handleCopy)}},[selectedApiKey]);const extractLanguageFromCode=useCallback(codeElement=>{if(!codeElement||!codeElement.props.className)return null;const classNames=codeElement.props.className.split(" ");const langClass=classNames.find(cls=>cls.startsWith("language-"));if(!langClass)return null;return langClass.substring(9)},[]);const findCodeElement=useCallback(preChildren=>{if(isValidElement(preChildren)){return preChildren}if(Array.isArray(preChildren)){const codeEl=preChildren.find(c=>isValidElement(c));return codeEl&&isValidElement(codeEl)?codeEl:null}return null},[]);const{codeData,languages,sdkTypes,isSinglePlainCommand}=useMemo(()=>{const childrenArray=Children.toArray(children);const languages=[];const sdkTypes=new Set;const codeData=[];const isSinglePlainCommand=childrenArray.length===1&&["language-shell","language-text"].some(lang=>{if(!isValidElement(childrenArray[0]))return false;const codeEl=findCodeElement(childrenArray[0].props.children);return codeEl?.props.className?.includes(lang)});childrenArray.forEach(child=>{if(!isValidElement(child))return;const preElement=child;const codeElement=findCodeElement(preElement.props.children);if(!codeElement)return;const rawLanguage=extractLanguageFromCode(codeElement);if(!rawLanguage)return;const meta=codeElement.props?.["data-meta"];const{lang:codeLanguage,highlights:lineHighlights}=parseLineHighlights(rawLanguage,meta);for(const prefix of SDK_PREFIXES){if(codeLanguage.startsWith(`${prefix}_`)){sdkTypes.add(prefix);break}}if(!languages.includes(codeLanguage)){languages.push(codeLanguage)}const codeContent=codeElement.props.children;codeData.push({language:codeLanguage,content:codeContent,lineHighlights})});return{codeData,languages,sdkTypes,isSinglePlainCommand}},[children,extractLanguageFromCode,findCodeElement]);const resolvedSdk=useMemo(()=>{if(sdkTypes.size===1&&sdk&&!sdkTypes.has(sdk)){return Array.from(sdkTypes)[0]}return sdk},[sdk,sdkTypes]);const showSDKSelector=sdkTypes.has("realtime")||sdkTypes.has("rest");const filteredLanguages=useMemo(()=>{const filtered=!resolvedSdk||!showSDKSelector?[...languages]:languages.filter(lang=>lang.startsWith(`${resolvedSdk}_`));if(languageOrdering&&languageOrdering.length>0){filtered.sort((a,b)=>{const aBase=stripSdkType(a);const bBase=stripSdkType(b);const aIndex=languageOrdering.indexOf(aBase);const bIndex=languageOrdering.indexOf(bBase);if(aIndex!==-1&&bIndex!==-1)return aIndex-bIndex;if(aIndex!==-1)return-1;if(bIndex!==-1)return 1;return 0})}return filtered},[resolvedSdk,showSDKSelector,languages,languageOrdering]);const activeLanguage=useMemo(()=>{if(resolvedSdk==="client"||resolvedSdk==="agent"){const fullLang=`${resolvedSdk}_${lang}`;if(languages.includes(fullLang)){return fullLang}const prefixMatch=languages.find(l=>l.startsWith(`${resolvedSdk}_`));if(prefixMatch)return prefixMatch}if(resolvedSdk&&sdkTypes.has(resolvedSdk)){return`${resolvedSdk}_${lang}`}if(lang)return lang;if(filteredLanguages.length>0)return filteredLanguages[0];return languages[0]},[resolvedSdk,sdkTypes,lang,filteredLanguages,languages]);const requiresApiKeySubstitution=useMemo(()=>{const containsPlaceholder=codeData.some(code=>code?.content.includes("{{API_KEY}}"));return containsPlaceholder&&!!apiKeys&&apiKeys.length>0&&!!selectedApiKey},[codeData,apiKeys,selectedApiKey]);const[isHovering,setIsHovering]=useState(false);const hasOnlyJsonSnippet=useMemo(()=>languages.length===1&&languages[0]==="json",[languages]);const processedChildren=useMemo(()=>{if(!activeLanguage)return[];const targetLanguage=hasOnlyJsonSnippet?"json":activeLanguage;return codeData.filter(code=>{return code?.language===targetLanguage}).map(code=>{if(!code)return null;const cleanLang=hasOnlyJsonSnippet?"json":code.language;const langInfo=getLanguageInfo(cleanLang??"");if(typeof code.content==="string"||typeof code.content==="number"||typeof code.content==="boolean"){let processedContent=String(code.content);if(requiresApiKeySubstitution){processedContent=substituteApiKey(processedContent,selectedApiKey)}if(!langInfo.syntaxHighlighterKey||!cleanLang)return null;return React.createElement(Code,{key:code.language,language:langInfo.syntaxHighlighterKey||cleanLang,snippet:processedContent,additionalCSS:"!bg-neutral-000 text-neutral-1300 dark:!bg-neutral-1300 dark:text-neutral-200 px-6 py-4",showLines:showCodeLines,wrap:wrapCode,lineHighlights:Object.keys(code.lineHighlights).length>0?code.lineHighlights:undefined})}return null})},[activeLanguage,hasOnlyJsonSnippet,codeData,requiresApiKeySubstitution,showCodeLines,wrapCode,selectedApiKey]);const hasSnippetForActiveLanguage=useMemo(()=>{if(!activeLanguage)return false;if(hasOnlyJsonSnippet)return true;return codeData.some(code=>{return code?.language===activeLanguage})},[activeLanguage,hasOnlyJsonSnippet,codeData]);const handleSDKTypeChange=useCallback(type=>{const nextLang=stripSdkType(languages.find(l=>l===`${type}_${stripSdkType(activeLanguage)}`)??languages.find(l=>l.startsWith(`${type}_`))??activeLanguage);if(onChange&&nextLang){onChange(stripSdkType(activeLanguage),type)}},[activeLanguage,languages,onChange]);const handleLanguageChange=useCallback(language=>{if(onChange){onChange(stripSdkType(language),resolvedSdk)}},[onChange,resolvedSdk]);const noSnippetMessage=useMemo(()=>{if(!activeLanguage)return null;const activeLanguageInfo=getLanguageInfo(activeLanguage);return React.createElement("div",{className:"px-16 py-6 ui-text-body2 text-neutral-800 dark:text-neutral-400 text-center flex flex-col gap-3 items-center"},React.createElement(Icon,{name:"icon-gui-exclamation-triangle-outline",color:"text-yellow-600 dark:text-yellow-400",size:"24px"}),React.createElement("p",{className:"ui-text-p3 text-neutral-700 dark:text-neutral-600"},"You're currently viewing the ",activeLanguageInfo.label," docs. There either isn't a ",activeLanguageInfo.label," code sample for this example, or this feature isn't supported in"," ",activeLanguageInfo.label,". Switch language to view this example in a different language, or check which SDKs support this feature."))},[activeLanguage]);const showLanguageSelector=!fixed&&filteredLanguages.length>0;const showFullSelector=filteredLanguages.length>1;const showFixedLanguageLabel=fixed&&activeLanguage;const renderLanguageLabel=(langKey,onClick)=>React.createElement("div",{className:cn("border-b border-neutral-300 dark:border-neutral-1000 h-[2.125rem] inline-flex items-center px-3 w-full",{"rounded-t-lg":!headerRow})},React.createElement("div",{className:cn("inline-flex items-center",onClick&&"cursor-pointer"),...onClick&&{onClick}},React.createElement(Icon,{name:getLanguageInfo(langKey).icon,size:"16px",additionalCSS:"mr-2"}),React.createElement("span",{className:"ui-text-label4 font-semibold text-neutral-800 dark:text-neutral-500 select-none"},getLanguageInfo(langKey).label)));const renderContent=useMemo(()=>{if(!activeLanguage)return null;if(hasSnippetForActiveLanguage){return processedChildren}return noSnippetMessage},[activeLanguage,hasSnippetForActiveLanguage,processedChildren,noSnippetMessage]);if(isSinglePlainCommand){const plainChild=codeData[0];if(plainChild){const codeContent=plainChild.content;const language=plainChild.language;if(!language||!codeContent)return null;let processedContent=String(codeContent);if(requiresApiKeySubstitution){processedContent=substituteApiKey(processedContent,selectedApiKey)}return React.createElement(PlainCodeView,{content:processedContent,className:className,language:language,icon:language==="shell"?"icon-gui-command-line-outline":null})}}return React.createElement("div",{className:cn("rounded-lg overflow-hidden bg-neutral-100 dark:bg-neutral-1200 border border-neutral-300 dark:border-neutral-1000 min-h-[3.375rem]",className)},headerRow&&React.createElement("div",{className:"h-[2.375rem] bg-neutral-200 dark:bg-neutral-1100 border-b border-neutral-300 dark:border-neutral-1000 flex items-center py-1 px-3 rounded-t-lg"},React.createElement("div",{className:"flex space-x-1.5"},React.createElement("div",{className:"w-3 h-3 rounded-full bg-orange-500"}),React.createElement("div",{className:"w-3 h-3 rounded-full bg-yellow-500"}),React.createElement("div",{className:"w-3 h-3 rounded-full bg-green-500"})),React.createElement("div",{className:"flex-1 text-center ui-text-p3 font-bold text-neutral-1300 dark:text-neutral-000"},title),React.createElement("div",{className:"w-12"})),showSDKSelector&&React.createElement("div",{className:cn("p-2 border-b border-neutral-300 dark:border-neutral-1000",sdkTypes.size===1&&"p-1",headerRow?"":"rounded-t-lg")},React.createElement("div",{className:"flex gap-1 justify-start"},["realtime","rest"].map(type=>sdkTypes.has(type)&&React.createElement(SegmentedControl,{key:type,onClick:()=>handleSDKTypeChange(type),size:"xs",active:resolvedSdk===type,className:cn("text-[11px] font-semibold px-2 py-1 h-auto",sdkTypes.size===1&&"pointer-events-none bg-neutral-100 dark:bg-neutral-1200 !text-neutral-800 !dark:text-neutral-500",sdkTypes.size>1&&resolvedSdk!==type&&"bg-neutral-100 dark:bg-neutral-1200 hover:bg-neutral-200 dark:hover:bg-neutral-1100 active:bg-neutral-400 dark:active:bg-neutral-900",sdkTypes.size>1&&resolvedSdk===type&&"bg-neutral-000 dark:bg-neutral-1100")},type==="realtime"?"Realtime":"REST")))),showFixedLanguageLabel&&renderLanguageLabel(activeLanguage),showLanguageSelector&&(showFullSelector?React.createElement(LanguageSelector,{languages:filteredLanguages,activeLanguage:activeLanguage,onLanguageChange:handleLanguageChange}):renderLanguageLabel(filteredLanguages[0],()=>handleLanguageChange(filteredLanguages[0]))),React.createElement("div",{ref:codeRef,className:"relative",onMouseEnter:()=>setIsHovering(true),onMouseLeave:()=>setIsHovering(false),onFocus:()=>setIsHovering(true),onBlur:()=>setIsHovering(false)},renderContent,isHovering&&activeLanguage&&hasSnippetForActiveLanguage&&React.createElement(CopyButton,{onCopy:()=>{const text=codeData.find(code=>code.language===activeLanguage)?.content;if(text)navigator.clipboard.writeText(substituteApiKey(text,selectedApiKey,false))}})),requiresApiKeySubstitution&&React.createElement(ApiKeySelector,{apiKeys:apiKeys,selectedApiKey:selectedApiKey,onApiKeyChange:setSelectedApiKey}))};export default CodeSnippet;
|
|
1
|
+
import React,{useState,useEffect,Children,isValidElement,useRef,useCallback,useMemo}from"react";import Code from"./Code";import cn from"./utils/cn";import{parseLineHighlights}from"./utils/syntax-highlighter";import Icon from"./Icon";import{getLanguageInfo,stripSdkType,SDK_PREFIXES}from"./CodeSnippet/languages";import LanguageSelector from"./CodeSnippet/LanguageSelector";import ApiKeySelector from"./CodeSnippet/ApiKeySelector";import PlainCodeView from"./CodeSnippet/PlainCodeView";import CopyButton from"./CodeSnippet/CopyButton";import copyToClipboard from"./CodeSnippet/copyToClipboard";import SegmentedControl from"./SegmentedControl";const substituteApiKey=(content,apiKey,mask=true)=>{return content.replace(/\{\{API_KEY\}\}/g,mask?`${apiKey.split(":")[0]}:*****`:apiKey)};const CodeSnippet=({fixed=false,headerRow=false,title="Code",children,className,lang,onChange,apiKeys,sdk,showCodeLines=true,languageOrdering,wrapCode=false,onCopy})=>{const codeRef=useRef(null);const[selectedApiKey,setSelectedApiKey]=useState(()=>apiKeys?.[0]?.keys?.[0]?.key??"");const[prevApiKeys,setPrevApiKeys]=useState(apiKeys);if(prevApiKeys!==apiKeys){setPrevApiKeys(apiKeys);if(!selectedApiKey&&apiKeys&&apiKeys.length>0){setSelectedApiKey(apiKeys[0].keys?.[0]?.key??"")}}useEffect(()=>{const element=codeRef.current;if(!element)return;const unmaskRenderedApiKey=(content,apiKey)=>{return content.replace(/(['"]?)([^:'"]+):\*{5}\1/g,`$1${apiKey}$1`)};const handleCopy=event=>{const selection=window.getSelection();if(!selection||selection.rangeCount===0)return;const selectedText=selection.toString();if(!selectedText)return;const range=selection.getRangeAt(0);if(!element.contains(range.commonAncestorContainer))return;const modifiedText=unmaskRenderedApiKey(selectedText,selectedApiKey);event.clipboardData?.setData("text/plain",modifiedText);event.preventDefault()};document.addEventListener("copy",handleCopy);return()=>{document.removeEventListener("copy",handleCopy)}},[selectedApiKey]);const extractLanguageFromCode=useCallback(codeElement=>{if(!codeElement||!codeElement.props.className)return null;const classNames=codeElement.props.className.split(" ");const langClass=classNames.find(cls=>cls.startsWith("language-"));if(!langClass)return null;return langClass.substring(9)},[]);const findCodeElement=useCallback(preChildren=>{if(isValidElement(preChildren)){return preChildren}if(Array.isArray(preChildren)){const codeEl=preChildren.find(c=>isValidElement(c));return codeEl&&isValidElement(codeEl)?codeEl:null}return null},[]);const{codeData,languages,sdkTypes,isSinglePlainCommand}=useMemo(()=>{const childrenArray=Children.toArray(children);const languages=[];const sdkTypes=new Set;const codeData=[];const isSinglePlainCommand=childrenArray.length===1&&["language-shell","language-text"].some(lang=>{if(!isValidElement(childrenArray[0]))return false;const codeEl=findCodeElement(childrenArray[0].props.children);return codeEl?.props.className?.includes(lang)});childrenArray.forEach(child=>{if(!isValidElement(child))return;const preElement=child;const codeElement=findCodeElement(preElement.props.children);if(!codeElement)return;const rawLanguage=extractLanguageFromCode(codeElement);if(!rawLanguage)return;const meta=codeElement.props?.["data-meta"];const{lang:codeLanguage,highlights:lineHighlights}=parseLineHighlights(rawLanguage,meta);for(const prefix of SDK_PREFIXES){if(codeLanguage.startsWith(`${prefix}_`)){sdkTypes.add(prefix);break}}if(!languages.includes(codeLanguage)){languages.push(codeLanguage)}const codeContent=codeElement.props.children;codeData.push({language:codeLanguage,content:codeContent,lineHighlights})});return{codeData,languages,sdkTypes,isSinglePlainCommand}},[children,extractLanguageFromCode,findCodeElement]);const resolvedSdk=useMemo(()=>{if(sdkTypes.size===1&&sdk&&!sdkTypes.has(sdk)){return Array.from(sdkTypes)[0]}return sdk},[sdk,sdkTypes]);const showSDKSelector=sdkTypes.has("realtime")||sdkTypes.has("rest");const filteredLanguages=useMemo(()=>{const filtered=!resolvedSdk||!showSDKSelector?[...languages]:languages.filter(lang=>lang.startsWith(`${resolvedSdk}_`));if(languageOrdering&&languageOrdering.length>0){filtered.sort((a,b)=>{const aBase=stripSdkType(a);const bBase=stripSdkType(b);const aIndex=languageOrdering.indexOf(aBase);const bIndex=languageOrdering.indexOf(bBase);if(aIndex!==-1&&bIndex!==-1)return aIndex-bIndex;if(aIndex!==-1)return-1;if(bIndex!==-1)return 1;return 0})}return filtered},[resolvedSdk,showSDKSelector,languages,languageOrdering]);const activeLanguage=useMemo(()=>{if(resolvedSdk==="client"||resolvedSdk==="agent"){const fullLang=`${resolvedSdk}_${lang}`;if(languages.includes(fullLang)){return fullLang}const prefixMatch=languages.find(l=>l.startsWith(`${resolvedSdk}_`));if(prefixMatch)return prefixMatch}if(resolvedSdk&&sdkTypes.has(resolvedSdk)){return`${resolvedSdk}_${lang}`}if(lang)return lang;if(filteredLanguages.length>0)return filteredLanguages[0];return languages[0]},[resolvedSdk,sdkTypes,lang,filteredLanguages,languages]);const requiresApiKeySubstitution=useMemo(()=>{const containsPlaceholder=codeData.some(code=>code?.content.includes("{{API_KEY}}"));return containsPlaceholder&&!!apiKeys&&apiKeys.length>0&&!!selectedApiKey},[codeData,apiKeys,selectedApiKey]);const[isHovering,setIsHovering]=useState(false);const hasOnlyJsonSnippet=useMemo(()=>languages.length===1&&languages[0]==="json",[languages]);const processedChildren=useMemo(()=>{if(!activeLanguage)return[];const targetLanguage=hasOnlyJsonSnippet?"json":activeLanguage;return codeData.filter(code=>{return code?.language===targetLanguage}).map(code=>{if(!code)return null;const cleanLang=hasOnlyJsonSnippet?"json":code.language;const langInfo=getLanguageInfo(cleanLang??"");if(typeof code.content==="string"||typeof code.content==="number"||typeof code.content==="boolean"){let processedContent=String(code.content);if(requiresApiKeySubstitution){processedContent=substituteApiKey(processedContent,selectedApiKey)}if(!langInfo.syntaxHighlighterKey||!cleanLang)return null;return React.createElement(Code,{key:code.language,language:langInfo.syntaxHighlighterKey||cleanLang,snippet:processedContent,additionalCSS:"!bg-neutral-000 text-neutral-1300 dark:!bg-neutral-1300 dark:text-neutral-200 px-6 py-4",showLines:showCodeLines,wrap:wrapCode,lineHighlights:Object.keys(code.lineHighlights).length>0?code.lineHighlights:undefined})}return null})},[activeLanguage,hasOnlyJsonSnippet,codeData,requiresApiKeySubstitution,showCodeLines,wrapCode,selectedApiKey]);const hasSnippetForActiveLanguage=useMemo(()=>{if(!activeLanguage)return false;if(hasOnlyJsonSnippet)return true;return codeData.some(code=>{return code?.language===activeLanguage})},[activeLanguage,hasOnlyJsonSnippet,codeData]);const handleSDKTypeChange=useCallback(type=>{const nextLang=stripSdkType(languages.find(l=>l===`${type}_${stripSdkType(activeLanguage)}`)??languages.find(l=>l.startsWith(`${type}_`))??activeLanguage);if(onChange&&nextLang){onChange(stripSdkType(activeLanguage),type)}},[activeLanguage,languages,onChange]);const handleLanguageChange=useCallback(language=>{if(onChange){onChange(stripSdkType(language),resolvedSdk)}},[onChange,resolvedSdk]);const noSnippetMessage=useMemo(()=>{if(!activeLanguage)return null;const activeLanguageInfo=getLanguageInfo(activeLanguage);return React.createElement("div",{className:"px-16 py-6 ui-text-body2 text-neutral-800 dark:text-neutral-400 text-center flex flex-col gap-3 items-center"},React.createElement(Icon,{name:"icon-gui-exclamation-triangle-outline",color:"text-yellow-600 dark:text-yellow-400",size:"24px"}),React.createElement("p",{className:"ui-text-p3 text-neutral-700 dark:text-neutral-600"},"You're currently viewing the ",activeLanguageInfo.label," docs. There either isn't a ",activeLanguageInfo.label," code sample for this example, or this feature isn't supported in"," ",activeLanguageInfo.label,". Switch language to view this example in a different language, or check which SDKs support this feature."))},[activeLanguage]);const showLanguageSelector=!fixed&&filteredLanguages.length>0;const showFullSelector=filteredLanguages.length>1;const showFixedLanguageLabel=fixed&&activeLanguage;const renderLanguageLabel=(langKey,onClick)=>React.createElement("div",{className:cn("border-b border-neutral-300 dark:border-neutral-1000 h-[2.125rem] inline-flex items-center px-3 w-full",{"rounded-t-lg":!headerRow})},React.createElement("div",{className:cn("inline-flex items-center",onClick&&"cursor-pointer"),...onClick&&{onClick}},React.createElement(Icon,{name:getLanguageInfo(langKey).icon,size:"16px",additionalCSS:"mr-2"}),React.createElement("span",{className:"ui-text-label4 font-semibold text-neutral-800 dark:text-neutral-500 select-none"},getLanguageInfo(langKey).label)));const renderContent=useMemo(()=>{if(!activeLanguage)return null;if(hasSnippetForActiveLanguage){return processedChildren}return noSnippetMessage},[activeLanguage,hasSnippetForActiveLanguage,processedChildren,noSnippetMessage]);if(isSinglePlainCommand){const plainChild=codeData[0];if(plainChild){const codeContent=plainChild.content;const language=plainChild.language;if(!language||!codeContent)return null;let processedContent=String(codeContent);if(requiresApiKeySubstitution){processedContent=substituteApiKey(processedContent,selectedApiKey)}return React.createElement(PlainCodeView,{content:processedContent,className:className,language:language,icon:language==="shell"?"icon-gui-command-line-outline":null,onCopy:onCopy})}}return React.createElement("div",{className:cn("rounded-lg overflow-hidden bg-neutral-100 dark:bg-neutral-1200 border border-neutral-300 dark:border-neutral-1000 min-h-[3.375rem]",className)},headerRow&&React.createElement("div",{className:"h-[2.375rem] bg-neutral-200 dark:bg-neutral-1100 border-b border-neutral-300 dark:border-neutral-1000 flex items-center py-1 px-3 rounded-t-lg"},React.createElement("div",{className:"flex space-x-1.5"},React.createElement("div",{className:"w-3 h-3 rounded-full bg-orange-500"}),React.createElement("div",{className:"w-3 h-3 rounded-full bg-yellow-500"}),React.createElement("div",{className:"w-3 h-3 rounded-full bg-green-500"})),React.createElement("div",{className:"flex-1 text-center ui-text-p3 font-semibold text-neutral-1300 dark:text-neutral-000"},title),React.createElement("div",{className:"w-12"})),showSDKSelector&&React.createElement("div",{className:cn("p-2 border-b border-neutral-300 dark:border-neutral-1000",sdkTypes.size===1&&"p-1",headerRow?"":"rounded-t-lg")},React.createElement("div",{className:"flex gap-1 justify-start"},["realtime","rest"].map(type=>sdkTypes.has(type)&&React.createElement(SegmentedControl,{key:type,onClick:()=>handleSDKTypeChange(type),size:"xs",active:resolvedSdk===type,className:cn("text-[11px] font-semibold px-2 py-1 h-auto",sdkTypes.size===1&&"pointer-events-none bg-neutral-100 dark:bg-neutral-1200 !text-neutral-800 !dark:text-neutral-500",sdkTypes.size>1&&resolvedSdk!==type&&"bg-neutral-100 dark:bg-neutral-1200 hover:bg-neutral-200 dark:hover:bg-neutral-1100 active:bg-neutral-400 dark:active:bg-neutral-900",sdkTypes.size>1&&resolvedSdk===type&&"bg-neutral-000 dark:bg-neutral-1100")},type==="realtime"?"Realtime":"REST")))),showFixedLanguageLabel&&renderLanguageLabel(activeLanguage),showLanguageSelector&&(showFullSelector?React.createElement(LanguageSelector,{languages:filteredLanguages,activeLanguage:activeLanguage,onLanguageChange:handleLanguageChange}):renderLanguageLabel(filteredLanguages[0],()=>handleLanguageChange(filteredLanguages[0]))),React.createElement("div",{ref:codeRef,className:"relative",onMouseEnter:()=>setIsHovering(true),onMouseLeave:()=>setIsHovering(false),onFocus:()=>setIsHovering(true),onBlur:()=>setIsHovering(false)},renderContent,isHovering&&activeLanguage&&hasSnippetForActiveLanguage&&React.createElement(CopyButton,{onCopy:()=>{const text=codeData.find(code=>code.language===activeLanguage)?.content;if(!text)return;copyToClipboard(substituteApiKey(text,selectedApiKey,false),onCopy)}})),requiresApiKeySubstitution&&React.createElement(ApiKeySelector,{apiKeys:apiKeys,selectedApiKey:selectedApiKey,onApiKeyChange:setSelectedApiKey}))};export default CodeSnippet;
|
|
2
2
|
//# sourceMappingURL=CodeSnippet.js.map
|