Przeglądaj źródła

Create a Clipboard button

tags/7.5
Grégoire Aubert 6 lat temu
rodzic
commit
b3a593c103

+ 2
- 55
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/Command.js Wyświetl plik

@@ -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>
);
}

+ 11
- 4
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Command-test.js.snap Wyświetl plik

@@ -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>
`;

+ 12
- 75
server/sonar-web/src/main/js/apps/users/components/TokensFormNewToken.tsx Wyświetl plik

@@ -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>
);
}

+ 98
- 0
server/sonar-web/src/main/js/components/controls/ClipboardButton.tsx Wyświetl plik

@@ -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;
}
}

+ 35
- 0
server/sonar-web/src/main/js/components/controls/__tests__/ClipboardButton-test.tsx Wyświetl plik

@@ -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);
});

+ 26
- 0
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ClipboardButton-test.tsx.snap Wyświetl plik

@@ -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>
`;

+ 2
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties Wyświetl plik

@@ -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

Ładowanie…
Anuluj
Zapisz