@@ -19,9 +19,8 @@ | |||
*/ | |||
// @flow | |||
import React from 'react'; | |||
import Clipboard from 'clipboard'; | |||
import classNames from 'classnames'; | |||
import Tooltip from '../../../../components/controls/Tooltip'; | |||
import ClipboardButton from '../../../../components/controls/ClipboardButton'; | |||
import { translate } from '../../../../helpers/l10n'; | |||
/*:: | |||
@@ -31,73 +30,21 @@ type Props = { | |||
}; | |||
*/ | |||
/*:: | |||
type State = { | |||
tooltipShown: boolean | |||
}; | |||
*/ | |||
const s = ' \\' + '\n '; | |||
export default class Command extends React.PureComponent { | |||
/*:: clipboard: Object; */ | |||
/*:: copyButton: HTMLButtonElement; */ | |||
/*:: mounted: boolean; */ | |||
/*:: props: Props; */ | |||
state /*: State */ = { tooltipShown: false }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
this.clipboard = new Clipboard(this.copyButton); | |||
this.clipboard.on('success', this.showTooltip); | |||
} | |||
componentDidUpdate() { | |||
this.clipboard.destroy(); | |||
this.clipboard = new Clipboard(this.copyButton); | |||
this.clipboard.on('success', this.showTooltip); | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
this.clipboard.destroy(); | |||
} | |||
showTooltip = () => { | |||
if (this.mounted) { | |||
this.setState({ tooltipShown: true }); | |||
setTimeout(this.hideTooltip, 1000); | |||
} | |||
}; | |||
hideTooltip = () => { | |||
if (this.mounted) { | |||
this.setState({ tooltipShown: false }); | |||
} | |||
}; | |||
render() { | |||
const { command, isWindows } = this.props; | |||
const commandArray = Array.isArray(command) ? command.filter(line => line != null) : [command]; | |||
const finalCommand = isWindows ? commandArray.join(' ') : commandArray.join(s); | |||
const button = ( | |||
<button data-clipboard-text={finalCommand} ref={node => (this.copyButton = node)}> | |||
{translate('copy')} | |||
</button> | |||
); | |||
return ( | |||
<div | |||
className={classNames('onboarding-command', { 'onboarding-command-windows': isWindows })}> | |||
<pre>{finalCommand}</pre> | |||
{this.state.tooltipShown ? ( | |||
<Tooltip defaultVisible={true} placement="top" overlay="Copied!" trigger="manual"> | |||
{button} | |||
</Tooltip> | |||
) : ( | |||
button | |||
)} | |||
<ClipboardButton copyValue={finalCommand} tooltipPlacement="top" /> | |||
</div> | |||
); | |||
} |
@@ -12,12 +12,19 @@ bar" | |||
foo | |||
bar | |||
</pre> | |||
<button | |||
data-clipboard-text="foo | |||
<ClipboardButton | |||
copyValue="foo | |||
bar" | |||
tooltipPlacement="top" | |||
> | |||
copy | |||
</button> | |||
<button | |||
className="js-copy-to-clipboard no-select" | |||
data-clipboard-text="foo | |||
bar" | |||
> | |||
copy | |||
</button> | |||
</ClipboardButton> | |||
</div> | |||
</Command> | |||
`; |
@@ -18,84 +18,21 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import * as Clipboard from 'clipboard'; | |||
import Tooltip from '../../../components/controls/Tooltip'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import ClipboardButton from '../../../components/controls/ClipboardButton'; | |||
import { translateWithParameters } from '../../../helpers/l10n'; | |||
interface Props { | |||
token: { name: string; token: string }; | |||
} | |||
interface State { | |||
tooltipShown: boolean; | |||
} | |||
export default class TokensFormNewToken extends React.PureComponent<Props, State> { | |||
clipboard: Clipboard; | |||
copyButton: HTMLButtonElement | null; | |||
mounted: boolean; | |||
state: State = { tooltipShown: false }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
if (this.copyButton) { | |||
this.clipboard = new Clipboard(this.copyButton); | |||
this.clipboard.on('success', this.showTooltip); | |||
} | |||
} | |||
componentDidUpdate() { | |||
this.clipboard.destroy(); | |||
if (this.copyButton) { | |||
this.clipboard = new Clipboard(this.copyButton); | |||
this.clipboard.on('success', this.showTooltip); | |||
} | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
this.clipboard.destroy(); | |||
} | |||
showTooltip = () => { | |||
if (this.mounted) { | |||
this.setState({ tooltipShown: true }); | |||
setTimeout(() => { | |||
if (this.mounted) { | |||
this.setState({ tooltipShown: false }); | |||
} | |||
}, 1000); | |||
} | |||
}; | |||
render() { | |||
const { name, token } = this.props.token; | |||
const button = ( | |||
<button | |||
className="js-copy-to-clipboard no-select" | |||
data-clipboard-text={token} | |||
ref={node => (this.copyButton = node)}> | |||
{translate('copy')} | |||
</button> | |||
); | |||
return ( | |||
<div className="panel panel-white big-spacer-top"> | |||
<p className="alert alert-warning"> | |||
{translateWithParameters('users.tokens.new_token_created', name)} | |||
</p> | |||
{this.state.tooltipShown ? ( | |||
<Tooltip | |||
defaultVisible={true} | |||
placement="bottom" | |||
overlay={translate('users.tokens.copied')} | |||
trigger="manual"> | |||
{button} | |||
</Tooltip> | |||
) : ( | |||
button | |||
)} | |||
<code className="big-spacer-left text-success">{token}</code> | |||
</div> | |||
); | |||
} | |||
export default function TokensFormNewToken({ token }: Props) { | |||
return ( | |||
<div className="panel panel-white big-spacer-top"> | |||
<p className="alert alert-warning"> | |||
{translateWithParameters('users.tokens.new_token_created', token.name)} | |||
</p> | |||
<ClipboardButton copyValue={token.token} /> | |||
<code className="big-spacer-left text-success">{token.token}</code> | |||
</div> | |||
); | |||
} |
@@ -0,0 +1,98 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import * as classNames from 'classnames'; | |||
import * as Clipboard from 'clipboard'; | |||
import Tooltip from './Tooltip'; | |||
import { translate } from '../../helpers/l10n'; | |||
interface Props { | |||
className?: string; | |||
copyValue: string; | |||
tooltipPlacement?: string; | |||
} | |||
interface State { | |||
tooltipShown: boolean; | |||
} | |||
export default class ClipboardButton extends React.PureComponent<Props, State> { | |||
clipboard: Clipboard; | |||
copyButton: HTMLButtonElement | null; | |||
mounted: boolean; | |||
state: State = { tooltipShown: false }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
if (this.copyButton) { | |||
this.clipboard = new Clipboard(this.copyButton); | |||
this.clipboard.on('success', this.showTooltip); | |||
} | |||
} | |||
componentDidUpdate() { | |||
if (this.clipboard) { | |||
this.clipboard.destroy(); | |||
} | |||
if (this.copyButton) { | |||
this.clipboard = new Clipboard(this.copyButton); | |||
this.clipboard.on('success', this.showTooltip); | |||
} | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
this.clipboard.destroy(); | |||
} | |||
showTooltip = () => { | |||
if (this.mounted) { | |||
this.setState({ tooltipShown: true }); | |||
setTimeout(() => { | |||
if (this.mounted) { | |||
this.setState({ tooltipShown: false }); | |||
} | |||
}, 1000); | |||
} | |||
}; | |||
render() { | |||
const button = ( | |||
<button | |||
className={classNames('js-copy-to-clipboard no-select', this.props.className)} | |||
data-clipboard-text={this.props.copyValue} | |||
ref={node => (this.copyButton = node)}> | |||
{translate('copy')} | |||
</button> | |||
); | |||
if (this.state.tooltipShown) { | |||
return ( | |||
<Tooltip | |||
defaultVisible={true} | |||
placement={this.props.tooltipPlacement || 'bottom'} | |||
overlay={translate('copied_action')} | |||
trigger="manual"> | |||
{button} | |||
</Tooltip> | |||
); | |||
} | |||
return button; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { shallow } from 'enzyme'; | |||
import ClipboardButton from '../ClipboardButton'; | |||
jest.useFakeTimers(); | |||
it('should display correctly', () => { | |||
const wrapper = shallow(<ClipboardButton copyValue="foo" />); | |||
expect(wrapper).toMatchSnapshot(); | |||
(wrapper.instance() as ClipboardButton).showTooltip(); | |||
wrapper.update(); | |||
expect(wrapper).toMatchSnapshot(); | |||
jest.runAllTimers(); | |||
wrapper.update(); | |||
expect(wrapper.find('Tooltip')).toHaveLength(0); | |||
}); |
@@ -0,0 +1,26 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should display correctly 1`] = ` | |||
<button | |||
className="js-copy-to-clipboard no-select" | |||
data-clipboard-text="foo" | |||
> | |||
copy | |||
</button> | |||
`; | |||
exports[`should display correctly 2`] = ` | |||
<Tooltip | |||
defaultVisible={true} | |||
overlay="copied_action" | |||
placement="bottom" | |||
trigger="manual" | |||
> | |||
<button | |||
className="js-copy-to-clipboard no-select" | |||
data-clipboard-text="foo" | |||
> | |||
copy | |||
</button> | |||
</Tooltip> | |||
`; |
@@ -198,9 +198,10 @@ are_you_sure=Are you sure? | |||
assigned_to=Assigned to | |||
bulk_change=Bulk Change | |||
bulleted_point=Bulleted point | |||
coding_rules=Rules | |||
clear=Clear | |||
clear_all_filters=Clear All Filters | |||
coding_rules=Rules | |||
copied_action=Copied! | |||
created_by=Created by | |||
default_error_message=The request cannot be processed. Try again later. | |||
default_severity=Default severity |