diff --git a/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-chrome-linux.png b/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-chrome-linux.png index d5413750a4..18cae0f349 100644 Binary files a/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-chrome-linux.png and b/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-chrome-linux.png differ diff --git a/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-firefox-linux.png b/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-firefox-linux.png index 7c62418ffb..8d4544a6ff 100644 Binary files a/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-firefox-linux.png and b/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-firefox-linux.png differ diff --git a/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-ipad-linux.png b/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-ipad-linux.png index 76fbd67f91..9cbccdb7ed 100644 Binary files a/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-ipad-linux.png and b/e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts-snapshots/intro-to-channel-as-regular-user-ipad-linux.png differ diff --git a/webapp/channels/src/components/__snapshots__/get_link_modal.test.tsx.snap b/webapp/channels/src/components/__snapshots__/get_link_modal.test.tsx.snap index 1605e40299..ccfe41c9d6 100644 --- a/webapp/channels/src/components/__snapshots__/get_link_modal.test.tsx.snap +++ b/webapp/channels/src/components/__snapshots__/get_link_modal.test.tsx.snap @@ -36,11 +36,11 @@ exports[`components/GetLinkModal should have called onHide 1`] = ` closeLabel="Close" id="getLinkModalLabel" > -

title -

+ -

- -

+ `; @@ -131,11 +125,11 @@ exports[`components/GetLinkModal should have called onHide 2`] = ` closeLabel="Close" id="getLinkModalLabel" > -

title -

+ + -

title -

+ + -

title -

+ + { link: 'https://mattermost.com', }; + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + test('should match snapshot when all props is set', () => { const helpText = 'help text'; const props = {...requiredProps, helpText}; @@ -68,4 +77,44 @@ describe('components/GetLinkModal', () => { wrapper.find('#linkModalTextArea').simulate('click'); expect(wrapper.state('copiedLink')).toBe(true); }); + + test('should change button state when copying', () => { + const wrapper = mountWithIntl( + , + ); + + // 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( + , + ); + + 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 + }); }); diff --git a/webapp/channels/src/components/get_link_modal.tsx b/webapp/channels/src/components/get_link_modal.tsx index c656f388e3..b9dd26dd38 100644 --- a/webapp/channels/src/components/get_link_modal.tsx +++ b/webapp/channels/src/components/get_link_modal.tsx @@ -5,8 +5,6 @@ import React from 'react'; import {Modal} from 'react-bootstrap'; import {FormattedMessage} from 'react-intl'; -import SuccessIcon from 'components/widgets/icons/fa_success_icon'; - type Props = { show: boolean; onHide: () => void; @@ -22,6 +20,7 @@ type State = { export default class GetLinkModal extends React.PureComponent { private textAreaRef = React.createRef(); + private resetTimeout: NodeJS.Timeout | null = null; public static defaultProps = { helpText: null, }; @@ -33,7 +32,16 @@ export default class GetLinkModal extends React.PureComponent { }; } + public componentWillUnmount(): void { + if (this.resetTimeout) { + clearTimeout(this.resetTimeout); + } + } + public onHide = (): void => { + if (this.resetTimeout) { + clearTimeout(this.resetTimeout); + } this.setState({copiedLink: false}); this.props.onHide(); }; @@ -47,6 +55,12 @@ export default class GetLinkModal extends React.PureComponent { try { this.setState({copiedLink: document.execCommand('copy')}); + if (this.resetTimeout) { + clearTimeout(this.resetTimeout); + } + this.resetTimeout = setTimeout(() => { + this.setState({copiedLink: false}); + }, 1000); } catch (err) { this.setState({copiedLink: false}); } @@ -73,13 +87,26 @@ export default class GetLinkModal extends React.PureComponent { id='linkModalCopyLink' data-copy-btn='true' type='button' - className='btn btn-primary pull-left' + className={`btn ${this.state.copiedLink ? 'btn-primary btn-success' : 'btn-primary'} pull-left`} onClick={this.copyLink} > - + {this.state.copiedLink ? ( + <> + + + + ) : ( + <> + + + + )} ); } @@ -96,19 +123,6 @@ export default class GetLinkModal extends React.PureComponent { /> ); - let copyLinkConfirm = null; - if (this.state.copiedLink) { - copyLinkConfirm = ( -

- - -

- ); - } - return ( { id='getLinkModalLabel' closeButton={true} > -

{this.props.title}

+

{this.props.title}

{helpText} @@ -141,7 +155,6 @@ export default class GetLinkModal extends React.PureComponent { /> {copyLink} - {copyLinkConfirm}
); diff --git a/webapp/channels/src/sass/components/_buttons.scss b/webapp/channels/src/sass/components/_buttons.scss index ea0f06e8a3..9133bc9e18 100644 --- a/webapp/channels/src/sass/components/_buttons.scss +++ b/webapp/channels/src/sass/components/_buttons.scss @@ -230,6 +230,19 @@ button { color: rgba(var(--center-channel-color-rgb), 0.32) !important; 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 { diff --git a/webapp/channels/src/sass/components/_modal.scss b/webapp/channels/src/sass/components/_modal.scss index bc6d3d3124..6cd70b3bfd 100644 --- a/webapp/channels/src/sass/components/_modal.scss +++ b/webapp/channels/src/sass/components/_modal.scss @@ -328,6 +328,7 @@ background: transparent; color: functions.v(center-channel-color); font-size: 22px; + font-weight: 600; line-height: 28px; word-break: break-word; }