diff --git a/src/components/CodeSnippets.js b/src/components/CodeSnippets.js index bacf9c6d..ca29fff6 100644 --- a/src/components/CodeSnippets.js +++ b/src/components/CodeSnippets.js @@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { Clipboard } from './Clipboard'; +import { Clipboard } from './clipboard/Clipboard'; import { Highlight } from './Highlight'; import { color, spacing, typography } from './shared/styles'; diff --git a/src/components/Clipboard.stories.js b/src/components/clipboard/Clipboard.stories.tsx similarity index 60% rename from src/components/Clipboard.stories.js rename to src/components/clipboard/Clipboard.stories.tsx index 7c1bb4e9..edc21a0a 100644 --- a/src/components/Clipboard.stories.js +++ b/src/components/clipboard/Clipboard.stories.tsx @@ -1,17 +1,18 @@ import React from 'react'; - +import { action } from '@storybook/addon-actions'; import { Clipboard } from './Clipboard'; -import { TooltipNote } from './tooltip/TooltipNote'; +// @ts-ignore +import { TooltipNote } from '../tooltip/TooltipNote'; export default { - title: 'Design System/Clipboard', - decorators: [(storyFn) =>
{storyFn()}
], + title: 'Design System/Clipboard/Clipboard', + decorators: [(storyFn: any) =>
{storyFn()}
], }; export const Default = () => Click to copy; export const Callback = () => ( - 'linky from callback'}>Click to copy + Click to copy ); export const Tooltips = () => ( @@ -19,6 +20,8 @@ export const Tooltips = () => ( toCopy="linky" renderCopiedTooltip={() => } renderUncopiedTooltip={() => } + // @ts-ignore + startOpen > Click to copy diff --git a/src/components/Clipboard.js b/src/components/clipboard/Clipboard.tsx similarity index 55% rename from src/components/Clipboard.js rename to src/components/clipboard/Clipboard.tsx index a3db894e..c69c0605 100644 --- a/src/components/Clipboard.js +++ b/src/components/clipboard/Clipboard.tsx @@ -2,14 +2,25 @@ import copyToClipboard from 'copy-to-clipboard'; import PropTypes from 'prop-types'; import React, { useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; - -import { TooltipNote } from './tooltip/TooltipNote'; -import WithTooltip from './tooltip/WithTooltip'; +// @ts-ignore +import { TooltipNote } from '../tooltip/TooltipNote'; +// @ts-ignore +import WithTooltip from '../tooltip/WithTooltip'; const Tooltip = styled(WithTooltip)` cursor: pointer; `; +interface ClipboardProps { + children: React.ReactNode | ((copied: boolean) => React.ReactNode); + toCopy?: string; + getCopyContent?: () => string; + copyOptions?: any; + renderCopiedTooltip?: () => React.ReactNode; + renderUncopiedTooltip?: () => React.ReactNode; + resetTimeout?: number; +} + export const Clipboard = ({ children, toCopy, @@ -19,19 +30,20 @@ export const Clipboard = ({ renderCopiedTooltip, renderUncopiedTooltip, ...props -}) => { - const timeout = useRef(); +}: ClipboardProps) => { const [copied, setCopied] = useState(false); useEffect(() => { - if (copied && timeout.current) { - clearTimeout(timeout.current); + let timeoutId: NodeJS.Timeout; + + if (copied && timeoutId) { + clearTimeout(timeoutId); } - if (copied && resetTimeout) { - timeout.current = setTimeout(() => setCopied(false), resetTimeout); + if (copied && resetTimeout && timeoutId) { + timeoutId = setTimeout(() => setCopied(false), resetTimeout); } return () => { - clearTimeout(timeout.current); + clearTimeout(timeoutId); }; }, [copied]); @@ -53,21 +65,11 @@ export const Clipboard = ({ ); }; -Clipboard.propTypes = { - children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired, - toCopy: PropTypes.string, - getCopyContent: PropTypes.func, - copyOptions: PropTypes.object, // eslint-disable-line - renderCopiedTooltip: PropTypes.func, - renderUncopiedTooltip: PropTypes.func, - resetTimeout: PropTypes.number, -}; - Clipboard.defaultProps = { copyOptions: undefined, - renderCopiedTooltip: () => null, - renderUncopiedTooltip: () => null, + renderCopiedTooltip: () => {}, + renderUncopiedTooltip: () => {}, resetTimeout: 3000, toCopy: undefined, - getCopyContent: () => undefined, + getCopyContent: () => '', }; diff --git a/src/components/clipboard/ClipboardCode.stories.tsx b/src/components/clipboard/ClipboardCode.stories.tsx new file mode 100644 index 00000000..a872032c --- /dev/null +++ b/src/components/clipboard/ClipboardCode.stories.tsx @@ -0,0 +1,20 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import React from 'react'; + +import { ClipboardCode } from './ClipboardCode'; + +export default { + title: 'Design System/Clipboard/ClipboardCode', +}; + +export const Default = () => ( +
+ +
+); + +export const Wrapped = () => ( +
+ +
+); diff --git a/src/components/clipboard/ClipboardCode.tsx b/src/components/clipboard/ClipboardCode.tsx new file mode 100644 index 00000000..b7089404 --- /dev/null +++ b/src/components/clipboard/ClipboardCode.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import styled from 'styled-components'; + +import { ClipboardIcon } from './ClipboardIcon'; + +const Container = styled.div` + position: relative; +`; + +const Code = styled.pre` + width: 100%; + display: block; + margin: 0; + padding-right: 32px; + overflow: hidden; +`; + +const StyledClipboardIcon = styled(ClipboardIcon)` + position: absolute; + top: 8px; + right: 8px; +`; + +interface ClipboardCodeProps { + code: string; +} + +export const ClipboardCode = ({ code, ...props }: ClipboardCodeProps) => ( + + + {code} + + + +); diff --git a/src/components/clipboard/ClipboardIcon.stories.tsx b/src/components/clipboard/ClipboardIcon.stories.tsx new file mode 100644 index 00000000..b4e246e3 --- /dev/null +++ b/src/components/clipboard/ClipboardIcon.stories.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import { ClipboardIcon } from './ClipboardIcon'; + +export default { + title: 'Design System/Clipboard/ClipboardIcon', +}; + +export const Default = () => ( +
+ +
+); diff --git a/src/components/clipboard/ClipboardIcon.tsx b/src/components/clipboard/ClipboardIcon.tsx new file mode 100644 index 00000000..a879faf2 --- /dev/null +++ b/src/components/clipboard/ClipboardIcon.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import styled from 'styled-components'; +import { color } from '../shared/styles'; +import { Icon } from '../Icon'; +import { Clipboard } from './Clipboard'; + +const StyledClipboard = styled(Clipboard)` + line-height: 14px; + padding: 5px; + color: ${color.mediumdark}; + &:hover { + color: ${color.darker}; + } +`; + +interface StyledIconProps { + copied: boolean; +} + +const StyledIcon = styled(Icon)` + width: 14px; + height: 14px; + vertical-align: top; + color: ${(props) => (props.copied ? color.positive : 'inherit')}; +`; + +type ClipboardIconProps = Omit, 'children'>; + +export const ClipboardIcon = (props: ClipboardIconProps) => ( + + {(copied) => } + +); diff --git a/src/components/clipboard/ClipboardInput.stories.tsx b/src/components/clipboard/ClipboardInput.stories.tsx new file mode 100644 index 00000000..e839d08b --- /dev/null +++ b/src/components/clipboard/ClipboardInput.stories.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import { ClipboardInput } from './ClipboardInput'; + +export default { + title: 'Design System/Clipboard/ClipboardInput', +}; + +export const Default = () => ( +
+ +
+); + +export const Clipped = () => ( +
+ +
+); diff --git a/src/components/clipboard/ClipboardInput.tsx b/src/components/clipboard/ClipboardInput.tsx new file mode 100644 index 00000000..45a2f925 --- /dev/null +++ b/src/components/clipboard/ClipboardInput.tsx @@ -0,0 +1,55 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import styled from 'styled-components'; +// @ts-ignore +import { Input } from '../Input'; +import { color, spacing } from '../shared/styles'; +import { ClipboardIcon } from './ClipboardIcon'; + +const Container = styled.div` + position: relative; + margin-top: 15px; +`; + +const StyledInput = styled(Input)` + width: 100%; + display: block; + && input { + padding-top: 7px !important; + padding-bottom: 7px !important; + padding-right: 26px !important; + background: ${color.lightest}; + border: 1px solid ${color.border}; + border-radius: ${spacing.borderRadius.small}px; + font-size: 11px; + &:focus { + box-shadow: none; + border: 1px solid ${color.secondary}; + } + } +`; + +const StyledClipboardIcon = styled(ClipboardIcon)` + position: absolute; + top: 4px; + right: 4px; +`; + +interface ClipboardInputProps extends React.ComponentPropsWithoutRef { + value: string; +} + +export const ClipboardInput = ({ value, ...props }: ClipboardInputProps) => ( + + + + +); diff --git a/src/components/index.js b/src/components/index.js index cfdff00c..39614d65 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -35,7 +35,11 @@ export { default as WithModal } from './modal/WithModal'; export * from './table-of-contents/TableOfContents'; export * from './ShadowBoxCTA'; -export * from './Clipboard'; +export * from './clipboard/Clipboard'; +export * from './clipboard/ClipboardIcon'; +export * from './clipboard/ClipboardCode'; +export * from './clipboard/ClipboardInput'; + export * from './LinkTabs'; export * from './CodeSnippets';