@descope/web-components-ui 1.0.393 → 1.0.395

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 (27) hide show
  1. package/dist/cjs/index.cjs.js +1637 -1167
  2. package/dist/cjs/index.cjs.js.map +1 -1
  3. package/dist/index.esm.js +1842 -1370
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/umd/DescopeDev.js +1 -1
  6. package/dist/umd/descope-combo-box-index-js.js +1 -1
  7. package/dist/umd/descope-date-field-descope-calendar-index-js.js +1 -1
  8. package/dist/umd/descope-security-questions-setup-index-js.js +2 -0
  9. package/dist/umd/descope-security-questions-setup-index-js.js.LICENSE.txt +5 -0
  10. package/dist/umd/descope-security-questions-verify-index-js.js +37 -0
  11. package/dist/umd/descope-security-questions-verify-index-js.js.LICENSE.txt +11 -0
  12. package/dist/umd/index.js +1 -1
  13. package/dist/umd/mapping-fields-descope-mappings-field-index-js.js +1 -1
  14. package/dist/umd/phone-fields-descope-phone-field-descope-phone-field-internal-index-js.js +1 -1
  15. package/dist/umd/phone-fields-descope-phone-field-index-js.js +1 -1
  16. package/dist/umd/phone-fields-descope-phone-input-box-field-index-js.js +1 -1
  17. package/package.json +1 -1
  18. package/src/components/descope-combo-box/ComboBoxClass.js +1 -1
  19. package/src/components/descope-security-questions-setup/SecurityQuestionsSetupClass.js +237 -0
  20. package/src/components/descope-security-questions-setup/index.js +6 -0
  21. package/src/components/descope-security-questions-verify/SecurityQuestionsVerifyClass.js +208 -0
  22. package/src/components/descope-security-questions-verify/index.js +7 -0
  23. package/src/index.cjs.js +2 -0
  24. package/src/index.js +2 -0
  25. package/src/theme/components/index.js +4 -0
  26. package/src/theme/components/securityQuestionsSetup.js +12 -0
  27. package/src/theme/components/securityQuestionsVerify.js +16 -0
@@ -0,0 +1,237 @@
1
+ import { createBaseClass } from '../../baseClasses/createBaseClass';
2
+ import { compose } from '../../helpers';
3
+ import { getComponentName, observeAttributes } from '../../helpers/componentHelpers';
4
+ import { componentNameValidationMixin, createStyleMixin, draggableMixin } from '../../mixins';
5
+ import { ComboBoxClass } from '../descope-combo-box/ComboBoxClass';
6
+ import { TextFieldClass } from '../descope-text-field/TextFieldClass';
7
+
8
+ export const componentName = getComponentName('security-questions-setup');
9
+
10
+ const attrsToSync = [
11
+ 'full-width',
12
+ 'readonly',
13
+ 'size',
14
+ 'label-type',
15
+ 'question-label',
16
+ 'question-placeholder',
17
+ 'question-data-errormessage-value-missing',
18
+ 'answer-label',
19
+ 'answer-placeholder',
20
+ 'answer-data-errormessage-value-missing',
21
+ ];
22
+
23
+ const attrsToReRender = ['count', 'questions'];
24
+ class RawSecurityQuestionsSetup extends createBaseClass({ componentName, baseSelector: 'div' }) {
25
+ constructor() {
26
+ super();
27
+
28
+ this.attachShadow({ mode: 'open' }).innerHTML = `
29
+ <style>
30
+ :host {
31
+ display: inline-flex;
32
+ }
33
+
34
+ div {
35
+ display: flex;
36
+ flex-direction: column;
37
+ }
38
+
39
+ </style>
40
+ <div></div>
41
+ `;
42
+ }
43
+
44
+ get count() {
45
+ return Number(this.getAttribute('count')) || 0;
46
+ }
47
+
48
+ #renderQuestions() {
49
+ const res = Array.from({ length: this.count }).map(
50
+ (_, index) =>
51
+ // <!--html-->
52
+ `
53
+ <div class="question-wrapper">
54
+ <descope-combo-box
55
+ data-id="${index}"
56
+ item-label-path="data-name"
57
+ item-value-path="data-id"
58
+ bordered="true"
59
+ required="true"
60
+ clear-button-visible="true"
61
+ ></descope-combo-box>
62
+
63
+ <descope-text-field
64
+ data-id="${index}"
65
+ required="true"
66
+ bordered="true"
67
+ minlength="2"
68
+ ></descope-text-field>
69
+ </div>
70
+ `
71
+ // <!--!html-->
72
+ );
73
+
74
+ this.baseElement.innerHTML = res.join(
75
+ '<spacer></spacer><descope-divider></descope-divider><spacer></spacer>'
76
+ );
77
+
78
+ this.comboBoxes.forEach((el) => {
79
+ el.addEventListener('change', (e) => {
80
+ this.updateRemainingCombosData(e.target);
81
+ const AttachedTextField = this.getAttachedTextField(e.target);
82
+
83
+ AttachedTextField.value = '';
84
+ });
85
+
86
+ // eslint-disable-next-line no-param-reassign
87
+ el.data = this.data;
88
+ });
89
+
90
+ this.#syncAttrs(attrsToSync);
91
+ }
92
+
93
+ getAttachedTextField(combo) {
94
+ const selector = `descope-text-field[data-id="${combo.getAttribute('data-id')}"]`;
95
+
96
+ return this.baseElement.querySelector(selector);
97
+ }
98
+
99
+ filterData(comboValue) {
100
+ const res = this.data.filter((item) => {
101
+ return item.value === comboValue || !this.selectedQuestionIds.includes(item.value);
102
+ });
103
+
104
+ return res;
105
+ }
106
+
107
+ reportValidity() {
108
+ this.inputs.reverse().forEach((el) => el.reportValidity());
109
+
110
+ return this.checkValidity();
111
+ }
112
+
113
+ checkValidity() {
114
+ return [...this.comboBoxes, ...this.textFields].every((el) => el.checkValidity());
115
+ }
116
+
117
+ updateRemainingCombosData(sourceCombo) {
118
+ this.comboBoxes
119
+ .filter((combo) => combo !== sourceCombo)
120
+ .forEach((combo) => {
121
+ // eslint-disable-next-line no-param-reassign
122
+ combo.data = this.filterData(combo.value);
123
+ });
124
+ }
125
+
126
+ get questions() {
127
+ try {
128
+ return JSON.parse(this.getAttribute('questions')) || [];
129
+ } catch (e) {
130
+ // eslint-disable-next-line no-console
131
+ console.error(componentName, 'Error parsing questions attribute', e);
132
+ return [];
133
+ }
134
+ }
135
+
136
+ // this returns the structure expected by the combo box
137
+ get data() {
138
+ return this.questions.map(({ id, text }) => ({ value: id, label: text }));
139
+ }
140
+
141
+ get comboBoxes() {
142
+ return Array.from(this.baseElement.querySelectorAll('descope-combo-box'));
143
+ }
144
+
145
+ get textFields() {
146
+ return Array.from(this.baseElement.querySelectorAll('descope-text-field'));
147
+ }
148
+
149
+ get inputs() {
150
+ return Array.from(this.baseElement.querySelectorAll('descope-combo-box, descope-text-field'));
151
+ }
152
+
153
+ get selectedQuestionIds() {
154
+ return this.comboBoxes.map((el) => el.value);
155
+ }
156
+
157
+ get value() {
158
+ return this.comboBoxes.map((combo) => {
159
+ const id = combo.value;
160
+ const answer = this.getAttachedTextField(combo).value;
161
+
162
+ return { id, answer };
163
+ });
164
+ }
165
+
166
+ set value(val = []) {
167
+ val.forEach((item, index) => {
168
+ const combo = this.comboBoxes[index];
169
+ const textField = this.getAttachedTextField(combo);
170
+
171
+ combo.value = item.id;
172
+ textField.value = item.answer;
173
+ });
174
+ }
175
+
176
+ // eslint-disable-next-line class-methods-use-this
177
+ #updateAttribute(ele, attrName, value) {
178
+ if (value === null) ele.removeAttribute(attrName);
179
+ else ele.setAttribute(attrName, value);
180
+ }
181
+
182
+ #syncAttrs(attrs) {
183
+ const componentMap = {
184
+ question: this.comboBoxes,
185
+ answer: this.textFields,
186
+ };
187
+
188
+ attrs.forEach((attr) => {
189
+ const [maybeComponentType, ...rest] = attr.split('-');
190
+ const elements = componentMap[maybeComponentType] || this.inputs;
191
+ const attrName = componentMap[maybeComponentType] ? rest.join('-') : attr;
192
+
193
+ elements.forEach((el) => {
194
+ this.#updateAttribute(el, attrName, this.getAttribute(attr));
195
+ });
196
+ });
197
+ }
198
+
199
+ init() {
200
+ super.init?.();
201
+ // render new components
202
+ observeAttributes(
203
+ this,
204
+ () => {
205
+ this.#renderQuestions();
206
+ },
207
+ { includeAttrs: attrsToReRender }
208
+ );
209
+
210
+ // update existing components
211
+ observeAttributes(this, this.#syncAttrs.bind(this), {
212
+ includeAttrs: attrsToSync,
213
+ });
214
+ }
215
+ }
216
+
217
+ export const SecurityQuestionsSetupClass = compose(
218
+ createStyleMixin({
219
+ mappings: {
220
+ hostWidth: [{ selector: () => ':host', property: 'width' }, { property: 'width' }],
221
+ hostDirection: [
222
+ { selector: () => ':host', property: 'direction' },
223
+ {
224
+ selector: () => ComboBoxClass.componentName,
225
+ property: ComboBoxClass.cssVarList.hostDirection,
226
+ },
227
+ {
228
+ selector: () => TextFieldClass.componentName,
229
+ property: TextFieldClass.cssVarList.hostDirection,
230
+ },
231
+ ],
232
+ gap: { selector: () => 'div', property: 'gap' },
233
+ },
234
+ }),
235
+ draggableMixin,
236
+ componentNameValidationMixin
237
+ )(RawSecurityQuestionsSetup);
@@ -0,0 +1,6 @@
1
+ import { componentName, SecurityQuestionsSetupClass } from './SecurityQuestionsSetupClass';
2
+ import '../boolean-fields/descope-checkbox';
3
+
4
+ customElements.define(componentName, SecurityQuestionsSetupClass);
5
+
6
+ export { SecurityQuestionsSetupClass };
@@ -0,0 +1,208 @@
1
+ import { createBaseClass } from '../../baseClasses/createBaseClass';
2
+ import { compose } from '../../helpers';
3
+ import { getComponentName, observeAttributes } from '../../helpers/componentHelpers';
4
+ import { componentNameValidationMixin, createStyleMixin, draggableMixin } from '../../mixins';
5
+ import { TextClass } from '../descope-text/TextClass';
6
+ import { TextFieldClass } from '../descope-text-field/TextFieldClass';
7
+
8
+ export const componentName = getComponentName('security-questions-verify');
9
+
10
+ const textFieldsAttrs = [
11
+ 'full-width',
12
+ 'readonly',
13
+ 'size',
14
+ 'answer-placeholder',
15
+ 'answer-data-errormessage-value-missing',
16
+ ];
17
+
18
+ const textsAttrs = ['question-mode'];
19
+
20
+ const attrMappings = {
21
+ 'answer-placeholder': 'placeholder',
22
+ 'answer-data-errormessage-value-missing': 'data-errormessage-value-missing',
23
+ 'question-mode': 'mode',
24
+ };
25
+
26
+ const attrsToSync = [...textFieldsAttrs, ...textsAttrs];
27
+
28
+ const attrsToReRender = ['questions'];
29
+ class RawSecurityQuestionsVerify extends createBaseClass({ componentName, baseSelector: 'div' }) {
30
+ constructor() {
31
+ super();
32
+
33
+ this.attachShadow({ mode: 'open' }).innerHTML = `
34
+ <style>
35
+ :host {
36
+ display: inline-flex;
37
+ }
38
+
39
+ div {
40
+ display: flex;
41
+ flex-direction: column;
42
+ }
43
+
44
+ </style>
45
+ <div></div>
46
+ `;
47
+ }
48
+
49
+ #renderQuestions() {
50
+ const res = this.questions.map(
51
+ ({ id, text }) =>
52
+ // <!--html-->
53
+ `
54
+ <div class="question-wrapper">
55
+ <descope-text
56
+ data-id="${id}"
57
+ >${text}</descope-text>
58
+
59
+ <descope-text-field
60
+ data-id="${id}"
61
+ required="true"
62
+ bordered="true"
63
+ ></descope-text-field>
64
+ </div>
65
+ `
66
+ // <!--!html-->
67
+ );
68
+
69
+ this.baseElement.innerHTML = res.join(
70
+ '<spacer></spacer><descope-divider></descope-divider><spacer></spacer>'
71
+ );
72
+
73
+ this.#syncAttrs(attrsToSync);
74
+
75
+ // focus input when text is clicked
76
+ this.texts.forEach((el) => {
77
+ const input = this.textFields.find(
78
+ (field) => field.getAttribute('data-id') === el.getAttribute('data-id')
79
+ );
80
+ // eslint-disable-next-line no-param-reassign
81
+ if (input) el.onclick = input.focus.bind(input);
82
+ });
83
+ }
84
+
85
+ reportValidity() {
86
+ this.textFields.reverse().forEach((el) => el.reportValidity());
87
+
88
+ return this.checkValidity();
89
+ }
90
+
91
+ checkValidity() {
92
+ return this.textFields.every((el) => el.checkValidity());
93
+ }
94
+
95
+ get questions() {
96
+ try {
97
+ return JSON.parse(this.getAttribute('questions')) || [];
98
+ } catch (e) {
99
+ // eslint-disable-next-line no-console
100
+ console.error(componentName, 'Error parsing questions attribute', e);
101
+ return [];
102
+ }
103
+ }
104
+
105
+ get texts() {
106
+ return Array.from(this.baseElement.querySelectorAll('descope-text'));
107
+ }
108
+
109
+ get textFields() {
110
+ return Array.from(this.baseElement.querySelectorAll('descope-text-field'));
111
+ }
112
+
113
+ get elements() {
114
+ return Array.from(this.baseElement.querySelectorAll('descope-text, descope-text-field'));
115
+ }
116
+
117
+ get value() {
118
+ return this.textFields.map((el) => ({ id: el.getAttribute('data-id'), answer: el.value }));
119
+ }
120
+
121
+ set value(val = []) {
122
+ val.forEach(({ id, answer }) => {
123
+ const textField = this.textFields.find((el) => el.getAttribute('data-id') === id);
124
+ if (textField) textField.value = answer;
125
+ });
126
+ }
127
+
128
+ // eslint-disable-next-line class-methods-use-this
129
+ #updateAttribute(ele, attrName, value) {
130
+ if (value === null) ele.removeAttribute(attrName);
131
+ else ele.setAttribute(attrName, value);
132
+ }
133
+
134
+ #getElementsToUpdate(attr) {
135
+ switch (true) {
136
+ case textFieldsAttrs.includes(attr):
137
+ return this.textFields;
138
+ case textsAttrs.includes(attr):
139
+ return this.texts;
140
+ default:
141
+ return [];
142
+ }
143
+ }
144
+
145
+ #syncAttrs(attrs) {
146
+ attrs.forEach((attr) => {
147
+ this.#getElementsToUpdate(attr).forEach((el) => {
148
+ this.#updateAttribute(el, attrMappings[attr] || attr, this.getAttribute(attr));
149
+ });
150
+ });
151
+ }
152
+
153
+ init() {
154
+ super.init?.();
155
+ // render new components
156
+ observeAttributes(
157
+ this,
158
+ () => {
159
+ this.#renderQuestions();
160
+ },
161
+ { includeAttrs: attrsToReRender }
162
+ );
163
+
164
+ // update existing components
165
+ observeAttributes(this, this.#syncAttrs.bind(this), {
166
+ includeAttrs: attrsToSync,
167
+ });
168
+ }
169
+ }
170
+
171
+ export const SecurityQuestionsVerifyClass = compose(
172
+ createStyleMixin({ componentNameOverride: getComponentName('input-wrapper') }),
173
+ createStyleMixin({
174
+ mappings: {
175
+ hostWidth: [{ selector: () => ':host', property: 'width' }, { property: 'width' }],
176
+ hostDirection: [
177
+ { selector: () => ':host', property: 'direction' },
178
+ {
179
+ selector: () => TextClass.componentName,
180
+ property: TextClass.cssVarList.hostDirection,
181
+ },
182
+ {
183
+ selector: () => TextFieldClass.componentName,
184
+ property: TextFieldClass.cssVarList.hostDirection,
185
+ },
186
+ ],
187
+ gap: { selector: () => 'div', property: 'gap' },
188
+ questionTextAlign: {
189
+ selector: () => TextClass.componentName,
190
+ property: TextClass.cssVarList.textAlign,
191
+ },
192
+ questionFontSize: {
193
+ selector: () => TextClass.componentName,
194
+ property: TextClass.cssVarList.fontSize,
195
+ },
196
+ questionFontFamily: {
197
+ selector: () => TextClass.componentName,
198
+ property: TextClass.cssVarList.fontFamily,
199
+ },
200
+ questionCursor: {
201
+ selector: () => TextClass.componentName,
202
+ property: 'cursor',
203
+ },
204
+ },
205
+ }),
206
+ draggableMixin,
207
+ componentNameValidationMixin
208
+ )(RawSecurityQuestionsVerify);
@@ -0,0 +1,7 @@
1
+ import { componentName, SecurityQuestionsVerifyClass } from './SecurityQuestionsVerifyClass';
2
+ import '../descope-text';
3
+ import '../descope-text-field';
4
+
5
+ customElements.define(componentName, SecurityQuestionsVerifyClass);
6
+
7
+ export { SecurityQuestionsVerifyClass };
package/src/index.cjs.js CHANGED
@@ -54,3 +54,5 @@ export { ListItemClass } from './components/descope-list/ListItemClass';
54
54
  export { AppsListClass } from './components/descope-apps-list/AppsListClass';
55
55
  export { ScopesListClass } from './components/descope-scopes-list/ScopesListClass';
56
56
  export { ThirdPartyAppLogoClass } from './components/descope-third-party-app-logo/ThirdPartyAppLogoClass';
57
+ export { SecurityQuestionsSetupClass } from './components/descope-security-questions-setup/SecurityQuestionsSetupClass';
58
+ export { SecurityQuestionsVerifyClass } from './components/descope-security-questions-verify/SecurityQuestionsVerifyClass';
package/src/index.js CHANGED
@@ -46,6 +46,8 @@ export * from './components/descope-list';
46
46
  export * from './components/descope-apps-list';
47
47
  export * from './components/descope-scopes-list';
48
48
  export * from './components/descope-third-party-app-logo';
49
+ export * from './components/descope-security-questions-setup';
50
+ export * from './components/descope-security-questions-verify';
49
51
 
50
52
  export {
51
53
  globalsThemeToStyle,
@@ -48,6 +48,8 @@ import * as listItem from './list/listItem';
48
48
  import * as appsList from './appsList';
49
49
  import * as scopesList from './scopesList';
50
50
  import * as thirdPartyAppLogo from './thirdPartyAppLogo';
51
+ import * as securityQuestionsSetup from './securityQuestionsSetup';
52
+ import * as securityQuestionsVerify from './securityQuestionsVerify';
51
53
 
52
54
  const components = {
53
55
  button,
@@ -101,6 +103,8 @@ const components = {
101
103
  appsList,
102
104
  scopesList,
103
105
  thirdPartyAppLogo,
106
+ securityQuestionsSetup,
107
+ securityQuestionsVerify,
104
108
  };
105
109
 
106
110
  const theme = Object.keys(components).reduce(
@@ -0,0 +1,12 @@
1
+ import { SecurityQuestionsSetupClass } from '../../components/descope-security-questions-setup/SecurityQuestionsSetupClass';
2
+
3
+ export const vars = SecurityQuestionsSetupClass.cssVarList;
4
+
5
+ const securityQuestionsSetup = {
6
+ [vars.hostWidth]: 'fit-content',
7
+ _fullWidth: {
8
+ [vars.hostWidth]: '100%',
9
+ },
10
+ };
11
+
12
+ export default securityQuestionsSetup;
@@ -0,0 +1,16 @@
1
+ import { SecurityQuestionsVerifyClass } from '../../components/descope-security-questions-verify/SecurityQuestionsVerifyClass';
2
+ import { refs } from './inputWrapper';
3
+
4
+ export const vars = SecurityQuestionsVerifyClass.cssVarList;
5
+
6
+ const securityQuestionsVerify = {
7
+ [vars.hostWidth]: 'min-content',
8
+ [vars.questionCursor]: 'pointer',
9
+ _fullWidth: {
10
+ [vars.hostWidth]: '100%',
11
+ },
12
+ [vars.questionFontSize]: refs.fontSize,
13
+ [vars.questionFontFamily]: refs.fontFamily,
14
+ };
15
+
16
+ export default securityQuestionsVerify;