Skip to content

Commit 2ff3795

Browse files
committed
fix up the successmessage and callbacks
1 parent ae4e49f commit 2ff3795

File tree

8 files changed

+153
-148
lines changed

8 files changed

+153
-148
lines changed

packages/feedback2/src/constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ export const SUCCESS_MESSAGE_TEXT = 'Thank you for your report!';
2222

2323
export const FEEDBACK_WIDGET_SOURCE = 'widget';
2424
export const FEEDBACK_API_SOURCE = 'api';
25+
26+
export const SUCCESS_MESSAGE_TIMEOUT = 5000;

packages/feedback2/src/core/integration.ts

Lines changed: 54 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,9 @@ import {
1818
SUCCESS_MESSAGE_TEXT,
1919
} from '../constants';
2020
import type { DialogComponent } from '../modal/components/Dialog';
21-
import type { DialogLifecycleCallbacks, feedback2ModalIntegration } from '../modal/integration';
21+
import type { feedback2ModalIntegration } from '../modal/integration';
2222
import type { feedback2ScreenshotIntegration } from '../screenshot/integration';
23-
import type {
24-
FeedbackCallbacks,
25-
FeedbackFormData,
26-
FeedbackInternalOptions,
27-
OptionalFeedbackConfiguration,
28-
} from '../types';
23+
import type { FeedbackCallbacks, FeedbackInternalOptions, OptionalFeedbackConfiguration } from '../types';
2924
import { DEBUG_BUILD } from '../util/debug-build';
3025
import { mergeOptions } from '../util/mergeOptions';
3126
import { Actor } from './components/Actor';
@@ -70,21 +65,21 @@ export class Feedback2 implements Integration {
7065
*/
7166
private _shadow: ShadowRoot | null;
7267

73-
/**
74-
* The sentry-provided button to trigger the modal
75-
*/
76-
private _triggerButton: null | any;
68+
// /**
69+
// * The sentry-provided button to trigger the modal
70+
// */
71+
// private _triggerButton: null | any;
7772

78-
/**
79-
* The integration that we will use to render the modal
80-
* This value can be either passed in, or will be async loaded
81-
*/
82-
private _dialogRenderStrategy: null | any;
73+
// /**
74+
// * The integration that we will use to render the modal
75+
// * This value can be either passed in, or will be async loaded
76+
// */
77+
// private _dialogRenderStrategy: null | any;
8378

8479
/**
85-
* The ModalComponent itself, as rendered on the screen
80+
* The DialogComponent itself, as rendered on the screen
8681
*/
87-
private _modal: null | any;
82+
private _dialog: null | DialogComponent;
8883

8984
public constructor({
9085
// FeedbackGeneralConfiguration
@@ -169,6 +164,8 @@ export class Feedback2 implements Integration {
169164

170165
this._host = null;
171166
this._shadow = null;
167+
168+
this._dialog = null;
172169
}
173170

174171
/**
@@ -196,13 +193,9 @@ export class Feedback2 implements Integration {
196193
insertActor();
197194
options.onFormClose && options.onFormClose();
198195
},
199-
onSubmitSuccess(data: FeedbackFormData) {
200-
insertActor();
201-
options.onSubmitSuccess && options.onSubmitSuccess(data);
202-
},
203-
onSubmitError() {
196+
onFormSubmitted() {
204197
insertActor();
205-
options.onSubmitError && options.onSubmitError();
198+
options.onFormSubmitted && options.onFormSubmitted();
206199
},
207200
});
208201

@@ -237,19 +230,8 @@ export class Feedback2 implements Integration {
237230
}
238231

239232
const handleClick = async (): Promise<void> => {
240-
const shadow = this._getShadow(options); // options have not changed, because optionOverrides is a subset!
241-
242-
await this._loadAndRenderDialog(options, {
243-
onCreate: (dialog: DialogComponent) => {
244-
shadow.appendChild(dialog.style);
245-
shadow.appendChild(dialog.el);
246-
},
247-
onSubmit: sendFeedback,
248-
onDone(dialog: DialogComponent) {
249-
shadow.removeChild(dialog.el);
250-
shadow.removeChild(dialog.style);
251-
},
252-
});
233+
const dialog = await this._loadAndRenderDialog(options);
234+
dialog.open();
253235
};
254236
targetEl.addEventListener('click', handleClick);
255237
return () => {
@@ -260,29 +242,19 @@ export class Feedback2 implements Integration {
260242
/**
261243
* Creates a new widget. Accepts partial options to override any options passed to constructor.
262244
*/
263-
public createWidget(optionOverrides: OptionalFeedbackConfiguration & { shouldCreateActor?: boolean } = {}): null {
264-
// FeedbackWidget
245+
public createWidget(
246+
optionOverrides: OptionalFeedbackConfiguration & { shouldCreateActor?: boolean } = {},
247+
): Promise<DialogComponent> {
265248
const options = mergeOptions(this.options, optionOverrides);
266249

267-
// the dialog should have some standard callbacks:
268-
// onFormClose: () => this._triggerButton.show(); this.options.onFormClose()
269-
// onFormOpen: () => this._triggerButton.hide(); this.options.onFormOpen()
270-
// onSubmitError: () => this._triggerButton.show(); this.options.onSubmitError();
271-
// onSubmitSuccess: () => this._triggerButton.show(); this.options.onSubmitSuccss();
272-
//
273-
// actually, we might want to rename the callbacks that the form itself takes... or expand the list so that
274-
// we can allow the form to render the SuccessMessage
275-
276-
return null;
250+
return this._loadAndRenderDialog(options);
277251
}
278252

279253
/**
280-
* Returns the default (first-created) widget
254+
* Returns the default widget, if it exists
281255
*/
282-
public getWidget(): null {
283-
// FeedbackWidget (incl dialog!)
284-
//
285-
return null;
256+
public getWidget(): DialogComponent | null {
257+
return this._dialog;
286258
}
287259

288260
/**
@@ -291,21 +263,25 @@ export class Feedback2 implements Integration {
291263
* created during initialization of the integration.
292264
*/
293265
public openDialog(): void {
294-
//
266+
this._dialog && this._dialog.open();
295267
}
296268

297269
/**
298270
* Closes the dialog for the default widget, if it exists
299271
*/
300272
public closeDialog(): void {
301-
//
273+
this._dialog && this._dialog.close();
302274
}
303275

304276
/**
305-
* Removes a single widget
277+
* Removes the rendered widget, if it exists
306278
*/
307279
public removeWidget(_widget: null | undefined): void {
308-
//
280+
if (this._shadow && this._dialog) {
281+
this._shadow.removeChild(this._dialog.el);
282+
this._shadow.removeChild(this._dialog.style);
283+
}
284+
this._dialog = null;
309285
}
310286

311287
/**
@@ -348,10 +324,11 @@ export class Feedback2 implements Integration {
348324
/**
349325
*
350326
*/
351-
protected async _loadAndRenderDialog(
352-
options: FeedbackInternalOptions,
353-
callbacks: DialogLifecycleCallbacks,
354-
): Promise<void> {
327+
protected async _loadAndRenderDialog(options: FeedbackInternalOptions): Promise<DialogComponent> {
328+
if (this._dialog) {
329+
return this._dialog;
330+
}
331+
355332
const client = getClient<BrowserClient>();
356333
if (!client) {
357334
throw new Error('Sentry Client is not initialized correctly');
@@ -387,7 +364,21 @@ export class Feedback2 implements Integration {
387364
throw new Error('Not implemented yet');
388365
}
389366

367+
const shadow = this._getShadow(options);
368+
390369
// TODO: some combination stuff when screenshots exists:
391-
modalIntegration.renderDialog(options, callbacks);
370+
this._dialog = modalIntegration.createDialog(options, {
371+
onCreate: (dialog: DialogComponent) => {
372+
shadow.appendChild(dialog.style);
373+
shadow.appendChild(dialog.el);
374+
},
375+
onSubmit: sendFeedback,
376+
onDone: (dialog: DialogComponent) => {
377+
shadow.removeChild(dialog.el);
378+
shadow.removeChild(dialog.style);
379+
this._dialog = null;
380+
},
381+
});
382+
return this._dialog;
392383
}
393384
}

packages/feedback2/src/modal/components/Dialog.css.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ export function createDialogStyles(): HTMLStyleElement {
194194
cursor: default;
195195
}
196196
197+
.success-icon {
198+
display: flex;
199+
}
200+
197201
.success-icon path {
198202
fill: var(--success);
199203
}

packages/feedback2/src/modal/components/Dialog.tsx

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { Fragment, h, render } from 'preact'; // eslint-disable-line @typescript-eslint/no-unused-vars
33
import type { VNode } from 'preact';
44
import { useCallback, useMemo, useState } from 'preact/hooks';
5-
import { DOCUMENT } from '../../constants';
5+
import { DOCUMENT, SUCCESS_MESSAGE_TIMEOUT } from '../../constants';
66
import type { FeedbackFormData } from '../../types';
77
import { createDialogStyles } from './Dialog.css';
88
import { DialogContent } from './DialogContent';
@@ -11,7 +11,7 @@ import { SuccessIcon } from './SuccessIcon';
1111

1212
export interface Props extends DialogContentProps {
1313
successMessageText: string;
14-
onDone: () => void;
14+
onFormSubmitted: () => void;
1515
}
1616

1717
export interface DialogComponent {
@@ -24,15 +24,29 @@ export interface DialogComponent {
2424
* The style element for this component
2525
*/
2626
style: HTMLStyleElement;
27+
28+
/**
29+
* Open/Show the dialog & form inside it
30+
*/
31+
open: () => void;
32+
33+
/**
34+
* Close/Hide the dialog & form inside it
35+
*/
36+
close: () => void;
2737
}
2838

2939
/**
3040
*
3141
*/
3242
export function Dialog(props: Props): DialogComponent {
3343
const el = DOCUMENT.createElement('div');
34-
render(<DialogContainer {...props} />, el);
3544

45+
const renderContent = (open: boolean): void => {
46+
render(<DialogContainer {...props} open={open} />, el);
47+
};
48+
49+
renderContent(false);
3650
const style = createDialogStyles();
3751

3852
return {
@@ -42,34 +56,48 @@ export function Dialog(props: Props): DialogComponent {
4256
get style() {
4357
return style;
4458
},
59+
open() {
60+
renderContent(true);
61+
},
62+
close() {
63+
renderContent(false);
64+
},
4565
};
4666
}
4767

48-
function DialogContainer({ onDone, ...props }: Props): VNode {
68+
function DialogContainer({ open, onFormSubmitted, ...props }: Props & { open: boolean }): VNode {
4969
const successIconHtml = useMemo(() => {
5070
const logo = SuccessIcon();
5171
return { __html: logo.outerHTML };
5272
}, []);
5373

5474
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | null>(null);
5575

76+
const handlOnSuccessClick = useCallback(() => {
77+
if (timeoutId) {
78+
clearTimeout(timeoutId);
79+
setTimeoutId(null);
80+
}
81+
onFormSubmitted();
82+
}, [timeoutId]);
83+
5684
const onSubmitSuccess = useCallback(
5785
(data: FeedbackFormData) => {
5886
props.onSubmitSuccess(data);
59-
setTimeoutId(() => setTimeout(onDone, 5000));
87+
setTimeoutId(() => setTimeout(onFormSubmitted, SUCCESS_MESSAGE_TIMEOUT));
6088
},
61-
[onDone],
89+
[onFormSubmitted],
6290
);
6391

6492
return (
6593
<Fragment>
66-
{didSubmit ? (
67-
<div class="success-message" onClick={onDone}>
94+
{timeoutId ? (
95+
<div class="success-message" onClick={handlOnSuccessClick}>
6896
{props.successMessageText}
69-
<span dangerouslySetInnerHTML={successIconHtml} />
97+
<span class="success-icon" dangerouslySetInnerHTML={successIconHtml} />
7098
</div>
7199
) : (
72-
<dialog class="dialog" onClick={props.onFormClose} open>
100+
<dialog class="dialog" onClick={props.onFormClose} open={open}>
73101
<DialogContent {...props} onSubmitSuccess={onSubmitSuccess} />
74102
</dialog>
75103
)}

packages/feedback2/src/modal/components/SuccessIcon.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export function SuccessIcon(): SVGElement {
1212
const createElementNS = <K extends keyof SVGElementTagNameMap>(tagName: K): SVGElementTagNameMap[K] =>
1313
WINDOW.document.createElementNS(XMLNS, tagName);
1414
const svg = setAttributesNS(createElementNS('svg'), {
15-
class: 'success-icon',
1615
width: `${WIDTH}`,
1716
height: `${HEIGHT}`,
1817
viewBox: `0 0 ${WIDTH} ${HEIGHT}`,

packages/feedback2/src/modal/components/SuccessMessage.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)