@solucx/react-native-solucx-widget 0.1.14 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.intern.md +513 -473
- package/README.md +285 -257
- package/package.json +23 -23
- package/src/SoluCXWidget.tsx +119 -119
- package/src/__tests__/urlUtils.test.ts +56 -56
- package/src/__tests__/useWidgetState.test.ts +188 -188
- package/src/components/CloseButton.tsx +36 -36
- package/src/components/InlineWidget.tsx +36 -32
- package/src/components/ModalWidget.tsx +59 -43
- package/src/components/OverlayWidget.tsx +88 -81
- package/src/constants/webViewConstants.ts +14 -14
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useHeightAnimation.ts +22 -0
- package/src/hooks/useWidgetHeight.ts +38 -0
- package/src/hooks/useWidgetState.ts +101 -77
- package/src/index.ts +8 -8
- package/src/interfaces/WidgetData.ts +19 -19
- package/src/interfaces/WidgetOptions.ts +7 -7
- package/src/interfaces/WidgetResponse.ts +15 -15
- package/src/interfaces/WidgetSamplerLog.ts +5 -5
- package/src/interfaces/index.ts +23 -23
- package/src/services/storage.ts +20 -20
- package/src/services/widgetEventService.ts +111 -111
- package/src/services/widgetValidationService.ts +86 -86
- package/src/styles/widgetStyles.ts +58 -83
- package/src/utils/urlUtils.ts +13 -13
package/src/SoluCXWidget.tsx
CHANGED
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useCallback } from 'react';
|
|
2
|
-
import { Dimensions } from 'react-native';
|
|
3
|
-
import { WebView } from 'react-native-webview';
|
|
4
|
-
|
|
5
|
-
import { SoluCXKey, WidgetData, WidgetOptions, WidgetType } from './interfaces';
|
|
6
|
-
import { useWidgetState } from './hooks/useWidgetState';
|
|
7
|
-
import { WidgetEventService } from './services/widgetEventService';
|
|
8
|
-
import { buildWidgetURL } from './utils/urlUtils';
|
|
9
|
-
import { WEB_VIEW_MESSAGE_LISTENER } from './constants/webViewConstants';
|
|
10
|
-
import { ModalWidget } from './components/ModalWidget';
|
|
11
|
-
import { InlineWidget } from './components/InlineWidget';
|
|
12
|
-
import { OverlayWidget } from './components/OverlayWidget';
|
|
13
|
-
|
|
14
|
-
interface SoluCXWidgetProps {
|
|
15
|
-
soluCXKey: SoluCXKey;
|
|
16
|
-
type: WidgetType;
|
|
17
|
-
data: WidgetData;
|
|
18
|
-
options: WidgetOptions;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({
|
|
22
|
-
soluCXKey,
|
|
23
|
-
type,
|
|
24
|
-
data,
|
|
25
|
-
options
|
|
26
|
-
}) => {
|
|
27
|
-
const webviewRef = useRef<WebView>(null);
|
|
28
|
-
const { width } = Dimensions.get('window');
|
|
29
|
-
|
|
30
|
-
const {
|
|
31
|
-
widgetHeight,
|
|
32
|
-
isWidgetVisible,
|
|
33
|
-
setIsWidgetVisible,
|
|
34
|
-
loadSavedData,
|
|
35
|
-
resize,
|
|
36
|
-
open,
|
|
37
|
-
close,
|
|
38
|
-
userId,
|
|
39
|
-
} = useWidgetState(data, options, type);
|
|
40
|
-
|
|
41
|
-
const eventService = new WidgetEventService(setIsWidgetVisible, resize, open, userId, options);
|
|
42
|
-
|
|
43
|
-
const uri = buildWidgetURL(soluCXKey, data);
|
|
44
|
-
const isForm = Boolean(data.form_id);
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
loadSavedData();
|
|
48
|
-
}, [loadSavedData]);
|
|
49
|
-
|
|
50
|
-
const handleWebViewMessage = useCallback(async (message: string) => {
|
|
51
|
-
if (message && message.length > 0) {
|
|
52
|
-
try {
|
|
53
|
-
await eventService.handleMessage(message, isForm);
|
|
54
|
-
} catch (error) {
|
|
55
|
-
console.error('Error handling widget message:', error);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}, [eventService, isForm]);
|
|
59
|
-
|
|
60
|
-
const handleWebViewLoad = useCallback(() => {
|
|
61
|
-
webviewRef.current?.injectJavaScript(WEB_VIEW_MESSAGE_LISTENER);
|
|
62
|
-
}, []);
|
|
63
|
-
|
|
64
|
-
const handleClose = useCallback(() => {
|
|
65
|
-
if (type === 'inline' || type === 'modal') {
|
|
66
|
-
close();
|
|
67
|
-
}
|
|
68
|
-
setIsWidgetVisible(false);
|
|
69
|
-
}, [setIsWidgetVisible]);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const webViewStyle = [
|
|
73
|
-
{ height: widgetHeight },
|
|
74
|
-
{ width }
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
if (type === 'modal') {
|
|
78
|
-
return (
|
|
79
|
-
<ModalWidget visible={isWidgetVisible} height={widgetHeight} onClose={handleClose}>
|
|
80
|
-
<WebView
|
|
81
|
-
ref={webviewRef}
|
|
82
|
-
style={webViewStyle}
|
|
83
|
-
source={{ uri }}
|
|
84
|
-
onLoadEnd={handleWebViewLoad}
|
|
85
|
-
onMessage={(event) => handleWebViewMessage(event.nativeEvent.data)}
|
|
86
|
-
originWhitelist={['*']}
|
|
87
|
-
/>
|
|
88
|
-
</ModalWidget>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (type === 'inline') {
|
|
93
|
-
return (
|
|
94
|
-
<InlineWidget visible={isWidgetVisible} height={widgetHeight} onClose={handleClose}>
|
|
95
|
-
<WebView
|
|
96
|
-
ref={webviewRef}
|
|
97
|
-
style={webViewStyle}
|
|
98
|
-
source={{ uri }}
|
|
99
|
-
onLoadEnd={handleWebViewLoad}
|
|
100
|
-
onMessage={(event) => handleWebViewMessage(event.nativeEvent.data)}
|
|
101
|
-
originWhitelist={['*']}
|
|
102
|
-
/>
|
|
103
|
-
</InlineWidget>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<OverlayWidget visible={isWidgetVisible} width={width} height={widgetHeight} position={type} onClose={handleClose}>
|
|
109
|
-
<WebView
|
|
110
|
-
ref={webviewRef}
|
|
111
|
-
style={webViewStyle}
|
|
112
|
-
source={{ uri }}
|
|
113
|
-
onLoadEnd={handleWebViewLoad}
|
|
114
|
-
onMessage={(event) => handleWebViewMessage(event.nativeEvent.data)}
|
|
115
|
-
originWhitelist={['*']}
|
|
116
|
-
/>
|
|
117
|
-
</OverlayWidget>
|
|
118
|
-
);
|
|
119
|
-
};
|
|
1
|
+
import React, { useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
import { Dimensions } from 'react-native';
|
|
3
|
+
import { WebView } from 'react-native-webview';
|
|
4
|
+
|
|
5
|
+
import { SoluCXKey, WidgetData, WidgetOptions, WidgetType } from './interfaces';
|
|
6
|
+
import { useWidgetState } from './hooks/useWidgetState';
|
|
7
|
+
import { WidgetEventService } from './services/widgetEventService';
|
|
8
|
+
import { buildWidgetURL } from './utils/urlUtils';
|
|
9
|
+
import { WEB_VIEW_MESSAGE_LISTENER } from './constants/webViewConstants';
|
|
10
|
+
import { ModalWidget } from './components/ModalWidget';
|
|
11
|
+
import { InlineWidget } from './components/InlineWidget';
|
|
12
|
+
import { OverlayWidget } from './components/OverlayWidget';
|
|
13
|
+
|
|
14
|
+
interface SoluCXWidgetProps {
|
|
15
|
+
soluCXKey: SoluCXKey;
|
|
16
|
+
type: WidgetType;
|
|
17
|
+
data: WidgetData;
|
|
18
|
+
options: WidgetOptions;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({
|
|
22
|
+
soluCXKey,
|
|
23
|
+
type,
|
|
24
|
+
data,
|
|
25
|
+
options
|
|
26
|
+
}) => {
|
|
27
|
+
const webviewRef = useRef<WebView>(null);
|
|
28
|
+
const { width } = Dimensions.get('window');
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
widgetHeight,
|
|
32
|
+
isWidgetVisible,
|
|
33
|
+
setIsWidgetVisible,
|
|
34
|
+
loadSavedData,
|
|
35
|
+
resize,
|
|
36
|
+
open,
|
|
37
|
+
close,
|
|
38
|
+
userId,
|
|
39
|
+
} = useWidgetState(data, options, type);
|
|
40
|
+
|
|
41
|
+
const eventService = new WidgetEventService(setIsWidgetVisible, resize, open, userId, options);
|
|
42
|
+
|
|
43
|
+
const uri = buildWidgetURL(soluCXKey, data);
|
|
44
|
+
const isForm = Boolean(data.form_id);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
loadSavedData();
|
|
48
|
+
}, [loadSavedData]);
|
|
49
|
+
|
|
50
|
+
const handleWebViewMessage = useCallback(async (message: string) => {
|
|
51
|
+
if (message && message.length > 0) {
|
|
52
|
+
try {
|
|
53
|
+
await eventService.handleMessage(message, isForm);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('Error handling widget message:', error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}, [eventService, isForm]);
|
|
59
|
+
|
|
60
|
+
const handleWebViewLoad = useCallback(() => {
|
|
61
|
+
webviewRef.current?.injectJavaScript(WEB_VIEW_MESSAGE_LISTENER);
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const handleClose = useCallback(() => {
|
|
65
|
+
if (type === 'inline' || type === 'modal') {
|
|
66
|
+
close();
|
|
67
|
+
}
|
|
68
|
+
setIsWidgetVisible(false);
|
|
69
|
+
}, [setIsWidgetVisible]);
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const webViewStyle = [
|
|
73
|
+
{ height: widgetHeight },
|
|
74
|
+
{ width }
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
if (type === 'modal') {
|
|
78
|
+
return (
|
|
79
|
+
<ModalWidget visible={isWidgetVisible} height={widgetHeight} onClose={handleClose}>
|
|
80
|
+
<WebView
|
|
81
|
+
ref={webviewRef}
|
|
82
|
+
style={webViewStyle}
|
|
83
|
+
source={{ uri }}
|
|
84
|
+
onLoadEnd={handleWebViewLoad}
|
|
85
|
+
onMessage={(event) => handleWebViewMessage(event.nativeEvent.data)}
|
|
86
|
+
originWhitelist={['*']}
|
|
87
|
+
/>
|
|
88
|
+
</ModalWidget>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (type === 'inline') {
|
|
93
|
+
return (
|
|
94
|
+
<InlineWidget visible={isWidgetVisible} height={widgetHeight} onClose={handleClose}>
|
|
95
|
+
<WebView
|
|
96
|
+
ref={webviewRef}
|
|
97
|
+
style={webViewStyle}
|
|
98
|
+
source={{ uri }}
|
|
99
|
+
onLoadEnd={handleWebViewLoad}
|
|
100
|
+
onMessage={(event) => handleWebViewMessage(event.nativeEvent.data)}
|
|
101
|
+
originWhitelist={['*']}
|
|
102
|
+
/>
|
|
103
|
+
</InlineWidget>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<OverlayWidget visible={isWidgetVisible} width={width} height={widgetHeight} position={type} onClose={handleClose}>
|
|
109
|
+
<WebView
|
|
110
|
+
ref={webviewRef}
|
|
111
|
+
style={webViewStyle}
|
|
112
|
+
source={{ uri }}
|
|
113
|
+
onLoadEnd={handleWebViewLoad}
|
|
114
|
+
onMessage={(event) => handleWebViewMessage(event.nativeEvent.data)}
|
|
115
|
+
originWhitelist={['*']}
|
|
116
|
+
/>
|
|
117
|
+
</OverlayWidget>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { buildWidgetURL } from '../utils/urlUtils';
|
|
2
|
-
|
|
3
|
-
describe('urlUtils', () => {
|
|
4
|
-
describe('buildWidgetURL', () => {
|
|
5
|
-
const mockKey = 'test-widget-key';
|
|
6
|
-
|
|
7
|
-
it('should build URL with transaction_id when provided', () => {
|
|
8
|
-
const data = {
|
|
9
|
-
customer_id: 'customer123',
|
|
10
|
-
form_id: 'form456',
|
|
11
|
-
transaction_id: 'trans789'
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const result = buildWidgetURL(mockKey, data);
|
|
15
|
-
|
|
16
|
-
expect(result).toContain('https://survey-link.solucx.com.br/link/test-widget-key/?mode=widget');
|
|
17
|
-
expect(result).toContain('customer_id=customer123');
|
|
18
|
-
expect(result).toContain('form_id=form456');
|
|
19
|
-
expect(result).toContain('transaction_id=trans789');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should build URL with empty transaction_id when not provided', () => {
|
|
23
|
-
const data = {
|
|
24
|
-
customer_id: 'customer123',
|
|
25
|
-
form_id: 'form456'
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const result = buildWidgetURL(mockKey, data);
|
|
29
|
-
|
|
30
|
-
expect(result).toContain('https://survey-link.solucx.com.br/link/test-widget-key/?mode=widget');
|
|
31
|
-
expect(result).toContain('transaction_id=&');
|
|
32
|
-
expect(result).toContain('customer_id=customer123');
|
|
33
|
-
expect(result).toContain('form_id=form456');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should handle empty data object', () => {
|
|
37
|
-
const data = {};
|
|
38
|
-
|
|
39
|
-
const result = buildWidgetURL(mockKey, data);
|
|
40
|
-
|
|
41
|
-
expect(result).toBe('https://survey-link.solucx.com.br/link/test-widget-key/?mode=widget&transaction_id=&');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should encode URL parameters correctly', () => {
|
|
45
|
-
const data = {
|
|
46
|
-
email: 'test@example.com',
|
|
47
|
-
name: 'John Doe'
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const result = buildWidgetURL(mockKey, data);
|
|
51
|
-
|
|
52
|
-
expect(result).toContain('email=test%40example.com');
|
|
53
|
-
expect(result).toContain('name=John+Doe');
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
1
|
+
import { buildWidgetURL } from '../utils/urlUtils';
|
|
2
|
+
|
|
3
|
+
describe('urlUtils', () => {
|
|
4
|
+
describe('buildWidgetURL', () => {
|
|
5
|
+
const mockKey = 'test-widget-key';
|
|
6
|
+
|
|
7
|
+
it('should build URL with transaction_id when provided', () => {
|
|
8
|
+
const data = {
|
|
9
|
+
customer_id: 'customer123',
|
|
10
|
+
form_id: 'form456',
|
|
11
|
+
transaction_id: 'trans789'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const result = buildWidgetURL(mockKey, data);
|
|
15
|
+
|
|
16
|
+
expect(result).toContain('https://survey-link.solucx.com.br/link/test-widget-key/?mode=widget');
|
|
17
|
+
expect(result).toContain('customer_id=customer123');
|
|
18
|
+
expect(result).toContain('form_id=form456');
|
|
19
|
+
expect(result).toContain('transaction_id=trans789');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should build URL with empty transaction_id when not provided', () => {
|
|
23
|
+
const data = {
|
|
24
|
+
customer_id: 'customer123',
|
|
25
|
+
form_id: 'form456'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const result = buildWidgetURL(mockKey, data);
|
|
29
|
+
|
|
30
|
+
expect(result).toContain('https://survey-link.solucx.com.br/link/test-widget-key/?mode=widget');
|
|
31
|
+
expect(result).toContain('transaction_id=&');
|
|
32
|
+
expect(result).toContain('customer_id=customer123');
|
|
33
|
+
expect(result).toContain('form_id=form456');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle empty data object', () => {
|
|
37
|
+
const data = {};
|
|
38
|
+
|
|
39
|
+
const result = buildWidgetURL(mockKey, data);
|
|
40
|
+
|
|
41
|
+
expect(result).toBe('https://survey-link.solucx.com.br/link/test-widget-key/?mode=widget&transaction_id=&');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should encode URL parameters correctly', () => {
|
|
45
|
+
const data = {
|
|
46
|
+
email: 'test@example.com',
|
|
47
|
+
name: 'John Doe'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const result = buildWidgetURL(mockKey, data);
|
|
51
|
+
|
|
52
|
+
expect(result).toContain('email=test%40example.com');
|
|
53
|
+
expect(result).toContain('name=John+Doe');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|