mirror of
https://github.com/ansible/awx.git
synced 2026-03-03 09:48:51 -03:30
Merge pull request #5085 from mabashian/5054-revision-column
Adds revision to project list row items Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button, Tooltip } from '@patternfly/react-core';
|
||||||
|
import { CopyIcon } from '@patternfly/react-icons';
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const CopyButton = styled(Button)`
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin-left: 8px;
|
||||||
|
border: none;
|
||||||
|
&:hover {
|
||||||
|
background-color: #0066cc;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const clipboardCopyFunc = (event, text) => {
|
||||||
|
const clipboard = event.currentTarget.parentElement;
|
||||||
|
const el = document.createElement('input');
|
||||||
|
el.value = text;
|
||||||
|
clipboard.appendChild(el);
|
||||||
|
el.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
clipboard.removeChild(el);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClipboardCopyButton extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
copied: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleCopyClick = this.handleCopyClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCopyClick = event => {
|
||||||
|
const { stringToCopy, switchDelay } = this.props;
|
||||||
|
if (this.timer) {
|
||||||
|
window.clearTimeout(this.timer);
|
||||||
|
this.setState({ copied: false });
|
||||||
|
}
|
||||||
|
clipboardCopyFunc(event, stringToCopy);
|
||||||
|
this.setState({ copied: true }, () => {
|
||||||
|
this.timer = window.setTimeout(() => {
|
||||||
|
this.setState({ copied: false });
|
||||||
|
this.timer = null;
|
||||||
|
}, switchDelay);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { clickTip, entryDelay, exitDelay, hoverTip } = this.props;
|
||||||
|
const { copied } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
entryDelay={entryDelay}
|
||||||
|
exitDelay={exitDelay}
|
||||||
|
trigger="mouseenter focus click"
|
||||||
|
content={copied ? clickTip : hoverTip}
|
||||||
|
>
|
||||||
|
<CopyButton
|
||||||
|
variant="plain"
|
||||||
|
onClick={this.handleCopyClick}
|
||||||
|
aria-label={hoverTip}
|
||||||
|
>
|
||||||
|
<CopyIcon />
|
||||||
|
</CopyButton>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipboardCopyButton.propTypes = {
|
||||||
|
clickTip: PropTypes.string.isRequired,
|
||||||
|
entryDelay: PropTypes.number,
|
||||||
|
exitDelay: PropTypes.number,
|
||||||
|
hoverTip: PropTypes.string.isRequired,
|
||||||
|
stringToCopy: PropTypes.string.isRequired,
|
||||||
|
switchDelay: PropTypes.number,
|
||||||
|
};
|
||||||
|
|
||||||
|
ClipboardCopyButton.defaultProps = {
|
||||||
|
entryDelay: 100,
|
||||||
|
exitDelay: 1600,
|
||||||
|
switchDelay: 2000,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClipboardCopyButton;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
|
import ClipboardCopyButton from './ClipboardCopyButton';
|
||||||
|
|
||||||
|
document.execCommand = jest.fn();
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
describe('ClipboardCopyButton', () => {
|
||||||
|
test('renders the expected content', () => {
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<ClipboardCopyButton
|
||||||
|
clickTip="foo"
|
||||||
|
hoverTip="bar"
|
||||||
|
stringToCopy="foobar!"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(wrapper).toHaveLength(1);
|
||||||
|
});
|
||||||
|
test('clicking button calls execCommand to copy to clipboard', () => {
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<ClipboardCopyButton
|
||||||
|
clickTip="foo"
|
||||||
|
hoverTip="bar"
|
||||||
|
stringToCopy="foobar!"
|
||||||
|
/>
|
||||||
|
).find('ClipboardCopyButton');
|
||||||
|
expect(wrapper.state('copied')).toBe(false);
|
||||||
|
wrapper.find('Button').simulate('click');
|
||||||
|
expect(document.execCommand).toBeCalledWith('copy');
|
||||||
|
expect(wrapper.state('copied')).toBe(true);
|
||||||
|
jest.runAllTimers();
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.state('copied')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
1
awx/ui_next/src/components/ClipboardCopyButton/index.js
Normal file
1
awx/ui_next/src/components/ClipboardCopyButton/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './ClipboardCopyButton';
|
||||||
@@ -13,6 +13,7 @@ const mockProjects = [
|
|||||||
url: '/api/v2/projects/1',
|
url: '/api/v2/projects/1',
|
||||||
type: 'project',
|
type: 'project',
|
||||||
scm_type: 'git',
|
scm_type: 'git',
|
||||||
|
scm_revision: 'hfadsh89sa9gsaisdf0jogos0fgd9sgdf89adsf98',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
last_job: {
|
last_job: {
|
||||||
id: 9000,
|
id: 9000,
|
||||||
@@ -30,6 +31,7 @@ const mockProjects = [
|
|||||||
url: '/api/v2/projects/2',
|
url: '/api/v2/projects/2',
|
||||||
type: 'project',
|
type: 'project',
|
||||||
scm_type: 'svn',
|
scm_type: 'svn',
|
||||||
|
scm_revision: '7788f7erga0jijodfgsjisiodf98sdga9hg9a98gaf',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
last_job: {
|
last_job: {
|
||||||
id: 9002,
|
id: 9002,
|
||||||
@@ -47,6 +49,7 @@ const mockProjects = [
|
|||||||
url: '/api/v2/projects/3',
|
url: '/api/v2/projects/3',
|
||||||
type: 'project',
|
type: 'project',
|
||||||
scm_type: 'insights',
|
scm_type: 'insights',
|
||||||
|
scm_revision: '4893adfi749493afjksjoaiosdgjoaisdjadfisjaso',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
last_job: {
|
last_job: {
|
||||||
id: 9003,
|
id: 9003,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Link as _Link } from 'react-router-dom';
|
|||||||
import { SyncIcon } from '@patternfly/react-icons';
|
import { SyncIcon } from '@patternfly/react-icons';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import ClipboardCopyButton from '@components/ClipboardCopyButton';
|
||||||
import DataListCell from '@components/DataListCell';
|
import DataListCell from '@components/DataListCell';
|
||||||
import DataListCheck from '@components/DataListCheck';
|
import DataListCheck from '@components/DataListCheck';
|
||||||
import ListActionButton from '@components/ListActionButton';
|
import ListActionButton from '@components/ListActionButton';
|
||||||
@@ -102,6 +103,14 @@ class ProjectListItem extends React.Component {
|
|||||||
<DataListCell key="type">
|
<DataListCell key="type">
|
||||||
{project.scm_type.toUpperCase()}
|
{project.scm_type.toUpperCase()}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
|
<DataListCell key="revision">
|
||||||
|
{project.scm_revision.substring(0, 7)}
|
||||||
|
<ClipboardCopyButton
|
||||||
|
stringToCopy={project.scm_revision}
|
||||||
|
hoverTip={i18n._(t`Copy full revision to clipboard.`)}
|
||||||
|
clickTip={i18n._(t`Successfully copied to clipboard!`)}
|
||||||
|
/>
|
||||||
|
</DataListCell>,
|
||||||
<DataListCell lastcolumn="true" key="action">
|
<DataListCell lastcolumn="true" key="action">
|
||||||
{project.summary_fields.user_capabilities.start && (
|
{project.summary_fields.user_capabilities.start && (
|
||||||
<Tooltip content={i18n._(t`Sync Project`)} position="top">
|
<Tooltip content={i18n._(t`Sync Project`)} position="top">
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ describe('<ProjectsListItem />', () => {
|
|||||||
url: '/api/v2/projects/1',
|
url: '/api/v2/projects/1',
|
||||||
type: 'project',
|
type: 'project',
|
||||||
scm_type: 'git',
|
scm_type: 'git',
|
||||||
|
scm_revision: '7788f7erga0jijodfgsjisiodf98sdga9hg9a98gaf',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
last_job: {
|
last_job: {
|
||||||
id: 9000,
|
id: 9000,
|
||||||
@@ -43,6 +44,7 @@ describe('<ProjectsListItem />', () => {
|
|||||||
url: '/api/v2/projects/1',
|
url: '/api/v2/projects/1',
|
||||||
type: 'project',
|
type: 'project',
|
||||||
scm_type: 'git',
|
scm_type: 'git',
|
||||||
|
scm_revision: '7788f7erga0jijodfgsjisiodf98sdga9hg9a98gaf',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
last_job: {
|
last_job: {
|
||||||
id: 9000,
|
id: 9000,
|
||||||
|
|||||||
Reference in New Issue
Block a user