diff --git a/content/about/about.en.md b/content/about/about.en.md index 656e3ce025..691a6ac730 100644 --- a/content/about/about.en.md +++ b/content/about/about.en.md @@ -11,7 +11,7 @@ scalable network applications. In the following "hello world" example, many connections can be handled concurrently. Upon each connection, the callback is fired, but if there is no work to be done, Node.js will sleep. -```javascript +```js const http = require('http'); const hostname = '127.0.0.1'; diff --git a/content/about/about.ja.md b/content/about/about.ja.md index 48deb2b26d..8794d90980 100644 --- a/content/about/about.ja.md +++ b/content/about/about.ja.md @@ -8,7 +8,7 @@ category: about Node.js は、非同期のイベントドリブン JavaScript ランタイムとして、スケーラブルなネットワークアプリケーションを構築するために設計されています。次の "hello world" の例では、多くの接続を同時に処理できます。接続を並列に処理することができます。各接続の際に、コールバックが呼び出されます。しかし、何もすることがない場合は、Node.js はスリープします。 -```javascript +```js const http = require('http'); const hostname = '127.0.0.1'; diff --git a/content/about/about.pt.md b/content/about/about.pt.md index 0e739f85bc..1c887439e5 100644 --- a/content/about/about.pt.md +++ b/content/about/about.pt.md @@ -8,7 +8,7 @@ category: about Como executor de JavaScript orientado a eventos assíncronos, a Node está desenhada para construir aplicações de rede escaláveis. No seguinte exemplo "hello world" ou "olá mundo" em Português, várias conexões podem ser manipuladas simultaneamente. Sobre cada conexão, uma função de resposta é disparada, mas se não existe nada a ser feito, a Node.js adormecerá. -```javascript +```js const http = require('http'); const hostname = '127.0.0.1'; diff --git a/src/components/ArticleComponents/Codebox/__tests__/__snapshots__/index.test.tsx.snap b/src/components/ArticleComponents/Codebox/__tests__/__snapshots__/index.test.tsx.snap index 069bec5298..7133d858bd 100644 --- a/src/components/ArticleComponents/Codebox/__tests__/__snapshots__/index.test.tsx.snap +++ b/src/components/ArticleComponents/Codebox/__tests__/__snapshots__/index.test.tsx.snap @@ -1,17 +1,147 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Codebox component renders correctly 1`] = ` +exports[`Codebox component (multiple langs) renders correctly 1`] = `
-
- HTML
+
+
+
+
+
+
+
+
+ const
+
+ http
+
+ =
+
+
+
+ require
+
+
+ (
+
+
+ 'http'
+
+
+ )
+
+
+ ;
+
+
+
+
+ --
+
+
+ --
+
+
+ --
+
+
+ -
+
+
+
+ import
+
+ http
+
+ from
+
+
+
+ 'http'
+
+
+ ;
+
+
+
+++diff --git a/src/components/ArticleComponents/Codebox/__tests__/index.test.tsx b/src/components/ArticleComponents/Codebox/__tests__/index.test.tsx index e467286d77..4f6e3a00bd 100644 --- a/src/components/ArticleComponents/Codebox/__tests__/index.test.tsx +++ b/src/components/ArticleComponents/Codebox/__tests__/index.test.tsx @@ -1,8 +1,10 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import { render, screen } from '@testing-library/react'; +import { highlight, languages } from 'prismjs'; +import { sanitize } from 'isomorphic-dompurify'; -import Codebox from '../index'; +import Codebox, { replaceLabelLanguages, replaceLanguages } from '../index'; Object.assign(navigator, { clipboard: { @@ -16,14 +18,30 @@ afterEach(() => { jest.clearAllMocks(); }); -describe('Codebox component', (): void => { +describe('Replacer tests', (): void => { + it('replaceLabelLanguages', (): void => { + expect(replaceLabelLanguages('language-console')).toBe('language-bash'); + }); + + it('replaceLanguages', (): void => { + expect(replaceLanguages('language-mjs')).toBe('language-js'); + expect(replaceLanguages('language-cjs')).toBe('language-js'); + expect(replaceLanguages('language-javascript')).toBe('language-js'); + expect(replaceLanguages('language-console')).toBe('language-bash'); + expect(replaceLanguages('language-shell')).toBe('language-bash'); + }); +}); + +describe('Codebox component (one lang)', (): void => { + const code = 'const a = 1;'; + const textToCopy = sanitize(highlight(code, languages.js, 'js')); + it('renders correctly', (): void => { - const textToCopy =+ +text to be copy
; const { container } = render({{ props: { - className: 'language-html', + className: 'language-js', children: textToCopy, }, }} @@ -33,13 +51,11 @@ describe('Codebox component', (): void => { }); it('renders correctly', async () => { - const textToCopy = text to be copy
; - render({{ props: { - className: 'language-html', + className: 'language-js', children: textToCopy, }, }} @@ -57,3 +73,63 @@ describe('Codebox component', (): void => { expect(navigatorClipboardSpy).toHaveBeenCalledWith(textToCopy.toString()); }); }); + +describe('Codebox component (multiple langs)', (): void => { + const code = `const http = require('http'); + ------- + import http from 'http';`; + + it('renders correctly', (): void => { + const { container } = render( + + {{ + props: { + className: 'language-js|language-js', + children: code, + }, + }} + + ); + expect(container).toMatchSnapshot(); + }); + + it('switch between languages', async () => { + render( ++ {{ + props: { + className: 'language-cjs|language-mjs', + children: code, + }, + }} + + ); + + const buttonElement = screen.getByText('cjs'); + userEvent.click(buttonElement); + + await screen.findByText('cjs'); + + expect(screen.getByText('cjs')).toBeInTheDocument(); + }); + + it('switch between sync and async', async () => { + render( ++ {{ + props: { + className: 'language-async|language-sync', + children: code, + }, + }} + + ); + + const buttonElement = screen.getByText('async'); + userEvent.click(buttonElement); + + await screen.findByText('async'); + + expect(screen.getByText('async')).toBeInTheDocument(); + }); +}); diff --git a/src/components/ArticleComponents/Codebox/index.module.scss b/src/components/ArticleComponents/Codebox/index.module.scss index 0cd2be98a0..bdbea3cb85 100644 --- a/src/components/ArticleComponents/Codebox/index.module.scss +++ b/src/components/ArticleComponents/Codebox/index.module.scss @@ -41,8 +41,8 @@ flex-direction: row; justify-content: space-between; - span, - button { + .lang, + .copy { align-items: center; display: inherit; font-size: var(--font-size-code); @@ -50,14 +50,23 @@ justify-content: center; width: 86px; } - - span { + .langBox { background-color: var(--black4); border-radius: 0 0 0.3rem 0; - color: var(--black9); + display: flex; + flex-direction: row; + justify-content: center; + width: 100px; + + .lang { + background-color: var(--black4); + border-width: 0; + color: var(--black9); + width: max-content; + } } - button { + .copy { background-color: var(--black9); border-radius: 0 0 0 0.3rem; border-width: 0; diff --git a/src/components/ArticleComponents/Codebox/index.tsx b/src/components/ArticleComponents/Codebox/index.tsx index 442cb626d6..97a2492320 100644 --- a/src/components/ArticleComponents/Codebox/index.tsx +++ b/src/components/ArticleComponents/Codebox/index.tsx @@ -15,54 +15,59 @@ interface Props { }; } -const replaceLanguages = (language: string) => +export const replaceLabelLanguages = (language: string) => + language.replace(/console/i, 'bash'); + +export const replaceLanguages = (language: string) => language .replace(/mjs|cjs|javascript/i, 'js') .replace(/console|shell/i, 'bash'); -const replaceLabelLanguages = (language: string) => - language - .replace(/javascript/i, 'cjs') - .replace(/console|shell/i, 'bash') - .toUpperCase(); - const Codebox = ({ children: { props } }: Props): JSX.Element => { const [parsedCode, setParsedCode] = useState(''); const [copied, copyText] = useCopyToClipboard(); - + const [langIndex, setLangIndex] = useState(0); // eslint-disable-next-line react/prop-types const className = props.className || 'text'; - // Language Matches in class const matches = className.match(/language-(?.*)/); - - // Language name from classname - const language = matches?.groups?.lang || 'text'; - - // Actual Code into a stringified format - const stringCode = props.children?.toString() || ''; + const languageOptions = (matches?.groups?.lang || 'text').split('|'); + const language = languageOptions[langIndex]; + const codeArray = props.children + ? props.children.toString().split('--------------\n') + : ['']; const handleCopyCode = async (event: React.MouseEvent ) => { event.preventDefault(); - copyText(stringCode); + copyText(codeArray[langIndex]); }; useEffect(() => { const parsedLanguage = replaceLanguages(language); - const prismLanguage = languages[parsedLanguage] || languages.text; setParsedCode( - sanitize(highlight(stringCode, prismLanguage, parsedLanguage)) + sanitize(highlight(codeArray[langIndex], prismLanguage, parsedLanguage)) ); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [langIndex]); return ( - {replaceLabelLanguages(language)} -