MM-49865 - add telemetry to account creation screen (#22702)

* MM-49865 - add telemetry to account creation screen

* fix snapshots
Этот коммит содержится в:
Pablo Andrés Vélez Vidal
2023-03-29 13:39:27 +02:00
коммит произвёл GitHub
родитель 529ab959e2
Коммит b892fe0555
3 изменённых файлов: 60 добавлений и 11 удалений

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

@@ -58,6 +58,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
disabled={false}
inputSize="large"
name="email"
onBlur={[Function]}
onChange={[Function]}
placeholder="Email address"
type="text"
@@ -75,6 +76,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
disabled={false}
inputSize="large"
name="name"
onBlur={[Function]}
onChange={[Function]}
placeholder="Choose a Username"
type="text"
@@ -87,6 +89,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
error=""
info="Must be 5-64 characters long."
inputSize="large"
onBlur={[Function]}
onChange={[Function]}
value=""
/>
@@ -206,6 +209,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
disabled={false}
inputSize="large"
name="email"
onBlur={[Function]}
onChange={[Function]}
placeholder="Email address"
type="text"
@@ -223,6 +227,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
disabled={false}
inputSize="large"
name="name"
onBlur={[Function]}
onChange={[Function]}
placeholder="Choose a Username"
type="text"
@@ -235,6 +240,7 @@ exports[`components/signup/Signup should match snapshot for all signup options e
error=""
info="Must be 5-64 characters long."
inputSize="large"
onBlur={[Function]}
onChange={[Function]}
value=""
/>

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

@@ -1,7 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useState, useEffect, useRef, useCallback} from 'react';
import React, {useState, useEffect, useRef, useCallback, FocusEvent} from 'react';
import {useIntl} from 'react-intl';
import {useLocation, useHistory} from 'react-router-dom';
import {useSelector, useDispatch} from 'react-redux';
@@ -455,16 +456,25 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
}
};
function sendSignUpTelemetryEvents(telemetryId: string, props?: any) {
trackEvent('signup', telemetryId, props);
}
type TelemetryErrorList = {errors: Array<{field: string; rule: string}>; success: boolean};
const isUserValid = () => {
let isValid = true;
const providedEmail = emailInput.current?.value.trim();
const telemetryEvents: TelemetryErrorList = {errors: [], success: true};
if (!providedEmail) {
setEmailError(formatMessage({id: 'signup_user_completed.required', defaultMessage: 'This field is required'}));
telemetryEvents.errors.push({field: 'email', rule: 'not_provided'});
isValid = false;
} else if (!isEmail(providedEmail)) {
setEmailError(formatMessage({id: 'signup_user_completed.validEmail', defaultMessage: 'Please enter a valid email address'}));
telemetryEvents.errors.push({field: 'email', rule: 'invalid_email'});
isValid = false;
}
@@ -474,10 +484,11 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
const usernameError = isValidUsername(providedUsername);
if (usernameError) {
setNameError(usernameError.id === ValidationErrors.RESERVED_NAME ? (
formatMessage({id: 'signup_user_completed.reserved', defaultMessage: 'This username is reserved, please choose a new one.'})
) : (
formatMessage(
let nameError = '';
if (usernameError.id === ValidationErrors.RESERVED_NAME) {
nameError = formatMessage({id: 'signup_user_completed.reserved', defaultMessage: 'This username is reserved, please choose a new one.'});
} else {
nameError = formatMessage(
{
id: 'signup_user_completed.usernameLength',
defaultMessage: 'Usernames have to begin with a lowercase letter and be {min}-{max} characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.',
@@ -486,23 +497,33 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
min: Constants.MIN_USERNAME_LENGTH,
max: Constants.MAX_USERNAME_LENGTH,
},
)
));
);
}
telemetryEvents.errors.push({field: 'username', rule: usernameError.id.toLowerCase()});
setNameError(nameError);
isValid = false;
}
} else {
setNameError(formatMessage({id: 'signup_user_completed.required', defaultMessage: 'This field is required'}));
telemetryEvents.errors.push({field: 'username', rule: 'not_provided'});
isValid = false;
}
const providedPassword = passwordInput.current?.value ?? '';
const {error} = isValidPassword(providedPassword, getPasswordConfig(config), intl);
const {error, telemetryErrorIds} = isValidPassword(providedPassword, getPasswordConfig(config), intl);
if (error) {
setPasswordError(error as string);
telemetryEvents.errors = [...telemetryEvents.errors, ...telemetryErrorIds];
isValid = false;
}
if (telemetryEvents.errors.length) {
telemetryEvents.success = false;
}
sendSignUpTelemetryEvents('validate_user', telemetryEvents);
return isValid;
};
@@ -512,7 +533,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
const handleSubmit = async (e: React.MouseEvent | React.KeyboardEvent) => {
e.preventDefault();
trackEvent('signup_email', 'click_create_account', getRoleFromTrackFlow());
sendSignUpTelemetryEvents('click_create_account', getRoleFromTrackFlow());
setIsWaiting(true);
if (isUserValid()) {
@@ -550,6 +571,14 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
const handleReturnButtonOnClick = () => history.replace('/');
const handleOnBlur = (e: FocusEvent<HTMLInputElement>, inputId: string) => {
const text = e.target.value;
if (!text) {
return;
}
sendSignUpTelemetryEvents(`typed_input_${inputId}`);
};
const getContent = () => {
if (!enableSignUpWithEmail && !enableExternalSignup) {
return (
@@ -671,6 +700,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
disabled={isWaiting || Boolean(parsedEmail)}
autoFocus={true}
customMessage={emailCustomLabelForInput}
onBlur={(e) => handleOnBlur(e, 'email')}
/>
<Input
ref={nameInput}
@@ -692,6 +722,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
value: formatMessage({id: 'signup_user_completed.userHelp', defaultMessage: 'You can use lowercase letters, numbers, periods, dashes, and underscores.'}),
}
}
onBlur={(e) => handleOnBlur(e, 'username')}
/>
<PasswordInput
ref={passwordInput}
@@ -703,6 +734,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
createMode={true}
info={passwordInfo as string}
error={passwordError}
onBlur={(e) => handleOnBlur(e, 'password')}
/>
<SaveButton
extraClasses='signup-body-card-form-button-submit large'

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

@@ -1366,11 +1366,13 @@ export function getPasswordConfig(config: Partial<ClientConfig>) {
export function isValidPassword(password: string, passwordConfig: ReturnType<typeof getPasswordConfig>, intl?: IntlShape) {
let errorId = t('user.settings.security.passwordError');
const telemetryErrorIds = [];
let valid = true;
const minimumLength = passwordConfig.minimumLength || Constants.MIN_PASSWORD_LENGTH;
if (password.length < minimumLength || password.length > Constants.MAX_PASSWORD_LENGTH) {
valid = false;
telemetryErrorIds.push({field: 'password', rule: 'error_length'});
}
if (passwordConfig.requireLowercase) {
@@ -1379,6 +1381,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
}
errorId += 'Lowercase';
telemetryErrorIds.push({field: 'password', rule: 'lowercase'});
}
if (passwordConfig.requireUppercase) {
@@ -1387,6 +1390,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
}
errorId += 'Uppercase';
telemetryErrorIds.push({field: 'password', rule: 'uppercase'});
}
if (passwordConfig.requireNumber) {
@@ -1395,6 +1399,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
}
errorId += 'Number';
telemetryErrorIds.push({field: 'password', rule: 'number'});
}
if (passwordConfig.requireSymbol) {
@@ -1403,6 +1408,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
}
errorId += 'Symbol';
telemetryErrorIds.push({field: 'password', rule: 'symbol'});
}
let error;
@@ -1430,7 +1436,7 @@ export function isValidPassword(password: string, passwordConfig: ReturnType<typ
);
}
return {valid, error};
return {valid, error, telemetryErrorIds};
}
function isChannelOrPermalink(link: string) {
@@ -1828,9 +1834,14 @@ export function getRoleForTrackFlow() {
return {started_by_role: startedByRole};
}
export function getRoleFromTrackFlow() {
export function getSbr() {
const params = new URLSearchParams(window.location.search);
const sbr = params.get('sbr') ?? '';
return sbr;
}
export function getRoleFromTrackFlow() {
const sbr = getSbr();
const startedByRole = TrackFlowRoles[sbr] ?? '';
return {started_by_role: startedByRole};