-
Notifications
You must be signed in to change notification settings - Fork 34
test: Assert Assistant code block #305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a4fd201
b2d6cc1
9095380
334a9f5
4f14689
164a78e
b12baeb
6d3d5ab
9adcb3f
2ae36fd
9e759ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function stripAnsi( str: string ) { | ||
return str; | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I explored enabling Jest's experimental ESM support to remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer this disposition of having a separated component. Thanks David for making the change 🙇 ! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { Spinner } from '@wordpress/components'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { useEffect } from 'react'; | ||
import { ExtraProps } from 'react-markdown'; | ||
import stripAnsi from 'strip-ansi'; | ||
import { useExecuteWPCLI } from '../hooks/use-execute-cli'; | ||
import Button from './button'; | ||
import { ChatMessageProps } from './chat-message'; | ||
import { CopyTextButton } from './copy-text-button'; | ||
import { ExecuteIcon } from './icons/execute'; | ||
|
||
type ContextProps = Pick< | ||
ChatMessageProps, | ||
'blocks' | 'updateMessage' | 'projectPath' | 'messageId' | ||
>; | ||
|
||
type CodeBlockProps = JSX.IntrinsicElements[ 'code' ] & ExtraProps; | ||
|
||
export default function createCodeComponent( contextProps: ContextProps ) { | ||
return ( props: CodeBlockProps ) => <CodeBlock { ...contextProps } { ...props } />; | ||
} | ||
|
||
function CodeBlock( props: ContextProps & CodeBlockProps ) { | ||
const content = String( props.children ).trim(); | ||
const containsWPCommand = /\bwp\s/.test( content ); | ||
const wpCommandCount = ( content.match( /\bwp\s/g ) || [] ).length; | ||
const containsSingleWPCommand = wpCommandCount === 1; | ||
const containsAngleBrackets = /<.*>/.test( content ); | ||
dcalhoun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const { node, blocks, updateMessage, projectPath, messageId, ...htmlAttributes } = props; | ||
const { | ||
cliOutput, | ||
cliStatus, | ||
cliTime, | ||
isRunning, | ||
handleExecute, | ||
setCliOutput, | ||
setCliStatus, | ||
setCliTime, | ||
} = useExecuteWPCLI( content, projectPath, updateMessage, messageId ); | ||
|
||
useEffect( () => { | ||
if ( blocks ) { | ||
const block = blocks?.find( ( block ) => block.codeBlockContent === content ); | ||
if ( block ) { | ||
setCliOutput( block?.cliOutput ? stripAnsi( block.cliOutput ) : null ); | ||
setCliStatus( block?.cliStatus ?? null ); | ||
setCliTime( block?.cliTime ?? null ); | ||
} | ||
} | ||
}, [ blocks, cliOutput, content, setCliOutput, setCliStatus, setCliTime ] ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
const { children, className } = props; | ||
const match = /language-(\w+)/.exec( className || '' ); | ||
return match ? ( | ||
<> | ||
<div className="p-3"> | ||
<code className={ className } { ...htmlAttributes }> | ||
{ children } | ||
</code> | ||
</div> | ||
<div className="p-3 pt-1 flex justify-start items-center"> | ||
<CopyTextButton | ||
text={ content } | ||
label={ __( 'Copy' ) } | ||
copyConfirmation={ __( 'Copied!' ) } | ||
showText={ true } | ||
variant="outlined" | ||
className="h-auto mr-2 !px-2.5 py-0.5 !p-[6px] font-sans select-none" | ||
iconSize={ 16 } | ||
></CopyTextButton> | ||
{ containsWPCommand && containsSingleWPCommand && ! containsAngleBrackets && ( | ||
<Button | ||
icon={ <ExecuteIcon /> } | ||
onClick={ handleExecute } | ||
disabled={ isRunning } | ||
variant="outlined" | ||
className="h-auto mr-2 !px-2.5 py-0.5 font-sans select-none" | ||
> | ||
{ cliOutput ? __( 'Run again' ) : __( 'Run' ) } | ||
</Button> | ||
) } | ||
</div> | ||
{ isRunning && ( | ||
<div className="p-3 flex justify-start items-center bg-[#2D3337] text-white"> | ||
<Spinner className="!text-white [&>circle]:stroke-a8c-gray-60" /> | ||
<span className="ml-2 font-sans">{ __( 'Running...' ) }</span> | ||
</div> | ||
) } | ||
{ ! isRunning && cliOutput && cliStatus && ( | ||
<InlineCLI output={ cliOutput } status={ cliStatus } time={ cliTime } /> | ||
) } | ||
</> | ||
) : ( | ||
<code className={ className } { ...htmlAttributes }> | ||
{ children } | ||
</code> | ||
); | ||
} | ||
|
||
interface InlineCLIProps { | ||
output?: string; | ||
status?: 'success' | 'error'; | ||
time?: string | null; | ||
} | ||
|
||
function InlineCLI( { output, status, time }: InlineCLIProps ) { | ||
return ( | ||
<div className="p-3 bg-[#2D3337]"> | ||
<div className="flex justify-between mb-2 font-sans"> | ||
<span className={ status === 'success' ? 'text-[#63CE68]' : 'text-[#E66D6C]' }> | ||
{ status === 'success' ? __( 'Success' ) : __( 'Error' ) } | ||
</span> | ||
<span className="text-gray-400">{ time }</span> | ||
</div> | ||
<pre className="text-white !bg-transparent !m-0 !px-0"> | ||
<code className="!bg-transparent !mx-0 !px-0 !text-nowrap">{ output }</code> | ||
</pre> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relocated to the
__mocks__
directory to co-locate third-party mocks and make the mock universal for all tests.