Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ability to copy EuiCodeBlock and adds snippet prop to docs #1556

Merged
merged 11 commits into from
Feb 14, 2019
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [`master`](https://github.com/elastic/eui/tree/master)

No public interface changes since `7.0.0`.
- Added `isCopyable` prop to `EuiCodeBlock` ([#1556](https://github.com/elastic/eui/pull/1556))
- Added optional `Snippet` tab to docs and renamed demo tabs ([#1556](https://github.com/elastic/eui/pull/1556))

## [`7.0.0`](https://github.com/elastic/eui/tree/v7.0.0)

Expand Down
55 changes: 47 additions & 8 deletions src-docs/src/components/guide_section/guide_section.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,33 @@ export class GuideSection extends Component {
super(props);

this.componentNames = Object.keys(props.props);
const hasSnippet = 'snippet' in props;

this.tabs = [{
name: 'Demo',
name: 'demo',
displayName: 'Demo',
}, {
name: 'JavaScript',
name: 'javascript',
displayName: 'Demo JS',
isCode: true,
}, {
name: 'HTML',
name: 'html',
displayName: 'Demo HTML',
isCode: true,
}];

if (hasSnippet) {
this.tabs.push({
name: 'snippet',
displayName: 'Snippet',
isCode: true,
});
}

if (this.componentNames.length) {
this.tabs.push({
name: 'Props',
name: 'props',
displayName: 'Props',
});
}

Expand All @@ -121,7 +134,7 @@ export class GuideSection extends Component {
isSelected={tab === this.state.selectedTab}
key={tab.name}
>
{tab.name}
{tab.displayName}
</EuiTab>
));
}
Expand All @@ -138,6 +151,23 @@ export class GuideSection extends Component {
];
}

renderSnippet() {
const { snippet } = this.props;

if (!snippet) {
return;
}

return [
<Fragment key="snippet">
<EuiSpacer size="m" />
<EuiCodeBlock language="html" fontSize="m" paddingSize="m" isCopyable>
ryankeairns marked this conversation as resolved.
Show resolved Hide resolved
{snippet}
</EuiCodeBlock>
</Fragment>
];
}

renderPropsForComponent = (componentName, component) => {
if (!component.__docgenInfo) {
return;
Expand Down Expand Up @@ -308,8 +338,8 @@ export class GuideSection extends Component {

renderCode(name) {
const nameToCodeClassMap = {
JavaScript: 'javascript',
HTML: 'html',
javascript: 'javascript',
html: 'html',
};

const codeClass = nameToCodeClassMap[name];
Expand All @@ -333,6 +363,14 @@ export class GuideSection extends Component {
}

renderContent() {
if (this.state.selectedTab.name === 'snippet') {
return (
<EuiErrorBoundary>
{this.renderSnippet()}
</EuiErrorBoundary>
);
}

if (this.state.selectedTab.isCode) {
return (
<EuiErrorBoundary>
Expand All @@ -341,7 +379,7 @@ export class GuideSection extends Component {
);
}

if (this.state.selectedTab.name === 'Props') {
if (this.state.selectedTab.name === 'props') {
return (
<EuiErrorBoundary>
{this.renderProps()}
Expand Down Expand Up @@ -375,6 +413,7 @@ GuideSection.propTypes = {
title: PropTypes.string,
id: PropTypes.string,
source: PropTypes.array,
snippet: PropTypes.string,
children: PropTypes.any,
toggleTheme: PropTypes.func.isRequired,
theme: PropTypes.string.isRequired,
Expand Down
4 changes: 2 additions & 2 deletions src-docs/src/components/guide_section/guide_section_types.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const GuideSectionTypes = {
JS: 'JavaScript',
HTML: 'HTML',
JS: 'javascript',
HTML: 'html',
};
4 changes: 2 additions & 2 deletions src-docs/src/views/code/code_block.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {

const htmlCode = `<!--I'm an example of HTML-->
<div>
asdf
<h1>Title</h1>
</div>
`;

Expand All @@ -21,7 +21,7 @@ export default () => (

<EuiSpacer />

<EuiCodeBlock language="js" fontSize="l" paddingSize="s" color="dark" overflowHeight={300}>
<EuiCodeBlock language="js" fontSize="m" paddingSize="m" color="dark" overflowHeight={300} isCopyable>
{jsCode}
</EuiCodeBlock>

Expand Down
12 changes: 11 additions & 1 deletion src-docs/src/views/code/code_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ import {
import Code from './code';
const codeSource = require('!!raw-loader!./code');
const codeHtml = renderToHtml(Code);
const codeSnippet = '<EuiCode>Text to be formatted</EuiCode>';

import CodeBlock from './code_block';
const codeBlockSource = require('!!raw-loader!./code_block');
const codeBlockHtml = renderToHtml(CodeBlock);
const codeBlockSnippet = `<EuiCodeBlock language="html" paddingSize="s" isCopyable>
{ \`<h1>Title</h1>\` }
</EuiCodeBlock>
`;

export const CodeExample = {
title: 'Code',
Expand All @@ -36,6 +41,7 @@ export const CodeExample = {
within or next to bodies of text.
</p>
),
snippet: codeSnippet,
demo: <Code />,
}, {
title: 'CodeBlock',
Expand All @@ -48,9 +54,13 @@ export const CodeExample = {
}],
text: (
<p>
<EuiCode>EuiCodeBlock</EuiCode> can be used to create multi-line code blocks.
<EuiCode>EuiCodeBlock</EuiCode> can be used to create multi-line code
blocks. Copy and fullscreen buttons can be enabled via the
<EuiCode>isCopyable</EuiCode> and <EuiCode>overflowHeight</EuiCode>
props, respectively.
</p>
),
snippet: codeBlockSnippet,
props: { EuiCodeBlockImpl },
demo: <CodeBlock />,
}],
Expand Down
4 changes: 3 additions & 1 deletion src-docs/src/views/progress/progress.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ import {
} from '../../../../src/components';

export default () => (
<EuiProgress size="xs" />
<div>
<EuiProgress size="xs" color="accent" />
</div>
);
35 changes: 26 additions & 9 deletions src-docs/src/views/progress/progress_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,31 @@ import {
} from '../../components';

import {
EuiCallOut,
EuiCode,
EuiProgress,
} from '../../../../src/components';

import Progress from './progress';
const progressSource = require('!!raw-loader!./progress');
const progressHtml = renderToHtml(Progress);
const progressSnippet = `<EuiProgress size="xs" color="accent" />`;

import ProgressValue from './progress_value';
const progressValueSource = require('!!raw-loader!./progress_value');
const progressValueHtml = renderToHtml(ProgressValue);
const progressValueSnippet = '<EuiProgress value={22} max={100} size="xs" />';

import ProgressFixed from './progress_fixed';
const progressFixedSource = require('!!raw-loader!./progress_fixed');
const progressFixedHtml = renderToHtml(ProgressFixed);
const progressFixedSnippet = `<!-- Position at top of parent container -->
<EuiProgress size="xs" color="accent" position="absolute" />

<!-- Position at top of screen, above global header -->
<EuiPortal>
<EuiProgress size="xs" color="accent" position="fixed" />
</EuiPortal>`;

import ProgressSizeColor from './progress_size_color';
const progressSizeColorSource = require('!!raw-loader!./progress_size_color');
Expand All @@ -46,6 +56,7 @@ export const ProgressExample = {
always strech <EuiCode>100%</EuiCode> to its container.
</p>
),
snippet: progressSnippet,
props: { EuiProgress },
demo: <Progress />,
}, {
Expand All @@ -64,6 +75,7 @@ export const ProgressExample = {
using an HTML5 <EuiCode>progress</EuiCode> tag.
</p>
),
snippet: progressValueSnippet,
demo: <ProgressValue />,
}, {
title: 'Progress can have absolute or fixed positions',
Expand All @@ -84,17 +96,22 @@ export const ProgressExample = {
absolute option, make sure that your wrapping element
has <EuiCode>position: relative</EuiCode> applied.
</p>

<p>
Using <EuiCode>EuiProgress</EuiCode> with
a <EuiCode>fixed</EuiCode> position may result in it being overlayed
when its parent wrapper has a <EuiCode>z-index</EuiCode> value lower
than another fixed element, such as <EuiCode>EuiHeader</EuiCode>. In
that case, wrap <EuiCode>EuiProgress</EuiCode> in
an <EuiCode>EuiPortal</EuiCode>.
</p>
<EuiCallOut
title="Note about progress bars over fixed headers"
iconType="iInCircle"
>
<p>
Using <EuiCode>EuiProgress</EuiCode> with
a <EuiCode>fixed</EuiCode> position may result in it being overlayed
when its parent wrapper has a <EuiCode>z-index</EuiCode> value lower
than another fixed element, such as <EuiCode>EuiHeader</EuiCode>. In
that case, wrap <EuiCode>EuiProgress</EuiCode> in
an <EuiCode>EuiPortal</EuiCode> as seen on the Snippet tab.
</p>
</EuiCallOut>
</div>
),
snippet: progressFixedSnippet,
demo: <ProgressFixed />,
}, {
title: 'Progress has a range of sizes and colors',
Expand Down
52 changes: 50 additions & 2 deletions src/components/code/_code_block.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import classNames from 'classnames';
import FocusTrap from 'focus-trap-react';
import hljs from 'highlight.js';

import {
EuiCopy,
} from '../copy';

import {
EuiButtonIcon,
} from '../button';
Expand Down Expand Up @@ -95,6 +99,7 @@ export class EuiCodeBlockImpl extends Component {
overflowHeight,
paddingSize,
transparentBackground,
isCopyable,
...otherProps
} = this.props;

Expand All @@ -105,6 +110,7 @@ export class EuiCodeBlockImpl extends Component {
{
'euiCodeBlock--transparentBackground': transparentBackground,
'euiCodeBlock--inline': inline,
'euiCodeBlock-isCopyable': isCopyable,
},
className
);
Expand Down Expand Up @@ -140,6 +146,31 @@ export class EuiCodeBlockImpl extends Component {
);
}

let copyButton;

if (isCopyable) {
copyButton = (
// TODO add i18n
ryankeairns marked this conversation as resolved.
Show resolved Hide resolved
ryankeairns marked this conversation as resolved.
Show resolved Hide resolved
<div className="euiCodeBlock__copyButton">
<EuiI18n token="euiCodeBlock.copyButton" default="Copy">
{copyButton => (
<EuiCopy textToCopy={children}>
{(copy) => (
<EuiButtonIcon
size="s"
onClick={copy}
iconType="copy"
color="text"
aria-label={copyButton}
/>
)}
</EuiCopy>
)}
</EuiI18n>
</div>
);
}

let fullScreenButton;

if (!inline && overflowHeight) {
Expand All @@ -162,6 +193,17 @@ export class EuiCodeBlockImpl extends Component {
);
}

let codeBlockControls;

if (copyButton || fullScreenButton) {
codeBlockControls = (
<div className="euiCodeBlock__controls">
{fullScreenButton}
{copyButton}
</div>
);
}

let fullScreenDisplay;

if (this.state.isFullScreen) {
Expand Down Expand Up @@ -196,7 +238,7 @@ export class EuiCodeBlockImpl extends Component {
</code>
</pre>

{fullScreenButton}
{codeBlockControls}
</div>
</EuiOverlayMask>
</FocusTrap>
Expand All @@ -213,7 +255,7 @@ export class EuiCodeBlockImpl extends Component {
If the below fullScreen code renders, it actually attaches to the body because of
EuiOverlayMask's React portal usage.
*/}
{fullScreenButton}
{codeBlockControls}
{fullScreenDisplay}
</div>
);
Expand All @@ -237,10 +279,16 @@ EuiCodeBlockImpl.propTypes = {
* Displays the passed code in an inline format. Also removes any margins set.
*/
inline: PropTypes.bool,

/**
* Displays an icon button to copy the code snippet to the clipboard.
*/
isCopyable: PropTypes.bool,
};

EuiCodeBlockImpl.defaultProps = {
transparentBackground: false,
paddingSize: 'l',
fontSize: 's',
isCopyable: false,
ryankeairns marked this conversation as resolved.
Show resolved Hide resolved
};
Loading