@aquera/nile-elements 0.1.8 → 0.1.10

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 (43) hide show
  1. package/README.md +7 -0
  2. package/demo/index.html +0 -10
  3. package/dist/nile-accordion/nile-accordian.test.cjs.js +1 -1
  4. package/dist/nile-accordion/nile-accordian.test.cjs.js.map +1 -1
  5. package/dist/nile-accordion/nile-accordian.test.esm.js +1 -1
  6. package/dist/nile-accordion/nile-accordion.cjs.js +1 -1
  7. package/dist/nile-accordion/nile-accordion.cjs.js.map +1 -1
  8. package/dist/nile-accordion/nile-accordion.css.cjs.js +1 -1
  9. package/dist/nile-accordion/nile-accordion.css.cjs.js.map +1 -1
  10. package/dist/nile-accordion/nile-accordion.css.esm.js +54 -23
  11. package/dist/nile-accordion/nile-accordion.esm.js +9 -8
  12. package/dist/nile-avatar/nile-avatar.test.cjs.js +1 -1
  13. package/dist/nile-avatar/nile-avatar.test.cjs.js.map +1 -1
  14. package/dist/nile-avatar/nile-avatar.test.esm.js +11 -1
  15. package/dist/nile-code-editor/nile-code-editor.cjs.js +1 -1
  16. package/dist/nile-code-editor/nile-code-editor.cjs.js.map +1 -1
  17. package/dist/nile-code-editor/nile-code-editor.css.cjs.js +1 -1
  18. package/dist/nile-code-editor/nile-code-editor.css.cjs.js.map +1 -1
  19. package/dist/nile-code-editor/nile-code-editor.css.esm.js +18 -10
  20. package/dist/nile-code-editor/nile-code-editor.esm.js +9 -9
  21. package/dist/src/nile-accordion/nile-accordian.test.js +24 -29
  22. package/dist/src/nile-accordion/nile-accordian.test.js.map +1 -1
  23. package/dist/src/nile-accordion/nile-accordion.css.js +53 -22
  24. package/dist/src/nile-accordion/nile-accordion.css.js.map +1 -1
  25. package/dist/src/nile-accordion/nile-accordion.d.ts +31 -16
  26. package/dist/src/nile-accordion/nile-accordion.js +68 -34
  27. package/dist/src/nile-accordion/nile-accordion.js.map +1 -1
  28. package/dist/src/nile-avatar/nile-avatar.test.d.ts +1 -0
  29. package/dist/src/nile-avatar/nile-avatar.test.js +64 -30
  30. package/dist/src/nile-avatar/nile-avatar.test.js.map +1 -1
  31. package/dist/src/nile-code-editor/nile-code-editor.css.js +18 -10
  32. package/dist/src/nile-code-editor/nile-code-editor.css.js.map +1 -1
  33. package/dist/src/nile-code-editor/nile-code-editor.js +10 -10
  34. package/dist/src/nile-code-editor/nile-code-editor.js.map +1 -1
  35. package/dist/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +1 -1
  37. package/src/nile-accordion/nile-accordian.test.ts +30 -57
  38. package/src/nile-accordion/nile-accordion.css.ts +53 -22
  39. package/src/nile-accordion/nile-accordion.ts +61 -33
  40. package/src/nile-avatar/nile-avatar.test.ts +84 -33
  41. package/src/nile-code-editor/nile-code-editor.css.ts +18 -10
  42. package/src/nile-code-editor/nile-code-editor.ts +10 -10
  43. package/vscode-html-custom-data.json +82 -43
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Webcomponent nile-elements following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "nile-elements",
6
- "version": "0.1.8",
6
+ "version": "0.1.10",
7
7
  "main": "dist/src/index.js",
8
8
  "type": "module",
9
9
  "module": "dist/src/index.js",
@@ -2,8 +2,6 @@ import { fixture, html, expect, oneEvent, elementUpdated } from '@open-wc/testin
2
2
  import './nile-accordion';
3
3
  import { NileAccordion } from './nile-accordion';
4
4
  import Sinon from 'sinon';
5
- const wait=(ms:number=50000)=>new Promise(resolve => setTimeout(resolve, ms))
6
-
7
5
 
8
6
  describe('NileAccordion', () => {
9
7
  it('should render correctly', async () => {
@@ -16,132 +14,107 @@ describe('NileAccordion', () => {
16
14
  it('should initialize closed by default', async () => {
17
15
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary"></nile-accordion>`);
18
16
  expect(el.open).to.be.false;
19
- const body = el.shadowRoot?.querySelector<any>('.details__body');
17
+ const body = el.shadowRoot?.querySelector<HTMLElement>('.accordian__body');
20
18
  expect(body?.hidden).to.be.true;
21
19
  });
22
20
 
23
21
  it('should open when clicked', async () => {
24
22
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary"></nile-accordion>`);
25
-
26
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
23
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header') as HTMLElement;
27
24
  header.click();
28
-
29
25
  await elementUpdated(el);
30
-
31
26
  expect(el.open).to.be.true;
32
- const body = el.shadowRoot?.querySelector<any>('.details__body');
27
+ const body = el.shadowRoot?.querySelector<HTMLElement>('.accordian__body');
33
28
  expect(body?.hidden).to.be.false;
34
29
  });
35
30
 
36
31
  it('should emit "nile-show" and "nile-after-show" when opened', async () => {
37
32
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary"></nile-accordion>`);
38
-
39
33
  const showSpy = Sinon.spy(el, 'emit');
40
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
41
- setTimeout(() => header.click());
42
-
43
- // Wait for the "nile-show" and "nile-after-show" events to be emitted
34
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header') as HTMLElement;
35
+ setTimeout(() => header.click());
44
36
  await oneEvent(el, 'nile-after-show');
45
-
46
37
  expect(showSpy.calledWith('nile-show')).to.be.true;
47
38
  expect(showSpy.calledWith('nile-after-show')).to.be.true;
48
39
  });
49
40
 
50
41
  it('should emit "nile-hide" and "nile-after-hide" when closed', async () => {
51
42
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary" open></nile-accordion>`);
52
-
53
43
  const hideSpy = Sinon.spy(el, 'emit');
54
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
55
- setTimeout(() => header.click());
56
-
57
- // Wait for the "nile-hide" and "nile-after-hide" events to be emitted
44
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header') as HTMLElement;
45
+ setTimeout(() => header.click());
58
46
  await oneEvent(el, 'nile-after-hide');
59
-
60
47
  expect(hideSpy.calledWith('nile-hide')).to.be.true;
61
48
  expect(hideSpy.calledWith('nile-after-hide')).to.be.true;
62
49
  });
63
50
 
64
51
  it('should be disabled when the "disabled" property is set', async () => {
65
52
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary" disabled></nile-accordion>`);
66
-
67
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
53
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header') as HTMLElement;
68
54
  header.click();
69
-
70
55
  await elementUpdated(el);
71
-
72
56
  expect(el.open).to.be.false;
73
- const body = el.shadowRoot?.querySelector<any>('.details__body');
74
- expect(body?.hidden).to.be.true;
75
- });
76
-
77
- it('should open when pressing "Enter"', async () => {
78
- const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary"></nile-accordion>`);
79
-
80
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
81
-
82
- // Simulate pressing the "Enter" key
83
- const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' });
84
- header.dispatchEvent(enterEvent);
85
-
86
- await elementUpdated(el);
87
- expect(el.open).to.be.true;
88
57
  });
89
58
 
90
59
  it('should handle keyboard interaction (Enter and Space)', async () => {
91
60
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary"></nile-accordion>`);
92
-
93
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
61
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header') as HTMLElement;
94
62
 
95
- // Simulate pressing the "Enter" key
96
63
  const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' });
97
64
  header.dispatchEvent(enterEvent);
98
-
99
65
  await elementUpdated(el);
100
66
  expect(el.open).to.be.true;
101
67
 
102
- // Simulate pressing the "Space" key
103
68
  const spaceEvent = new KeyboardEvent('keydown', { key: ' ' });
104
69
  header.dispatchEvent(spaceEvent);
105
-
106
70
  await elementUpdated(el);
107
71
  expect(el.open).to.be.false;
108
72
  });
109
73
 
110
74
  it('should handle ArrowUp/ArrowLeft to close and ArrowDown/ArrowRight to open', async () => {
111
75
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary"></nile-accordion>`);
112
-
113
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
76
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header') as HTMLElement;
114
77
 
115
- // Simulate pressing the "ArrowDown" key to open
116
78
  const arrowDownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown' });
117
79
  header.dispatchEvent(arrowDownEvent);
118
-
119
80
  await elementUpdated(el);
120
81
  expect(el.open).to.be.true;
121
82
 
122
- // Simulate pressing the "ArrowUp" key to close
123
83
  const arrowUpEvent = new KeyboardEvent('keydown', { key: 'ArrowUp' });
124
84
  header.dispatchEvent(arrowUpEvent);
125
-
126
85
  await elementUpdated(el);
127
86
  expect(el.open).to.be.false;
128
87
  });
129
88
 
89
+ it('should apply the "variant" class based on the variant property', async () => {
90
+ const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary" variant="dark"></nile-accordion>`);
91
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header');
92
+ expect(header).to.have.class('accordian__header--dark');
93
+ });
94
+
95
+ it('should apply the correct size class based on the size property', async () => {
96
+ const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary" size="lg"></nile-accordion>`);
97
+ const container = el.shadowRoot?.querySelector<HTMLElement>('.accordian');
98
+ expect(container).to.have.class('accordian--lg');
99
+ });
100
+
101
+ it('should apply the correct expand icon placement class based on the expandIconPlacement property', async () => {
102
+ const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary" expandIconPlacement="left"></nile-accordion>`);
103
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header');
104
+ expect(header).to.have.class('accordian__header--arrow-left');
105
+ });
106
+
130
107
  it('should animate when opening and closing', async () => {
131
108
  const el = await fixture<NileAccordion>(html`<nile-accordion summary="Test Summary"></nile-accordion>`);
132
-
133
109
  const animateSpy = Sinon.spy(el, 'handleOpenChange');
110
+ const header = el.shadowRoot?.querySelector<HTMLElement>('.accordian__header') as HTMLElement;
134
111
 
135
- // Simulate opening
136
- const header = el.shadowRoot?.querySelector('.details__header') as HTMLElement;
137
112
  header.click();
138
-
139
113
  await elementUpdated(el);
140
114
  expect(animateSpy.called).to.be.true;
141
115
 
142
- // Simulate closing
143
116
  header.click();
144
117
  await elementUpdated(el);
145
118
  expect(animateSpy.calledTwice).to.be.true;
146
119
  });
147
- });
120
+ });
@@ -30,77 +30,108 @@ export const styles = css`
30
30
  display: block;
31
31
  }
32
32
 
33
- .details {
34
- border: solid 1px hsl(240 5.9% 90%);
35
- border-radius: 0.25rem;
33
+ .accordian {
34
+ border-top:solid 1px var(--nile-colors-neutral-500);
36
35
  background-color: #FFFFFF;
37
36
  overflow-anchor: none;
38
37
  }
39
38
 
40
- .details--disabled {
39
+ .accordian--lg{
40
+ --accordian-text-size:var(--sm-tesx, 14px);
41
+ --accordian-heading-padding: 16px 12px;
42
+ --accordian-content-padding: 6px 12px 18px;
43
+ }
44
+
45
+ .accordian--md{
46
+ --accordian-text-size:var(--sm-tesx, 14px);
47
+ --accordian-heading-padding: 12px 12px;
48
+ --accordian-content-padding: 6px 12px 18px;
49
+ }
50
+
51
+ .accordian--sm{
52
+ --accordian-text-size:var(--sm-tesx, 12px);
53
+ --accordian-heading-padding: 6px 12px;
54
+ --accordian-content-padding: 6px 12px 12px;
55
+ }
56
+
57
+ .accordian {
58
+ font-size: var(--accordian-text-size);
59
+ }
60
+
61
+ .accordian--disabled {
41
62
  opacity: 0.5;
42
63
  }
43
64
 
44
- .details__header {
65
+ .accordian__header {
45
66
  display: flex;
67
+ gap:12px;
46
68
  align-items: center;
47
69
  border-radius: inherit;
48
- padding: 1rem;
70
+ font-weight:500;
71
+ padding: var(--accordian-heading-padding);
49
72
  user-select: none;
50
73
  cursor: pointer;
51
74
  }
52
75
 
53
- .details__header:focus {
76
+ .accordian__header--dark{
77
+ background-color:var(--nile-colors-dark-200);
78
+ }
79
+
80
+ .accordian__header--arrow-left{
81
+ flex-direction: row-reverse;
82
+ }
83
+
84
+ .accordian__header:focus {
54
85
  outline: none;
55
86
  }
56
87
 
57
- .details__header:focus-visible {
88
+ .accordian__header:focus-visible {
58
89
  outline: solid 3px hsl(200.4, 98%, 39.4%);
59
90
  outline-offset: calc(1px + 1px);
60
91
  }
61
92
 
62
- .details--disabled .details__header {
93
+ .accordian--disabled .accordian__header {
63
94
  cursor: not-allowed;
64
95
  }
65
96
 
66
- .details--disabled .details__header:focus-visible {
97
+ .accordian--disabled .accordian__header:focus-visible {
67
98
  outline: none;
68
99
  box-shadow: none;
69
100
  }
70
101
 
71
- .details__summary {
102
+ .accordian__summary {
72
103
  flex: 1 1 auto;
73
104
  display: flex;
74
105
  align-items: center;
75
106
  }
76
107
 
77
- .details__summary-icon {
108
+ .accordian__summary-icon {
78
109
  flex: 0 0 auto;
79
110
  display: flex;
80
111
  align-items: center;
81
112
  transition: 250ms rotate ease;
82
113
  }
83
114
 
84
- .details--open .details__summary-icon {
115
+ .accordian--open .accordian__summary-icon {
85
116
  rotate: 90deg;
86
117
  }
87
118
 
88
- .details--open.details--rtl .details__summary-icon {
89
- rotate: -90deg;
90
- }
91
-
92
- .details--open slot[name='expand-icon'],
93
- .details:not(.details--open) slot[name='collapse-icon'] {
119
+ .accordian--open slot[name='expand-icon'],
120
+ .accordian:not(.accordian--open) slot[name='collapse-icon'] {
94
121
  display: none;
95
122
  }
96
123
 
97
- .details__body {
124
+ .accordian__body {
98
125
  overflow: hidden;
99
126
  }
100
127
 
101
- .details__content {
128
+ .accordian__content {
102
129
  display: block;
103
- padding: 1rem;
130
+ padding: var(--accordian-content-padding);
131
+ }
132
+
133
+ .accordian__content--arrow-left{
134
+ margin-left:28px;
104
135
  }
105
136
  `;
106
137
 
@@ -26,48 +26,66 @@ import type { CSSResultGroup } from 'lit';
26
26
  export class NileAccordion extends NileElement {
27
27
 
28
28
  /**
29
- * @summary Details show a brief summary and expand to show additional content.
29
+ * @summary Accordian show a brief summary and expand to show additional content.
30
30
  *
31
31
  * @dependency nile-icon
32
32
  *
33
- * @slot - The details' main content.
34
- * @slot summary - The details' summary. Alternatively, you can use the `summary` attribute.
33
+ * @slot - The accordian' main content.
34
+ * @slot summary - The accordian' summary. Alternatively, you can use the `summary` attribute.
35
35
  * @slot expand-icon - Optional expand icon to use instead of the default. Works best with `<nile-icon>`.
36
36
  * @slot collapse-icon - Optional collapse icon to use instead of the default. Works best with `<nile-icon>`.
37
37
  *
38
- * @event nile-show - Emitted when the details opens.
39
- * @event nile-after-show - Emitted after the details opens and all animations are complete.
40
- * @event nile-hide - Emitted when the details closes.
41
- * @event nile-after-hide - Emitted after the details closes and all animations are complete.
38
+ * @event nile-show - Emitted when the accordian opens.
39
+ * @event nile-after-show - Emitted after the accordian opens and all animations are complete.
40
+ * @event nile-hide - Emitted when the accordian closes.
41
+ * @event nile-after-hide - Emitted after the accordian closes and all animations are complete.
42
42
  *
43
43
  * @csspart base - The component's base wrapper.
44
44
  * @csspart header - The header that wraps both the summary and the expand/collapse icon.
45
45
  * @csspart summary - The container that wraps the summary.
46
46
  * @csspart summary-icon - The container that wraps the expand/collapse icons.
47
- * @csspart content - The details content.
47
+ * @csspart content - The accordian content.
48
48
  *
49
- * @animation details.show - The animation to use when showing details. You can use `height: auto` with this animation.
50
- * @animation details.hide - The animation to use when hiding details. You can use `height: auto` with this animation.
49
+ * @animation accordian.show - The animation to use when showing accordian. You can use `height: auto` with this animation.
50
+ * @animation accordian.hide - The animation to use when hiding accordian. You can use `height: auto` with this animation.
51
51
  */
52
52
 
53
53
  static styles: CSSResultGroup = styles;
54
54
 
55
55
 
56
- @query('.details') details: HTMLElement;
57
- @query('.details__header') header: HTMLElement;
58
- @query('.details__body') body: HTMLElement;
59
- @query('.details__expand-icon-slot') expandIconSlot: HTMLSlotElement;
56
+ @query('.accordian') accordian: HTMLElement;
57
+ @query('.accordian__header') header: HTMLElement;
58
+ @query('.accordian__body') body: HTMLElement;
59
+ @query('.accordian__expand-icon-slot') expandIconSlot: HTMLSlotElement;
60
60
 
61
61
  /**
62
- * Indicates whether or not the details is open. You can toggle this attribute to show and hide the details, or you
63
- * can use the `show()` and `hide()` methods and this attribute will reflect the details' open state.
62
+ * Indicates whether or not the accordian is open. You can toggle this attribute to show and hide the accordian, or you
63
+ * can use the `show()` and `hide()` methods and this attribute will reflect the accordian' open state.
64
64
  */
65
65
  @property({ type: Boolean, reflect: true }) open = false;
66
66
 
67
+ /**
68
+ * Indicates the visual style of the accordian component. Accepted values are `'dark'` or `'light'`.
69
+ * Defaults to `'light'`.
70
+ */
71
+ @property({ reflect: true }) variant: 'dark' | 'light' = 'light';
72
+
73
+ /**
74
+ * Specifies the direction of the arrow indicator. Accepted values are `'left'` or `'right'`.
75
+ * Defaults to `'right'`.
76
+ */
77
+ @property({ reflect: true }) expandIconPlacement: 'left' | 'right' = 'right';
78
+
79
+ /**
80
+ * Specifies the size of the accordian component. Accepted values are `'sm'`, `'md'`, or `'lg'`.
81
+ * Defaults to `'md'`.
82
+ */
83
+ @property({ reflect: true }) size: 'sm' | 'md' | 'lg' = 'md';
84
+
67
85
  /** The summary to show in the header. If you need to display HTML, use the `summary` slot instead. */
68
86
  @property() summary: string;
69
87
 
70
- /** Disables the details so it can't be toggled. */
88
+ /** Disables the accordian so it can't be toggled. */
71
89
  @property({ type: Boolean, reflect: true }) disabled = false;
72
90
 
73
91
  firstUpdated() {
@@ -122,7 +140,7 @@ async handleOpenChange() {
122
140
  await stopAnimations(this.body);
123
141
  this.body.hidden = false;
124
142
 
125
- const { keyframes, options } = getAnimation(this, 'details.show', { dir: 'ltr' });
143
+ const { keyframes, options } = getAnimation(this, 'accordian.show', { dir: 'ltr' });
126
144
  await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options);
127
145
  this.body.style.height = 'auto';
128
146
 
@@ -137,7 +155,7 @@ async handleOpenChange() {
137
155
 
138
156
  await stopAnimations(this.body);
139
157
 
140
- const { keyframes, options } = getAnimation(this, 'details.hide', { dir: 'ltr' });
158
+ const { keyframes, options } = getAnimation(this, 'accordian.hide', { dir: 'ltr' });
141
159
  await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options);
142
160
  this.body.hidden = true;
143
161
  this.body.style.height = 'auto';
@@ -146,7 +164,7 @@ async handleOpenChange() {
146
164
  }
147
165
  }
148
166
 
149
- /** Shows the details. */
167
+ /** Shows the accordian. */
150
168
  async show() {
151
169
  if (this.open || this.disabled) {
152
170
  return undefined;
@@ -156,7 +174,7 @@ async show() {
156
174
  return waitForEvent(this, 'nile-after-show');
157
175
  }
158
176
 
159
- /** Hides the details */
177
+ /** Hides the accordian */
160
178
  async hide() {
161
179
  if (!this.open || this.disabled) {
162
180
  return undefined;
@@ -167,22 +185,28 @@ async hide() {
167
185
  }
168
186
 
169
187
  render() {
170
- const isRtl = false;
188
+ const isRtl = true;
171
189
 
172
190
  return html`
173
191
  <div
174
192
  part="base"
175
193
  class=${classMap({
176
- details: true,
177
- 'details--open': this.open,
178
- 'details--disabled': this.disabled,
179
- 'details--rtl': isRtl
194
+ accordian: true,
195
+ 'accordian--open': this.open,
196
+ 'accordian--disabled': this.disabled,
197
+ 'accordian--sm':this.size=='sm',
198
+ 'accordian--md':this.size=='md',
199
+ 'accordian--lg':this.size=='lg',
180
200
  })}
181
201
  >
182
202
  <div
183
203
  part="header"
184
204
  id="header"
185
- class="details__header"
205
+ class="${classMap({
206
+ 'accordian__header':true,
207
+ 'accordian__header--dark':this.variant=='dark',
208
+ 'accordian__header--arrow-left':'left'==this.expandIconPlacement
209
+ })}"
186
210
  role="button"
187
211
  aria-expanded=${this.open ? 'true' : 'false'}
188
212
  aria-controls="content"
@@ -191,9 +215,9 @@ render() {
191
215
  @click=${this.handleSummaryClick}
192
216
  @keydown=${this.handleSummaryKeyDown}
193
217
  >
194
- <slot name="summary" part="summary" class="details__summary">${this.summary}</slot>
218
+ <slot name="summary" part="summary" class="accordian__summary">${this.summary}</slot>
195
219
 
196
- <span part="summary-icon" class="details__summary-icon">
220
+ <span part="summary-icon" class="accordian__summary-icon">
197
221
  <slot name="expand-icon">
198
222
  <nile-icon name="arrowright"></nile-icon>
199
223
  </slot>
@@ -203,15 +227,19 @@ render() {
203
227
  </span>
204
228
  </div>
205
229
 
206
- <div class="details__body" role="region" aria-labelledby="header">
207
- <slot part="content" id="content" class="details__content"></slot>
230
+ <div class="accordian__body" role="region" aria-labelledby="header">
231
+ <slot part="content" id="content"
232
+ class="${classMap({
233
+ 'accordian__content':true,
234
+ 'accordian__content--arrow-left':this.expandIconPlacement=='left'
235
+ })}"></slot>
208
236
  </div>
209
237
  </div>
210
238
  `;
211
239
  }
212
240
  }
213
241
 
214
- setDefaultAnimation('details.show', {
242
+ setDefaultAnimation('accordian.show', {
215
243
  keyframes: [
216
244
  { height: '0', opacity: '0' },
217
245
  { height: 'auto', opacity: '1' }
@@ -219,7 +247,7 @@ keyframes: [
219
247
  options: { duration: 250, easing: 'linear' }
220
248
  });
221
249
 
222
- setDefaultAnimation('details.hide', {
250
+ setDefaultAnimation('accordian.hide', {
223
251
  keyframes: [
224
252
  { height: 'auto', opacity: '1' },
225
253
  { height: '0', opacity: '0' }
@@ -1,65 +1,116 @@
1
1
  import { html, fixture, expect } from '@open-wc/testing';
2
2
  import './nile-avatar';
3
- import {NileAvatar} from './nile-avatar';
3
+ import '../nile-icon'
4
+ import { NileAvatar } from './nile-avatar';
5
+ const wait=(ms:number=50000)=>new Promise(resolve => setTimeout(resolve, ms))
6
+
4
7
 
5
8
  describe('NileAvatar', () => {
6
- it('should display an image when the src is provided', async () => {
7
- const el = await fixture<NileAvatar>(html`<nile-avatar src="https://example.com/avatar.png"></nile-avatar>`);
9
+ it('should display an image when the src is provided and variant is "image"', async () => {
10
+ const el = await fixture<NileAvatar>(
11
+ html`<nile-avatar src="https://example.com/avatar.png" variant="image"></nile-avatar>`
12
+ );
8
13
  const img = el.shadowRoot!.querySelector('img');
9
14
  expect(img).to.exist;
10
15
  expect(img!.getAttribute('src')).to.equal('https://example.com/avatar.png');
11
16
  });
12
17
 
13
- it('should display initials when the image fails to load and name is provided', async () => {
14
- const el = await fixture<NileAvatar>(html`<nile-avatar name="John Doe"></nile-avatar>`);
18
+ it('should fall back to text initials when image fails to load and variant is "text"', async () => {
19
+ const el = await fixture<NileAvatar>(
20
+ html`<nile-avatar name="John Doe" variant="image" src="https://example.com/avatar.png"></nile-avatar>`
21
+ );
15
22
  const img = el.shadowRoot!.querySelector('img');
16
23
  img!.dispatchEvent(new Event('error'));
17
24
  await el.updateComplete;
25
+ const icon = el.shadowRoot!.querySelector('nile-icon');
26
+ expect(icon).to.exist;
27
+ expect(icon!.getAttribute('name')).to.equal('user');
28
+ });
29
+
30
+ it('should display initials when variant is "text" and name is provided', async () => {
31
+ const el = await fixture<NileAvatar>(
32
+ html`<nile-avatar name="John Doe" variant="text"></nile-avatar>`
33
+ );
18
34
  const initialsDiv = el.shadowRoot!.querySelector('.text__avatar');
19
35
  expect(initialsDiv).to.exist;
20
36
  expect(initialsDiv!.textContent!.trim()).to.equal('JD');
21
37
  });
22
38
 
23
- it('should use provided background and text colors when bg-color and text-color are set', async () => {
24
- const el = await fixture<NileAvatar>(html`<nile-avatar name="John Doe" bg-color="#ff0000" text-color="#00ff00"></nile-avatar>`);
25
- const img = el.shadowRoot!.querySelector('img');
26
- img!.dispatchEvent(new Event('error'));
27
- await el.updateComplete;
28
- const initialsDiv = el.shadowRoot!.querySelector('.text__avatar');
29
- expect(initialsDiv).to.have.style('background-color', 'rgb(255, 0, 0)');
30
- expect(initialsDiv).to.have.style('color', 'rgb(0, 255, 0)');
39
+ it('should display an icon when variant is "icon"', async () => {
40
+ const el = await fixture<NileAvatar>(
41
+ html`<nile-avatar variant="icon" icon="user"></nile-avatar>`
42
+ );
43
+ const icon = el.shadowRoot!.querySelector('nile-icon');
44
+ expect(icon).to.exist;
45
+ expect(icon!.getAttribute('name')).to.equal('user');
31
46
  });
32
47
 
33
48
  it('should apply the appropriate size class based on the size property', async () => {
34
- const el = await fixture<NileAvatar>(html`<nile-avatar size="large"></nile-avatar>`);
35
- const img = el.shadowRoot!.querySelector('img');
36
- expect(img).to.have.class('avatar__large');
49
+ const el = await fixture<NileAvatar>(html`<nile-avatar size="lg"></nile-avatar>`);
50
+ const div = el.shadowRoot!.querySelector('.text__avatar') || el.shadowRoot!.querySelector('img');
51
+ expect(div).to.have.class('avatar__large');
37
52
  });
38
53
 
39
- it('should have rounded class when isRounded is true', async () => {
54
+ it('should have the rounded class when isRounded is true', async () => {
40
55
  const el = await fixture<NileAvatar>(html`<nile-avatar isRounded></nile-avatar>`);
41
- const img = el.shadowRoot!.querySelector('img');
42
- expect(img).to.have.class('avatar__rounded');
43
- });
44
-
45
- it('should display a default icon when image fails to load and no name is provided', async () => {
46
- const el = await fixture<NileAvatar>(html`<nile-avatar></nile-avatar>`);
47
- const img = el.shadowRoot!.querySelector('img');
48
- img!.dispatchEvent(new Event('error'));
49
- await el.updateComplete;
50
- const defaultIcon = el.shadowRoot!.querySelector('nile-icon');
51
- expect(defaultIcon).to.exist;
52
- expect(defaultIcon!.getAttribute('name')).to.equal('user');
56
+ const div = el.shadowRoot!.querySelector('.text__avatar') || el.shadowRoot!.querySelector('img');
57
+ expect(div).to.have.class('avatar__rounded');
53
58
  });
54
59
 
55
- it('should reflect properties to attributes', async () => {
56
- const el = await fixture<NileAvatar>(html`<nile-avatar src="https://example.com/avatar.png" name="Jane Doe" bg-color="#123456" text-color="#654321" border-color="#abcdef" size="small" isRounded></nile-avatar>`);
60
+ it('should reflect properties to attributes correctly', async () => {
61
+ const el = await fixture<NileAvatar>(
62
+ html`<nile-avatar
63
+ src="https://example.com/avatar.png"
64
+ name="Jane Doe"
65
+ bg-color="#123456"
66
+ text-color="#654321"
67
+ border-color="#abcdef"
68
+ size="sm"
69
+ isRounded
70
+ variant="image"
71
+ icon="user"
72
+ ></nile-avatar>`
73
+ );
57
74
  expect(el.getAttribute('src')).to.equal('https://example.com/avatar.png');
58
75
  expect(el.getAttribute('name')).to.equal('Jane Doe');
59
76
  expect(el.getAttribute('bg-color')).to.equal('#123456');
60
77
  expect(el.getAttribute('text-color')).to.equal('#654321');
61
78
  expect(el.getAttribute('border-color')).to.equal('#abcdef');
62
- expect(el.getAttribute('size')).to.equal('small');
79
+ expect(el.getAttribute('size')).to.equal('sm');
80
+ expect(el.getAttribute('variant')).to.equal('image');
81
+ expect(el.getAttribute('icon')).to.equal('user');
63
82
  expect(el.hasAttribute('isRounded')).to.be.true;
64
83
  });
65
- });
84
+
85
+ it('should use provided background and text colors when bg-color and text-color are set', async () => {
86
+ const el = await fixture<NileAvatar>(
87
+ html`<nile-avatar name="John Doe" bg-color="#ff0000" text-color="#00ff00"></nile-avatar>`
88
+ );
89
+ const initialsDiv = el.shadowRoot!.querySelector('.text__avatar');
90
+ expect(initialsDiv).to.have.style('background-color', 'rgb(255, 0, 0)');
91
+ expect(initialsDiv).to.have.style('color', 'rgb(0, 255, 0)');
92
+ });
93
+
94
+ it('should display a default icon when variant is "icon" and icon is not provided', async () => {
95
+ const el = await fixture<NileAvatar>(html`<nile-avatar variant="icon"></nile-avatar>`);
96
+ const icon = el.shadowRoot!.querySelector('nile-icon');
97
+ expect(icon).to.exist;
98
+ expect(icon!.getAttribute('name')).to.equal('user');
99
+ });
100
+
101
+ it('should use the default initials if name is not provided and variant is "text"', async () => {
102
+ const el = await fixture<NileAvatar>(html`<nile-avatar variant="text"></nile-avatar>`);
103
+ const initialsDiv = el.shadowRoot!.querySelector('.text__avatar');
104
+ expect(initialsDiv).to.exist;
105
+ expect(initialsDiv!.textContent!.trim()).to.equal('');
106
+ });
107
+
108
+ it('should handle image error gracefully by switching to fallback content', async () => {
109
+ const el = await fixture<NileAvatar>(html`<nile-avatar variant="image" src="invalid_url"></nile-avatar>`);
110
+ const img = el.shadowRoot!.querySelector('img');
111
+ img!.dispatchEvent(new Event('error'));
112
+ await el.updateComplete;
113
+ const fallbackContent = el.shadowRoot!.querySelector('.text__avatar') || el.shadowRoot!.querySelector('nile-icon');
114
+ expect(fallbackContent).to.exist;
115
+ });
116
+ });