Skip to content

Commit

Permalink
feat: create Code Block Package (#2641)
Browse files Browse the repository at this point in the history
Co-authored-by: Shadi <TheSisb@users.noreply.github.com>
Co-authored-by: gloriliale <77117846+gloriliale@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 6, 2022
1 parent e9312c1 commit 280c872
Show file tree
Hide file tree
Showing 43 changed files with 2,055 additions and 12 deletions.
6 changes: 6 additions & 0 deletions .changeset/great-tools-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@twilio-paste/code-block': major
'@twilio-paste/core': minor
---

[Code Block] create a new CodeBlock component, which allows you to display readable blocks of code 🎉
6 changes: 6 additions & 0 deletions .changeset/lucky-balloons-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@twilio-paste/core': minor
'@twilio-paste/syntax-highlighter-library': major
---

[Syntax Highlighter] Create a new library, @twilio-paste/syntax-highlighter-library. Supports: javascript, jsx, csharp, php, ruby, python, java, json, c, bash, shell, go, and groovy.
6 changes: 6 additions & 0 deletions .changeset/tall-falcons-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@twilio-paste/button': patch
'@twilio-paste/core': patch
---

[Button] support <Button as='a' variant='inverse'> and add the `target` prop
2 changes: 2 additions & 0 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"/packages/paste-core/components/chat-log",
"/packages/paste-core/components/checkbox",
"/packages/paste-libraries/clipboard-copy",
"/packages/paste-core/components/code-block",
"/packages/paste-color-contrast-utils",
"/packages/paste-core/components/combobox",
"/packages/paste-core/primitives/combobox",
Expand Down Expand Up @@ -66,6 +67,7 @@
"/packages/paste-style-props",
"/packages/paste-libraries/styling",
"/packages/paste-core/components/switch",
"/packages/paste-libraries/syntax-highlighter",
"/packages/paste-core/components/table",
"/packages/paste-core/components/tabs",
"/packages/paste-core/primitives/tabs",
Expand Down
9 changes: 9 additions & 0 deletions @types/react-syntax-highlighter/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module 'react-syntax-highlighter/dist/esm/languages/prism/shell-session' {
const language: any;
export default language;
}

declare module 'react-syntax-highlighter/dist/esm/styles/prism/night-owl' {
const style: { [key: string]: React.CSSProperties };
export default style;
}
1 change: 1 addition & 0 deletions cypress/integration/sitemap-vrt/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const SITEMAP = [
'/components/breadcrumb/',
'/components/badge/',
'/components/callout/',
'/components/code-block/',
'/components/card/',
'/components/chat-log/',
'/components/checkbox/',
Expand Down
6 changes: 6 additions & 0 deletions packages/paste-codemods/tools/.cache/mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
"Checkbox": "@twilio-paste/core/checkbox",
"CheckboxDisclaimer": "@twilio-paste/core/checkbox",
"CheckboxGroup": "@twilio-paste/core/checkbox",
"CodeBlock": "@twilio-paste/core/code-block",
"CodeBlockHeader": "@twilio-paste/core/code-block",
"CodeBlockTab": "@twilio-paste/core/code-block",
"CodeBlockTabList": "@twilio-paste/core/code-block",
"CodeBlockTabPanel": "@twilio-paste/core/code-block",
"CodeBlockWrapper": "@twilio-paste/core/code-block",
"Combobox": "@twilio-paste/core/combobox",
"ComboboxInputWrapper": "@twilio-paste/core/combobox",
"ComboboxListbox": "@twilio-paste/core/combobox",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,6 @@ describe('Button', () => {
</Button>
)
).toThrow();
expect(() =>
render(
<Button as="a" href="#" variant="inverse">
Go to Paste
</Button>
)
).toThrow();
spy.mockRestore();
});

Expand Down
4 changes: 2 additions & 2 deletions packages/paste-core/components/button/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ const handlePropValidation = ({
if (variant === 'link' || variant === 'inverse_link') {
throw new Error(`[Paste: Button] Using Button component as an Anchor. Use the Paste Anchor component instead.`);
}
if (variant !== 'primary' && variant !== 'secondary' && variant !== 'reset') {
throw new Error(`[Paste: Button] <Button as="a"> only works with the following variants: primary or secondary.`);
if (variant !== 'primary' && variant !== 'secondary' && variant !== 'reset' && variant !== 'inverse') {
throw new Error(`[Paste: Button] <Button as="a"> only works with the following variants: primary and secondary.`);
}
if (disabled || loading) {
throw new Error(`[Paste: Button] <Button as="a"> cannot be disabled or loading.`);
Expand Down
2 changes: 2 additions & 0 deletions packages/paste-core/components/button/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface DirectButtonProps extends React.ButtonHTMLAttributes<HTMLButton
buttonState: ButtonStates;
variant: ButtonVariants;
pressed?: boolean;
target?: string;
}

export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, Pick<BoxProps, 'element'> {
Expand All @@ -61,4 +62,5 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
children: React.ReactNode;
i18nExternalLinkLabel?: string;
pressed?: boolean;
target?: string;
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import * as React from 'react';
import {CustomizationProvider} from '@twilio-paste/customization';
import {render, screen} from '@testing-library/react';

import {CodeBlock, CodeBlockWrapper, CodeBlockHeader, CodeBlockTabList, CodeBlockTab, CodeBlockTabPanel} from '../src';

const jsCode = `(num) => num + 1`;

const CustomizationWrapper: React.FC = ({children}) => (
<CustomizationProvider
baseTheme="default"
theme={TestTheme}
elements={{
CODE_BLOCK_CONTENT: {width: 'size50'},
CODE_BLOCK_COPY_BUTTON: {backgroundColor: 'colorBackgroundErrorWeakest'},
CODE_BLOCK_EXTERNAL_LINK: {backgroundColor: 'colorBackgroundErrorWeakest'},
CODE_BLOCK_HEADER: {borderTopRightRadius: 'borderRadius30'},
CODE_BLOCK_TAB_LIST: {columnGap: 'space0'},
CODE_BLOCK_TAB_PANEL: {borderBottomRightRadius: 'borderRadius30'},
CODE_BLOCK_TAB: {borderRadius: 'borderRadius0'},
CODE_BLOCK_WRAPPER: {width: 'size50'},
CODE_BLOCK: {width: 'size50'},
}}
>
{children}
</CustomizationProvider>
);

const CustomizationMyWrapper: React.FC = ({children}) => (
<CustomizationProvider
baseTheme="default"
theme={TestTheme}
elements={{
MY_CODE_BLOCK_CONTENT: {width: 'size50'},
MY_CODE_BLOCK_COPY_BUTTON: {backgroundColor: 'colorBackgroundErrorWeakest'},
MY_CODE_BLOCK_EXTERNAL_LINK: {backgroundColor: 'colorBackgroundErrorWeakest'},
MY_CODE_BLOCK_HEADER: {borderTopRightRadius: 'borderRadius30'},
MY_CODE_BLOCK_TAB_LIST: {columnGap: 'space0'},
MY_CODE_BLOCK_TAB_PANEL: {borderBottomRightRadius: 'borderRadius30'},
MY_CODE_BLOCK_TAB: {borderRadius: 'borderRadius0'},
MY_CODE_BLOCK_WRAPPER: {width: 'size50'},
MY_CODE_BLOCK: {width: 'size50'},
}}
>
{children}
</CustomizationProvider>
);

describe('Customization', () => {
describe('CodeBlock', () => {
it('should set a default element data attribute', () => {
render(
<CodeBlockWrapper>
<CodeBlockHeader>My code block</CodeBlockHeader>
<CodeBlockTabList>
<CodeBlockTab>JavaScript</CodeBlockTab>
</CodeBlockTabList>
<CodeBlockTabPanel>
<CodeBlock language="javascript" code={jsCode} data-testid="code-block" externalLink="www.google.com" />
</CodeBlockTabPanel>
</CodeBlockWrapper>,
{
wrapper: CustomizationWrapper,
}
);

const codeBlock = screen.getByTestId('code-block');
const content = codeBlock.querySelector('pre')?.parentElement;
const heading = screen.getByRole('heading', {name: 'My code block'});
const wrapper = heading.parentElement;
const tabList = screen.getByRole('tablist');
const tab = screen.getByRole('tab', {name: 'JavaScript'});
const tabPanel = codeBlock.parentElement;
const copyButton = screen.getByRole('button', {name: 'Copy code block'});
const externalLink = screen.getByRole('link', {name: 'Open code block in new page'});

expect(wrapper?.getAttribute('data-paste-element')).toBe('CODE_BLOCK_WRAPPER');
expect(content?.getAttribute('data-paste-element')).toBe('CODE_BLOCK_CONTENT');
expect(tabList.getAttribute('data-paste-element')).toBe('CODE_BLOCK_TAB_LIST');
expect(tab.getAttribute('data-paste-element')).toBe('CODE_BLOCK_TAB');
expect(tabPanel?.getAttribute('data-paste-element')).toBe('CODE_BLOCK_TAB_PANEL');
expect(codeBlock.getAttribute('data-paste-element')).toBe('CODE_BLOCK');
expect(heading.getAttribute('data-paste-element')).toBe('CODE_BLOCK_HEADER');
expect(copyButton.getAttribute('data-paste-element')).toBe('CODE_BLOCK_COPY_BUTTON');
expect(externalLink.getAttribute('data-paste-element')).toBe('CODE_BLOCK_EXTERNAL_LINK');
});

it('should set a custom element data attribute', () => {
render(
<CodeBlockWrapper data-testid="wrapper" element="MY_CODE_BLOCK_WRAPPER">
<CodeBlockHeader element="MY_CODE_BLOCK_HEADER">My code block</CodeBlockHeader>
<CodeBlockTabList element="MY_CODE_BLOCK_TAB_LIST">
<CodeBlockTab element="MY_CODE_BLOCK_TAB">JavaScript</CodeBlockTab>
</CodeBlockTabList>
<CodeBlockTabPanel element="MY_CODE_BLOCK_TAB_PANEL">
<CodeBlock
language="javascript"
code={jsCode}
data-testid="code-block"
externalLink="www.google.com"
element="MY_CODE_BLOCK"
/>
</CodeBlockTabPanel>
</CodeBlockWrapper>,
{
wrapper: CustomizationMyWrapper,
}
);

const codeBlock = screen.getByTestId('code-block');
const content = codeBlock.querySelector('pre')?.parentElement;
const heading = screen.getByRole('heading', {name: 'My code block'});
const wrapper = heading.parentElement;
const tabList = screen.getByRole('tablist');
const tab = screen.getByRole('tab', {name: 'JavaScript'});
const tabPanel = codeBlock.parentElement;
const copyButton = screen.getByRole('button', {name: 'Copy code block'});
const externalLink = screen.getByRole('link', {name: 'Open code block in new page'});

expect(wrapper?.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_WRAPPER');
expect(content?.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_CONTENT');
expect(tabList.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_TAB_LIST');
expect(tab.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_TAB');
expect(tabPanel?.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_TAB_PANEL');
expect(codeBlock.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK');
expect(heading.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_HEADER');
expect(copyButton.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_COPY_BUTTON');
expect(externalLink.getAttribute('data-paste-element')).toBe('MY_CODE_BLOCK_EXTERNAL_LINK');
});

it('should add custom styles to the component', () => {
render(
<CodeBlockWrapper>
<CodeBlockHeader>My code block</CodeBlockHeader>
<CodeBlockTabList>
<CodeBlockTab>JavaScript</CodeBlockTab>
</CodeBlockTabList>
<CodeBlockTabPanel>
<CodeBlock language="javascript" code={jsCode} data-testid="code-block" externalLink="www.google.com" />
</CodeBlockTabPanel>
</CodeBlockWrapper>,
{
wrapper: CustomizationWrapper,
}
);

const codeBlock = screen.getByTestId('code-block');
const content = codeBlock.querySelector('pre')?.parentElement;
const heading = screen.getByRole('heading', {name: 'My code block'});
const wrapper = heading.parentElement;
const tabList = screen.getByRole('tablist');
const tab = screen.getByRole('tab', {name: 'JavaScript'});
const tabPanel = codeBlock.parentElement;
const copyButton = screen.getByRole('button', {name: 'Copy code block'});
const externalLink = screen.getByRole('link', {name: 'Open code block in new page'});

expect(codeBlock).toHaveStyleRule('width', '31.5rem');
expect(content).toHaveStyleRule('width', '31.5rem');
expect(wrapper).toHaveStyleRule('width', '31.5rem');
expect(heading).toHaveStyleRule('border-top-right-radius', '8px');
expect(tabList).toHaveStyleRule('column-gap', '0');
expect(tab).toHaveStyleRule('border-radius', '0');
expect(tabPanel).toHaveStyleRule('border-bottom-right-radius', '8px');
expect(copyButton).toHaveStyleRule('background-color', 'rgb(254, 236, 236)');
expect(externalLink).toHaveStyleRule('background-color', 'rgb(254, 236, 236)');
});

it('should set custom styles with custom element names', () => {
render(
<CodeBlockWrapper data-testid="wrapper" element="MY_CODE_BLOCK_WRAPPER">
<CodeBlockHeader element="MY_CODE_BLOCK_HEADER">My code block</CodeBlockHeader>
<CodeBlockTabList element="MY_CODE_BLOCK_TAB_LIST">
<CodeBlockTab element="MY_CODE_BLOCK_TAB">JavaScript</CodeBlockTab>
</CodeBlockTabList>
<CodeBlockTabPanel element="MY_CODE_BLOCK_TAB_PANEL">
<CodeBlock
language="javascript"
code={jsCode}
data-testid="code-block"
externalLink="www.google.com"
element="MY_CODE_BLOCK"
/>
</CodeBlockTabPanel>
</CodeBlockWrapper>,
{
wrapper: CustomizationMyWrapper,
}
);

const codeBlock = screen.getByTestId('code-block');
const content = codeBlock.querySelector('pre')?.parentElement;
const heading = screen.getByRole('heading', {name: 'My code block'});
const wrapper = heading.parentElement;
const tabList = screen.getByRole('tablist');
const tab = screen.getByRole('tab', {name: 'JavaScript'});
const tabPanel = codeBlock.parentElement;
const copyButton = screen.getByRole('button', {name: 'Copy code block'});
const externalLink = screen.getByRole('link', {name: 'Open code block in new page'});

expect(codeBlock).toHaveStyleRule('width', '31.5rem');
expect(content).toHaveStyleRule('width', '31.5rem');
expect(wrapper).toHaveStyleRule('width', '31.5rem');
expect(heading).toHaveStyleRule('border-top-right-radius', '8px');
expect(tabList).toHaveStyleRule('column-gap', '0');
expect(tab).toHaveStyleRule('border-radius', '0');
expect(tabPanel).toHaveStyleRule('border-bottom-right-radius', '8px');
expect(copyButton).toHaveStyleRule('background-color', 'rgb(254, 236, 236)');
expect(externalLink).toHaveStyleRule('background-color', 'rgb(254, 236, 236)');
});
});
});
Loading

0 comments on commit 280c872

Please sign in to comment.