mirror of https://github.com/msgbyte/tailchat
				
				
				
			feat: 增加Markdown组件用于渲染markdown格式的字符串
							parent
							
								
									e449bf9dac
								
							
						
					
					
						commit
						9328c9b8de
					
				
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								@ -0,0 +1,30 @@
 | 
			
		||||
import React, { useCallback } from 'react';
 | 
			
		||||
import { isValidStr } from 'tailchat-shared';
 | 
			
		||||
 | 
			
		||||
const ReactMarkdown = React.lazy(() => import('react-markdown'));
 | 
			
		||||
 | 
			
		||||
export const Markdown: React.FC<{
 | 
			
		||||
  raw: string;
 | 
			
		||||
  baseUrl?: string;
 | 
			
		||||
}> = React.memo(({ raw, baseUrl }) => {
 | 
			
		||||
  const transformUrl = useCallback(
 | 
			
		||||
    (url: string) => {
 | 
			
		||||
      if (!isValidStr(baseUrl)) {
 | 
			
		||||
        return url;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return new URL(url, baseUrl).href;
 | 
			
		||||
    },
 | 
			
		||||
    [baseUrl]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ReactMarkdown
 | 
			
		||||
      transformImageUri={(src) => transformUrl(src)}
 | 
			
		||||
      transformLinkUri={(href) => transformUrl(href)}
 | 
			
		||||
    >
 | 
			
		||||
      {raw}
 | 
			
		||||
    </ReactMarkdown>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
Markdown.displayName = 'Markdown';
 | 
			
		||||
@ -0,0 +1,49 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { renderLazy } from '@test/utils/lazy';
 | 
			
		||||
import { Markdown } from '../Markdown';
 | 
			
		||||
 | 
			
		||||
describe('Markdown', () => {
 | 
			
		||||
  test('heading', async () => {
 | 
			
		||||
    const text = `
 | 
			
		||||
# Heading1
 | 
			
		||||
## Heading2
 | 
			
		||||
### Heading3
 | 
			
		||||
#### Heading4
 | 
			
		||||
##### Heading5
 | 
			
		||||
    `;
 | 
			
		||||
    const wrapper = await renderLazy(<Markdown raw={text} />);
 | 
			
		||||
    expect(wrapper.container).toMatchSnapshot();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('list', async () => {
 | 
			
		||||
    const text = `
 | 
			
		||||
- 1
 | 
			
		||||
- 2
 | 
			
		||||
- 3
 | 
			
		||||
  - 4
 | 
			
		||||
  - 5
 | 
			
		||||
    - 6
 | 
			
		||||
        `;
 | 
			
		||||
    const wrapper = await renderLazy(<Markdown raw={text} />);
 | 
			
		||||
    expect(wrapper.container).toMatchSnapshot();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('link', () => {
 | 
			
		||||
    const text = `
 | 
			
		||||
[https://tailchat.msgbyte.com/](https://tailchat.msgbyte.com/)
 | 
			
		||||
[./README.md](./README.md)
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
    test('without baseUrl', async () => {
 | 
			
		||||
      const wrapper = await renderLazy(<Markdown raw={text} />);
 | 
			
		||||
      expect(wrapper.container).toMatchSnapshot();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('with baseUrl', async () => {
 | 
			
		||||
      const wrapper = await renderLazy(
 | 
			
		||||
        <Markdown raw={text} baseUrl="https://tailchat.msgbyte.com/" />
 | 
			
		||||
      );
 | 
			
		||||
      expect(wrapper.container).toMatchSnapshot();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,134 @@
 | 
			
		||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
 | 
			
		||||
 | 
			
		||||
exports[`Markdown heading 1`] = `
 | 
			
		||||
<div>
 | 
			
		||||
  <h1>
 | 
			
		||||
    Heading1
 | 
			
		||||
  </h1>
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  <h2>
 | 
			
		||||
    Heading2
 | 
			
		||||
  </h2>
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  <h3>
 | 
			
		||||
    Heading3
 | 
			
		||||
  </h3>
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  <h4>
 | 
			
		||||
    Heading4
 | 
			
		||||
  </h4>
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  <h5>
 | 
			
		||||
    Heading5
 | 
			
		||||
  </h5>
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
exports[`Markdown link with baseUrl 1`] = `
 | 
			
		||||
<div>
 | 
			
		||||
  <p>
 | 
			
		||||
    <a
 | 
			
		||||
      href="https://tailchat.msgbyte.com/"
 | 
			
		||||
    >
 | 
			
		||||
      https://tailchat.msgbyte.com/
 | 
			
		||||
    </a>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <a
 | 
			
		||||
      href="https://tailchat.msgbyte.com/README.md"
 | 
			
		||||
    >
 | 
			
		||||
      ./README.md
 | 
			
		||||
    </a>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <img
 | 
			
		||||
      alt=""
 | 
			
		||||
      src="https://tailchat.msgbyte.com/demo.jpg"
 | 
			
		||||
    />
 | 
			
		||||
  </p>
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
exports[`Markdown link without baseUrl 1`] = `
 | 
			
		||||
<div>
 | 
			
		||||
  <p>
 | 
			
		||||
    <a
 | 
			
		||||
      href="https://tailchat.msgbyte.com/"
 | 
			
		||||
    >
 | 
			
		||||
      https://tailchat.msgbyte.com/
 | 
			
		||||
    </a>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <a
 | 
			
		||||
      href="./README.md"
 | 
			
		||||
    >
 | 
			
		||||
      ./README.md
 | 
			
		||||
    </a>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <img
 | 
			
		||||
      alt=""
 | 
			
		||||
      src="./demo.jpg"
 | 
			
		||||
    />
 | 
			
		||||
  </p>
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
exports[`Markdown list 1`] = `
 | 
			
		||||
<div>
 | 
			
		||||
  <ul>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <li>
 | 
			
		||||
      1
 | 
			
		||||
    </li>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <li>
 | 
			
		||||
      2
 | 
			
		||||
    </li>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <li>
 | 
			
		||||
      3
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      <ul>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <li>
 | 
			
		||||
          4
 | 
			
		||||
        </li>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <li>
 | 
			
		||||
          5
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          <ul>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <li>
 | 
			
		||||
              6
 | 
			
		||||
            </li>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
          </ul>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
        </li>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
      </ul>
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
    </li>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
  </ul>
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
import { render } from '@testing-library/react';
 | 
			
		||||
import React, { Suspense } from 'react';
 | 
			
		||||
import { sleep } from 'tailchat-shared';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 在普通的组件上面加一个 Suspense
 | 
			
		||||
 */
 | 
			
		||||
export function renderWithSuspense(ui: React.ReactElement) {
 | 
			
		||||
  return render(ui, {
 | 
			
		||||
    wrapper: (props) => (
 | 
			
		||||
      <Suspense fallback="JestLoading">{props.children}</Suspense>
 | 
			
		||||
    ),
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 渲染一个懒加载组件
 | 
			
		||||
 */
 | 
			
		||||
export async function renderLazy(ui: React.ReactElement, ms = 400) {
 | 
			
		||||
  const wrapper = renderWithSuspense(ui);
 | 
			
		||||
  await sleep(ms);
 | 
			
		||||
 | 
			
		||||
  return wrapper;
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue