@descope/web-components-ui 1.0.393 → 1.0.394

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.
@@ -0,0 +1,245 @@
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 isReadOnly() {
45
+ return this.getAttribute('readonly') === 'true';
46
+ }
47
+
48
+ get size() {
49
+ return this.getAttribute('size') || 'sm';
50
+ }
51
+
52
+ get count() {
53
+ return Number(this.getAttribute('count')) || 0;
54
+ }
55
+
56
+ #renderQuestions() {
57
+ const res = Array.from({ length: this.count }).map(
58
+ (_, index) =>
59
+ // <!--html-->
60
+ `
61
+ <div class="question-wrapper">
62
+ <descope-combo-box
63
+ data-id="${index}"
64
+ item-label-path="data-name"
65
+ item-value-path="data-id"
66
+ bordered="true"
67
+ required="true"
68
+ clear-button-visible="true"
69
+ ></descope-combo-box>
70
+
71
+ <descope-text-field
72
+ data-id="${index}"
73
+ required="true"
74
+ bordered="true"
75
+ minlength="2"
76
+ ></descope-text-field>
77
+ </div>
78
+ `
79
+ // <!--!html-->
80
+ );
81
+
82
+ this.baseElement.innerHTML = res.join(
83
+ '<spacer></spacer><descope-divider></descope-divider><spacer></spacer>'
84
+ );
85
+
86
+ this.comboBoxes.forEach((el) => {
87
+ el.addEventListener('change', (e) => {
88
+ this.updateRemainingCombosData(e.target);
89
+ const AttachedTextField = this.getAttachedTextField(e.target);
90
+
91
+ AttachedTextField.value = '';
92
+ });
93
+
94
+ // eslint-disable-next-line no-param-reassign
95
+ el.data = this.data;
96
+ });
97
+
98
+ this.#syncAttrs(attrsToSync);
99
+ }
100
+
101
+ getAttachedTextField(combo) {
102
+ const selector = `descope-text-field[data-id="${combo.getAttribute('data-id')}"]`;
103
+
104
+ return this.baseElement.querySelector(selector);
105
+ }
106
+
107
+ filterData(comboValue) {
108
+ const res = this.data.filter((item) => {
109
+ return item.value === comboValue || !this.selectedQuestionIds.includes(item.value);
110
+ });
111
+
112
+ return res;
113
+ }
114
+
115
+ reportValidity() {
116
+ this.inputs.reverse().forEach((el) => el.reportValidity());
117
+
118
+ return this.checkValidity();
119
+ }
120
+
121
+ checkValidity() {
122
+ return [...this.comboBoxes, ...this.textFields].every((el) => el.checkValidity());
123
+ }
124
+
125
+ updateRemainingCombosData(sourceCombo) {
126
+ this.comboBoxes
127
+ .filter((combo) => combo !== sourceCombo)
128
+ .forEach((combo) => {
129
+ // eslint-disable-next-line no-param-reassign
130
+ combo.data = this.filterData(combo.value);
131
+ });
132
+ }
133
+
134
+ get questions() {
135
+ try {
136
+ return JSON.parse(this.getAttribute('questions')) || [];
137
+ } catch (e) {
138
+ // eslint-disable-next-line no-console
139
+ console.error(componentName, 'Error parsing data attribute', e);
140
+ return [];
141
+ }
142
+ }
143
+
144
+ // this returns the structure expected by the combo box
145
+ get data() {
146
+ return this.questions.map(({ id, text }) => ({ value: id, label: text }));
147
+ }
148
+
149
+ get comboBoxes() {
150
+ return Array.from(this.baseElement.querySelectorAll('descope-combo-box'));
151
+ }
152
+
153
+ get textFields() {
154
+ return Array.from(this.baseElement.querySelectorAll('descope-text-field'));
155
+ }
156
+
157
+ get inputs() {
158
+ return Array.from(this.baseElement.querySelectorAll('descope-combo-box, descope-text-field'));
159
+ }
160
+
161
+ get selectedQuestionIds() {
162
+ return this.comboBoxes.map((el) => el.value);
163
+ }
164
+
165
+ get value() {
166
+ return this.comboBoxes.map((combo) => {
167
+ const id = combo.value;
168
+ const answer = this.getAttachedTextField(combo).value;
169
+
170
+ return { id, answer };
171
+ });
172
+ }
173
+
174
+ set value(val = []) {
175
+ val.forEach((item, index) => {
176
+ const combo = this.comboBoxes[index];
177
+ const textField = this.getAttachedTextField(combo);
178
+
179
+ combo.value = item.id;
180
+ textField.value = item.answer;
181
+ });
182
+ }
183
+
184
+ // eslint-disable-next-line class-methods-use-this
185
+ #updateAttribute(ele, attrName, value) {
186
+ if (value === null) ele.removeAttribute(attrName);
187
+ else ele.setAttribute(attrName, value);
188
+ }
189
+
190
+ #syncAttrs(attrs) {
191
+ const componentMap = {
192
+ question: this.comboBoxes,
193
+ answer: this.textFields,
194
+ };
195
+
196
+ attrs.forEach((attr) => {
197
+ const [maybeComponentType, ...rest] = attr.split('-');
198
+ const elements = componentMap[maybeComponentType] || this.inputs;
199
+ const attrName = componentMap[maybeComponentType] ? rest.join('-') : attr;
200
+
201
+ elements.forEach((el) => {
202
+ this.#updateAttribute(el, attrName, this.getAttribute(attr));
203
+ });
204
+ });
205
+ }
206
+
207
+ init() {
208
+ super.init?.();
209
+ // render new components
210
+ observeAttributes(
211
+ this,
212
+ () => {
213
+ this.#renderQuestions();
214
+ },
215
+ { includeAttrs: attrsToReRender }
216
+ );
217
+
218
+ // update existing components
219
+ observeAttributes(this, this.#syncAttrs.bind(this), {
220
+ includeAttrs: attrsToSync,
221
+ });
222
+ }
223
+ }
224
+
225
+ export const SecurityQuestionsSetupClass = compose(
226
+ createStyleMixin({
227
+ mappings: {
228
+ hostWidth: [{ selector: () => ':host', property: 'width' }, { property: 'width' }],
229
+ hostDirection: [
230
+ { selector: () => ':host', property: 'direction' },
231
+ {
232
+ selector: () => ComboBoxClass.componentName,
233
+ property: ComboBoxClass.cssVarList.hostDirection,
234
+ },
235
+ {
236
+ selector: () => TextFieldClass.componentName,
237
+ property: TextFieldClass.cssVarList.hostDirection,
238
+ },
239
+ ],
240
+ gap: { selector: () => 'div', property: 'gap' },
241
+ },
242
+ }),
243
+ draggableMixin,
244
+ componentNameValidationMixin
245
+ )(RawSecurityQuestionsSetup);
@@ -0,0 +1,7 @@
1
+ import { componentName, SecurityQuestionsSetupClass } from './SecurityQuestionsSetupClass';
2
+ import '../descope-list';
3
+ import '../boolean-fields/descope-checkbox';
4
+
5
+ customElements.define(componentName, SecurityQuestionsSetupClass);
6
+
7
+ export { SecurityQuestionsSetupClass };
package/src/index.cjs.js CHANGED
@@ -54,3 +54,4 @@ 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';
package/src/index.js CHANGED
@@ -46,6 +46,7 @@ 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';
49
50
 
50
51
  export {
51
52
  globalsThemeToStyle,
@@ -48,6 +48,7 @@ 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';
51
52
 
52
53
  const components = {
53
54
  button,
@@ -101,6 +102,7 @@ const components = {
101
102
  appsList,
102
103
  scopesList,
103
104
  thirdPartyAppLogo,
105
+ securityQuestionsSetup,
104
106
  };
105
107
 
106
108
  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 scopesList = {
6
+ [vars.hostWidth]: 'fit-content',
7
+ _fullWidth: {
8
+ [vars.hostWidth]: '100%',
9
+ },
10
+ };
11
+
12
+ export default scopesList;