*/ | */ | ||||
// @flow | // @flow | ||||
import React from 'react'; | import React from 'react'; | ||||
import Clipboard from 'clipboard'; | |||||
import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
import Tooltip from '../../../../components/controls/Tooltip'; | |||||
import ClipboardButton from '../../../../components/controls/ClipboardButton'; | |||||
import { translate } from '../../../../helpers/l10n'; | import { translate } from '../../../../helpers/l10n'; | ||||
/*:: | /*:: | ||||
}; | }; | ||||
*/ | */ | ||||
/*:: | |||||
type State = { | |||||
tooltipShown: boolean | |||||
}; | |||||
*/ | |||||
const s = ' \\' + '\n '; | const s = ' \\' + '\n '; | ||||
export default class Command extends React.PureComponent { | export default class Command extends React.PureComponent { | ||||
/*:: clipboard: Object; */ | |||||
/*:: copyButton: HTMLButtonElement; */ | |||||
/*:: mounted: boolean; */ | |||||
/*:: props: Props; */ | /*:: 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() { | render() { | ||||
const { command, isWindows } = this.props; | const { command, isWindows } = this.props; | ||||
const commandArray = Array.isArray(command) ? command.filter(line => line != null) : [command]; | const commandArray = Array.isArray(command) ? command.filter(line => line != null) : [command]; | ||||
const finalCommand = isWindows ? commandArray.join(' ') : commandArray.join(s); | const finalCommand = isWindows ? commandArray.join(' ') : commandArray.join(s); | ||||
const button = ( | |||||
<button data-clipboard-text={finalCommand} ref={node => (this.copyButton = node)}> | |||||
{translate('copy')} | |||||
</button> | |||||
); | |||||
return ( | return ( | ||||
<div | <div | ||||
className={classNames('onboarding-command', { 'onboarding-command-windows': isWindows })}> | className={classNames('onboarding-command', { 'onboarding-command-windows': isWindows })}> | ||||
<pre>{finalCommand}</pre> | <pre>{finalCommand}</pre> | ||||
{this.state.tooltipShown ? ( | |||||
<Tooltip defaultVisible={true} placement="top" overlay="Copied!" trigger="manual"> | |||||
{button} | |||||
</Tooltip> | |||||
) : ( | |||||
button | |||||
)} | |||||
<ClipboardButton copyValue={finalCommand} tooltipPlacement="top" /> | |||||
</div> | </div> | ||||
); | ); | ||||
} | } |
foo | foo | ||||
bar | bar | ||||
</pre> | </pre> | ||||
<button | |||||
data-clipboard-text="foo | |||||
<ClipboardButton | |||||
copyValue="foo | |||||
bar" | bar" | ||||
tooltipPlacement="top" | |||||
> | > | ||||
copy | |||||
</button> | |||||
<button | |||||
className="js-copy-to-clipboard no-select" | |||||
data-clipboard-text="foo | |||||
bar" | |||||
> | |||||
copy | |||||
</button> | |||||
</ClipboardButton> | |||||
</div> | </div> | ||||
</Command> | </Command> | ||||
`; | `; |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import * as React from 'react'; | 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 { | interface Props { | ||||
token: { name: string; token: string }; | 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> | |||||
); | |||||
} | } |
/* | |||||
* 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; | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
}); |
// 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> | |||||
`; |
assigned_to=Assigned to | assigned_to=Assigned to | ||||
bulk_change=Bulk Change | bulk_change=Bulk Change | ||||
bulleted_point=Bulleted point | bulleted_point=Bulleted point | ||||
coding_rules=Rules | |||||
clear=Clear | clear=Clear | ||||
clear_all_filters=Clear All Filters | clear_all_filters=Clear All Filters | ||||
coding_rules=Rules | |||||
copied_action=Copied! | |||||
created_by=Created by | created_by=Created by | ||||
default_error_message=The request cannot be processed. Try again later. | default_error_message=The request cannot be processed. Try again later. | ||||
default_severity=Default severity | default_severity=Default severity |