diff --git a/packages/core/src/components/CopyTextButton/CopyTextButton.js b/packages/core/src/components/CopyTextButton/CopyTextButton.js new file mode 100644 index 0000000000..49489319b3 --- /dev/null +++ b/packages/core/src/components/CopyTextButton/CopyTextButton.js @@ -0,0 +1,109 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { IconButton, Tooltip, withStyles } from '@material-ui/core'; +import CopyIcon from '@material-ui/icons/FileCopy'; + +const buttonStyles = theme => ({ + button: { + '&:hover': { + backgroundColor: theme.palette.highlight, + cursor: 'pointer', + }, + }, +}); + +/** + * Copy text button with visual feedback in the form of + * - a hover color + * - click ripple + * - Tooltip shown when user has clicked + * + * Properties: + * - text: the text to be copied + * - tooltipDelay: Number os ms to show the tooltip, default: 1000ms + * - tooltipText: Text to show in the tooltip when user has clicked the button, default: "Text + * copied to clipboard" + * + * Example: + * + */ +class CopyTextButton extends React.Component { + static propTypes = { + text: PropTypes.string.isRequired, + tooltipDelay: PropTypes.number, + tooltipText: PropTypes.string, + }; + + static defaultProps = { + tooltipDelay: 1000, + tooltipText: 'Text copied to clipboard', + }; + + state = { + open: false, + }; + + handleTooltipClose = () => { + this.setState({ open: false }); + }; + + handleTooltipOpen = () => { + this.setState({ open: true }); + }; + + handleCopyClick = e => { + e.stopPropagation(); + this.handleTooltipOpen(); + try { + this.clipboardInput.select(); + document.execCommand('copy'); + } catch (error) { + // console.error(error); + } + }; + + render() { + const { classes, text, tooltipDelay, tooltipText } = this.props; + + return ( + + (this.clipboardInput = el)} + type="text" + style={{ position: 'absolute', top: '-9999px', left: '-9999px' }} + defaultValue={text} + /> + + + + + + + ); + } +} + +export default withStyles(buttonStyles)(CopyTextButton); diff --git a/packages/core/src/components/CopyTextButton/CopyTextButton.stories.tsx b/packages/core/src/components/CopyTextButton/CopyTextButton.stories.tsx new file mode 100644 index 0000000000..9f5618ad0e --- /dev/null +++ b/packages/core/src/components/CopyTextButton/CopyTextButton.stories.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import CopyTextButton from '.'; + +export default { + title: 'CopyTextButton', + component: CopyTextButton, +}; + +export const Default = () => ( + +); + +export const WithTooltip = () => ( + +); diff --git a/packages/core/src/components/CopyTextButton/CopyTextButton.test.js b/packages/core/src/components/CopyTextButton/CopyTextButton.test.js new file mode 100644 index 0000000000..c1c2212be7 --- /dev/null +++ b/packages/core/src/components/CopyTextButton/CopyTextButton.test.js @@ -0,0 +1,45 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { wrapInThemedTestApp } from '@backstage/test-utils'; +import CopyTextButton from 'shared/components/CopyTextButton'; + +const props = { + text: 'mockText', + tooltipDelay: 2, + tooltipText: 'mockTooltip', +}; + +describe('', () => { + it('renders without exploding', () => { + const { getByDisplayValue } = render( + wrapInThemedTestApp(), + ); + getByDisplayValue('mockText'); + }); + + it('displays tooltip on click', () => { + const spy = jest.fn(); + Object.defineProperty(document, 'execCommand', { value: spy }); + const rendered = render(wrapInThemedTestApp()); + const button = rendered.getByTitle('mockTooltip'); + button.click(); + expect(spy).toHaveBeenCalled(); + rendered.getByText('mockTooltip'); + }); +}); diff --git a/packages/core/src/components/CopyTextButton/index.ts b/packages/core/src/components/CopyTextButton/index.ts new file mode 100644 index 0000000000..22fd7f05bb --- /dev/null +++ b/packages/core/src/components/CopyTextButton/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './CopyTextButton'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 740a4cc803..c94068b556 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -27,6 +27,7 @@ export * from './layout/Sidebar'; export { default as HorizontalScrollGrid } from './components/HorizontalScrollGrid'; export { default as ProgressCard } from './components/ProgressCard'; export { default as CircleProgress } from './components/CircleProgress'; +export { default as CopyTextButton } from './components/CopyTextButton'; export { default as Progress } from './components/Progress'; export { AlphaLabel, BetaLabel } from './components/Lifecycle'; export { default as SupportButton } from './components/SupportButton';