@smileid/web-components 1.0.0-beta

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.
Files changed (73) hide show
  1. package/.eslintrc.cjs +72 -0
  2. package/components/README.md +14 -0
  3. package/components/attribution/PoweredBySmileId.js +42 -0
  4. package/components/camera-permission/CameraPermission.js +136 -0
  5. package/components/camera-permission/CameraPermission.stories.js +21 -0
  6. package/components/combobox/src/Combobox.js +586 -0
  7. package/components/combobox/src/index.js +1 -0
  8. package/components/document/src/DocumentCaptureScreens.js +317 -0
  9. package/components/document/src/DocumentCaptureScreens.stories.js +51 -0
  10. package/components/document/src/README.md +108 -0
  11. package/components/document/src/document-capture/DocumentCapture.js +677 -0
  12. package/components/document/src/document-capture/DocumentCapture.stories.js +71 -0
  13. package/components/document/src/document-capture/README.md +89 -0
  14. package/components/document/src/document-capture/index.js +3 -0
  15. package/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +499 -0
  16. package/components/document/src/document-capture-instructions/DocumentCaptureInstructions.stories.js +17 -0
  17. package/components/document/src/document-capture-instructions/README.md +56 -0
  18. package/components/document/src/document-capture-instructions/index.js +3 -0
  19. package/components/document/src/document-capture-review/DocumentCaptureReview.js +378 -0
  20. package/components/document/src/document-capture-review/DocumentCaptureReview.stories.js +14 -0
  21. package/components/document/src/document-capture-review/README.md +79 -0
  22. package/components/document/src/document-capture-review/index.js +3 -0
  23. package/components/document/src/index.js +3 -0
  24. package/components/end-user-consent/src/EndUserConsent.js +809 -0
  25. package/components/end-user-consent/src/EndUserConsent.stories.js +23 -0
  26. package/components/end-user-consent/src/index.js +4 -0
  27. package/components/navigation/src/Navigation.js +160 -0
  28. package/components/navigation/src/Navigation.stories.js +24 -0
  29. package/components/navigation/src/index.js +3 -0
  30. package/components/selfie/README.md +201 -0
  31. package/components/selfie/src/SelfieCaptureScreens.js +224 -0
  32. package/components/selfie/src/SelfieCaptureScreens.stories.js +21 -0
  33. package/components/selfie/src/index.js +5 -0
  34. package/components/selfie/src/selfie-capture/SelfieCapture.js +878 -0
  35. package/components/selfie/src/selfie-capture/SelfieCapture.stories.js +23 -0
  36. package/components/selfie/src/selfie-capture/index.js +3 -0
  37. package/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +638 -0
  38. package/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.stories.js +17 -0
  39. package/components/selfie/src/selfie-capture-instructions/index.js +3 -0
  40. package/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +348 -0
  41. package/components/selfie/src/selfie-capture-review/SelfieCaptureReview.stories.js +17 -0
  42. package/components/selfie/src/selfie-capture-review/index.js +3 -0
  43. package/components/signature-pad/package.json +30 -0
  44. package/components/signature-pad/src/SignaturePad.js +477 -0
  45. package/components/signature-pad/src/SignaturePad.stories.js +24 -0
  46. package/components/signature-pad/src/index.js +3 -0
  47. package/components/smart-camera-web/src/SmartCameraWeb.js +246 -0
  48. package/components/smart-camera-web/src/SmartCameraWeb.stories.js +35 -0
  49. package/components/totp-consent/src/TotpConsent.js +935 -0
  50. package/components/totp-consent/src/index.js +4 -0
  51. package/cypress/e2e/smart-camera-web-attribution.cy.js +21 -0
  52. package/cypress/e2e/smart-camera-web-back-press.cy.js +481 -0
  53. package/cypress/e2e/smart-camera-web-hide-instructions.cy.js +334 -0
  54. package/cypress/e2e/smart-camera-web.cy.js +309 -0
  55. package/cypress/fixtures/example.json +5 -0
  56. package/cypress/pages/capture-back-of-id-hide-attribution.html +44 -0
  57. package/cypress/pages/capture-back-of-id-navigation.html +72 -0
  58. package/cypress/pages/smart-camera-web-hide-instructions.html +38 -0
  59. package/cypress/pages/smart-camera-web.html +38 -0
  60. package/cypress/support/commands.js +144 -0
  61. package/cypress/support/e2e.js +20 -0
  62. package/cypress.config.js +11 -0
  63. package/domain/camera/src/README.md +38 -0
  64. package/domain/camera/src/SmartCamera.js +81 -0
  65. package/domain/constants/src/Constants.js +27 -0
  66. package/domain/file-upload/README.md +35 -0
  67. package/domain/file-upload/src/SmartFileUpload.js +65 -0
  68. package/esbuild.js +119 -0
  69. package/index.js +5 -0
  70. package/package.json +46 -0
  71. package/styles/README.md +3 -0
  72. package/styles/src/styles.js +348 -0
  73. package/styles/src/typography.js +52 -0
@@ -0,0 +1,378 @@
1
+ import styles from '../../../../styles/src/styles';
2
+ import '../../../navigation/src';
3
+
4
+ function templateString() {
5
+ return `
6
+ ${styles}
7
+ <style>
8
+ .retake-photo.button[data-variant~="ghost"] {
9
+ color: #FF5805;
10
+ }
11
+
12
+
13
+ @media (max-width: 600px) {
14
+ .id-camera-screen {
15
+ width: 100%;
16
+ height: 100vh;
17
+ }
18
+
19
+ .section {
20
+ width: 100%;
21
+ height: 100vh;
22
+ justify-content: center;
23
+ }
24
+
25
+ .video-overlay {
26
+ position: absolute;
27
+ border-width: 1rem;
28
+ border-color: white;
29
+ border-style: solid;
30
+ inset: 0px;
31
+ }
32
+
33
+ img {
34
+ clip-path: polygon(10% 10%, 90% 10%, 90% 85%, 10% 85%);
35
+ }
36
+ }
37
+
38
+ .id-image-container {
39
+ display: flex;
40
+ flex-direction: column;
41
+ align-items: center;
42
+ gap: 1.75rem;
43
+ }
44
+
45
+ .id-image {
46
+ max-width: fit-content;
47
+ height: auto;
48
+ text-align: center;
49
+ position: relative;
50
+ overflow: hidden;
51
+ }
52
+ img {
53
+ height: 100%;
54
+ min-height: 100px;
55
+ clip-path: polygon(5% 5%, 95% 5%, 95% 90%, 5% 90%);
56
+ }
57
+
58
+ .video-overlay .inner-border {
59
+ position: absolute;
60
+ border-width: 1rem;
61
+ border-color: #9394ab;
62
+ border-style: solid;
63
+ border-radius: 1rem;
64
+ inset: -8px;
65
+ }
66
+
67
+ .action-buttons {
68
+ width: 80%;
69
+ }
70
+
71
+
72
+ .icon-btn {
73
+ appearance: none;
74
+ background: none;
75
+ border: none;
76
+ color: hsl(0deg 0% 94%);
77
+ cursor: pointer;
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ padding: 4px 8px;
82
+ }
83
+ .justify-right {
84
+ justify-content: end !important;
85
+ }
86
+ .nav {
87
+ display: flex;
88
+ justify-content: space-between;
89
+ }
90
+
91
+ .back-wrapper {
92
+ display: flex;
93
+ align-items: center;
94
+ }
95
+
96
+ .back-button-text {
97
+ font-size: 11px;
98
+ line-height: 11px;
99
+ color: rgb(21, 31, 114);
100
+ }
101
+
102
+
103
+
104
+ .tips,
105
+ .powered-by {
106
+ align-items: center;
107
+ border-radius: 0.25rem;
108
+ color: #4e6577;
109
+ display: flex;
110
+ justify-content: center;
111
+ letter-spacing: 0.075em;
112
+ }
113
+
114
+ .powered-by {
115
+ box-shadow: 0px 2.57415px 2.57415px rgba(0, 0, 0, 0.06);
116
+ display: inline-flex;
117
+ font-size: 0.5rem;
118
+ }
119
+
120
+ .tips {
121
+ margin-left: auto;
122
+ margin-right: auto;
123
+ max-width: 17rem;
124
+ }
125
+
126
+ .tips > * + *,
127
+ .powered-by > * + * {
128
+ display: inline-block;
129
+ margin-left: 0.5em;
130
+ }
131
+
132
+ .powered-by .company {
133
+ color: #18406d;
134
+ font-weight: 700;
135
+ letter-spacing: 0.15rem;
136
+ }
137
+
138
+ .logo-mark {
139
+ background-color: #004071;
140
+ display: inline-block;
141
+ padding: 0.25em 0.5em;
142
+ }
143
+
144
+ .logo-mark svg {
145
+ height: auto;
146
+ justify-self: center;
147
+ width: 0.75em;
148
+ }
149
+
150
+ #document-capture-review-screen {
151
+ block-size: 45rem;
152
+ padding-block: 2rem;
153
+ display: flex;
154
+ flex-direction: column;
155
+ max-block-size: 100%;
156
+ max-inline-size: 40ch;
157
+ }
158
+
159
+ #document-capture-review-screen .id-image-container.landscape {
160
+ height: auto;
161
+ }
162
+
163
+ #document-capture-review-screen header p {
164
+ margin-block: 0 !important;
165
+ }
166
+
167
+ .description {
168
+ color: var(--neutral-off-black, #2D2B2A);
169
+ text-align: center;
170
+
171
+ /* p */
172
+ font-family: DM Sans;
173
+ font-size: 0.875rem;
174
+ font-style: normal;
175
+ font-weight: 400;
176
+ line-height: 18px;
177
+ }
178
+
179
+ .padding-bottom-2 {
180
+ padding-bottom: 2rem;
181
+ }
182
+
183
+ .instructions-wrapper {
184
+ display: inline-flex;
185
+ flex-direction: column;
186
+ align-items: flex-start;
187
+ gap: 2rem;
188
+ margin-block-start: 2rem;
189
+ margin-block-end: 4rem;
190
+ }
191
+ .instructions {
192
+ display: flex;
193
+ align-items: center;
194
+ text-align: initial;
195
+ }
196
+
197
+ .instructions svg {
198
+ flex-shrink: 0;
199
+ margin-inline-end: 2rem;
200
+ }
201
+
202
+ .instructions p {
203
+ margin-block: 0;
204
+ }
205
+
206
+ .instruction-body {
207
+ font-size: 0.75rem;
208
+ }
209
+
210
+ h1 {
211
+ color: var(--web-digital-blue, #001096);
212
+ text-align: center;
213
+
214
+ /* h1 */
215
+ font-size: 1.5rem;
216
+ font-style: normal;
217
+ font-weight: 700;
218
+ line-height: 36px; /* 150% */
219
+ }
220
+
221
+ .p2 {
222
+ font-size: 1rem;
223
+ font-style: normal;
224
+ font-weight: 500;
225
+ line-height: 1rem;
226
+ }
227
+
228
+ .instruction-header {
229
+ color: var(--web-digital-blue, #001096);
230
+ }
231
+
232
+ .h2 {
233
+ font-size: 1rem;
234
+ font-style: normal;
235
+ font-weight: 700;
236
+ line-height: 1.5rem;
237
+ }
238
+ </style>
239
+ <div id='document-capture-review-screen' class='flow center'>
240
+ <smileid-navigation ${this.showNavigation ? 'show-navigation' : ''} hide-back></smileid-navigation>
241
+ <h1 class="header-title">
242
+ Is the document clear and readable?
243
+ </h1>
244
+ <p class="description">Make sure all corners of the document
245
+ are visible and there is no glare</p>
246
+ <div class='section | flow'>
247
+ <div class='id-image-container'>
248
+ <div class='id-image'>
249
+ <div class='video-overlay'></div>
250
+ ${this.imageSrc ? `<img alt='your ID card' id='document-capture-review-image' src='${this.imageSrc}' width='396' />` : ''}
251
+ </div>
252
+ <div class='flow action-buttons'>
253
+ <button data-variant='solid full-width' class='button' type='button' id='select-id-image'>
254
+ Yes, my ID is readable
255
+ </button>
256
+ <button data-variant='ghost full-width' class='button retake-photo' type='button' id='re-capture-id-image'>
257
+ No, retake photo
258
+ </button>
259
+ </div>
260
+ </div>
261
+
262
+ ${
263
+ this.hideAttribution
264
+ ? ''
265
+ : `
266
+ <powered-by-smile-id></powered-by-smile-id>
267
+ `
268
+ }
269
+ </div>
270
+ </div>
271
+ `;
272
+ }
273
+
274
+ class IdReview extends HTMLElement {
275
+ constructor() {
276
+ super();
277
+ this.templateString = templateString.bind(this);
278
+ this.render = () => this.templateString();
279
+
280
+ this.attachShadow({ mode: 'open' });
281
+ }
282
+
283
+ connectedCallback() {
284
+ const template = document.createElement('template');
285
+ template.innerHTML = this.render();
286
+ this.shadowRoot.innerHTML = '';
287
+ this.shadowRoot.appendChild(template.content.cloneNode(true));
288
+ this.setUpEventListeners();
289
+ }
290
+
291
+ static get observedAttributes() {
292
+ return ['hide-back-to-host', 'show-navigation', 'data-image'];
293
+ }
294
+
295
+ get hideBack() {
296
+ return this.hasAttribute('hide-back-to-host');
297
+ }
298
+
299
+ get showNavigation() {
300
+ return this.hasAttribute('show-navigation');
301
+ }
302
+
303
+ get themeColor() {
304
+ return this.getAttribute('theme-color') || '#043C93';
305
+ }
306
+
307
+ get hideAttribution() {
308
+ return this.hasAttribute('hide-attribution');
309
+ }
310
+
311
+ get imageSrc() {
312
+ return this.getAttribute('data-image');
313
+ }
314
+
315
+ get title() {
316
+ return this.getAttribute('title') || 'Submit Front of ID';
317
+ }
318
+
319
+ handleBackEvents() {
320
+ this.dispatchEvent(new CustomEvent('document-capture-review.cancelled'));
321
+ }
322
+
323
+ handleCloseEvents() {
324
+ this.dispatchEvent(new CustomEvent('document-capture-review.close'));
325
+ }
326
+
327
+ attributeChangedCallback(name) {
328
+ switch (name) {
329
+ case 'data-image':
330
+ case 'hide-back-to-host':
331
+ case 'show-navigation':
332
+ this.shadowRoot.innerHTML = this.render();
333
+ this.setUpEventListeners();
334
+ break;
335
+ default:
336
+ break;
337
+ }
338
+ }
339
+
340
+ setUpEventListeners() {
341
+ this.selectIDImage = this.shadowRoot.querySelector('#select-id-image');
342
+ this.reCaptureIDImage = this.shadowRoot.querySelector(
343
+ '#re-capture-id-image',
344
+ );
345
+ this.navigation = this.shadowRoot.querySelector('smileid-navigation');
346
+ this.navigation.addEventListener('navigation.back', () => {
347
+ this.handleBackEvents();
348
+ });
349
+
350
+ this.navigation.addEventListener('navigation.close', () => {
351
+ this.handleCloseEvents();
352
+ });
353
+
354
+ this.selectIDImage.addEventListener('click', () => {
355
+ this.dispatchEvent(
356
+ new CustomEvent('document-capture-review.accepted', {
357
+ detail: {},
358
+ }),
359
+ );
360
+ });
361
+ this.reCaptureIDImage.addEventListener('click', () => {
362
+ this.dispatchEvent(
363
+ new CustomEvent('document-capture-review.rejected', {
364
+ detail: {},
365
+ }),
366
+ );
367
+ });
368
+ }
369
+ }
370
+
371
+ if (
372
+ 'customElements' in window &&
373
+ !customElements.get('document-capture-review')
374
+ ) {
375
+ window.customElements.define('document-capture-review', IdReview);
376
+ }
377
+
378
+ export default IdReview;
@@ -0,0 +1,14 @@
1
+ import './index';
2
+
3
+ const meta = {
4
+ component: 'document-capture-review',
5
+ };
6
+
7
+ export default meta;
8
+
9
+ export const IdReview = {
10
+ render: () => `
11
+ <document-capture-review show-navigation data-image="https://placehold.co/600x400">
12
+ </document-capture-review>
13
+ `,
14
+ };
@@ -0,0 +1,79 @@
1
+ # IdReview Web Component
2
+
3
+ The `IdReview` component is designed to facilitate user interaction by allowing them to review and verify the accuracy of captured images. Users have the option to accept the captured image or initiate a recapture if necessary.
4
+
5
+ ## Usage
6
+
7
+ To integrate the `IdReview` component into your web application, insert the custom HTML tag as follows, specifying attributes for the captured image data and optional features like navigation controls:
8
+
9
+ ```html
10
+ <document-capture-review
11
+ data-image="base64image"
12
+ show-navigation
13
+ hide-back-to-host
14
+ ></document-capture-review>
15
+ ```
16
+
17
+ ### Attributes
18
+
19
+ - `data-image`: A base64 encoded string of the captured image to be reviewed.
20
+ - `show-navigation`: (Optional) Shows navigation controls for the review process. This attribute is boolean and does not require a value.
21
+ - `hide-back-to-host`: (Optional) Hides the option to return to the host application or page. This attribute is boolean and does not require a value.
22
+
23
+ ## Event Handling
24
+
25
+ ### Image Acceptance
26
+
27
+ When a user confirms the captured image as acceptable, the `document-capture-review.accepted` event is emitted. Implement an event listener to handle this action:
28
+
29
+ ```js
30
+ document
31
+ .querySelector('document-capture-review')
32
+ .addEventListener('document-capture-review.accepted', function (event) {
33
+ // Handle the image acceptance action here
34
+ });
35
+ ```
36
+
37
+ ### Recapture Request
38
+
39
+ If the user decides to recapture the image, the `document-capture-review.rejected` event is triggered. Set up an event listener to manage this scenario:
40
+
41
+ ```js
42
+ document
43
+ .querySelector('document-capture-review')
44
+ .addEventListener('document-capture-review.rejected', function (event) {
45
+ // Handle the recapture request here
46
+ });
47
+ ```
48
+
49
+ ## Example
50
+
51
+ Below is a sample implementation showcasing how to use the `IdReview` component with an event listener for both accepting an image and requesting a recapture:
52
+
53
+ ```html
54
+ <document-capture-review
55
+ data-image="base64image"
56
+ show-navigation
57
+ hide-back-to-host
58
+ ></document-capture-review>
59
+
60
+ <script>
61
+ const idReviewElement = document.querySelector('document-capture-review');
62
+
63
+ idReviewElement.addEventListener(
64
+ 'document-capture-review.accepted',
65
+ function (event) {
66
+ console.log('Image accepted by the user.');
67
+ // Additional logic for accepted image
68
+ },
69
+ );
70
+
71
+ idReviewElement.addEventListener(
72
+ 'document-capture-review.rejected',
73
+ function (event) {
74
+ console.log('User requested to recapture the image.');
75
+ // Additional logic for image recapture
76
+ },
77
+ );
78
+ </script>
79
+ ```
@@ -0,0 +1,3 @@
1
+ import IdReview from './DocumentCaptureReview';
2
+
3
+ export default IdReview;
@@ -0,0 +1,3 @@
1
+ import DocumentCaptureScreens from './DocumentCaptureScreens';
2
+
3
+ export default DocumentCaptureScreens;