@descope-ui/descope-outbound-apps 0.0.2 → 0.0.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [0.0.3](https://github.com/descope/web-components-ui/compare/@descope-ui/descope-outbound-apps-0.0.2...@descope-ui/descope-outbound-apps-0.0.3) (2025-07-21)
6
+
5
7
  ## [0.0.2](https://github.com/descope/web-components-ui/compare/@descope-ui/descope-outbound-apps-0.0.1...@descope-ui/descope-outbound-apps-0.0.2) (2025-07-21)
6
8
 
7
9
  ### Dependency Updates
@@ -5,6 +5,7 @@ const componentAttributes = {
5
5
  direction: ['ltr', 'rtl'],
6
6
  numberOfItems: ['1', '5', '10'],
7
7
  size: ['xs', 'sm', 'md', 'lg'],
8
+ readonly: ['false', 'true'],
8
9
  'connect-button-label': ['', 'My Connect'],
9
10
  'disconnect-button-label': ['', 'My Disconnect']
10
11
  };
@@ -79,4 +80,36 @@ test.describe('logic', () => {
79
80
  expect(eventDetail).toHaveProperty('action', 'disconnect');
80
81
  expect(eventDetail).toHaveProperty('id', 'appId_2');
81
82
  });
83
+
84
+ test('prevent dispatch when readonly', async ({ page }) => {
85
+ await page.goto(getStoryUrl(storyName, { readonly: "true" }), {
86
+ waitUntil: 'networkidle',
87
+ });
88
+
89
+ await page.addInitScript(setupEventListener('connect-clicked'));
90
+
91
+ await page.reload({ waitUntil: 'networkidle' });
92
+
93
+ await page.getByText('Connect').first().click();
94
+ await page.waitForTimeout(500);
95
+
96
+ const eventDetail = await page.evaluate(() => (window as any).eventDetails[0]);
97
+
98
+ expect(eventDetail).not.toBeDefined();
99
+ });
82
100
  });
101
+
102
+ test.describe('presets', () => {
103
+ test('empty state', async ({ page }) => {
104
+ await page.goto(getStoryUrl(storyName, {
105
+ numberOfItems: '0',
106
+ 'empty-state': 'Custom empty state'
107
+ }), {
108
+ waitUntil: 'networkidle',
109
+ });
110
+
111
+ const componentParent = page.locator(componentName);
112
+
113
+ expect(await componentParent.screenshot()).toMatchSnapshot();
114
+ });
115
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descope-ui/descope-outbound-apps",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./src/component/index.js"
@@ -18,9 +18,9 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@descope-ui/common": "0.0.18",
21
+ "@descope-ui/theme-globals": "0.0.19",
21
22
  "@descope-ui/descope-list": "0.0.1",
22
23
  "@descope-ui/descope-list-item": "0.0.1",
23
- "@descope-ui/theme-globals": "0.0.19",
24
24
  "@descope-ui/descope-text": "0.0.19",
25
25
  "@descope-ui/descope-avatar": "0.0.19",
26
26
  "@descope-ui/descope-icon": "0.0.17",
@@ -15,8 +15,6 @@ import { ListItemClass } from '@descope-ui/descope-list-item/class';
15
15
 
16
16
  export const componentName = getComponentName('outbound-apps');
17
17
 
18
- export const observedAttributes = ['connect-button-label', 'disconnect-button-label', 'data'];
19
-
20
18
  const itemRenderer = (
21
19
  { name, description, logo, appId, isConnected },
22
20
  _,
@@ -31,22 +29,21 @@ const itemRenderer = (
31
29
  size=${ref.size}
32
30
  class="app-logo"
33
31
  ></descope-avatar>
34
- <div class="content">
35
- <div class="app-title">
32
+
33
+ <div class="content">
36
34
  <descope-text
35
+ class="app-title"
37
36
  variant="subtitle1"
38
37
  mode="primary"
39
38
  >${name}</descope-text>
40
- </div>
41
39
  ${
42
40
  description
43
41
  ? `
44
- <div class="app-description">
45
42
  <descope-text
43
+ class="app-description"
46
44
  variant="body2"
47
45
  mode="primary"
48
46
  >${description}</descope-text>
49
- </div>
50
47
  `
51
48
  : ''
52
49
  }
@@ -66,15 +63,13 @@ const BaseClass = createBaseClass({
66
63
  });
67
64
 
68
65
  class RawOutboundAppsClass extends BaseClass {
69
- static get observedAttributes() {
70
- return observedAttributes;
71
- }
72
-
73
66
  constructor() {
74
67
  super();
75
68
 
76
69
  this.attachShadow({ mode: 'open' }).innerHTML = `
77
- <descope-list></descope-list>
70
+ <descope-list>
71
+ <slot name="empty-state" slot="empty-state"></slot>
72
+ </descope-list>
78
73
  `;
79
74
 
80
75
  this.appsList = this.shadowRoot.querySelector('descope-list');
@@ -91,6 +86,13 @@ class RawOutboundAppsClass extends BaseClass {
91
86
  }
92
87
  .content {
93
88
  flex-grow: 1;
89
+ display: flex;
90
+ flex-direction: column;
91
+ }
92
+ .app-title,
93
+ .app-description {
94
+ display: flex;
95
+ direction: inherit;
94
96
  }
95
97
  descope-list {
96
98
  display: block;
@@ -108,11 +110,17 @@ class RawOutboundAppsClass extends BaseClass {
108
110
  this.appsList.addEventListener('click', this.onAppsListClick.bind(this));
109
111
  }
110
112
 
113
+ get readonly() {
114
+ return this.getAttribute('readonly') === 'true';
115
+ }
116
+
111
117
  onAppsListClick(e) {
112
- const id = e.srcElement.getAttribute('data-outbound-app-id');
113
- const action = e.srcElement.getAttribute('data-action');
114
- if (id && action) {
115
- this.dispatchEvent(new CustomEvent(`${action}-clicked`, { bubbles: true, detail: { id, action } }));
118
+ if (!this.readonly) {
119
+ const id = e.srcElement.getAttribute('data-outbound-app-id');
120
+ const action = e.srcElement.getAttribute('data-action');
121
+ if (id && action) {
122
+ this.dispatchEvent(new CustomEvent(`${action}-clicked`, { bubbles: true, detail: { id, action } }));
123
+ }
116
124
  }
117
125
  }
118
126
 
@@ -163,6 +171,10 @@ export const OutboundAppsClass = compose(
163
171
  selector: () => ' .app-logo',
164
172
  property: 'margin-inline-end'
165
173
  },
174
+ contentGap: {
175
+ selector: () => ' .content',
176
+ property: 'gap'
177
+ },
166
178
  itemsTextAlign: {
167
179
  selector: TextClass.componentName,
168
180
  property: TextClass.cssVarList.textAlign,
package/src/theme.js CHANGED
@@ -9,6 +9,7 @@ const outboundApps = {
9
9
 
10
10
  [vars.appLogoBackgroundColor]: 'none',
11
11
  [vars.appLogoGap]: globals.spacing.md,
12
+ [vars.contentGap]: globals.spacing.xs,
12
13
 
13
14
  // list-item overrides
14
15
  [vars.itemCursor]: 'default',
@@ -1,16 +1,24 @@
1
1
  import { componentName } from '../src/component';
2
- import { directionControl, sizeControl } from '@descope-ui/common/sb-controls';
2
+ import { directionControl, sizeControl, readOnlyControl } from '@descope-ui/common/sb-controls';
3
3
  import appIcon from './google.svg';
4
4
 
5
- const Template = ({ direction, size, 'connect-button-label': connectButtonLabel, 'disconnect-button-label': disconnectButtonLabel }) => `
5
+ const Template = ({
6
+ direction,
7
+ size,
8
+ 'connect-button-label': connectButtonLabel,
9
+ 'disconnect-button-label': disconnectButtonLabel,
10
+ readonly,
11
+ emptyState
12
+ }) => `
6
13
  <descope-outbound-apps
7
14
  st-host-direction="${direction || ''}"
8
15
  size=${size || ''}
9
16
  connect-button-label="${connectButtonLabel || ''}"
10
17
  disconnect-button-label="${disconnectButtonLabel || ''}"
18
+ readonly="${readonly || false}"
11
19
  >
20
+ <div slot="empty-state">${emptyState}</div>
12
21
  </descope-outbound-apps>
13
- <div class="test-output"></div>
14
22
  `;
15
23
 
16
24
  export default {
@@ -47,6 +55,7 @@ export default {
47
55
  argTypes: {
48
56
  ...directionControl,
49
57
  ...sizeControl,
58
+ ...readOnlyControl,
50
59
  'connect-button-label': {
51
60
  control: { type: 'text' },
52
61
  },
@@ -61,5 +70,5 @@ export const Default = Template.bind({});
61
70
  Default.args = {
62
71
  size: 'sm',
63
72
  numberOfItems: '5',
64
- emptyState: 'No items in the list...',
73
+ emptyState: 'No outbound apps found!',
65
74
  };