MM-63911 Fix link copied behavior and style in get link modal (#30854)
* changed copy link behavior to match behavior elsewhere * update timing, fix linting issues, fix style in modal header * updated test * updated test and snapshot * fix linting error * Fix snapshots --------- Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
f09df430b9
Коммит
a001367d43
Двоичный файл не отображается.
|
До Ширина: | Высота: | Размер: 67 KiB После Ширина: | Высота: | Размер: 63 KiB |
Двоичный файл не отображается.
|
До Ширина: | Высота: | Размер: 106 KiB После Ширина: | Высота: | Размер: 106 KiB |
Двоичный файл не отображается.
|
До Ширина: | Высота: | Размер: 66 KiB После Ширина: | Высота: | Размер: 62 KiB |
@@ -36,11 +36,11 @@ exports[`components/GetLinkModal should have called onHide 1`] = `
|
|||||||
closeLabel="Close"
|
closeLabel="Close"
|
||||||
id="getLinkModalLabel"
|
id="getLinkModalLabel"
|
||||||
>
|
>
|
||||||
<h4
|
<h2
|
||||||
className="modal-title"
|
className="modal-title"
|
||||||
>
|
>
|
||||||
title
|
title
|
||||||
</h4>
|
</h2>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody
|
<ModalBody
|
||||||
bsClass="modal-body"
|
bsClass="modal-body"
|
||||||
@@ -71,26 +71,20 @@ exports[`components/GetLinkModal should have called onHide 1`] = `
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary pull-left"
|
className="btn btn-primary btn-success pull-left"
|
||||||
data-copy-btn="true"
|
data-copy-btn="true"
|
||||||
id="linkModalCopyLink"
|
id="linkModalCopyLink"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<MemoizedFormattedMessage
|
<i
|
||||||
defaultMessage="Copy Link"
|
className="icon icon-check"
|
||||||
id="get_link.copy"
|
|
||||||
/>
|
/>
|
||||||
</button>
|
|
||||||
<p
|
|
||||||
className="alert alert-success alert--confirm"
|
|
||||||
>
|
|
||||||
<SuccessIcon />
|
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage=" Link copied"
|
defaultMessage="Copied"
|
||||||
id="get_link.clipboard"
|
id="get_link.clipboard"
|
||||||
/>
|
/>
|
||||||
</p>
|
</button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
`;
|
`;
|
||||||
@@ -131,11 +125,11 @@ exports[`components/GetLinkModal should have called onHide 2`] = `
|
|||||||
closeLabel="Close"
|
closeLabel="Close"
|
||||||
id="getLinkModalLabel"
|
id="getLinkModalLabel"
|
||||||
>
|
>
|
||||||
<h4
|
<h2
|
||||||
className="modal-title"
|
className="modal-title"
|
||||||
>
|
>
|
||||||
title
|
title
|
||||||
</h4>
|
</h2>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody
|
<ModalBody
|
||||||
bsClass="modal-body"
|
bsClass="modal-body"
|
||||||
@@ -172,6 +166,9 @@ exports[`components/GetLinkModal should have called onHide 2`] = `
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
<i
|
||||||
|
className="icon icon-link-variant"
|
||||||
|
/>
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="Copy Link"
|
defaultMessage="Copy Link"
|
||||||
id="get_link.copy"
|
id="get_link.copy"
|
||||||
@@ -217,11 +214,11 @@ exports[`components/GetLinkModal should match snapshot when all props is set 1`]
|
|||||||
closeLabel="Close"
|
closeLabel="Close"
|
||||||
id="getLinkModalLabel"
|
id="getLinkModalLabel"
|
||||||
>
|
>
|
||||||
<h4
|
<h2
|
||||||
className="modal-title"
|
className="modal-title"
|
||||||
>
|
>
|
||||||
title
|
title
|
||||||
</h4>
|
</h2>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody
|
<ModalBody
|
||||||
bsClass="modal-body"
|
bsClass="modal-body"
|
||||||
@@ -263,6 +260,9 @@ exports[`components/GetLinkModal should match snapshot when all props is set 1`]
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
<i
|
||||||
|
className="icon icon-link-variant"
|
||||||
|
/>
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="Copy Link"
|
defaultMessage="Copy Link"
|
||||||
id="get_link.copy"
|
id="get_link.copy"
|
||||||
@@ -308,11 +308,11 @@ exports[`components/GetLinkModal should match snapshot when helpText is not set
|
|||||||
closeLabel="Close"
|
closeLabel="Close"
|
||||||
id="getLinkModalLabel"
|
id="getLinkModalLabel"
|
||||||
>
|
>
|
||||||
<h4
|
<h2
|
||||||
className="modal-title"
|
className="modal-title"
|
||||||
>
|
>
|
||||||
title
|
title
|
||||||
</h4>
|
</h2>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody
|
<ModalBody
|
||||||
bsClass="modal-body"
|
bsClass="modal-body"
|
||||||
@@ -349,6 +349,9 @@ exports[`components/GetLinkModal should match snapshot when helpText is not set
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
<i
|
||||||
|
className="icon icon-link-variant"
|
||||||
|
/>
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="Copy Link"
|
defaultMessage="Copy Link"
|
||||||
id="get_link.copy"
|
id="get_link.copy"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Modal} from 'react-bootstrap';
|
import {Modal} from 'react-bootstrap';
|
||||||
|
import {act} from 'react-dom/test-utils';
|
||||||
|
|
||||||
import GetLinkModal from 'components/get_link_modal';
|
import GetLinkModal from 'components/get_link_modal';
|
||||||
|
|
||||||
@@ -19,6 +20,14 @@ describe('components/GetLinkModal', () => {
|
|||||||
link: 'https://mattermost.com',
|
link: 'https://mattermost.com',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
test('should match snapshot when all props is set', () => {
|
test('should match snapshot when all props is set', () => {
|
||||||
const helpText = 'help text';
|
const helpText = 'help text';
|
||||||
const props = {...requiredProps, helpText};
|
const props = {...requiredProps, helpText};
|
||||||
@@ -68,4 +77,44 @@ describe('components/GetLinkModal', () => {
|
|||||||
wrapper.find('#linkModalTextArea').simulate('click');
|
wrapper.find('#linkModalTextArea').simulate('click');
|
||||||
expect(wrapper.state('copiedLink')).toBe(true);
|
expect(wrapper.state('copiedLink')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should change button state when copying', () => {
|
||||||
|
const wrapper = mountWithIntl(
|
||||||
|
<GetLinkModal {...requiredProps}/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').text()).toContain('Copy Link');
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-primary')).toBe(true);
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-success')).toBe(false);
|
||||||
|
|
||||||
|
// After copying
|
||||||
|
wrapper.find('#linkModalCopyLink').simulate('click');
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').text()).toContain('Copied');
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-primary')).toBe(true);
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-success')).toBe(true);
|
||||||
|
|
||||||
|
// After timeout
|
||||||
|
act(() => {
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').text()).toContain('Copy Link');
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-primary')).toBe(true);
|
||||||
|
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-success')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should cleanup timeout on unmount', () => {
|
||||||
|
const wrapper = mountWithIntl(
|
||||||
|
<GetLinkModal {...requiredProps}/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
wrapper.find('#linkModalCopyLink').simulate('click');
|
||||||
|
expect(wrapper.state('copiedLink')).toBe(true);
|
||||||
|
|
||||||
|
wrapper.unmount();
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
|
||||||
|
// If we get here without errors, the timeout was properly cleaned up
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import React from 'react';
|
|||||||
import {Modal} from 'react-bootstrap';
|
import {Modal} from 'react-bootstrap';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
import SuccessIcon from 'components/widgets/icons/fa_success_icon';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
@@ -22,6 +20,7 @@ type State = {
|
|||||||
|
|
||||||
export default class GetLinkModal extends React.PureComponent<Props, State> {
|
export default class GetLinkModal extends React.PureComponent<Props, State> {
|
||||||
private textAreaRef = React.createRef<HTMLTextAreaElement>();
|
private textAreaRef = React.createRef<HTMLTextAreaElement>();
|
||||||
|
private resetTimeout: NodeJS.Timeout | null = null;
|
||||||
public static defaultProps = {
|
public static defaultProps = {
|
||||||
helpText: null,
|
helpText: null,
|
||||||
};
|
};
|
||||||
@@ -33,7 +32,16 @@ export default class GetLinkModal extends React.PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount(): void {
|
||||||
|
if (this.resetTimeout) {
|
||||||
|
clearTimeout(this.resetTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public onHide = (): void => {
|
public onHide = (): void => {
|
||||||
|
if (this.resetTimeout) {
|
||||||
|
clearTimeout(this.resetTimeout);
|
||||||
|
}
|
||||||
this.setState({copiedLink: false});
|
this.setState({copiedLink: false});
|
||||||
this.props.onHide();
|
this.props.onHide();
|
||||||
};
|
};
|
||||||
@@ -47,6 +55,12 @@ export default class GetLinkModal extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
this.setState({copiedLink: document.execCommand('copy')});
|
this.setState({copiedLink: document.execCommand('copy')});
|
||||||
|
if (this.resetTimeout) {
|
||||||
|
clearTimeout(this.resetTimeout);
|
||||||
|
}
|
||||||
|
this.resetTimeout = setTimeout(() => {
|
||||||
|
this.setState({copiedLink: false});
|
||||||
|
}, 1000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({copiedLink: false});
|
this.setState({copiedLink: false});
|
||||||
}
|
}
|
||||||
@@ -73,13 +87,26 @@ export default class GetLinkModal extends React.PureComponent<Props, State> {
|
|||||||
id='linkModalCopyLink'
|
id='linkModalCopyLink'
|
||||||
data-copy-btn='true'
|
data-copy-btn='true'
|
||||||
type='button'
|
type='button'
|
||||||
className='btn btn-primary pull-left'
|
className={`btn ${this.state.copiedLink ? 'btn-primary btn-success' : 'btn-primary'} pull-left`}
|
||||||
onClick={this.copyLink}
|
onClick={this.copyLink}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
{this.state.copiedLink ? (
|
||||||
id='get_link.copy'
|
<>
|
||||||
defaultMessage='Copy Link'
|
<i className='icon icon-check'/>
|
||||||
/>
|
<FormattedMessage
|
||||||
|
id='get_link.clipboard'
|
||||||
|
defaultMessage='Copied'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<i className='icon icon-link-variant'/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='get_link.copy'
|
||||||
|
defaultMessage='Copy Link'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -96,19 +123,6 @@ export default class GetLinkModal extends React.PureComponent<Props, State> {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
let copyLinkConfirm = null;
|
|
||||||
if (this.state.copiedLink) {
|
|
||||||
copyLinkConfirm = (
|
|
||||||
<p className='alert alert-success alert--confirm'>
|
|
||||||
<SuccessIcon/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='get_link.clipboard'
|
|
||||||
defaultMessage=' Link copied'
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
dialogClassName='a11y__modal'
|
dialogClassName='a11y__modal'
|
||||||
@@ -122,7 +136,7 @@ export default class GetLinkModal extends React.PureComponent<Props, State> {
|
|||||||
id='getLinkModalLabel'
|
id='getLinkModalLabel'
|
||||||
closeButton={true}
|
closeButton={true}
|
||||||
>
|
>
|
||||||
<h4 className='modal-title'>{this.props.title}</h4>
|
<h2 className='modal-title'>{this.props.title}</h2>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
{helpText}
|
{helpText}
|
||||||
@@ -141,7 +155,6 @@ export default class GetLinkModal extends React.PureComponent<Props, State> {
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{copyLink}
|
{copyLink}
|
||||||
{copyLinkConfirm}
|
|
||||||
</Modal.Footer>
|
</Modal.Footer>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -230,6 +230,19 @@ button {
|
|||||||
color: rgba(var(--center-channel-color-rgb), 0.32) !important;
|
color: rgba(var(--center-channel-color-rgb), 0.32) !important;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.btn-success {
|
||||||
|
background-color: var(--online-indicator);
|
||||||
|
color: var(--button-color-rgb);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--online-indicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: var(--online-indicator);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-secondary {
|
&.btn-secondary {
|
||||||
|
|||||||
@@ -328,6 +328,7 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
color: functions.v(center-channel-color);
|
color: functions.v(center-channel-color);
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user