[MM-63015] Add more descriptive page titles to login/create account/password reset pages (#30857)

* [MM-63015] Add more descriptive page titles to login/create account/password reset pages

* PR feedback

* Fix e2e

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Devin Binnie
2025-05-08 10:48:31 -04:00
коммит произвёл GitHub
родитель 0e806bf8e8
Коммит c33dbe8d37
9 изменённых файлов: 62 добавлений и 81 удалений

Просмотреть файл

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

Просмотреть файл

@@ -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(() => {

Просмотреть файл

@@ -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<HTMLInputElement>(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();

Просмотреть файл

@@ -1,60 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/PasswordResetSendLink should match snapshot 1`] = `
<div>
<BackButton
url="/"
/>
<div
className="col-sm-12"
>
<div
className="signup-team__container"
>
<MemoizedFormattedMessage
defaultMessage="Password Reset"
id="password_send.title"
tagName="h1"
/>
<form
onSubmit={[Function]}
>
<p>
<MemoizedFormattedMessage
defaultMessage="To reset your password, enter the email address you used to sign up"
id="password_send.description"
/>
</p>
<div
className="form-group"
>
<LocalizedPlaceholderInput
autoFocus={true}
className="form-control"
id="passwordResetEmailInput"
name="email"
placeholder={
Object {
"defaultMessage": "Email",
"id": "password_send.email",
}
}
spellCheck="false"
type="email"
/>
</div>
<button
className="btn btn-primary"
id="passwordResetButton"
type="submit"
>
<MemoizedFormattedMessage
defaultMessage="Reset my password"
id="password_send.reset"
/>
</button>
</form>
</div>
</div>
</div>
`;

Просмотреть файл

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

Просмотреть файл

@@ -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(<PasswordResetSendLink {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
});
it('should calls sendPasswordResetEmail() action on submit', () => {
const props = {...baseProps};
@@ -28,9 +23,9 @@ describe('components/PasswordResetSendLink', () => {
<MemoryRouter>
<PasswordResetSendLink {...props}/>
</MemoryRouter>,
).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');

Просмотреть файл

@@ -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<ActionResult>;
};
@@ -21,7 +24,7 @@ interface State {
updateText: React.ReactNode;
}
export default class PasswordResetSendLink extends React.PureComponent<Props, State> {
export class PasswordResetSendLink extends React.PureComponent<Props, State> {
state = {
error: null,
updateText: null,
@@ -29,6 +32,17 @@ export default class PasswordResetSendLink extends React.PureComponent<Props, St
resetForm = React.createRef<HTMLFormElement>();
emailInput = React.createRef<HTMLInputElement>();
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<Props, St
);
}
}
export default injectIntl(PasswordResetSendLink);

Просмотреть файл

@@ -355,10 +355,14 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
}, []);
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) {

Просмотреть файл

@@ -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 <a1>Terms of Use</a1> and <a2>Privacy Policy</a2>. 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",