Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions src/components/flow/actions/action/Action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
moveActionUp,
OnOpenNodeEditor,
onOpenNodeEditor,
removeAction
removeAction,
CopyNode,
copyNode
} from 'store/thunks';
import { createClickHandler, getLocalization } from 'utils';

Expand All @@ -43,6 +45,7 @@ export interface ActionWrapperStoreProps {
onOpenNodeEditor: OnOpenNodeEditor;
removeAction: ActionAC;
moveActionUp: ActionAC;
copyNode: CopyNode;
scrollToAction: string;
}

Expand Down Expand Up @@ -98,6 +101,17 @@ export class ActionWrapper extends React.Component<ActionWrapperProps> {
this.props.moveActionUp(this.props.renderNode.node.uuid, this.props.action);
}

public handleCopy(event: React.MouseEvent<HTMLElement>): void {
if (event) {
event.preventDefault();
event.stopPropagation();
}
// Only show copy on the first action to copy the entire node
if (this.props.first) {
this.props.copyNode(this.props.renderNode);
}
}

public getAction(): Action {
// if we are translating, us our localized version
if (this.props.translating) {
Expand Down Expand Up @@ -196,6 +210,8 @@ export class ActionWrapper extends React.Component<ActionWrapperProps> {
showRemoval={showRemoval}
showMove={showMove}
onMoveUp={this.handleMoveUp}
showCopy={!this.props.translating && this.props.first}
onCopy={this.handleCopy}
shouldCancelClick={() => this.props.selected}
/>
<div className={styles.body + ' ' + actionClass} data-spec={actionBodySpecId}>
Expand Down Expand Up @@ -243,16 +259,14 @@ const mapDispatchToProps = (dispatch: DispatchWithState) =>
{
onOpenNodeEditor,
removeAction,
moveActionUp
moveActionUp,
copyNode
},
dispatch
);

const ConnectedActionWrapper = connect(
mapStateToProps,
mapDispatchToProps,
null,
{ forwardRef: true }
)(ActionWrapper);
const ConnectedActionWrapper = connect(mapStateToProps, mapDispatchToProps, null, {
forwardRef: true
})(ActionWrapper);

export default ConnectedActionWrapper;
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ exports[`ActionWrapper render should display hybrid style 1`] = `
>
<TitleBar
__className="send_msg"
onCopy={[Function]}
onMoveUp={[Function]}
onRemoval={[Function]}
shouldCancelClick={[Function]}
showCopy={true}
showMove={false}
showRemoval={true}
title="Send Message"
Expand Down Expand Up @@ -49,9 +51,11 @@ exports[`ActionWrapper render should display missing_localization style 1`] = `
>
<TitleBar
__className="send_msg"
onCopy={[Function]}
onMoveUp={[Function]}
onRemoval={[Function]}
shouldCancelClick={[Function]}
showCopy={false}
showMove={false}
showRemoval={false}
title="Send Message"
Expand Down Expand Up @@ -81,9 +85,11 @@ exports[`ActionWrapper render should display not_localizable style 1`] = `
>
<TitleBar
__className="enter_flow"
onCopy={[Function]}
onMoveUp={[Function]}
onRemoval={[Function]}
shouldCancelClick={[Function]}
showCopy={false}
showMove={false}
showRemoval={false}
title="Enter a Flow"
Expand Down Expand Up @@ -113,9 +119,11 @@ exports[`ActionWrapper render should display translating style 1`] = `
>
<TitleBar
__className="send_msg"
onCopy={[Function]}
onMoveUp={[Function]}
onRemoval={[Function]}
shouldCancelClick={[Function]}
showCopy={false}
showMove={false}
showRemoval={false}
title="Send Message"
Expand All @@ -132,9 +140,11 @@ exports[`ActionWrapper render should render self, children with base props 1`] =
Object {
"__className": "send_msg",
"nodeUUID": undefined,
"onCopy": [Function],
"onMoveUp": [Function],
"onRemoval": [Function],
"shouldCancelClick": [Function],
"showCopy": true,
"showMove": false,
"showRemoval": true,
"title": "Send Message",
Expand All @@ -158,9 +168,11 @@ exports[`ActionWrapper render should render self, children with base props 2`] =
>
<TitleBar
__className="send_msg"
onCopy={[Function]}
onMoveUp={[Function]}
onRemoval={[Function]}
shouldCancelClick={[Function]}
showCopy={true}
showMove={false}
showRemoval={true}
title="Send Message"
Expand Down Expand Up @@ -190,9 +202,11 @@ exports[`ActionWrapper render should show move icon 1`] = `
>
<TitleBar
__className="send_msg"
onCopy={[Function]}
onMoveUp={[Function]}
onRemoval={[Function]}
shouldCancelClick={[Function]}
showCopy={false}
showMove={true}
showRemoval={true}
title="Send Message"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ exports[`ActionWrapper can have localized quick replies when empty on default la
Send Message

</div>
<div
class="copy_button"
data-testid="copy-icon"
/>
<div
class="remove_button"
data-testid="remove-icon"
Expand Down Expand Up @@ -109,6 +113,15 @@ exports[`ActionWrapper renders a base language 1`] = `
Send Message

</div>
<div
class="copy_button"
data-testid="copy-icon"
>
<temba-icon
name="copy"
size="1.2"
/>
</div>
<div
class="remove_button"
data-testid="remove-icon"
Expand Down
21 changes: 14 additions & 7 deletions src/components/flow/node/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import {
OnOpenNodeEditor,
onOpenNodeEditor,
RemoveNode,
removeNode
removeNode,
CopyNode,
copyNode
} from 'store/thunks';
import { ClickHandler, createClickHandler } from 'utils';

Expand Down Expand Up @@ -75,6 +77,7 @@ export interface NodeStoreProps {
onAddToNode: OnAddToNode;
onOpenNodeEditor: OnOpenNodeEditor;
removeNode: RemoveNode;
copyNode: CopyNode;
mergeEditorState: MergeEditorState;
scrollToNode: string;
scrollToAction: string;
Expand Down Expand Up @@ -211,6 +214,12 @@ export class NodeComp extends React.PureComponent<NodeProps> {
this.props.removeNode(this.props.renderNode.node);
}

private handleCopy(event: React.MouseEvent<HTMLElement>): void {
event.preventDefault();
event.stopPropagation();
this.props.copyNode(this.props.renderNode);
}

private getExits(): JSX.Element[] {
if (this.props.renderNode.node.exits) {
return this.props.renderNode.node.exits.map((exit: Exit, idx: number) => (
Expand Down Expand Up @@ -370,6 +379,8 @@ export class NodeComp extends React.PureComponent<NodeProps> {
nodeUUID={showLabel && this.props.nodeUUID}
showRemoval={!this.props.translating}
onRemoval={this.handleRemoval}
showCopy={!this.props.translating}
onCopy={this.handleCopy}
shouldCancelClick={this.handleShouldCancelClick}
title={title}
/>
Expand Down Expand Up @@ -542,14 +553,10 @@ const mapDispatchToProps = (dispatch: DispatchWithState) =>
onAddToNode,
onOpenNodeEditor,
removeNode,
copyNode,
mergeEditorState
},
dispatch
);

export default connect(
mapStateToProps,
mapDispatchToProps,
null,
{ forwardRef: true }
)(NodeComp);
export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })(NodeComp);
9 changes: 9 additions & 0 deletions src/components/flow/node/__snapshots__/Node.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ exports[`NodeComp renders a named random split 1`] = `
Split Randomly

</div>
<div
class="copy_button"
data-testid="copy-icon"
>
<temba-icon
name="copy"
size="1.2"
/>
</div>
<div
class="remove_button"
data-testid="remove-icon"
Expand Down
20 changes: 19 additions & 1 deletion src/components/titlebar/TitleBar.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,26 @@
}
}

.copy_button {
padding-right: 5px;
width: 20px;
color: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s;
}

.remove_button {
padding-right: 0px;
visibility: hidden;
width: 20px;
right: 0;
color: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
}

.up_button {
Expand All @@ -53,6 +67,10 @@
}

&:hover {
.copy_button {
opacity: 1;
}

.remove_button {
visibility: visible;
}
Expand Down
24 changes: 24 additions & 0 deletions src/components/titlebar/TitleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface TitleBarProps {
showRemoval?: boolean;
showMove?: boolean;
onMoveUp?(event: React.MouseEvent<HTMLElement>): any;
onCopy?(event: React.MouseEvent<HTMLElement>): any;
showCopy?: boolean;
shouldCancelClick?: () => boolean;
}

Expand Down Expand Up @@ -106,6 +108,26 @@ export default class TitleBar extends React.Component<TitleBarProps, TitleBarSta
return moveArrow;
}

private getCopy(): JSX.Element {
if (this.props.showCopy && this.context.config.mutable && this.props.onCopy) {
return (
<div
className={styles.copy_button}
{...createClickHandler(
this.props.onCopy,
this.props.shouldCancelClick,
this.handleMouseUpCapture
)}
data-testid="copy-icon"
>
<temba-icon name="copy" size="1.2"></temba-icon>
</div>
);
}

return <div className={styles.copy_button} data-testid="copy-icon"></div>;
}

private getRemove(): JSX.Element {
let remove: JSX.Element = (
<div className={styles.remove_button} data-testid={removeIconSpecId}></div>
Expand Down Expand Up @@ -159,6 +181,7 @@ export default class TitleBar extends React.Component<TitleBarProps, TitleBarSta
public render(): JSX.Element {
const confirmation: JSX.Element = this.getConfirmationEl();
const moveArrow: JSX.Element = this.getMoveArrow();
const copy: JSX.Element = this.getCopy();
const remove: JSX.Element = this.getRemove();
return (
<div className={styles.titlebar} data-spec={titlebarContainerSpecId}>
Expand All @@ -167,6 +190,7 @@ export default class TitleBar extends React.Component<TitleBarProps, TitleBarSta
<div className={styles.titletext}>
{this.props.title} {this.props.nodeUUID && `: ${this.props.nodeUUID.slice(-4)}`}
</div>
{copy}
{remove}
</div>
{confirmation}
Expand Down
Loading