@descope/web-components-ui 3.22.3 → 3.23.0

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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "git+https://github.com/descope/web-components-ui.git",
6
6
  "directory": "packages/web-components/web-components-ui"
7
7
  },
8
- "version": "3.22.3",
8
+ "version": "3.23.0",
9
9
  "description": "",
10
10
  "main": "dist/cjs/index.cjs.js",
11
11
  "module": "dist/index.esm.js",
@@ -57,11 +57,11 @@
57
57
  "webpack-cli": "^7.0.0",
58
58
  "webpack-dev-server": "^5.0.0",
59
59
  "webpack-subresource-integrity": "5.2.0-rc.1",
60
- "eslint-plugin-no-bare-class-import": "3.22.3",
61
- "test-drivers": "3.22.3",
62
- "webpack-extract-font-loader": "3.22.3",
63
- "webpack-replace-plugin": "3.22.3",
64
- "rollup-replace-plugin": "3.22.3"
60
+ "eslint-plugin-no-bare-class-import": "3.23.0",
61
+ "test-drivers": "3.23.0",
62
+ "webpack-extract-font-loader": "3.23.0",
63
+ "webpack-replace-plugin": "3.23.0",
64
+ "rollup-replace-plugin": "3.23.0"
65
65
  },
66
66
  "dependencies": {
67
67
  "@vaadin/checkbox": "24.3.4",
@@ -85,42 +85,43 @@
85
85
  "libphonenumber-js": "^1.11.12",
86
86
  "lodash.debounce": "4.0.8",
87
87
  "lodash.merge": "4.6.2",
88
- "@descope-ui/common": "3.22.3",
89
- "@descope-ui/descope-address-field": "3.22.3",
90
- "@descope-ui/descope-country-subdivision-city-field": "3.22.3",
91
- "@descope-ui/descope-apps-list": "3.22.3",
92
- "@descope-ui/descope-autocomplete-field": "3.22.3",
93
- "@descope-ui/descope-avatar": "3.22.3",
94
- "@descope-ui/descope-collapsible-container": "3.22.3",
95
- "@descope-ui/descope-combo-box": "3.22.3",
96
- "@descope-ui/descope-enriched-text": "3.22.3",
97
- "@descope-ui/descope-icon": "3.22.3",
98
- "@descope-ui/descope-image": "3.22.3",
99
- "@descope-ui/descope-link": "3.22.3",
100
- "@descope-ui/descope-badge": "3.22.3",
101
- "@descope-ui/descope-button": "3.22.3",
102
- "@descope-ui/descope-list": "3.22.3",
103
- "@descope-ui/descope-month-day-field-picker": "3.22.3",
104
- "@descope-ui/descope-multi-line-mappings": "3.22.3",
105
- "@descope-ui/descope-multi-select-combo-box": "3.22.3",
106
- "@descope-ui/descope-outbound-app-button": "3.22.3",
107
- "@descope-ui/descope-outbound-apps": "3.22.3",
108
- "@descope-ui/descope-password-strength": "3.22.3",
109
- "@descope-ui/descope-ponyhot": "3.22.3",
110
- "@descope-ui/descope-recovery-codes": "3.22.3",
111
- "@descope-ui/descope-text": "3.22.3",
112
- "@descope-ui/descope-text-field": "3.22.3",
113
- "@descope-ui/descope-timer": "3.22.3",
114
- "@descope-ui/descope-multi-sso": "3.22.3",
115
- "@descope-ui/descope-trusted-devices": "3.22.3",
116
- "@descope-ui/descope-anchored": "3.22.3",
117
- "@descope-ui/descope-timer-button": "3.22.3",
118
- "@descope-ui/descope-tooltip": "3.22.3",
119
- "@descope-ui/descope-user-passkeys": "3.22.3",
120
- "@descope-ui/descope-filter": "3.22.3",
121
- "@descope-ui/descope-list-item": "3.22.3",
122
- "@descope-ui/descope-month-day-field": "3.22.3",
123
- "@descope-ui/descope-attachment": "3.22.3"
88
+ "@descope-ui/common": "3.23.0",
89
+ "@descope-ui/descope-active-sessions": "3.23.0",
90
+ "@descope-ui/descope-apps-list": "3.23.0",
91
+ "@descope-ui/descope-autocomplete-field": "3.23.0",
92
+ "@descope-ui/descope-avatar": "3.23.0",
93
+ "@descope-ui/descope-badge": "3.23.0",
94
+ "@descope-ui/descope-address-field": "3.23.0",
95
+ "@descope-ui/descope-collapsible-container": "3.23.0",
96
+ "@descope-ui/descope-button": "3.23.0",
97
+ "@descope-ui/descope-combo-box": "3.23.0",
98
+ "@descope-ui/descope-country-subdivision-city-field": "3.23.0",
99
+ "@descope-ui/descope-enriched-text": "3.23.0",
100
+ "@descope-ui/descope-icon": "3.23.0",
101
+ "@descope-ui/descope-link": "3.23.0",
102
+ "@descope-ui/descope-list": "3.23.0",
103
+ "@descope-ui/descope-image": "3.23.0",
104
+ "@descope-ui/descope-month-day-field": "3.23.0",
105
+ "@descope-ui/descope-month-day-field-picker": "3.23.0",
106
+ "@descope-ui/descope-multi-line-mappings": "3.23.0",
107
+ "@descope-ui/descope-multi-select-combo-box": "3.23.0",
108
+ "@descope-ui/descope-outbound-app-button": "3.23.0",
109
+ "@descope-ui/descope-outbound-apps": "3.23.0",
110
+ "@descope-ui/descope-password-strength": "3.23.0",
111
+ "@descope-ui/descope-ponyhot": "3.23.0",
112
+ "@descope-ui/descope-recovery-codes": "3.23.0",
113
+ "@descope-ui/descope-text-field": "3.23.0",
114
+ "@descope-ui/descope-timer": "3.23.0",
115
+ "@descope-ui/descope-timer-button": "3.23.0",
116
+ "@descope-ui/descope-tooltip": "3.23.0",
117
+ "@descope-ui/descope-multi-sso": "3.23.0",
118
+ "@descope-ui/descope-trusted-devices": "3.23.0",
119
+ "@descope-ui/descope-attachment": "3.23.0",
120
+ "@descope-ui/descope-anchored": "3.23.0",
121
+ "@descope-ui/descope-user-passkeys": "3.23.0",
122
+ "@descope-ui/descope-filter": "3.23.0",
123
+ "@descope-ui/descope-list-item": "3.23.0",
124
+ "@descope-ui/descope-text": "3.23.0"
124
125
  },
125
126
  "overrides": {
126
127
  "@vaadin/avatar": "24.3.4",
@@ -67,6 +67,40 @@ export const syncAttrs = (ele1, ele2, options) => {
67
67
  observeAttributes(ele2, createSyncAttrsCb(ele2, ele1), options);
68
68
  };
69
69
 
70
+ // A custom element has no shadow root on the first tick, so anything reaching into one has to
71
+ // wait for it. Takes a function rather than the element, because the element itself is often not
72
+ // there yet either.
73
+ //
74
+ // On giving up it resolves the element it was given, not a shadow root. `portalMixin` relies on
75
+ // that to carry on with something usable, so a caller that cannot use the element must check what
76
+ // it got back.
77
+ export const waitForShadowRoot = (getRootElementFn) => async (that) => {
78
+ const ele = await getRootElementFn(that);
79
+
80
+ return new Promise((res) => {
81
+ const MAX_RETRIES = 20;
82
+ let counter = 0;
83
+
84
+ const check = () => {
85
+ if (counter > MAX_RETRIES) {
86
+ // eslint-disable-next-line no-console
87
+ console.error('could not get shadow root for element', ele);
88
+ res(ele);
89
+ return;
90
+ }
91
+
92
+ counter++;
93
+
94
+ if (!ele?.shadowRoot) {
95
+ setTimeout(check);
96
+ } else {
97
+ res(ele.shadowRoot);
98
+ }
99
+ };
100
+ check();
101
+ });
102
+ };
103
+
70
104
  export const getComponentName = (name) => kebabCaseJoin(DESCOPE_PREFIX, name);
71
105
 
72
106
  export const getCssVarName = (...args) => `--${kebabCaseJoin(...args)}`;
@@ -1,6 +1,6 @@
1
1
  import { injectStyle } from '@descope-ui/common/components-helpers';
2
2
  import { BASE_THEME_SECTION, CSS_SELECTOR_SPECIFIER_MULTIPLY } from '../../constants';
3
- import { kebabCaseJoin } from '../../helpers';
3
+ import { isFunction, kebabCaseJoin } from '../../helpers';
4
4
  import { getCssVarName, observeAttributes } from '../../helpers/componentHelpers';
5
5
  import { componentsThemeManager } from '../../helpers/themeHelpers';
6
6
  import { createStyle, createCssVarsList, createClassSelectorSpecifier } from './helpers';
@@ -8,7 +8,7 @@ import { createStyle, createCssVarsList, createClassSelectorSpecifier } from './
8
8
  const STYLE_OVERRIDE_ATTR_PREFIX = 'st';
9
9
 
10
10
  export const createStyleMixin =
11
- ({ mappings = {}, componentNameOverride = '' }) =>
11
+ ({ mappings = {}, componentNameOverride = '', style = '' }) =>
12
12
  (superclass) => {
13
13
  const componentName = componentNameOverride || superclass.componentName;
14
14
 
@@ -130,6 +130,14 @@ export const createStyleMixin =
130
130
  }
131
131
  }
132
132
 
133
+ // Rules the component needs wherever this mixin writes, which for a portal is a shadow root
134
+ // it does not own. Prepended after the mapping rules so those still win: the mapping is the
135
+ // themeable half, this is the structure underneath it.
136
+ #createStaticStyle() {
137
+ const css = isFunction(style) ? style() : style;
138
+ if (css) injectStyle(css, this.#rootElement, { prepend: true });
139
+ }
140
+
133
141
  #addClassName(className) {
134
142
  (this.#rootElement.classList || this.#rootElement.host.classList).add(className);
135
143
  }
@@ -142,6 +150,7 @@ export const createStyleMixin =
142
150
  this.#addClassName(componentName);
143
151
 
144
152
  this.#createMappingStyle();
153
+ this.#createStaticStyle();
145
154
  this.#createComponentTheme();
146
155
  this.#createOverridesStyle();
147
156
 
@@ -1,5 +1,5 @@
1
- import { kebabCaseJoin } from '../helpers';
2
- import { forwardAttrs } from '../helpers/componentHelpers';
1
+ import { isFunction, kebabCaseJoin } from '../helpers';
2
+ import { forwardAttrs, waitForShadowRoot } from '../helpers/componentHelpers';
3
3
  import { createStyleMixin } from './createStyleMixin';
4
4
  import { createCssVarsList } from './createStyleMixin/helpers';
5
5
 
@@ -12,40 +12,31 @@ const DISPLAY_NAME_SEPARATOR = '_';
12
12
 
13
13
  const sanitizeSelector = (selector) => selector.replace(/[^\w\s]/gi, '');
14
14
 
15
- const withWaitForShadowRoot = (getRootElementFn) => async (that) => {
16
- const ele = await getRootElementFn(that);
17
-
18
- return new Promise((res) => {
19
- const MAX_RETRIES = 20;
20
- let counter = 0;
21
-
22
- const check = () => {
23
- if (counter > MAX_RETRIES) {
24
- // eslint-disable-next-line no-console
25
- console.error('could not get shadow root for element', ele);
26
- res(ele);
27
- return;
28
- }
29
-
30
- counter++;
31
-
32
- if (!ele?.shadowRoot) {
33
- setTimeout(check);
34
- } else {
35
- res(ele.shadowRoot);
36
- }
37
- };
38
- check();
39
- });
40
- };
41
-
42
15
  export const portalMixin =
43
- ({ name, selector, mappings = {}, forward: { attributes = [], include = true } = {} }) =>
16
+ ({
17
+ name,
18
+ selector,
19
+ mappings = {},
20
+ // Static css for the portal's own root, for rules that should not become themeable knobs.
21
+ style = '',
22
+ // `false` forwards nothing to the portal element. It needs its own value because an empty
23
+ // `attributes` list already means "no filter", so it forwards everything rather than nothing.
24
+ // Forwarding is wrong for a portal whose element is not a copy of the host, where attributes
25
+ // like `data` or `label` would land somewhere that does not expect them.
26
+ forward = {},
27
+ }) =>
44
28
  (superclass) => {
29
+ // A function selector has no text to derive a name from, and an empty one would quietly
30
+ // produce empty css var names, so say so instead.
31
+ if (isFunction(selector) && !name) {
32
+ throw new Error('portalMixin: a function selector requires a name');
33
+ }
34
+
45
35
  const eleDisplayName = name || sanitizeSelector(selector);
46
36
 
47
37
  const BaseClass = createStyleMixin({
48
38
  mappings,
39
+ style,
49
40
  })(superclass);
50
41
 
51
42
  return class PortalMixinClass extends BaseClass {
@@ -73,11 +64,16 @@ export const portalMixin =
73
64
 
74
65
  // in case we have a selector, we should first wait for the base element shadow root
75
66
  // and then look for the internal element
76
- const baseEleShadowRoot = await withWaitForShadowRoot(() => baseEle)(that);
77
- return baseEleShadowRoot.querySelector(selector);
67
+ const baseEleShadowRoot = await waitForShadowRoot(() => baseEle)(that);
68
+
69
+ // a string selector only reaches inside that one shadow root. a portal that sits deeper
70
+ // takes a function instead, which receives the root and may await its own way down.
71
+ return isFunction(selector)
72
+ ? selector(baseEleShadowRoot)
73
+ : baseEleShadowRoot.querySelector(selector);
78
74
  };
79
75
 
80
- const getPortalElement = withWaitForShadowRoot(getRootElement);
76
+ const getPortalElement = waitForShadowRoot(getRootElement);
81
77
 
82
78
  super({
83
79
  getRootElement: getPortalElement,
@@ -108,9 +104,12 @@ export const portalMixin =
108
104
  // Initialize portal element lookup here (deferred from constructor for React compatibility)
109
105
  this.#portalEle = this.#getPortalElement();
110
106
  const portalEle = await this.#portalEle;
111
- forwardAttrs(this, portalEle, {
112
- [include ? 'includeAttrs' : 'excludeAttrs']: attributes,
113
- });
107
+ if (forward !== false) {
108
+ const { attributes = [], include = true } = forward;
109
+ forwardAttrs(this, portalEle, {
110
+ [include ? 'includeAttrs' : 'excludeAttrs']: attributes,
111
+ });
112
+ }
114
113
 
115
114
  this.#handleHoverAttribute();
116
115
  }
@@ -19,6 +19,7 @@ import * as timerButton from '@descope-ui/descope-timer-button/theme';
19
19
  import * as passwordStrength from '@descope-ui/descope-password-strength/theme';
20
20
  import * as collapsibleContainer from '@descope-ui/descope-collapsible-container/theme';
21
21
  import * as recoveryCodes from '@descope-ui/descope-recovery-codes/theme';
22
+ import * as activeSessions from '@descope-ui/descope-active-sessions/theme';
22
23
  import * as outboundApps from '@descope-ui/descope-outbound-apps/theme';
23
24
  import * as outboundAppButton from '@descope-ui/descope-outbound-app-button/theme';
24
25
  import * as trustedDevices from '@descope-ui/descope-trusted-devices/theme';
@@ -140,6 +141,7 @@ const components = {
140
141
  passwordStrength,
141
142
  collapsibleContainer,
142
143
  recoveryCodes,
144
+ activeSessions,
143
145
  hcaptcha,
144
146
  outboundApps,
145
147
  outboundAppButton,