Skip to content

Commit 51dc070

Browse files
authored
feat(feedback): Save form state for un-submitted data (#4538)
* Save form state for unsubmitted data * Omit isVisible from state * Save/clear form state on unmount * Pass the missing attachment parameter in the onSubmitSuccess * Use instance variable for _didSubmitForm * Fixes lint issue
1 parent 2135c96 commit 51dc070

File tree

2 files changed

+90
-4
lines changed

2 files changed

+90
-4
lines changed

packages/core/src/js/feedback/FeedbackWidget.tsx

+43-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ export class FeedbackWidget extends React.Component<FeedbackWidgetProps, Feedbac
3333
...defaultConfiguration
3434
}
3535

36+
private static _savedState: Omit<FeedbackWidgetState, 'isVisible'> = {
37+
name: '',
38+
email: '',
39+
description: '',
40+
filename: undefined,
41+
attachment: undefined,
42+
};
43+
44+
private _didSubmitForm: boolean = false;
45+
3646
public constructor(props: FeedbackWidgetProps) {
3747
super(props);
3848

@@ -45,9 +55,11 @@ export class FeedbackWidget extends React.Component<FeedbackWidgetProps, Feedbac
4555

4656
this.state = {
4757
isVisible: true,
48-
name: currentUser.useSentryUser.name,
49-
email: currentUser.useSentryUser.email,
50-
description: '',
58+
name: FeedbackWidget._savedState.name || currentUser.useSentryUser.name,
59+
email: FeedbackWidget._savedState.email || currentUser.useSentryUser.email,
60+
description: FeedbackWidget._savedState.description || '',
61+
filename: FeedbackWidget._savedState.filename || undefined,
62+
attachment: FeedbackWidget._savedState.attachment || undefined,
5163
};
5264
}
5365

@@ -92,9 +104,10 @@ export class FeedbackWidget extends React.Component<FeedbackWidgetProps, Feedbac
92104
this.setState({ isVisible: false });
93105
}
94106
captureFeedback(userFeedback, attachments ? { attachments } : undefined);
95-
onSubmitSuccess({ name: trimmedName, email: trimmedEmail, message: trimmedDescription, attachments: undefined });
107+
onSubmitSuccess({ name: trimmedName, email: trimmedEmail, message: trimmedDescription, attachments: attachments });
96108
Alert.alert(text.successMessageText);
97109
onFormSubmitted();
110+
this._didSubmitForm = true;
98111
} catch (error) {
99112
const errorString = `Feedback form submission failed: ${error}`;
100113
onSubmitError(new Error(errorString));
@@ -152,6 +165,18 @@ export class FeedbackWidget extends React.Component<FeedbackWidgetProps, Feedbac
152165
}
153166
}
154167

168+
/**
169+
* Save the state before unmounting the component.
170+
*/
171+
public componentWillUnmount(): void {
172+
if (this._didSubmitForm) {
173+
this._clearFormState();
174+
this._didSubmitForm = false;
175+
} else {
176+
this._saveFormState();
177+
}
178+
}
179+
155180
/**
156181
* Renders the feedback form screen.
157182
*/
@@ -259,4 +284,18 @@ export class FeedbackWidget extends React.Component<FeedbackWidgetProps, Feedbac
259284
</SafeAreaView>
260285
);
261286
}
287+
288+
private _saveFormState = (): void => {
289+
FeedbackWidget._savedState = { ...this.state };
290+
};
291+
292+
private _clearFormState = (): void => {
293+
FeedbackWidget._savedState = {
294+
name: '',
295+
email: '',
296+
description: '',
297+
filename: undefined,
298+
attachment: undefined,
299+
};
300+
};
262301
}

packages/core/test/feedback/FeedbackWidget.test.tsx

+47
Original file line numberDiff line numberDiff line change
@@ -354,4 +354,51 @@ describe('FeedbackWidget', () => {
354354

355355
expect(mockOnFormClose).toHaveBeenCalled();
356356
});
357+
358+
it('onUnmount the input is saved and restored when the form reopens', async () => {
359+
const { getByPlaceholderText, unmount } = render(<FeedbackWidget {...defaultProps} />);
360+
361+
fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe');
362+
fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), '[email protected]');
363+
fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.');
364+
365+
unmount();
366+
const { queryByPlaceholderText } = render(<FeedbackWidget {...defaultProps} />);
367+
368+
expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('John Doe');
369+
expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('[email protected]');
370+
expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe('This is a feedback message.');
371+
});
372+
373+
it('onCancel the input is saved and restored when the form reopens', async () => {
374+
const { getByPlaceholderText, getByText, unmount } = render(<FeedbackWidget {...defaultProps} />);
375+
376+
fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe');
377+
fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), '[email protected]');
378+
fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.');
379+
380+
fireEvent.press(getByText(defaultProps.cancelButtonLabel));
381+
unmount();
382+
const { queryByPlaceholderText } = render(<FeedbackWidget {...defaultProps} />);
383+
384+
expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('John Doe');
385+
expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('[email protected]');
386+
expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe('This is a feedback message.');
387+
});
388+
389+
it('onSubmit the saved input is cleared and not restored when the form reopens', async () => {
390+
const { getByPlaceholderText, getByText, unmount } = render(<FeedbackWidget {...defaultProps} />);
391+
392+
fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe');
393+
fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), '[email protected]');
394+
fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.');
395+
396+
fireEvent.press(getByText(defaultProps.submitButtonLabel));
397+
unmount();
398+
const { queryByPlaceholderText } = render(<FeedbackWidget {...defaultProps} />);
399+
400+
expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('Test User');
401+
expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('[email protected]');
402+
expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe('');
403+
});
357404
});

0 commit comments

Comments
 (0)