diff --git a/docs/migration.md b/docs/migration.md index 60009de917b..bd3147b0cf0 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -222,6 +222,12 @@ consider additional positioning prop support on a case-by-case basis. #### @zendeskgarden/react-typography +- `CodeBlock`: The `language` set has been reduced from 32 to 13, for a + significant decrease in bundle size. If you encounter any essential languages + that are missing, please [create an + issue](https://github.com/zendeskgarden/react-components/issues). Garden will + evaluate incorporating any business-critical + [languages](https://prismjs.com/#supported-languages). - The following React component types have changed: - `Span.Icon`: `HTMLAttributes` -> `SVGAttributes` - `Span.StartIcon`: `HTMLAttributes` -> `SVGAttributes` diff --git a/package-lock.json b/package-lock.json index d62a35104fc..81b60c4a625 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10410,6 +10410,12 @@ "integrity": "sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==", "dev": true }, + "node_modules/@types/prismjs": { + "version": "1.26.4", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz", + "integrity": "sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -14302,6 +14308,15 @@ "node": ">=0.10.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/cmd-shim": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.1.tgz", @@ -31211,6 +31226,48 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "node_modules/netlify-cli/node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "extraneous": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/netlify-cli/node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "extraneous": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/netlify-cli/node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "extraneous": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/netlify-cli/node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "extraneous": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "node_modules/netlify-cli/node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -31250,6 +31307,12 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/netlify-cli/node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "extraneous": true + }, "node_modules/netlify-cli/node_modules/@types/node": { "version": "20.14.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", @@ -31265,12 +31328,34 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "node_modules/netlify-cli/node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "extraneous": true + }, + "node_modules/netlify-cli/node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "extraneous": true + }, "node_modules/netlify-cli/node_modules/@types/retry": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", "dev": true }, + "node_modules/netlify-cli/node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "extraneous": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/netlify-cli/node_modules/@types/yargs-parser": { "version": "20.2.1", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", @@ -31651,6 +31736,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/netlify-cli/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "extraneous": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/netlify-cli/node_modules/ajv-formats": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", @@ -34881,6 +34982,12 @@ "node": ">=8.6.0" } }, + "node_modules/netlify-cli/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "extraneous": true + }, "node_modules/netlify-cli/node_modules/fast-json-stringify": { "version": "5.15.1", "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.15.1.tgz", @@ -36641,6 +36748,15 @@ "ipx": "bin/ipx.mjs" } }, + "node_modules/netlify-cli/node_modules/ipx/node_modules/@netlify/blobs": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-6.5.0.tgz", + "integrity": "sha512-wRFlNnL/Qv3WNLZd3OT/YYqF1zb6iPSo8T31sl9ccL1ahBxW1fBqKgF4b1XL7Z+6mRIkatvcsVPkWBcO+oJMNA==", + "extraneous": true, + "engines": { + "node": "^14.16.0 || >=16.0.0" + } + }, "node_modules/netlify-cli/node_modules/ipx/node_modules/lru-cache": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", @@ -37217,6 +37333,12 @@ "fast-deep-equal": "^3.1.3" } }, + "node_modules/netlify-cli/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "extraneous": true + }, "node_modules/netlify-cli/node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", @@ -45936,11 +46058,25 @@ } }, "node_modules/prism-react-renderer": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", - "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz", + "integrity": "sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw==", + "license": "MIT", + "dependencies": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + }, "peerDependencies": { - "react": ">=0.14.9" + "react": ">=16.0.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/proc-log": { @@ -52886,7 +53022,8 @@ "dependencies": { "@zendeskgarden/container-scrollregion": "^1.0.9", "polished": "^4.3.1", - "prism-react-renderer": "^1.1.1", + "prism-react-renderer": "^2.3.1", + "prismjs": "^1.29.0", "prop-types": "^15.5.7" }, "devDependencies": { diff --git a/packages/typography/demo/stories/data.ts b/packages/typography/demo/stories/data.ts index 707cac0ebf1..e91dafe1fa8 100644 --- a/packages/typography/demo/stories/data.ts +++ b/packages/typography/demo/stories/data.ts @@ -94,6 +94,14 @@ button, +important new additions +to this document. `, + graphql: `query HeroNameAndFriends($episode: Episode) { + hero(episode: $episode) { + name + friends { + name + } + } +}`, javascript: ` Prism.languages.markup = { comment: //, @@ -177,6 +185,13 @@ Prism.languages.markup = { "updated_at": "2018-01-01T10:20:30Z" } ] +}`, + jsx: `function getGreeting(user) { + if (user) { + return

Hello, {formatName(user)}

; + } + + return

Hello, stranger

; }`, markdown: ` # Title 1 @@ -431,7 +446,20 @@ export const execute = async (args: IGitHubPagesArgs): Promise { - it('passes ref to underlying DOM element', () => { + it('passes ref to underlying DOM element', async () => { const ref = React.createRef(); const { container } = render(); - expect(container.getElementsByTagName('pre')[0]).toBe(ref.current); + await waitFor(() => { + expect(container.getElementsByTagName('pre')[0]).toBe(ref.current); + }); }); - it('renders the expected element', () => { + it('renders the expected element', async () => { const { container } = render(); - expect(container.firstChild!.nodeName).toBe('DIV'); - expect(container.firstChild!.firstChild!.nodeName).toBe('PRE'); + await waitFor(() => { + expect(container.firstChild!.nodeName).toBe('DIV'); + expect(container.firstChild!.firstChild!.nodeName).toBe('PRE'); + }); }); - it('applies container props', () => { + it('applies container props', async () => { const { container } = render(); - expect(container.firstChild).toHaveAttribute('id', 'test'); + await waitFor(() => { + expect(container.firstChild).toHaveAttribute('id', 'test'); + }); }); - it('renders the expected language', () => { - const { container } = render(); + it('renders the expected language', async () => { + const { container } = render(); - expect(container.getElementsByTagName('pre')[0]).toHaveClass('language-go'); + await waitFor(() => { + expect(container.getElementsByTagName('pre')[0]).toHaveClass('language-graphql'); + }); }); - it('renders the expected fallback for an invalid language', () => { + it('renders the expected fallback for an invalid language', async () => { const { container } = render(); - expect(container.getElementsByTagName('pre')[0]).toHaveClass('language-tsx'); + await waitFor(() => { + expect(container.getElementsByTagName('pre')[0]).toHaveClass('language-tsx'); + }); }); - it('renders as expected in light mode', () => { + it('renders as expected in light mode', async () => { const { container } = render(); - expect(container.getElementsByTagName('pre')[0]).toHaveStyleRule( - 'background-color', - PALETTE_V8.grey[100] - ); + await waitFor(() => { + expect(container.getElementsByTagName('pre')[0]).toHaveStyleRule( + 'background-color', + PALETTE.grey[100] + ); + }); }); - it('renders line numbers as expected', () => { + it('renders line numbers as expected', async () => { const { container } = render(); - expect(container.getElementsByTagName('code')[0]).toHaveStyleRule( - 'content', - 'counter(linenumber)', - { - modifier: '&::before' - } - ); + await waitFor(() => { + expect(container.getElementsByTagName('code')[0]).toHaveStyleRule( + 'content', + 'counter(linenumber)', + { + modifier: '&::before' + } + ); + }); }); - it('renders diff lines as expected', () => { + it('renders diff lines as expected', async () => { const code = `@@ -1,3 +1,9 @@ +added line -deleted line @@ -74,40 +88,65 @@ describe('CodeBlock', () => { const { container } = render({code}); const codeElements = container.getElementsByTagName('code'); - expect(codeElements[0]).toHaveStyleRule('background-color', rgba(PALETTE_V8.royal[400], 0.2)); - expect(codeElements[1]).toHaveStyleRule('background-color', rgba(PALETTE_V8.lime[400], 0.2)); - expect(codeElements[2]).toHaveStyleRule('background-color', rgba(PALETTE_V8.crimson[400], 0.2)); - expect(codeElements[3]).toHaveStyleRule('background-color', rgba(PALETTE_V8.lemon[400], 0.2)); - expect(codeElements[4]).not.toHaveStyleRule('background-color'); + await waitFor(() => { + expect(codeElements[0]).toHaveStyleRule( + 'background-color', + rgba(PALETTE.royal[600], DEFAULT_THEME.opacity[200]) + ); + expect(codeElements[1]).toHaveStyleRule( + 'background-color', + rgba(PALETTE.lime[500], DEFAULT_THEME.opacity[200]) + ); + expect(codeElements[2]).toHaveStyleRule( + 'background-color', + rgba(PALETTE.crimson[700], DEFAULT_THEME.opacity[200]) + ); + expect(codeElements[3]).toHaveStyleRule( + 'background-color', + rgba(PALETTE.lemon[300], DEFAULT_THEME.opacity[200]) + ); + expect(codeElements[4]).not.toHaveStyleRule('background-color'); + }); }); - it('highlights lines as expected', () => { + it('highlights lines as expected', async () => { const code = `one two`; const { container } = render({code}); const codeElements = container.getElementsByTagName('code'); - expect(codeElements[0]).toHaveStyleRule('background-color', rgba(PALETTE_V8.white, 0.1)); - expect(codeElements[1]).not.toHaveStyleRule('background-color'); + await waitFor(() => { + expect(codeElements[0]).toHaveStyleRule( + 'background-color', + rgba(PALETTE.white, DEFAULT_THEME.opacity[100]) + ); + expect(codeElements[1]).not.toHaveStyleRule('background-color'); + }); }); describe('size', () => { - it('renders small size', () => { + it('renders small size', async () => { const { container } = render(); - expect(container.getElementsByTagName('code')[0]).toHaveStyleRule('font-size', '11px'); + await waitFor(() => { + expect(container.getElementsByTagName('code')[0]).toHaveStyleRule('font-size', '11px'); + }); }); - it('renders medium size', () => { + it('renders medium size', async () => { const { container } = render(); - expect(container.getElementsByTagName('code')[0]).toHaveStyleRule('font-size', '13px'); + await waitFor(() => { + expect(container.getElementsByTagName('code')[0]).toHaveStyleRule('font-size', '13px'); + }); }); - it('renders large size', () => { + it('renders large size', async () => { const { container } = render(); - expect(container.getElementsByTagName('code')[0]).toHaveStyleRule('font-size', '17px'); + await waitFor(() => { + expect(container.getElementsByTagName('code')[0]).toHaveStyleRule('font-size', '17px'); + }); }); }); }); diff --git a/packages/typography/src/elements/CodeBlock.tsx b/packages/typography/src/elements/CodeBlock.tsx index 810c1bfc07e..9c2501e42c1 100644 --- a/packages/typography/src/elements/CodeBlock.tsx +++ b/packages/typography/src/elements/CodeBlock.tsx @@ -5,9 +5,10 @@ * found at http://www.apache.org/licenses/LICENSE-2.0. */ -import React, { useMemo, useRef } from 'react'; -import Highlight, { Language, Prism } from 'prism-react-renderer'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Highlight, Language, Prism } from 'prism-react-renderer'; import { useScrollRegion } from '@zendeskgarden/container-scrollregion'; +import { ThemeProvider, useWindow } from '@zendeskgarden/react-theming'; import { Diff, ICodeBlockProps, LANGUAGES } from '../types'; import { StyledCodeBlock, @@ -28,13 +29,52 @@ interface IToken { */ export const CodeBlock = React.forwardRef( ( - { children, containerProps, highlightLines, isLight, isNumbered, language, size, ...other }, + { + children, + containerProps, + highlightLines, + isLight, + isNumbered, + language = 'tsx', + size = 'medium', + ...other + }, ref ) => { const containerRef = useRef(null); const code = (Array.isArray(children) ? children[0] : children) as string; const dependency = useMemo(() => [size, children], [size, children]); const containerTabIndex = useScrollRegion({ containerRef, dependency }); + const [isPrismImported, setIsPrismImported] = useState(false); + const win = useWindow(); + + const importPrism = useCallback(async () => { + // TODO remove `importPrism` if/when `prismJS` releases v2 ESM + // https://github.com/orgs/PrismJS/discussions/3531 + if (win && !isPrismImported) { + (win as any).Prism = Prism; + + try { + await import('prismjs/components/prism-bash' as any); + await import('prismjs/components/prism-diff' as any); + await import('prismjs/components/prism-json' as any); + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } finally { + // Allows code block to render, whether or not the language can be + // successfully tokenized and highlighted + setIsPrismImported(true); + } + } + }, [win, isPrismImported]); + + useEffect(() => { + // Import languages missing from the vendored `Prism` using a variant of + // https://github.com/FormidableLabs/prism-react-renderer?tab=readme-ov-file#custom-language-support + // that is compatible with both React and Jest + importPrism(); + }, [importPrism]); const getDiff = (line: IToken[]) => { let retVal: Diff | undefined; @@ -59,47 +99,54 @@ export const CodeBlock = React.forwardRef( }; return ( - - - {({ className, tokens, getLineProps, getTokenProps }) => ( - - {tokens.map((line, index) => ( - - {line.map((token, tokenKey) => ( - + {({ className, tokens, getLineProps, getTokenProps }) => ( + ({ + ...parentTheme, + colors: { ...parentTheme.colors, base: isLight ? 'light' : 'dark' } + })} + > + + {tokens.map((line, index) => ( + - {token.empty ? '\n' : token.content} - + {line.map((token, tokenKey) => ( + + {token.empty ? '\n' : token.content} + + ))} + ))} - - ))} - - )} - - + + + )} + + + ) ); } ); CodeBlock.displayName = 'CodeBlock'; - -CodeBlock.defaultProps = { - language: 'tsx', - size: 'medium' -}; diff --git a/packages/typography/src/styled/StyledCodeBlock.spec.tsx b/packages/typography/src/styled/StyledCodeBlock.spec.tsx index 65caf975c5d..a183aa7c770 100644 --- a/packages/typography/src/styled/StyledCodeBlock.spec.tsx +++ b/packages/typography/src/styled/StyledCodeBlock.spec.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { render, renderRtl } from 'garden-test-utils'; -import { PALETTE_V8 } from '@zendeskgarden/react-theming'; +import { PALETTE } from '@zendeskgarden/react-theming'; import { StyledCodeBlock } from './StyledCodeBlock'; describe('StyledCodeBlock', () => { @@ -24,8 +24,8 @@ describe('StyledCodeBlock', () => { }); it('renders as expected in light mode', () => { - const { container } = render(); + const { container } = render(); - expect(container.firstChild).toHaveStyleRule('background-color', PALETTE_V8.grey[100]); + expect(container.firstChild).toHaveStyleRule('background-color', PALETTE.grey[100]); }); }); diff --git a/packages/typography/src/styled/StyledCodeBlock.ts b/packages/typography/src/styled/StyledCodeBlock.ts index 8d3a4c8f952..37e5510b99c 100644 --- a/packages/typography/src/styled/StyledCodeBlock.ts +++ b/packages/typography/src/styled/StyledCodeBlock.ts @@ -6,15 +6,16 @@ */ import styled, { ThemeProps, DefaultTheme, css } from 'styled-components'; -import { DEFAULT_THEME, getColorV8, retrieveComponentStyles } from '@zendeskgarden/react-theming'; +import { DEFAULT_THEME, getColor, retrieveComponentStyles } from '@zendeskgarden/react-theming'; const COMPONENT_ID = 'typography.codeblock'; -const colorStyles = (props: IStyledCodeBlockProps & ThemeProps) => { - const backgroundColor = getColorV8('neutralHue', props.isLight ? 100 : 1000, props.theme); - const foregroundColor = props.isLight - ? getColorV8('foreground', 600 /* default shade */, props.theme) - : getColorV8('neutralHue', 300, props.theme); +const colorStyles = ({ theme }: ThemeProps) => { + const backgroundColor = getColor({ + theme, + variable: theme.colors.base === 'light' ? 'background.subtle' : 'background.default' + }); + const foregroundColor = getColor({ theme, variable: 'foreground.default' }); return css` background-color: ${backgroundColor}; @@ -22,14 +23,10 @@ const colorStyles = (props: IStyledCodeBlockProps & ThemeProps) => `; }; -export interface IStyledCodeBlockProps { - isLight?: boolean; -} - export const StyledCodeBlock = styled.pre.attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION -})` +})` display: table; margin: 0; padding: ${props => props.theme.space.base * 3}px; @@ -39,7 +36,7 @@ export const StyledCodeBlock = styled.pre.attrs({ white-space: pre; counter-reset: linenumber; - ${props => colorStyles(props)}; + ${colorStyles}; ${props => retrieveComponentStyles(COMPONENT_ID, props)}; `; diff --git a/packages/typography/src/styled/StyledCodeBlockLine.spec.tsx b/packages/typography/src/styled/StyledCodeBlockLine.spec.tsx index b71bc1fef96..b6ca6bbbae9 100644 --- a/packages/typography/src/styled/StyledCodeBlockLine.spec.tsx +++ b/packages/typography/src/styled/StyledCodeBlockLine.spec.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { rgba } from 'polished'; -import { render, renderRtl } from 'garden-test-utils'; -import { PALETTE_V8 } from '@zendeskgarden/react-theming'; +import { render, renderDark, renderRtl } from 'garden-test-utils'; +import { DEFAULT_THEME, PALETTE } from '@zendeskgarden/react-theming'; import { StyledCodeBlockLine } from './StyledCodeBlockLine'; describe('StyledCodeBlockLine', () => { it('renders the expected element', () => { - const { container } = render(); + const { container } = renderDark(); expect(container.firstChild!.nodeName).toBe('CODE'); }); @@ -26,21 +26,27 @@ describe('StyledCodeBlockLine', () => { describe('highlights', () => { it('renders highlight as expected', () => { - const { container } = render(); + const { container } = renderDark(); - expect(container.firstChild).toHaveStyleRule('background-color', rgba(PALETTE_V8.white, 0.1)); + expect(container.firstChild).toHaveStyleRule( + 'background-color', + rgba(PALETTE.white, DEFAULT_THEME.opacity[100]) + ); }); it('renders as expected in light mode', () => { - const { container } = render(); + const { container } = render(); - expect(container.firstChild).toHaveStyleRule('background-color', rgba(PALETTE_V8.black, 0.1)); + expect(container.firstChild).toHaveStyleRule( + 'background-color', + rgba(PALETTE.black, DEFAULT_THEME.opacity[100]) + ); }); }); describe('line numbers', () => { it('renders line numbers as expected', () => { - const { container } = render(); + const { container } = renderDark(); expect(container.firstChild).toHaveStyleRule('display', 'table-cell', { modifier: '&::before' @@ -48,9 +54,9 @@ describe('StyledCodeBlockLine', () => { }); it('renders as expected in light mode', () => { - const { container } = render(); + const { container } = render(); - expect(container.firstChild).toHaveStyleRule('color', PALETTE_V8.grey[600], { + expect(container.firstChild).toHaveStyleRule('color', PALETTE.grey[700], { modifier: '&::before' }); }); @@ -82,7 +88,7 @@ describe('StyledCodeBlockLine', () => { expect(container.firstChild).toHaveStyleRule( 'background-color', - rgba(PALETTE_V8.lime[400], 0.2) + rgba(PALETTE.lime[500], DEFAULT_THEME.opacity[200]) ); }); @@ -91,7 +97,7 @@ describe('StyledCodeBlockLine', () => { expect(container.firstChild).toHaveStyleRule( 'background-color', - rgba(PALETTE_V8.crimson[400], 0.2) + rgba(PALETTE.crimson[700], DEFAULT_THEME.opacity[200]) ); }); @@ -100,7 +106,7 @@ describe('StyledCodeBlockLine', () => { expect(container.firstChild).toHaveStyleRule( 'background-color', - rgba(PALETTE_V8.lemon[400], 0.2) + rgba(PALETTE.lemon[300], DEFAULT_THEME.opacity[200]) ); }); @@ -109,7 +115,7 @@ describe('StyledCodeBlockLine', () => { expect(container.firstChild).toHaveStyleRule( 'background-color', - rgba(PALETTE_V8.royal[400], 0.2) + rgba(PALETTE.royal[600], DEFAULT_THEME.opacity[200]) ); }); }); diff --git a/packages/typography/src/styled/StyledCodeBlockLine.ts b/packages/typography/src/styled/StyledCodeBlockLine.ts index bd3da3c744e..cf89721eb52 100644 --- a/packages/typography/src/styled/StyledCodeBlockLine.ts +++ b/packages/typography/src/styled/StyledCodeBlockLine.ts @@ -7,41 +7,43 @@ import styled, { css, ThemeProps, DefaultTheme } from 'styled-components'; import { Language } from 'prism-react-renderer'; -import { DEFAULT_THEME, retrieveComponentStyles, getColorV8 } from '@zendeskgarden/react-theming'; +import { DEFAULT_THEME, retrieveComponentStyles, getColor } from '@zendeskgarden/react-theming'; import { Diff, Size } from '../types'; import { StyledFont, THEME_SIZES } from './StyledFont'; const COMPONENT_ID = 'typography.codeblock_code'; -const colorStyles = (props: IStyledCodeBlockLineProps & ThemeProps) => { - let backgroundColor; - - if (props.diff) { - let hue; - - switch (props.diff) { - case 'hunk': - hue = 'royal'; - break; - - case 'add': - hue = 'lime'; - break; - - case 'delete': - hue = 'crimson'; - break; - - case 'change': - hue = 'lemon'; - break; - } +export interface IStyledCodeBlockLineProps { + language?: Language; + isHighlighted?: boolean; + isNumbered?: boolean; + diff?: Diff; + size?: Size; +} - backgroundColor = getColorV8(hue, 400, props.theme, 0.2); - } else if (props.isHighlighted) { - const hue = props.isLight ? props.theme.palette.black : props.theme.palette.white; +const colorStyles = ({ + theme, + diff, + isHighlighted +}: IStyledCodeBlockLineProps & ThemeProps) => { + let backgroundColor; - backgroundColor = getColorV8(hue, 600, props.theme, 0.1); + if (diff) { + const options = { + hunk: { hue: 'royal', shade: 600 }, + add: { hue: 'lime', shade: 500 }, + delete: { hue: 'crimson', shade: 700 }, + change: { hue: 'lemon', shade: 300 } + }[diff]; + + backgroundColor = getColor({ theme, ...options, transparency: theme.opacity[200] }); + } else if (isHighlighted) { + backgroundColor = getColor({ + theme, + dark: { hue: 'white' }, + light: { hue: 'black' }, + transparency: theme.opacity[100] + }); } return css` @@ -49,18 +51,22 @@ const colorStyles = (props: IStyledCodeBlockLineProps & ThemeProps `; }; -const lineNumberStyles = (props: IStyledCodeBlockLineProps & ThemeProps) => { - const color = getColorV8('neutralHue', props.isLight ? 600 : 500, props.theme); +const lineNumberStyles = ({ + theme, + language, + size +}: IStyledCodeBlockLineProps & ThemeProps) => { + const color = getColor({ theme, variable: 'foreground.subtle' }); let padding; - if (props.language && props.language === 'diff') { + if (language && language === 'diff') { padding = 0; - } else if (props.size === 'small') { - padding = props.theme.space.base * 4; - } else if (props.size === 'large') { - padding = props.theme.space.base * 7; + } else if (size === 'small') { + padding = theme.space.base * 4; + } else if (size === 'large') { + padding = theme.space.base * 7; } else { - padding = props.theme.space.base * 6; + padding = theme.space.base * 6; } return ` @@ -76,16 +82,7 @@ const lineNumberStyles = (props: IStyledCodeBlockLineProps & ThemeProps props.theme.lineHeights[THEME_SIZES[props.size!]]}; /* [1] */ direction: ltr; - ${props => colorStyles(props)}; + ${colorStyles}; ${props => props.isNumbered && lineNumberStyles(props)}; diff --git a/packages/typography/src/styled/StyledCodeBlockToken.ts b/packages/typography/src/styled/StyledCodeBlockToken.ts index 7196ad799fc..681aedc20c6 100644 --- a/packages/typography/src/styled/StyledCodeBlockToken.ts +++ b/packages/typography/src/styled/StyledCodeBlockToken.ts @@ -6,34 +6,65 @@ */ import styled, { css, DefaultTheme, ThemeProps } from 'styled-components'; -import { DEFAULT_THEME, retrieveComponentStyles } from '@zendeskgarden/react-theming'; +import { DEFAULT_THEME, getColor, retrieveComponentStyles } from '@zendeskgarden/react-theming'; import { StyledCodeBlock } from './StyledCodeBlock'; const COMPONENT_ID = 'typography.codeblock_token'; -/** +/* * 1. Isolate the tag name. * 2. Target opening/closing `<`, `/>`. * 3. Override string tokenization of `=` after an attribute name. */ -const colorStyles = (props: IStyledCodeBlockTokenProps & ThemeProps) => { - const palette = props.theme.palette; +const colorStyles = ({ theme }: ThemeProps) => { const colors = { - boolean: props.isLight ? palette.royal[600] : palette.azure[400], - builtin: palette.teal[400], - comment: props.isLight ? palette.lime[600] : palette.mint[400], - constant: props.isLight ? palette.azure[400] : palette.blue[500], - coord: props.isLight ? palette.purple[600] : palette.blue[200], - deleted: props.isLight ? palette.red[700] : palette.red[200], - diff: props.isLight ? palette.yellow[800] : palette.yellow[200], - function: props.isLight ? palette.orange['M600' as any] : palette.yellow[300], - inserted: props.isLight ? palette.green[700] : palette.green[200], - keyword: palette.fuschia['M400' as any], - name: props.isLight ? palette.crimson[400] : palette.blue[300], - number: props.isLight ? palette.green[600] : palette.green[300], - punctuation: props.isLight ? palette.red[800] : palette.grey[600], - regex: palette.red[400], - value: props.isLight ? palette.red[700] : palette.crimson['M400' as any] + boolean: getColor({ + theme, + dark: { hue: 'azure', shade: 600 }, + light: { hue: 'royal', shade: 700 } + }), + builtin: getColor({ theme, hue: 'teal', dark: { shade: 600 }, light: { shade: 700 } }), + comment: getColor({ + theme, + dark: { hue: 'mint', shade: 600 }, + light: { hue: 'lime', shade: 700 } + }), + constant: getColor({ + theme, + dark: { hue: 'blue', shade: 600 }, + light: { hue: 'azure', shade: 700 } + }), + coord: getColor({ + theme, + dark: { hue: 'blue', shade: 300 }, + light: { hue: 'purple', shade: 800 } + }), + deleted: getColor({ theme, hue: 'red', dark: { shade: 300 }, light: { shade: 800 } }), + diff: getColor({ theme, hue: 'yellow', dark: { shade: 100 }, light: { shade: 800 } }), + function: getColor({ + theme, + dark: { hue: 'yellow', shade: 300 }, + light: { hue: 'orange', shade: 700 } + }), + inserted: getColor({ theme, hue: 'green', dark: { shade: 300 }, light: { shade: 800 } }), + keyword: getColor({ theme, hue: 'fuschia', dark: { shade: 600 }, light: { shade: 700 } }), + name: getColor({ + theme, + dark: { hue: 'blue', shade: 400 }, + light: { hue: 'crimson', shade: 700 } + }), + number: getColor({ theme, hue: 'green', dark: { shade: 400 }, light: { shade: 700 } }), + punctuation: getColor({ + theme, + dark: { hue: 'grey', shade: 700 }, + light: { hue: 'red', shade: 900 } + }), + regex: getColor({ theme, hue: 'red', shade: 600 }), + value: getColor({ + theme, + dark: { hue: 'crimson', shade: 600 }, + light: { hue: 'red', shade: 800 } + }) }; return css` @@ -141,14 +172,10 @@ const colorStyles = (props: IStyledCodeBlockTokenProps & ThemeProps` +})` display: inline-block; &.bold:not(.diff) { @@ -168,7 +195,7 @@ export const StyledCodeBlockToken = styled.span.attrs({ text-align: center; } - ${props => colorStyles(props)}; + ${colorStyles}; ${props => retrieveComponentStyles(COMPONENT_ID, props)}; `; diff --git a/packages/typography/src/types/index.ts b/packages/typography/src/types/index.ts index 11c6b2088f7..6dc325ad15c 100644 --- a/packages/typography/src/types/index.ts +++ b/packages/typography/src/types/index.ts @@ -26,37 +26,18 @@ export const TYPE_UNORDERED_LIST = ['circle', 'disc', 'square'] as const; /* until https://github.com/FormidableLabs/prism-react-renderer/pull/127 is available */ export const LANGUAGES = [ - 'markup', 'bash', - 'clike', - 'c', - 'cpp', 'css', - 'javascript', - 'jsx', - 'coffeescript', - 'actionscript', - 'css-extr', 'diff', - 'git', - 'go', 'graphql', - 'handlebars', + 'javascript', 'json', - 'less', - 'makefile', + 'jsx', 'markdown', - 'objectivec', - 'ocaml', + 'markup', 'python', - 'reason', - 'sass', - 'scss', - 'sql', - 'stylus', - 'tsx', 'typescript', - 'wasm', + 'tsx', 'yaml' ] as const;