diff --git a/e2e-tests/cypress/tests/integration/channels/system_console/ui_and_api/custom_site_name_description_spec.ts b/e2e-tests/cypress/tests/integration/channels/system_console/ui_and_api/custom_site_name_description_spec.ts index 545137e09f..efc50eac6c 100644 --- a/e2e-tests/cypress/tests/integration/channels/system_console/ui_and_api/custom_site_name_description_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/system_console/ui_and_api/custom_site_name_description_spec.ts @@ -22,7 +22,7 @@ describe('Customization', () => { cy.visit('/login'); // * Verify that the head tag contains default title and without og:description - cy.get('head').find('title').should('have.text', defaultTitle); + cy.get('head').find('title').should('have.text', `${defaultDescription} - ${defaultTitle}`); cy.get('head').get('meta[property="og:description"]').should('not.exist'); // * Verify that the header contains default logo/image @@ -40,7 +40,7 @@ describe('Customization', () => { cy.visit(''); // * Verify that the head tag contains custom title and description - cy.get('head').find('title').should('have.text', customTitle); + cy.get('head').find('title').should('have.text', `${defaultDescription} - ${customTitle}`); cy.get('head').get('meta[property="og:description"]').should('have.attr', 'content', customDescription); // * Verify that the header contains custom title diff --git a/webapp/channels/src/components/login/login.tsx b/webapp/channels/src/components/login/login.tsx index 5283ae5de1..e4a11ae4be 100644 --- a/webapp/channels/src/components/login/login.tsx +++ b/webapp/channels/src/components/login/login.tsx @@ -244,11 +244,17 @@ const Login = ({onCustomizeHeader}: LoginProps) => { formatMessage( { id: 'login.session_expired.title', - defaultMessage: '* {siteName} - Session Expired', + defaultMessage: '* Session Expired - {siteName}', }, {siteName}, ) - ) : siteName; + ) : formatMessage( + { + id: 'login.pageTitle', + defaultMessage: 'Log in - {siteName}', + }, + {siteName}, + ); }, [sessionExpired, siteName]); const showSessionExpiredNotificationIfNeeded = useCallback(() => { diff --git a/webapp/channels/src/components/password_reset_form/password_reset_form.tsx b/webapp/channels/src/components/password_reset_form/password_reset_form.tsx index 42d6a35963..af53931be9 100644 --- a/webapp/channels/src/components/password_reset_form/password_reset_form.tsx +++ b/webapp/channels/src/components/password_reset_form/password_reset_form.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import classNames from 'classnames'; -import React, {useState, useRef, memo} from 'react'; +import React, {useState, useRef, memo, useEffect} from 'react'; import {FormattedMessage, useIntl} from 'react-intl'; import {useHistory} from 'react-router-dom'; @@ -27,6 +27,16 @@ const PasswordResetForm = ({location, siteName, actions}: Props) => { const passwordInput = useRef(null); + useEffect(() => { + document.title = intl.formatMessage( + { + id: 'password_form.pageTitle', + defaultMessage: 'Password Reset | {siteName}', + }, + {siteName: siteName || 'Mattermost'}, + ); + }, [intl, siteName]); + const handlePasswordReset = async (e: React.FormEvent) => { e.preventDefault(); diff --git a/webapp/channels/src/components/password_reset_send_link/__snapshots__/password_reset_send_link.test.tsx.snap b/webapp/channels/src/components/password_reset_send_link/__snapshots__/password_reset_send_link.test.tsx.snap deleted file mode 100644 index 4425152bfc..0000000000 --- a/webapp/channels/src/components/password_reset_send_link/__snapshots__/password_reset_send_link.test.tsx.snap +++ /dev/null @@ -1,60 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/PasswordResetSendLink should match snapshot 1`] = ` -
- -
-
- -
-

- -

-
- -
- -
-
-
-
-`; diff --git a/webapp/channels/src/components/password_reset_send_link/index.ts b/webapp/channels/src/components/password_reset_send_link/index.ts index c09f2c1c3e..99f086726b 100644 --- a/webapp/channels/src/components/password_reset_send_link/index.ts +++ b/webapp/channels/src/components/password_reset_send_link/index.ts @@ -6,13 +6,20 @@ import {bindActionCreators} from 'redux'; import type {Dispatch} from 'redux'; import {sendPasswordResetEmail} from 'mattermost-redux/actions/users'; +import {getConfig} from 'mattermost-redux/selectors/entities/general'; + +import type {GlobalState} from 'types/store'; import PasswordResetSendLink from './password_reset_send_link'; +function mapStateToProps(state: GlobalState) { + return {siteName: getConfig(state).SiteName}; +} + const mapDispatchToProps = (dispatch: Dispatch) => ({ actions: bindActionCreators({ sendPasswordResetEmail, }, dispatch), }); -export default connect(null, mapDispatchToProps)(PasswordResetSendLink); +export default connect(mapStateToProps, mapDispatchToProps)(PasswordResetSendLink); diff --git a/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.test.tsx b/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.test.tsx index 54d3fb5df9..e9d1ec5264 100644 --- a/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.test.tsx +++ b/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.test.tsx @@ -1,13 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; import {MemoryRouter} from 'react-router-dom'; import {mountWithIntl} from 'tests/helpers/intl-test-helper'; import PasswordResetSendLink from './password_reset_send_link'; +import type {PasswordResetSendLink as PasswordResetSendLinkType} from './password_reset_send_link'; describe('components/PasswordResetSendLink', () => { const baseProps = { @@ -16,11 +16,6 @@ describe('components/PasswordResetSendLink', () => { }, }; - it('should match snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - it('should calls sendPasswordResetEmail() action on submit', () => { const props = {...baseProps}; @@ -28,9 +23,9 @@ describe('components/PasswordResetSendLink', () => { , - ).children().children(); + ).children().children().children(); - (wrapper.instance() as PasswordResetSendLink).emailInput.current!.value = 'test@example.com'; + (wrapper.instance() as PasswordResetSendLinkType).emailInput.current!.value = 'test@example.com'; wrapper.find('form').simulate('submit', {preventDefault: () => {}}); expect(props.actions.sendPasswordResetEmail).toHaveBeenCalledWith('test@example.com'); diff --git a/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.tsx b/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.tsx index 6c90939fc2..5fc6452d4a 100644 --- a/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.tsx +++ b/webapp/channels/src/components/password_reset_send_link/password_reset_send_link.tsx @@ -2,7 +2,8 @@ // See LICENSE.txt for license information. import React from 'react'; -import {defineMessage, FormattedMessage} from 'react-intl'; +import {defineMessage, FormattedMessage, injectIntl} from 'react-intl'; +import type {IntlShape} from 'react-intl'; import type {ActionResult} from 'mattermost-redux/types/actions'; import {isEmail} from 'mattermost-redux/utils/helpers'; @@ -11,6 +12,8 @@ import BackButton from 'components/common/back_button'; import LocalizedPlaceholderInput from 'components/localized_placeholder_input'; export interface Props { + intl: IntlShape; + siteName?: string; actions: { sendPasswordResetEmail: (email: string) => Promise; }; @@ -21,7 +24,7 @@ interface State { updateText: React.ReactNode; } -export default class PasswordResetSendLink extends React.PureComponent { +export class PasswordResetSendLink extends React.PureComponent { state = { error: null, updateText: null, @@ -29,6 +32,17 @@ export default class PasswordResetSendLink extends React.PureComponent(); emailInput = React.createRef(); + componentDidMount() { + const {intl, siteName} = this.props; + document.title = intl.formatMessage( + { + id: 'password_form.pageTitle', + defaultMessage: 'Password Reset | {siteName}', + }, + {siteName: siteName || 'Mattermost'}, + ); + } + handleSendLink = async (e: React.FormEvent) => { e.preventDefault(); @@ -152,3 +166,5 @@ export default class PasswordResetSendLink extends React.PureComponent { }, []); useEffect(() => { - if (SiteName) { - document.title = SiteName; - } - }, [SiteName]); + document.title = formatMessage( + { + id: 'signup.title', + defaultMessage: 'Create Account | {siteName}', + }, + {siteName: SiteName || 'Mattermost'}, + ); + }, [formatMessage, SiteName]); useEffect(() => { if (onCustomizeHeader) { diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index 63d6243855..51a87f50e8 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -4390,12 +4390,13 @@ "login.office365": "Entra ID", "login.openid": "Open ID", "login.or": "or log in with", + "login.pageTitle": "Log in - {siteName}", "login.passwordChanged": " Password updated successfully", "login.placeholderOr": " or ", "login.saml": "SAML", "login.session_expired": "Your session has expired. Please log in again.", "login.session_expired.notification": "Session Expired: Please sign in to continue receiving notifications.", - "login.session_expired.title": "* {siteName} - Session Expired", + "login.session_expired.title": "* Session Expired - {siteName}", "login.subtitle": "Collaborate with your team in real-time", "login.terms_rejected": "You must agree to the terms of use before accessing {siteName}. Please contact your System Administrator for more details.", "login.title": "Log in to your account", @@ -4717,6 +4718,7 @@ "ONE_TO_50": "1-50", "password_form.change": "Change my password", "password_form.enter": "Enter a new password for your {siteName} account.", + "password_form.pageTitle": "Password Reset | {siteName}", "password_form.pwd": "Password", "password_form.title": "Password Reset", "password_send.checkInbox": "Please check your inbox.", @@ -5372,6 +5374,7 @@ "signup_user_completed.validEmail": "Please enter a valid email address", "signup.agreement": "By proceeding to create your account and use {siteName}, you agree to our Terms of Use and Privacy Policy. If you do not agree, you cannot use {siteName}.", "signup.ldap": "AD/LDAP Credentials", + "signup.title": "Create Account | {siteName}", "single_image_view.copied_link_tooltip": "Copied", "single_image_view.copy_link_tooltip": "Copy link", "single_image_view.download_tooltip": "Download",