Skip to content

Commit df5cae1

Browse files
authored
Merge pull request #18843 from mozilla/fxa-11614
feat(reg): remove newsletters from reg form, autofocus set password
2 parents dc97ac3 + ebd9042 commit df5cae1

10 files changed

Lines changed: 120 additions & 97 deletions

File tree

packages/fxa-settings/src/components/ChooseNewsletters/index.stories.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import React from 'react';
66
import ChooseNewsletters from '.';
77
import AppLayout from '../AppLayout';
88
import { Meta } from '@storybook/react';
9-
import { Subject } from './mocks';
9+
import { SubjectWithNewsletters, SubjectWithNone } from './mocks';
1010
import { withLocalization } from 'fxa-react/lib/storybooks';
1111

1212
export default {
@@ -15,10 +15,18 @@ export default {
1515
decorators: [withLocalization],
1616
} as Meta;
1717

18-
export const Default = () => {
18+
export const DefaultNone = () => {
1919
return (
2020
<AppLayout>
21-
<Subject />
21+
<SubjectWithNone />
22+
</AppLayout>
23+
);
24+
};
25+
26+
export const DefaultLetters = () => {
27+
return (
28+
<AppLayout>
29+
<SubjectWithNewsletters />
2230
</AppLayout>
2331
);
2432
};

packages/fxa-settings/src/components/ChooseNewsletters/index.test.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,27 @@ import { screen } from '@testing-library/react';
77
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
88
import { getFtlBundle, testAllL10n } from 'fxa-react/lib/test-utils';
99
import { FluentBundle } from '@fluent/bundle';
10-
import { Subject } from './mocks';
11-
import { newsletters } from './newsletters';
10+
import { SubjectWithNewsletters, SubjectWithNone } from './mocks';
1211

1312
describe('ChooseNewsletters component', () => {
1413
let bundle: FluentBundle;
1514
beforeAll(async () => {
1615
bundle = await getFtlBundle('settings');
1716
});
1817
it('renders newsletter options as expected', async () => {
19-
renderWithLocalizationProvider(<Subject />);
18+
renderWithLocalizationProvider(<SubjectWithNewsletters />);
2019
testAllL10n(screen, bundle);
2120

2221
screen.getByText('Get more from Mozilla:');
2322

2423
const checkboxes = await screen.findAllByRole('checkbox');
25-
expect(checkboxes).toHaveLength(newsletters.length);
24+
expect(checkboxes).toHaveLength(1);
25+
});
26+
27+
it('does not render with empty newletters', async () => {
28+
renderWithLocalizationProvider(<SubjectWithNone />);
29+
expect(
30+
screen.queryByText('Get more from Mozilla:')
31+
).not.toBeInTheDocument();
2632
});
2733
});

packages/fxa-settings/src/components/ChooseNewsletters/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ const ChooseNewsletters = ({
3030
});
3131
};
3232

33+
if (newsletters.length === 0) {
34+
return null;
35+
}
36+
3337
return (
3438
<>
3539
<FtlMsg id="choose-newsletters-prompt-2">

packages/fxa-settings/src/components/ChooseNewsletters/mocks.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,25 @@ import React, { useState } from 'react';
66
import ChooseNewsletters from '.';
77
import { newsletters } from './newsletters';
88

9-
export const Subject = () => {
9+
export const SubjectWithNewsletters = () => {
10+
const [, setSelected] = useState<string[]>([]);
11+
12+
const newsletters = [
13+
{
14+
label: 'Early access to test new products',
15+
slug: ['test-pilot'],
16+
ftlId: 'choose-newsletters-option-test-pilot',
17+
},
18+
];
19+
return (
20+
<ChooseNewsletters
21+
{...{ newsletters }}
22+
setSelectedNewsletterSlugs={setSelected}
23+
/>
24+
);
25+
};
26+
27+
export const SubjectWithNone = () => {
1028
const [, setSelected] = useState<string[]>([]);
1129

1230
return (

packages/fxa-settings/src/components/ChooseNewsletters/newsletters.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@ export type Newsletter = {
1414
ftlId: string;
1515
};
1616

17-
export const newsletters: Newsletter[] = [
18-
{
19-
label: 'Get our latest news and product updates',
20-
slug: ['mozilla-and-you', 'mozilla-accounts'],
21-
ftlId: 'choose-newsletters-option-latest-news',
22-
},
23-
{
24-
label: 'Action alerts to reclaim the internet',
25-
slug: ['mozilla-foundation'],
26-
ftlId: 'choose-newsletters-option-reclaim-the-internet',
27-
},
28-
{
29-
label: 'Early access to test new products',
30-
slug: ['test-pilot'],
31-
ftlId: 'choose-newsletters-option-test-pilot',
32-
},
33-
];
17+
/**
18+
* Example newsletters:
19+
* [{
20+
* label: 'Get our latest news and product updates',
21+
* slug: ['mozilla-and-you', 'mozilla-accounts'],
22+
* ftlId: 'choose-newsletters-option-latest-news',
23+
* },
24+
* {
25+
* label: 'Action alerts to reclaim the internet',
26+
* slug: ['mozilla-foundation'],
27+
* ftlId: 'choose-newsletters-option-reclaim-the-internet',
28+
* },
29+
* {
30+
* label: 'Early access to test new products',
31+
* slug: ['test-pilot'],
32+
* ftlId: 'choose-newsletters-option-test-pilot',
33+
* }]
34+
*/
35+
export const newsletters: Newsletter[] = [];

packages/fxa-settings/src/components/FormPasswordWithBalloons/index.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export const FormPasswordWithBalloons = ({
9595
useState<string>();
9696
const [srOnlyConfirmPwdFeedbackMessage, setSROnlyConfirmPwdFeedbackMessage] =
9797
useState<string>();
98+
const [emitEngageEvent, setEmitEngageEvent] = useState<boolean>(false);
9899

99100
const ftlMsgResolver = useFtlMsgResolver();
100101
const localizedPasswordMatchError = ftlMsgResolver.getMsg(
@@ -154,13 +155,31 @@ export const FormPasswordWithBalloons = ({
154155
setSROnlyConfirmPwdFeedbackMessage('');
155156
setPasswordMatchErrorText('');
156157
if (!hasNewPwdFocused) {
157-
if (onFocusMetricsEvent) {
158-
onFocusMetricsEvent();
159-
}
160158
setHasNewPwdFocused(true);
161159
}
162160
};
163161

162+
// We are currently on React form hook V6 and this version
163+
// does not expose any focus methods. Upgrading is major refactor
164+
// so instead we pass an optional `inputRefDOM` that can
165+
// trigger focus and click events.
166+
const newPasswordRef = useRef<HTMLInputElement>(null);
167+
168+
// Auto-focus the new password input on mount
169+
useEffect(() => {
170+
if (newPasswordRef.current) {
171+
newPasswordRef.current.focus();
172+
newPasswordRef.current.click();
173+
}
174+
}, []);
175+
176+
useEffect(() => {
177+
if (hasUserTakenAction && onFocusMetricsEvent && !emitEngageEvent) {
178+
onFocusMetricsEvent();
179+
setEmitEngageEvent(true);
180+
}
181+
}, [hasUserTakenAction, onFocusMetricsEvent, emitEngageEvent]);
182+
164183
const onNewPwdBlur = () => {
165184
// do not hide the password strength balloon if there are errors in the new password
166185
if (!errors.newPassword) {
@@ -326,6 +345,7 @@ export const FormPasswordWithBalloons = ({
326345
},
327346
},
328347
})}
348+
inputRefDOM={newPasswordRef}
329349
prefixDataTestId="new-password"
330350
aria-describedby="password-requirements"
331351
/>

packages/fxa-settings/src/components/InputPassword/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { ReactComponent as OpenEye } from './eye-open.svg';
88
import { ReactComponent as ClosedEye } from './eye-closed.svg';
99
import { useFtlMsgResolver } from '../../models';
1010

11-
export type InputPasswordProps = Omit<InputTextProps, 'type'>;
11+
export type InputPasswordProps = Omit<InputTextProps, 'type'> & {
12+
inputRefDOM?: React.RefObject<HTMLInputElement>;
13+
};
1214

1315
export const InputPassword = ({
1416
defaultValue,
@@ -20,6 +22,7 @@ export const InputPassword = ({
2022
onFocusCb,
2123
onBlurCb,
2224
inputRef,
25+
inputRefDOM,
2326
hasErrors,
2427
errorText,
2528
name,
@@ -69,6 +72,7 @@ export const InputPassword = ({
6972
aria-describedby=""
7073
isPasswordInput={true}
7174
{...{
75+
inputRefDOM,
7276
defaultValue,
7377
disabled,
7478
label,

packages/fxa-settings/src/components/InputText/index.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export type InputTextProps = {
2424
className?: string;
2525
inputOnlyClassName?: string;
2626
inputRef?: Ref<HTMLInputElement>;
27+
inputRefDOM?: Ref<HTMLInputElement>;
2728
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
2829
onFocusCb?: () => void;
2930
onBlurCb?: () => void;
@@ -63,6 +64,7 @@ export const InputText = ({
6364
className = '',
6465
inputOnlyClassName = '',
6566
inputRef,
67+
inputRefDOM,
6668
type = 'text',
6769
name,
6870
prefixDataTestId = '',
@@ -117,6 +119,20 @@ export const InputText = ({
117119
return prefixDataTestId ? `${prefixDataTestId}-${id}` : id;
118120
}
119121

122+
const combinedRef = useCallback(
123+
(element: HTMLInputElement | null) => {
124+
if (inputRefDOM) {
125+
(
126+
inputRefDOM as React.MutableRefObject<HTMLInputElement | null>
127+
).current = element;
128+
}
129+
if (inputRef && typeof inputRef === 'function') {
130+
inputRef(element);
131+
}
132+
},
133+
[inputRef, inputRefDOM]
134+
);
135+
120136
return (
121137
<label
122138
className={classNames(
@@ -157,7 +173,8 @@ export const InputText = ({
157173
)}
158174
data-testid={formatDataTestId('input-field')}
159175
onChange={textFieldChange}
160-
ref={inputRef}
176+
ref={combinedRef}
177+
// ref={inputRef}
161178
{...{
162179
name,
163180
disabled,

packages/fxa-settings/src/components/Settings/PageSecondaryEmailAdd/index.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const PageSecondaryEmailAdd = (_: RouteComponentProps) => {
1717
const [saveBtnDisabled, setSaveBtnDisabled] = useState(true);
1818
const [errorText, setErrorText] = useState<string>();
1919
const [email, setEmail] = useState<string>();
20-
const inputRef = useRef<HTMLInputElement>(null);
20+
const inputRefDOM = useRef<HTMLInputElement>(null);
2121
const { l10n } = useLocalization();
2222

2323
const subtitleText = l10n.getString(
@@ -60,10 +60,11 @@ export const PageSecondaryEmailAdd = (_: RouteComponentProps) => {
6060

6161
const checkEmail = useCallback(
6262
(ev: ChangeEvent<HTMLInputElement>) => {
63-
const email = inputRef.current?.value || '';
63+
const email = inputRefDOM.current?.value || '';
6464
const isValid = isEmailValid(email);
65+
6566
setSaveBtnDisabled(!isValid);
66-
setEmail(inputRef.current?.value);
67+
setEmail(inputRefDOM.current?.value);
6768
setErrorText('');
6869

6970
if (isEmailMask(email)) {
@@ -86,7 +87,7 @@ export const PageSecondaryEmailAdd = (_: RouteComponentProps) => {
8687
<form
8788
onSubmit={(ev) => {
8889
ev.preventDefault();
89-
if (inputRef.current) {
90+
if (inputRefDOM.current) {
9091
createSecondaryEmail(email!);
9192
logViewEvent('settings.emails', 'submit');
9293
}
@@ -101,7 +102,8 @@ export const PageSecondaryEmailAdd = (_: RouteComponentProps) => {
101102
label="Enter email address"
102103
type="email"
103104
onChange={checkEmail}
104-
{...{ inputRef, errorText }}
105+
inputRefDOM={inputRefDOM}
106+
{...{ errorText }}
105107
/>
106108
</Localized>
107109
</div>

0 commit comments

Comments
 (0)