@descope/web-components-ui 3.9.2 → 3.10.1

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.
@@ -18164,6 +18164,8 @@ const overrideAttrs = [
18164
18164
  'data-password-policy-value-minlength',
18165
18165
  'data-password-policy-value-passwordstrength',
18166
18166
  'data-password-policy-actual-passwordstrength',
18167
+ 'data-password-policy-value-disallowedchars',
18168
+ 'data-password-policy-value-email',
18167
18169
  ];
18168
18170
  const dataAttrs = ['data', 'active-policies', 'overrides', ...overrideAttrs];
18169
18171
  const policyAttrs = ['label', 'value', ...dataAttrs];
@@ -18282,6 +18284,46 @@ class RawPolicyValidation extends createBaseClass({ componentName: componentName
18282
18284
  };
18283
18285
  }
18284
18286
  }
18287
+
18288
+ // disallowedchars: this stores the configured char list in
18289
+ // overrides.disallowedchars.value so the message can interpolate
18290
+ // `{{value}}`. The regex pattern itself is built upstream (orchestrator
18291
+ // or caller) and shipped on the policy entry — it is not derived from
18292
+ // this override. When the attribute is cleared, drop the override so
18293
+ // the panel doesn't keep stale data.
18294
+ if (attrName === 'data-password-policy-value-disallowedchars') {
18295
+ if (newValue) {
18296
+ this.#overrides = {
18297
+ ...this.#overrides,
18298
+ disallowedchars: {
18299
+ ...this.#overrides?.disallowedchars,
18300
+ value: newValue,
18301
+ },
18302
+ };
18303
+ } else if (this.#overrides?.disallowedchars) {
18304
+ const { disallowedchars: _drop, ...rest } = this.#overrides;
18305
+ this.#overrides = rest;
18306
+ }
18307
+ }
18308
+
18309
+ // disallowemail: stash the user's email so the STR_NEQ_CI comparator
18310
+ // has an `expected` to compare against the live password value. Clear
18311
+ // the override when the attribute is removed/empty so we don't keep
18312
+ // blocking against a previous user's email.
18313
+ if (attrName === 'data-password-policy-value-email') {
18314
+ if (newValue) {
18315
+ this.#overrides = {
18316
+ ...this.#overrides,
18317
+ disallowemail: {
18318
+ ...this.#overrides?.disallowemail,
18319
+ expected: newValue,
18320
+ },
18321
+ };
18322
+ } else if (this.#overrides?.disallowemail) {
18323
+ const { disallowemail: _drop, ...rest } = this.#overrides;
18324
+ this.#overrides = rest;
18325
+ }
18326
+ }
18285
18327
  }
18286
18328
 
18287
18329
  this.renderItems(this.#availablePolicies, this.#activePolicies, this.#overrides);
@@ -18346,6 +18388,7 @@ class RawPolicyValidation extends createBaseClass({ componentName: componentName
18346
18388
  }
18347
18389
 
18348
18390
  const { pattern, message, data, compare } = policy;
18391
+ const normalizedCompare = typeof compare === 'string' ? compare.toUpperCase() : compare;
18349
18392
 
18350
18393
  if ((!pattern && !compare) || !message) {
18351
18394
  return results;
@@ -18359,9 +18402,23 @@ class RawPolicyValidation extends createBaseClass({ componentName: componentName
18359
18402
  if (pattern) {
18360
18403
  const exp = new RegExp(interpolateString(pattern, data));
18361
18404
  validationResult.valid = exp.test(this.value);
18405
+ } else if (normalizedCompare === 'STR_NEQ_CI') {
18406
+ // Compare the live password against the configured string AND its
18407
+ // local-part (before '@'), case-insensitively. Used by the
18408
+ // disallowemail policy.
18409
+ const expected = (data?.expected ?? '').toLowerCase();
18410
+ const actual = (this.value ?? '').toLowerCase();
18411
+ if (!expected || !actual) {
18412
+ // nothing to compare → mark valid so we don't block the flow
18413
+ validationResult.valid = true;
18414
+ } else {
18415
+ const at = expected.indexOf('@');
18416
+ const localPart = at > 0 ? expected.slice(0, at) : expected;
18417
+ validationResult.valid = actual !== expected && actual !== localPart;
18418
+ }
18362
18419
  } else if (compare) {
18363
18420
  validationResult.valid = this.compareValues(
18364
- compare,
18421
+ normalizedCompare,
18365
18422
  data?.expected ?? -1,
18366
18423
  data?.actual ?? -1
18367
18424
  );
@@ -18377,13 +18434,18 @@ class RawPolicyValidation extends createBaseClass({ componentName: componentName
18377
18434
  return !this.validate().some(({ valid }) => valid === false);
18378
18435
  }
18379
18436
 
18380
- getValidationItemTemplate({ valid, message }) {
18437
+ buildValidationItem({ valid, message }) {
18381
18438
  const status = !this.value ? 'none' : valid;
18382
- return `
18383
- <li class="item" data-valid="${status}">
18384
- <span class="message">${message}</span>
18385
- </li>
18386
- `;
18439
+ const li = document.createElement('li');
18440
+ li.className = 'item';
18441
+ li.dataset.valid = status;
18442
+ const span = document.createElement('span');
18443
+ span.className = 'message';
18444
+ // `textContent` handles any tenant-configured string in `message` safely
18445
+ // (e.g. the disallowedchars list) without needing to escape HTML.
18446
+ span.textContent = message ?? '';
18447
+ li.appendChild(span);
18448
+ return li;
18387
18449
  }
18388
18450
 
18389
18451
  renderItems(availablePolicies, activePolicies) {
@@ -18391,7 +18453,9 @@ class RawPolicyValidation extends createBaseClass({ componentName: componentName
18391
18453
  return;
18392
18454
  }
18393
18455
 
18394
- this.list.innerHTML = this.validate().map(this.getValidationItemTemplate.bind(this)).join('');
18456
+ this.list.replaceChildren(
18457
+ ...this.validate().map(this.buildValidationItem.bind(this)),
18458
+ );
18395
18459
  }
18396
18460
 
18397
18461
  updateLabel(val) {
@@ -18492,6 +18556,8 @@ const customMixin$4 = (superclass) =>
18492
18556
  'available-policies',
18493
18557
  'data-password-policy-value-minlength',
18494
18558
  'data-password-policy-value-passwordstrength',
18559
+ 'data-password-policy-value-disallowedchars',
18560
+ 'data-password-policy-value-email',
18495
18561
  'label-type',
18496
18562
  'manual-visibility-toggle',
18497
18563
  ],
@@ -26906,8 +26972,6 @@ class RawAnchored extends createBaseClass$1({
26906
26972
  `;
26907
26973
 
26908
26974
  this.defaultSlot = this.shadowRoot.querySelector('slot:not([name])');
26909
-
26910
- this.#syncComponentState();
26911
26975
  }
26912
26976
 
26913
26977
  init() {
@@ -26965,7 +27029,7 @@ class RawAnchored extends createBaseClass$1({
26965
27029
  // To support conditional components in flow, we need to sync the 'hidden' className to the root of the component.
26966
27030
  // Ideally, this would happen in the SDK, but we resolved to this patch to fix the issue without forcing users to update SDKs.
26967
27031
  #syncComponentState() {
26968
- const hasHidden = this.#anchor.classList.contains('hidden');
27032
+ const hasHidden = this.#anchor?.classList?.contains('hidden');
26969
27033
  this.classList.toggle('hidden', hasHidden);
26970
27034
  }
26971
27035
 
@@ -27030,6 +27094,7 @@ class RawAnchored extends createBaseClass$1({
27030
27094
  // empty host rather than reserving its layout box.
27031
27095
  #onAnchorChanged() {
27032
27096
  this.toggleAttribute('has-anchor', !!this.#anchor);
27097
+ this.#syncComponentState();
27033
27098
  }
27034
27099
  }
27035
27100