From 38a6085e0bb534ea1621d9a4d4c356a461887d1d Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 14 Feb 2025 13:47:36 +0200 Subject: [PATCH 1/6] Save form state for unsubmitted data --- .../core/src/js/feedback/FeedbackForm.tsx | 45 +++++++++++++++---- .../core/test/feedback/FeedbackForm.test.tsx | 30 +++++++++++++ 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/packages/core/src/js/feedback/FeedbackForm.tsx b/packages/core/src/js/feedback/FeedbackForm.tsx index b6aac2f412..8f5103854c 100644 --- a/packages/core/src/js/feedback/FeedbackForm.tsx +++ b/packages/core/src/js/feedback/FeedbackForm.tsx @@ -33,6 +33,15 @@ export class FeedbackForm extends React.Component { if (data != null) { - this.setState({ filename, attachment: data }); + this.setState({ filename, attachment: data }, this._saveFormState); } else { logger.error('Failed to read image data from uri:', imageUri); } @@ -142,11 +154,11 @@ export class FeedbackForm extends React.Component { - this.setState({ filename, attachment: attachement }); + this.setState({ filename, attachment: attachement }, this._saveFormState); }); } } else { - this.setState({ filename: undefined, attachment: undefined }); + this.setState({ filename: undefined, attachment: undefined }, this._saveFormState); } } @@ -199,7 +211,7 @@ export class FeedbackForm extends React.Component this.setState({ name: value })} + onChangeText={(value) => this.setState({ name: value }, this._saveFormState)} /> )} @@ -215,7 +227,7 @@ export class FeedbackForm extends React.Component this.setState({ email: value })} + onChangeText={(value) => this.setState({ email: value }, this._saveFormState)} /> )} @@ -228,7 +240,7 @@ export class FeedbackForm extends React.Component this.setState({ description: value })} + onChangeText={(value) => this.setState({ description: value }, this._saveFormState)} multiline /> {(config.enableScreenshot || imagePickerConfiguration.imagePicker) && ( @@ -254,4 +266,19 @@ export class FeedbackForm extends React.Component ); } + + private _saveFormState = (): void => { + FeedbackForm._savedState = { ...this.state }; + }; + + private _clearFormState = (): void => { + FeedbackForm._savedState = { + isVisible: false, + name: '', + email: '', + description: '', + filename: undefined, + attachment: undefined, + }; + }; } diff --git a/packages/core/test/feedback/FeedbackForm.test.tsx b/packages/core/test/feedback/FeedbackForm.test.tsx index 92ed48fe4a..15918799ca 100644 --- a/packages/core/test/feedback/FeedbackForm.test.tsx +++ b/packages/core/test/feedback/FeedbackForm.test.tsx @@ -354,4 +354,34 @@ describe('FeedbackForm', () => { expect(mockOnFormClose).toHaveBeenCalled(); }); + + it('onCancel the input is saved and restored when the form reopens', async () => { + const { getByPlaceholderText, getByText } = render(); + + fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe'); + fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), 'john.doe@example.com'); + fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.'); + + fireEvent.press(getByText(defaultProps.cancelButtonLabel)); + const { queryByPlaceholderText } = render(); + + expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('John Doe'); + expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('john.doe@example.com'); + expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe('This is a feedback message.'); + }); + + it('onSubmit the saved input is cleared and not restored when the form reopens', async () => { + const { getByPlaceholderText, getByText } = render(); + + fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe'); + fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), 'john.doe@example.com'); + fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.'); + + fireEvent.press(getByText(defaultProps.submitButtonLabel)); + const { queryByPlaceholderText } = render(); + + expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('Test User'); + expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('test@example.com'); + expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe(''); + }); }); From 1efb23e290f3aeef6977e8e714d1c191a1e8342c Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Mon, 17 Feb 2025 12:06:56 +0200 Subject: [PATCH 2/6] Omit isVisible from state --- packages/core/src/js/feedback/FeedbackWidget.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/core/src/js/feedback/FeedbackWidget.tsx b/packages/core/src/js/feedback/FeedbackWidget.tsx index 839617beeb..e992f08d37 100644 --- a/packages/core/src/js/feedback/FeedbackWidget.tsx +++ b/packages/core/src/js/feedback/FeedbackWidget.tsx @@ -33,8 +33,7 @@ export class FeedbackWidget extends React.Component = { name: '', email: '', description: '', @@ -273,7 +272,6 @@ export class FeedbackWidget extends React.Component { FeedbackWidget._savedState = { - isVisible: false, name: '', email: '', description: '', From 59f2f570ae0e6bb6744bb59566cdc13e549add75 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Mon, 17 Feb 2025 15:02:07 +0200 Subject: [PATCH 3/6] Save/clear form state on unmount --- .../core/src/js/feedback/FeedbackWidget.tsx | 27 ++++++++++++++----- .../test/feedback/FeedbackWidget.test.tsx | 21 +++++++++++++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/core/src/js/feedback/FeedbackWidget.tsx b/packages/core/src/js/feedback/FeedbackWidget.tsx index e992f08d37..8ad7f85ded 100644 --- a/packages/core/src/js/feedback/FeedbackWidget.tsx +++ b/packages/core/src/js/feedback/FeedbackWidget.tsx @@ -33,6 +33,7 @@ export class FeedbackWidget extends React.Component = { name: '', email: '', @@ -103,7 +104,7 @@ export class FeedbackWidget extends React.Component { if (data != null) { - this.setState({ filename, attachment: data }, this._saveFormState); + this.setState({ filename, attachment: data }); } else { logger.error('Failed to read image data from uri:', imageUri); } @@ -153,11 +154,23 @@ export class FeedbackWidget extends React.Component { - this.setState({ filename, attachment: attachement }, this._saveFormState); + this.setState({ filename, attachment: attachement }); }); } } else { - this.setState({ filename: undefined, attachment: undefined }, this._saveFormState); + this.setState({ filename: undefined, attachment: undefined }); + } + } + + /** + * Save the state before unmounting the component. + */ + public componentWillUnmount(): void { + if (FeedbackWidget._didSubmitForm) { + this._clearFormState(); + FeedbackWidget._didSubmitForm = false; + } else { + this._saveFormState(); } } @@ -210,7 +223,7 @@ export class FeedbackWidget extends React.Component this.setState({ name: value }, this._saveFormState)} + onChangeText={(value) => this.setState({ name: value })} /> )} @@ -226,7 +239,7 @@ export class FeedbackWidget extends React.Component this.setState({ email: value }, this._saveFormState)} + onChangeText={(value) => this.setState({ email: value })} /> )} @@ -239,7 +252,7 @@ export class FeedbackWidget extends React.Component this.setState({ description: value }, this._saveFormState)} + onChangeText={(value) => this.setState({ description: value })} multiline /> {(config.enableScreenshot || imagePickerConfiguration.imagePicker) && ( diff --git a/packages/core/test/feedback/FeedbackWidget.test.tsx b/packages/core/test/feedback/FeedbackWidget.test.tsx index c3fcc8fefe..0274651a31 100644 --- a/packages/core/test/feedback/FeedbackWidget.test.tsx +++ b/packages/core/test/feedback/FeedbackWidget.test.tsx @@ -355,14 +355,30 @@ describe('FeedbackWidget', () => { expect(mockOnFormClose).toHaveBeenCalled(); }); + it('onUnmount the input is saved and restored when the form reopens', async () => { + const { getByPlaceholderText, unmount } = render(); + + fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe'); + fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), 'john.doe@example.com'); + fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.'); + + unmount(); + const { queryByPlaceholderText } = render(); + + expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('John Doe'); + expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('john.doe@example.com'); + expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe('This is a feedback message.'); + }); + it('onCancel the input is saved and restored when the form reopens', async () => { - const { getByPlaceholderText, getByText } = render(); + const { getByPlaceholderText, getByText, unmount } = render(); fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe'); fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), 'john.doe@example.com'); fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.'); fireEvent.press(getByText(defaultProps.cancelButtonLabel)); + unmount(); const { queryByPlaceholderText } = render(); expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('John Doe'); @@ -371,13 +387,14 @@ describe('FeedbackWidget', () => { }); it('onSubmit the saved input is cleared and not restored when the form reopens', async () => { - const { getByPlaceholderText, getByText } = render(); + const { getByPlaceholderText, getByText, unmount } = render(); fireEvent.changeText(getByPlaceholderText(defaultProps.namePlaceholder), 'John Doe'); fireEvent.changeText(getByPlaceholderText(defaultProps.emailPlaceholder), 'john.doe@example.com'); fireEvent.changeText(getByPlaceholderText(defaultProps.messagePlaceholder), 'This is a feedback message.'); fireEvent.press(getByText(defaultProps.submitButtonLabel)); + unmount(); const { queryByPlaceholderText } = render(); expect(queryByPlaceholderText(defaultProps.namePlaceholder).props.value).toBe('Test User'); From 797611f898676086b4e9f3570244cf8783245055 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Mon, 17 Feb 2025 15:02:42 +0200 Subject: [PATCH 4/6] Pass the missing attachment parameter in the onSubmitSuccess --- packages/core/src/js/feedback/FeedbackWidget.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/js/feedback/FeedbackWidget.tsx b/packages/core/src/js/feedback/FeedbackWidget.tsx index 8ad7f85ded..b1da634aef 100644 --- a/packages/core/src/js/feedback/FeedbackWidget.tsx +++ b/packages/core/src/js/feedback/FeedbackWidget.tsx @@ -101,7 +101,7 @@ export class FeedbackWidget extends React.Component Date: Tue, 18 Feb 2025 11:38:18 +0200 Subject: [PATCH 5/6] Use instance variable for _didSubmitForm --- packages/core/src/js/feedback/FeedbackWidget.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/js/feedback/FeedbackWidget.tsx b/packages/core/src/js/feedback/FeedbackWidget.tsx index 92d2f6d3b8..c5415f4ddc 100644 --- a/packages/core/src/js/feedback/FeedbackWidget.tsx +++ b/packages/core/src/js/feedback/FeedbackWidget.tsx @@ -33,7 +33,7 @@ export class FeedbackWidget extends React.Component = { name: '', email: '', @@ -106,7 +106,7 @@ export class FeedbackWidget extends React.Component Date: Tue, 18 Feb 2025 11:45:18 +0200 Subject: [PATCH 6/6] Fixes lint issue --- packages/core/src/js/feedback/FeedbackWidget.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/js/feedback/FeedbackWidget.tsx b/packages/core/src/js/feedback/FeedbackWidget.tsx index c5415f4ddc..f0026751d3 100644 --- a/packages/core/src/js/feedback/FeedbackWidget.tsx +++ b/packages/core/src/js/feedback/FeedbackWidget.tsx @@ -33,7 +33,6 @@ export class FeedbackWidget extends React.Component = { name: '', email: '', @@ -42,6 +41,8 @@ export class FeedbackWidget extends React.Component