From 7d2a6445de05c0dc23ae4e8f8284675ecda955c9 Mon Sep 17 00:00:00 2001 From: melindali255 Date: Mon, 12 Jul 2021 10:03:51 -0700 Subject: [PATCH 1/6] enabled copy to clipboard shortcut for preview. button appears when copied successfully. --- lib/components/src/blocks/Preview.tsx | 65 +++++++++++++++++-- .../syntaxhighlighter/syntaxhighlighter.tsx | 12 +++- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/lib/components/src/blocks/Preview.tsx b/lib/components/src/blocks/Preview.tsx index 336d831489c6..56ab4c110b80 100644 --- a/lib/components/src/blocks/Preview.tsx +++ b/lib/components/src/blocks/Preview.tsx @@ -1,7 +1,15 @@ -import React, { Children, FunctionComponent, ReactElement, ReactNode, useState } from 'react'; +import React, { + Children, + ClipboardEvent, + FunctionComponent, + ReactElement, + ReactNode, + useState, +} from 'react'; import { darken } from 'polished'; import { styled } from '@storybook/theming'; +import global from 'global'; import { getBlockBackgroundStyle } from './BlockBackgroundStyles'; import { Source, SourceProps } from './Source'; import { ActionBar, ActionItem } from '../ActionBar/ActionBar'; @@ -130,7 +138,7 @@ const getSource = ( } default: { return { - source: null, + source: , actionItem: { title: 'Show code', className: 'docblock-code-toggle', @@ -175,6 +183,27 @@ const getLayout = (children: ReactElement[]): layout => { }, undefined); }; +const { navigator, document, window: globalWindow } = global; + +let copyToClipboard: (text: string) => Promise; + +if (navigator?.clipboard) { + copyToClipboard = (text: string) => navigator.clipboard.writeText(text); +} else { + copyToClipboard = async (text: string) => { + const tmp = document.createElement('TEXTAREA'); + const focus = document.activeElement; + + tmp.value = text; + + document.body.appendChild(tmp); + tmp.select(); + document.execCommand('copy'); + document.body.removeChild(tmp); + focus.focus(); + }; +} + /** * A preview component for showing one or more component `Story` * items. The preview also shows the source for the component @@ -197,13 +226,35 @@ const Preview: FunctionComponent = ({ const previewClasses = [className].concat(['sbdocs', 'sbdocs-preview']); const defaultActionItems = withSource ? [actionItem] : []; - const actionItems = additionalActions - ? [...defaultActionItems, ...additionalActions] - : defaultActionItems; + const [actionItems, setActionItems] = useState( + additionalActions ? [...defaultActionItems, ...additionalActions] : defaultActionItems + ); // @ts-ignore const layout = getLayout(Children.count(children) === 1 ? [children] : children); + const onCopyCapture = (e: ClipboardEvent) => { + e.preventDefault(); + if ( + actionItems.length <= + defaultActionItems.length + (additionalActions ? additionalActions.length : 0) + ) { + copyToClipboard(source.props.code).then(() => { + setActionItems([ + ...actionItems, + { + title: 'Copied', + onClick: () => {}, + }, + ]); + globalWindow.setTimeout( + () => setActionItems((arr) => arr.filter((item) => item.title !== 'Copied')), + 1500 + ); + }); + } + }; + return ( = ({ /> )} - + = ({ - {withSource && source} + {withSource && expanded && source} ); }; diff --git a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx index f3f73b2651cf..338b10dac396 100644 --- a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx +++ b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx @@ -1,4 +1,10 @@ -import React, { ComponentProps, FunctionComponent, MouseEvent, useState } from 'react'; +import React, { + ClipboardEvent, + ComponentProps, + FunctionComponent, + MouseEvent, + useState, +} from 'react'; import { logger } from '@storybook/client-logger'; import { styled } from '@storybook/theming'; import global from 'global'; @@ -152,7 +158,7 @@ export const SyntaxHighlighter: FunctionComponent = ({ const highlightableCode = format ? formatter(children) : children.trim(); const [copied, setCopied] = useState(false); - const onClick = (e: MouseEvent) => { + const onClick = (e: MouseEvent | ClipboardEvent) => { e.preventDefault(); copyToClipboard(highlightableCode) @@ -164,7 +170,7 @@ export const SyntaxHighlighter: FunctionComponent = ({ }; return ( - + Date: Mon, 12 Jul 2021 13:42:23 -0700 Subject: [PATCH 2/6] made copyToClipboard function for both files to use --- lib/components/src/blocks/Preview.tsx | 27 ++++-------------- .../syntaxhighlighter/syntaxhighlighter.tsx | 28 +++++++++++++++---- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/lib/components/src/blocks/Preview.tsx b/lib/components/src/blocks/Preview.tsx index 56ab4c110b80..a9d22f448327 100644 --- a/lib/components/src/blocks/Preview.tsx +++ b/lib/components/src/blocks/Preview.tsx @@ -16,6 +16,7 @@ import { ActionBar, ActionItem } from '../ActionBar/ActionBar'; import { Toolbar } from './Toolbar'; import { ZoomContext } from './ZoomContext'; import { Zoom } from '../Zoom/Zoom'; +import { createCopyToClipboardFunction } from '../syntaxhighlighter/syntaxhighlighter'; export interface PreviewProps { isColumn?: boolean; @@ -183,27 +184,6 @@ const getLayout = (children: ReactElement[]): layout => { }, undefined); }; -const { navigator, document, window: globalWindow } = global; - -let copyToClipboard: (text: string) => Promise; - -if (navigator?.clipboard) { - copyToClipboard = (text: string) => navigator.clipboard.writeText(text); -} else { - copyToClipboard = async (text: string) => { - const tmp = document.createElement('TEXTAREA'); - const focus = document.activeElement; - - tmp.value = text; - - document.body.appendChild(tmp); - tmp.select(); - document.execCommand('copy'); - document.body.removeChild(tmp); - focus.focus(); - }; -} - /** * A preview component for showing one or more component `Story` * items. The preview also shows the source for the component @@ -233,6 +213,9 @@ const Preview: FunctionComponent = ({ // @ts-ignore const layout = getLayout(Children.count(children) === 1 ? [children] : children); + const { window: globalWindow } = global; + const copyToClipboard: (text: string) => Promise = createCopyToClipboardFunction(); + const onCopyCapture = (e: ClipboardEvent) => { e.preventDefault(); if ( @@ -271,7 +254,7 @@ const Preview: FunctionComponent = ({ /> )} - + Object.entries(theme.code || {}).reduce((acc, [key, val]) => ({ ...acc, [`* .${key}`]: val }), {}) ); -let copyToClipboard: (text: string) => Promise; +const copyToClipboard: (text: string) => Promise = createCopyToClipboardFunction(); -if (navigator?.clipboard) { - copyToClipboard = (text: string) => navigator.clipboard.writeText(text); -} else { - copyToClipboard = async (text: string) => { +export function createCopyToClipboardFunction() { + if (navigator?.clipboard) { + return (text: string) => navigator.clipboard.writeText(text); + } + return async (text: string) => { const tmp = document.createElement('TEXTAREA'); const focus = document.activeElement; @@ -78,6 +79,23 @@ if (navigator?.clipboard) { focus.focus(); }; } + +// if (navigator?.clipboard) { +// copyToClipboard = (text: string) => navigator.clipboard.writeText(text); +// } else { +// copyToClipboard = async (text: string) => { +// const tmp = document.createElement('TEXTAREA'); +// const focus = document.activeElement; + +// tmp.value = text; + +// document.body.appendChild(tmp); +// tmp.select(); +// document.execCommand('copy'); +// document.body.removeChild(tmp); +// focus.focus(); +// }; +// } export interface WrapperProps { bordered?: boolean; padded?: boolean; From da598cabaf60fc4a7bc254df4f08dc30bf82e5e6 Mon Sep 17 00:00:00 2001 From: melindali255 Date: Mon, 12 Jul 2021 13:57:18 -0700 Subject: [PATCH 3/6] remove comments --- .../src/syntaxhighlighter/syntaxhighlighter.tsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx index 39c897e821ef..d54b2ae72759 100644 --- a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx +++ b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx @@ -80,22 +80,6 @@ export function createCopyToClipboardFunction() { }; } -// if (navigator?.clipboard) { -// copyToClipboard = (text: string) => navigator.clipboard.writeText(text); -// } else { -// copyToClipboard = async (text: string) => { -// const tmp = document.createElement('TEXTAREA'); -// const focus = document.activeElement; - -// tmp.value = text; - -// document.body.appendChild(tmp); -// tmp.select(); -// document.execCommand('copy'); -// document.body.removeChild(tmp); -// focus.focus(); -// }; -// } export interface WrapperProps { bordered?: boolean; padded?: boolean; From 49e67a0faf45d01357a774e726d4b9c599de9219 Mon Sep 17 00:00:00 2001 From: melindali255 Date: Mon, 12 Jul 2021 15:24:44 -0700 Subject: [PATCH 4/6] fixed Copied confirmation message --- lib/components/src/blocks/Preview.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/components/src/blocks/Preview.tsx b/lib/components/src/blocks/Preview.tsx index a9d22f448327..c06fcf3b8557 100644 --- a/lib/components/src/blocks/Preview.tsx +++ b/lib/components/src/blocks/Preview.tsx @@ -206,9 +206,10 @@ const Preview: FunctionComponent = ({ const previewClasses = [className].concat(['sbdocs', 'sbdocs-preview']); const defaultActionItems = withSource ? [actionItem] : []; - const [actionItems, setActionItems] = useState( - additionalActions ? [...defaultActionItems, ...additionalActions] : defaultActionItems + const [additionalActionItems, setAdditionalActionItems] = useState( + additionalActions ? [...additionalActions] : [] ); + const actionItems = [...defaultActionItems, ...additionalActionItems]; // @ts-ignore const layout = getLayout(Children.count(children) === 1 ? [children] : children); @@ -218,20 +219,20 @@ const Preview: FunctionComponent = ({ const onCopyCapture = (e: ClipboardEvent) => { e.preventDefault(); - if ( - actionItems.length <= - defaultActionItems.length + (additionalActions ? additionalActions.length : 0) - ) { + if (additionalActionItems.filter((item) => item.title === 'Copied').length === 0) { copyToClipboard(source.props.code).then(() => { - setActionItems([ - ...actionItems, + setAdditionalActionItems([ + ...additionalActionItems, { title: 'Copied', onClick: () => {}, }, ]); globalWindow.setTimeout( - () => setActionItems((arr) => arr.filter((item) => item.title !== 'Copied')), + () => + setAdditionalActionItems( + additionalActionItems.filter((item) => item.title !== 'Copied') + ), 1500 ); }); From ae5436aa528288b961624b8f69de1ca79781f17a Mon Sep 17 00:00:00 2001 From: melindali255 Date: Wed, 14 Jul 2021 17:06:46 -0700 Subject: [PATCH 5/6] keyboard shortcut copies highlighted code only --- lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx index d54b2ae72759..bfe1863c42ee 100644 --- a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx +++ b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx @@ -163,7 +163,12 @@ export const SyntaxHighlighter: FunctionComponent = ({ const onClick = (e: MouseEvent | ClipboardEvent) => { e.preventDefault(); - copyToClipboard(highlightableCode) + const textToCopy = + e.type !== 'click' && globalWindow.getSelection().toString() !== '' + ? globalWindow.getSelection().toString() + : highlightableCode; + + copyToClipboard(textToCopy) .then(() => { setCopied(true); globalWindow.setTimeout(() => setCopied(false), 1500); From 2cb39105773401dbe40f860de577421a6fc00eb1 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Fri, 16 Jul 2021 13:51:28 +0800 Subject: [PATCH 6/6] DRY tweak --- lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx index bfe1863c42ee..667c49ad6729 100644 --- a/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx +++ b/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx @@ -163,10 +163,8 @@ export const SyntaxHighlighter: FunctionComponent = ({ const onClick = (e: MouseEvent | ClipboardEvent) => { e.preventDefault(); - const textToCopy = - e.type !== 'click' && globalWindow.getSelection().toString() !== '' - ? globalWindow.getSelection().toString() - : highlightableCode; + const selectedText = globalWindow.getSelection().toString(); + const textToCopy = e.type !== 'click' && selectedText ? selectedText : highlightableCode; copyToClipboard(textToCopy) .then(() => {