diff --git a/components/Article/Codebox/__tests__/index.test.mjs b/components/Article/Codebox/__tests__/index.test.mjs deleted file mode 100644 index 2b60bbc318a16..0000000000000 --- a/components/Article/Codebox/__tests__/index.test.mjs +++ /dev/null @@ -1,155 +0,0 @@ -import userEvent from '@testing-library/user-event'; -import { render, screen, waitFor } from '@testing-library/react'; -import { IntlProvider } from 'react-intl'; - -import Codebox, { replaceLabelLanguages, replaceLanguages } from '..'; - -describe('Codebox component', () => { - it('should render Codebox component', async () => { - const code = 'const a = 1;'; - const { container } = render( - -
{code}
-
- ); - await waitFor(() => container.querySelector('pre[tabindex="0"]')); - }); -}); - -describe('Replacer tests', () => { - it('replaceLabelLanguages', () => { - expect(replaceLabelLanguages('language-console')).toBe('bash'); - }); - - it('replaceLanguages', () => { - 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)', () => { - const code = 'const a = 1;'; - - it('should copy content', async () => { - const user = userEvent.setup(); - - const { container } = render( - {}}> - -
{code}
-
-
- ); - - const navigatorClipboardWriteTextSpy = jest.spyOn( - navigator.clipboard, - 'writeText' - ); - - const buttonElement = container.querySelector('[aria-hidden=true]'); - - expect(buttonElement).not.toBeNull(); - - await user.click(buttonElement); - - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledTimes(1); - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledWith(code); - }); - - it('should copy content with textToCopy', async () => { - const user = userEvent.setup(); - - const textToCopy = ['Example code']; - - const { container } = render( - {}}> - -
{code}
-
-
- ); - - const navigatorClipboardWriteTextSpy = jest.spyOn( - navigator.clipboard, - 'writeText' - ); - - const buttonElement = container.querySelector('button[aria-hidden=true]'); - - expect(buttonElement).not.toBeNull(); - - await user.click(buttonElement); - - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledTimes(1); - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledWith(textToCopy[0]); - }); -}); - -describe('Codebox component (multiple langs)', () => { - const code = `const http = require('http'); --------------- -import http from 'http';`; - - it('switch between languages', async () => { - const user = userEvent.setup(); - - render( - {}}> - -
{code}
-
-
- ); - - const firstLanguage = await screen.findByText('cjs'); - - expect(firstLanguage).not.toBeNull(); - expect(firstLanguage.getAttribute('data-selected')).toBe('true'); - - const secondLanguage = await screen.findByText('mjs'); - expect(secondLanguage).not.toBeNull(); - - await user.click(secondLanguage); - - expect(secondLanguage.getAttribute('data-selected')).toBe('true'); - }); - - it('should copy content with textToCopy', async () => { - const user = userEvent.setup(); - - const textToCopy = ['Example code 1', 'Example code 2']; - - const { container } = render( - {}}> - -
{code}
-
-
- ); - - const navigatorClipboardWriteTextSpy = jest.spyOn( - navigator.clipboard, - 'writeText' - ); - - const copyButton = container.querySelector('button[aria-hidden=true]'); - - expect(copyButton).not.toBeNull(); - - await user.click(copyButton); - - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledTimes(1); - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledWith(textToCopy[0]); - - const buttonElement = await screen.findByText('mjs'); - await user.click(buttonElement); - - await user.click(copyButton); - - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledTimes(2); - expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledWith(textToCopy[1]); - }); -}); diff --git a/components/Article/Codebox/index.module.scss b/components/Article/Codebox/index.module.scss deleted file mode 100644 index e34742cd33a97..0000000000000 --- a/components/Article/Codebox/index.module.scss +++ /dev/null @@ -1,138 +0,0 @@ -@use 'themes/light'; -@use 'themes/dark'; -@use 'styles/mixins/components'; - -.pre { - border-radius: var(--border-radius-3); - margin: 0.5em 0 var(--space-24) 0; - overflow: hidden; - position: relative; - - .header { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between; - padding: var(--space-05) var(--space-08); - - .langBox { - border-radius: var(--border-radius-3); - display: flex; - flex-direction: row; - height: 28px; - overflow: hidden; - - .lang { - border-width: 0; - cursor: pointer; - font-family: var(--sans); - font-size: var(--font-size-body2); - font-weight: var(--font-weight-semibold); - padding: 0 var(--space-16); - text-transform: uppercase; - - &.selected { - font-weight: var(--font-weight-bold); - } - } - } - } - - .copy { - background-color: transparent; - border-radius: var(--border-radius-3); - border-width: 0; - cursor: pointer; - font-size: var(--font-size-code); - height: 28px; - padding: 0; - width: 28px; - - svg { - height: 18px; - stroke-width: 2.1; - vertical-align: middle; - width: 18px; - } - } - - .content { - @include components.code; - display: block; - padding: var(--space-12); - } - - &.inlineCode { - .header { - display: none; - } - - .copy { - opacity: 0; - position: absolute; - right: var(--space-08); - top: var(--space-08); - transition: opacity cubic-bezier(0.4, 0, 0.4, 1) 0.3s; - } - - &:hover .copy { - opacity: 1; - } - } -} - -[data-theme='light'] { - .pre { - @include light.theme; - } - - .header { - background-color: var(--black2); - - .langBox .lang { - background-color: var(--black3); - } - } - - .copy, - .header > .langBox .lang { - color: var(--color-text-primary); - - &.selected, - &:hover { - background-color: var(--black4); - } - } - - .inlineCode .copy:hover { - background-color: var(--black4); - } -} - -[data-theme='dark'] { - .pre { - @include dark.theme; - } - - .header { - background-color: var(--black10); - - .langBox .lang { - background-color: var(--black8); - } - } - - .copy, - .header > .langBox .lang { - color: var(--black2); - - &.selected, - &:hover { - background-color: var(--black9); - } - } - - .inlineCode .copy:hover { - background-color: var(--black10); - } -} diff --git a/components/Article/Codebox/index.stories.tsx b/components/Article/Codebox/index.stories.tsx deleted file mode 100644 index 75275fc7fa028..0000000000000 --- a/components/Article/Codebox/index.stories.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import Codebox from './index'; -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import type { FC } from 'react'; - -type DecoratedCodeBoxProps = { - language: string[]; - code: string[]; - textToCopy?: string[]; - hideHeader?: boolean; -}; - -const DecoratedCodeBox: FC = ({ - language, - code, - textToCopy, - hideHeader = false, -}) => ( - -
{code.join('--------------\n')}
-
-); - -type Story = StoryObj; -type Meta = MetaObj; - -const singleLangCode = ['const a = 1;']; - -export const Default: Story = { - args: { - language: ['language-js'], - code: singleLangCode, - }, -}; - -const multiLangCode = [ - "const http = require('http');", - "import http from 'http';", -]; - -export const MultiLang: Story = { - args: { - language: ['language-cjs', 'language-mjs'], - code: multiLangCode, - }, -}; - -export const HiddenHeader: Story = { - args: { - language: ['language-js'], - code: singleLangCode, - hideHeader: true, - }, -}; - -export const MultiLangWithTextToCopy: Story = { - args: { - language: ['language-cjs', 'language-mjs'], - code: multiLangCode, - textToCopy: ['cjs example', 'mjs example'], - }, -}; - -const bashCode = ['$ echo "Hello World"']; - -export const Shell: Story = { - args: { - language: ['language-shell'], - code: bashCode, - }, -}; - -export default { component: DecoratedCodeBox } as Meta; diff --git a/components/Article/Codebox/index.tsx b/components/Article/Codebox/index.tsx deleted file mode 100644 index cc907fde3a9c3..0000000000000 --- a/components/Article/Codebox/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { useMemo, useState } from 'react'; -import classnames from 'classnames'; -import { TbCopy, TbCheck } from 'react-icons/tb'; -import { useCopyToClipboard } from '@/hooks/useCopyToClipboard'; -import { usePrismJS } from '@/hooks/usePrismJS'; -import type { FC, PropsWithChildren, ReactElement, MouseEvent } from 'react'; - -import styles from './index.module.scss'; - -type CodeBoxProps = { - children: ReactElement>; - textToCopy?: string[]; - hideHeader?: boolean; -}; - -export const replaceLabelLanguages = (language: string) => - language.replace(/console/i, 'bash').replace('language-', ''); - -export const replaceLanguages = (language: string) => - language - .replace(/mjs|cjs|javascript/i, 'js') - .replace(/console|shell/i, 'bash'); - -const Codebox: FC = ({ - children: { - props: { children: sourceCode, className = 'language-text' }, - }, - textToCopy, - hideHeader = false, -}) => { - const [copied, copyText] = useCopyToClipboard(); - const [langIndex, setLangIndex] = useState(0); - - const languageOptions = className.split('|'); - - const language = replaceLanguages(languageOptions[langIndex]); - - const codeRef = usePrismJS(language); - - const codeArray = useMemo( - () => sourceCode?.toString().split('--------------\n') || [''], - [sourceCode] - ); - - const handleCopyCode = (event: MouseEvent) => { - event.preventDefault(); - - const _textToCopy = textToCopy - ? textToCopy[langIndex] - : codeArray[langIndex]; - - copyText(_textToCopy.replace(/^\$ /, '').trim()); - }; - - const copyButton = ( - - ); - - const containerClasses = classnames(styles.pre, className, { - [styles.inlineCode]: hideHeader, - }); - - const codeClasses = classnames(language, styles.content); - - return ( -
-      
-
- {languageOptions.map((lang, index) => { - const langClasses = classnames(styles.lang, { - [styles.selected]: index === langIndex, - }); - - return ( - - ); - })} -
- - {copyButton} -
- - {hideHeader && copyButton} - - - {codeArray[langIndex]} - -
- ); -}; - -export default Codebox; diff --git a/components/Article/Codebox/themes/_dark.scss b/components/Article/Codebox/themes/_dark.scss deleted file mode 100644 index 6f1fdbd7d522f..0000000000000 --- a/components/Article/Codebox/themes/_dark.scss +++ /dev/null @@ -1,90 +0,0 @@ -@mixin theme { - background: var(--black9); - color: var(--black2); - - :global .token { - &.comment, - &.prolog, - &.doctype, - &.cdata { - color: #8292a2; - } - - &.operator, - &.punctuation { - color: var(--black2); - } - - &.namespace { - opacity: 0.7; - } - - &.property, - &.tag, - &.constant, - &.symbol, - &.deleted { - color: #f92672; - } - - &.boolean { - color: #ae81ff; - } - - &.selector, - &.attr-name, - &.char, - &.builtin, - &.inserted { - color: #a6e22e; - } - - &.entity, - &.url, - .language-css &.string, - .style &.string, - &.variable { - color: #f8f8f2; - } - - &.atrule, - &.attr-value, - &.class-name { - color: #e6db74; - } - - &.function { - color: var(--warning3); - } - - &.string { - color: var(--brand3); - } - - &.keyword { - color: var(--info3); - } - - &.number { - color: var(--purple3); - } - - &.regex, - &.important { - color: #fd971f; - } - - &.important, - &.bold { - font-weight: var(--font-weight-bold); - } - - &.italic { - font-style: italic; - } - - &.entity { - cursor: help; - } - } -} diff --git a/components/Article/Codebox/themes/_light.scss b/components/Article/Codebox/themes/_light.scss deleted file mode 100644 index 403e30dffabd6..0000000000000 --- a/components/Article/Codebox/themes/_light.scss +++ /dev/null @@ -1,76 +0,0 @@ -@mixin theme { - background: var(--black3); - color: black; - - :global .token { - &.comment, - &.prolog, - &.doctype, - &.cdata { - color: slategray; - } - - &.namespace { - opacity: 0.7; - } - - &.property, - &.tag, - &.boolean, - &.number, - &.constant, - &.symbol, - &.deleted { - color: #905; - } - - &.selector, - &.attr-name, - &.char, - &.builtin, - &.inserted { - color: #690; - } - - &.entity, - &.url { - background: hsla(0, 0%, 100%, 0.5); - color: #9a6e3a; - } - - &.atrule, - &.attr-value, - &.keyword { - color: #07a; - } - - &.function, - &.class-name { - color: #dd4a68; - } - - &.regex, - &.important, - &.variable { - color: #e90; - } - - &.important, - &.bold { - font-weight: var(--font-weight-vold); - } - &.italic { - font-style: italic; - } - - &.entity { - cursor: help; - } - - &.punctuation, - &.operator, - &.string { - background-color: var(--black3); - } - } -} diff --git a/hooks/usePrismJS.ts b/hooks/usePrismJS.ts deleted file mode 100644 index 2bdd4fa786f20..0000000000000 --- a/hooks/usePrismJS.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { highlightElement } from 'prismjs'; -import { createRef, useMemo } from 'react'; -import { loadLanguage } from '@/next.prism.mjs'; - -export const usePrismJS = (language: string) => { - const codeRef = createRef(); - - useMemo( - () => - loadLanguage(language).then( - () => codeRef.current && highlightElement(codeRef.current) - ), - [language, codeRef] - ); - - return codeRef; -}; diff --git a/next.prism.mjs b/next.prism.mjs deleted file mode 100644 index 027bf1719b94c..0000000000000 --- a/next.prism.mjs +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Dynamic import Prism.js language components on demand - * based on language prefixes - * - * @param {string} language Prism language to be loaded - */ -export const loadLanguage = async language => { - switch (language.replace('language-', '')) { - case 'bash': - case 'zsh': - await import('prismjs/components/prism-bash'); - break; - case 'jsx': - case 'tsx': - await import('prismjs/components/prism-jsx'); - break; - default: - break; - } -}; diff --git a/package-lock.json b/package-lock.json index b7db6b4f9790b..73cb62cf62ccb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "next-mdx-remote": "^4.4.1", "next-sitemap": "^4.2.2", "next-themes": "^0.2.1", - "prismjs": "~1.29.0", "react-icons": "~4.10.1", "react-intl": "~6.4.4", "rehype-autolink-headings": "~6.1.1", @@ -46,7 +45,6 @@ "@testing-library/react": "~14.0.0", "@testing-library/user-event": "~14.4.3", "@types/jest": "29.5.3", - "@types/prismjs": "^1.26.0", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", "@types/semver": "^7.5.0", @@ -7045,12 +7043,6 @@ "integrity": "sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==", "dev": true }, - "node_modules/@types/prismjs": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz", - "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==", - "dev": true - }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", @@ -20704,14 +20696,6 @@ "node": ">= 0.8" } }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/proc-log": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", diff --git a/package.json b/package.json index dce8fceeb0608..5e8276af35d54 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "next-mdx-remote": "^4.4.1", "next-sitemap": "^4.2.2", "next-themes": "^0.2.1", - "prismjs": "~1.29.0", "react-icons": "~4.10.1", "react-intl": "~6.4.4", "rehype-autolink-headings": "~6.1.1", @@ -86,7 +85,6 @@ "@testing-library/react": "~14.0.0", "@testing-library/user-event": "~14.4.3", "@types/jest": "29.5.3", - "@types/prismjs": "^1.26.0", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", "@types/semver": "^7.5.0",