Skip to content
102 changes: 44 additions & 58 deletions packages/react-code-editor/src/components/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
Popover,
PopoverProps,
Title,
Tooltip,
TooltipPosition
} from '@patternfly/react-core';
import MonacoEditor, { ChangeHandler, EditorDidMount } from 'react-monaco-editor';
Expand All @@ -25,6 +24,7 @@ import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon';
import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
import Dropzone from 'react-dropzone';
import { CodeEditorContext } from './CodeEditorUtils';
import { CodeEditorControl } from './CodeEditorControl';

export interface Shortcut {
description: string;
Expand Down Expand Up @@ -440,18 +440,8 @@ export class CodeEditor extends React.Component<CodeEditorProps, CodeEditorState
};

copyCode = () => {
if (this.timer) {
window.clearTimeout(this.timer);
this.setState({ copied: false });
}
this.editor?.focus();
document.execCommand('copy');
this.setState({ copied: true }, () => {
this.timer = window.setTimeout(() => {
this.setState({ copied: false });
this.timer = null;
}, 2500);
});
navigator.clipboard.writeText(this.state.value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥳

this.setState({ copied: true });
};

download = () => {
Expand Down Expand Up @@ -555,55 +545,51 @@ export class CodeEditor extends React.Component<CodeEditorProps, CodeEditorState
</EmptyState>
));

const tooltipProps = {
position: toolTipPosition,
exitDelay: toolTipDelay,
entryDelay: toolTipDelay,
maxWidth: toolTipMaxWidth,
trigger: 'mouseenter focus'
};

const editorHeader = (
<div className={css(styles.codeEditorHeader)}>
{
<div className={css(styles.codeEditorControls)}>
{isCopyEnabled && (!showEmptyState || !!value) && (
<Tooltip
trigger="mouseenter"
content={<div>{copied ? copyButtonSuccessTooltipText : copyButtonToolTipText}</div>}
exitDelay={copied ? toolTipCopyExitDelay : toolTipDelay}
entryDelay={toolTipDelay}
maxWidth={toolTipMaxWidth}
position={toolTipPosition}
>
<Button onClick={this.copyCode} variant="control" aria-label={copyButtonAriaLabel}>
<CopyIcon />
</Button>
</Tooltip>
)}
{isUploadEnabled && (
<Tooltip
trigger="mouseenter focus click"
content={<div>{uploadButtonToolTipText}</div>}
entryDelay={toolTipDelay}
exitDelay={toolTipDelay}
maxWidth={toolTipMaxWidth}
position={toolTipPosition}
>
<Button onClick={open} variant="control" aria-label={uploadButtonAriaLabel}>
<UploadIcon />
</Button>
</Tooltip>
)}
{isDownloadEnabled && (!showEmptyState || !!value) && (
<Tooltip
trigger="mouseenter focus click"
content={<div>{downloadButtonToolTipText}</div>}
entryDelay={toolTipDelay}
exitDelay={toolTipDelay}
maxWidth={toolTipMaxWidth}
position={toolTipPosition}
>
<Button onClick={this.download} variant="control" aria-label={downloadButtonAriaLabel}>
<DownloadIcon />
</Button>
</Tooltip>
)}
{customControls && (
<CodeEditorContext.Provider value={{ code: value }}>{customControls}</CodeEditorContext.Provider>
)}
<CodeEditorContext.Provider value={{ code: value }}>
{isCopyEnabled && (!showEmptyState || !!value) && (
<CodeEditorControl
icon={<CopyIcon />}
aria-label={copyButtonAriaLabel}
tooltipProps={{
...tooltipProps,
'aria-live': 'polite',
content: <div>{copied ? copyButtonSuccessTooltipText : copyButtonToolTipText}</div>,
exitDelay: copied ? toolTipCopyExitDelay : toolTipDelay,
onTooltipHidden: () => this.setState({ copied: false })
}}
onClick={this.copyCode}
/>
)}
{isUploadEnabled && (
<CodeEditorControl
icon={<UploadIcon />}
aria-label={uploadButtonAriaLabel}
tooltipProps={{ content: <div>{uploadButtonToolTipText}</div>, ...tooltipProps }}
onClick={open}
/>
)}
{isDownloadEnabled && (!showEmptyState || !!value) && (
<CodeEditorControl
icon={<DownloadIcon />}
aria-label={downloadButtonAriaLabel}
tooltipProps={{ content: <div>{downloadButtonToolTipText}</div>, ...tooltipProps }}
onClick={this.download}
/>
)}
{customControls && customControls}
</CodeEditorContext.Provider>
</div>
}
{<div className={css(styles.codeEditorHeaderMain)}>{headerMainContent}</div>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ import { CodeEditorContext } from './CodeEditorUtils';
*/

export interface CodeEditorControlProps extends Omit<ButtonProps, 'onClick'> {
/** Accessible label for the code editor control. */
/** Accessible label for the code editor control */
'aria-label'?: string;
/** Additional classes added to the code editor control. */
className?: string;
/** Delay in ms before the tooltip appears. */
/** @deprecated Delay in ms before the tooltip appears. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you open an issue to remove these for breaking change release please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entryDelay?: number;
/** Delay in ms before the tooltip disappears. */
/** @deprecated Delay in ms before the tooltip disappears. */
exitDelay?: number;
/** Icon rendered inside the code editor control. */
icon: React.ReactNode;
/** Maximum width of the tooltip (default 150px). */
/** @deprecated Maximum width of the tooltip (default 150px). */
maxWidth?: string;
/** Copy button popover position. */
/** @deprecated Copy button popover position. */
position?:
| PopoverPosition
| 'auto'
Expand All @@ -35,41 +35,82 @@ export interface CodeEditorControlProps extends Omit<ButtonProps, 'onClick'> {
| 'left-end'
| 'right-start'
| 'right-end';
/** Text to display in the tooltip. */
toolTipText: React.ReactNode;
/** Event handler for the click of the button. */
/** @deprecated Text to display in the tooltip*/
toolTipText?: React.ReactNode;
/** Event handler for the click of the button */
onClick: (code: string, event?: any) => void;
/** Flag indicating that the button is visible above the code editor. */
isVisible?: boolean;
/** Additional tooltip props forwarded to the Tooltip component */
tooltipProps?: any;
}

export const CodeEditorControl: React.FunctionComponent<CodeEditorControlProps> = ({
icon,
className,
'aria-label': ariaLabel,
toolTipText,
exitDelay = 0,
entryDelay = 300,
maxWidth = '100px',
position = 'top',
exitDelay,
entryDelay,
maxWidth,
position,
onClick = () => {},
isVisible = true,
tooltipProps = {},
...props
}: CodeEditorControlProps) => {
const context = React.useContext(CodeEditorContext);

if (entryDelay !== undefined) {
// eslint-disable-next-line no-console
console.warn(
'CodeEditorControl: entryDelay prop has been deprecated. ' +
'Pass the entryDelay via the tooltipProps prop instead.'
);
}

if (exitDelay !== undefined) {
// eslint-disable-next-line no-console
console.warn(
'CodeEditorControl: exitDelay prop has been deprecated. ' +
'Pass the exitDelay via the tooltipProps prop instead.'
);
}

if (maxWidth !== undefined) {
// eslint-disable-next-line no-console
console.warn(
'CodeEditorControl: maxWidth prop has been deprecated. ' + 'Pass the maxWidth via the tooltipProps prop instead.'
);
}

if (position !== undefined) {
// eslint-disable-next-line no-console
console.warn(
'CodeEditorControl: position prop has been deprecated. ' + 'Pass the position via the tooltipProps prop instead.'
);
}

if (toolTipText !== undefined) {
// eslint-disable-next-line no-console
console.warn(
'CodeEditorControl: toolTipText prop has been deprecated. ' +
'Pass the toolTipText by setting the content field in tooltipProps prop instead.'
);
}

const onCustomClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
onClick(context.code, event);
};

return isVisible ? (
<Tooltip
trigger="mouseenter focus click"
exitDelay={exitDelay}
entryDelay={entryDelay}
maxWidth={maxWidth}
position={position}
content={<div>{toolTipText}</div>}
{...tooltipProps}
>
<Button className={className} onClick={onCustomClick} variant="control" aria-label={ariaLabel} {...props}>
{icon}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const CodeEditorCustomControl: React.FunctionComponent = () => {
<CodeEditorControl
icon={<PlayIcon />}
aria-label="Execute code"
toolTipText="Execute code"
tooltipProps={{ content: 'Execute code' }}
onClick={onExecuteCode}
isVisible={code !== ''}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,22 @@ export const CodeEditorShortcutMainHeader: React.FunctionComponent = () => {
];
const shortcutsPopoverProps = {
bodyContent: (
<Grid span={6} hasGutter>
{shortcuts.map(s => (
<>
<Grid span={6} hasGutter key="grid">
{shortcuts.map((shortcut, index) => (
<React.Fragment key={index}>
<GridItem style={{ textAlign: 'right', marginRight: '1em' }}>
{s.keys
.map(k => (
<Chip key={k} isReadOnly>
{k}
{shortcut.keys
.map(key => (
<Chip key={key} isReadOnly>
{key}
</Chip>
))
.reduce((prev, curr) => (
<>{[prev, ' + ', curr]}</>
))}
</GridItem>
<GridItem>{s.description}</GridItem>
</>
<GridItem>{shortcut.description}</GridItem>
</React.Fragment>
))}
</Grid>
),
Expand All @@ -60,7 +60,6 @@ export const CodeEditorShortcutMainHeader: React.FunctionComponent = () => {

return (
<CodeEditor
headerMainContent="Shortcut Example"
shortcutsPopoverProps={shortcutsPopoverProps}
isLanguageLabelVisible
code="Some example content"
Expand Down
Loading