@aquera/nile-elements 0.1.8 → 0.1.9
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/README.md +4 -0
- package/demo/index.html +0 -10
- package/dist/nile-accordion/nile-accordian.test.cjs.js +1 -1
- package/dist/nile-accordion/nile-accordian.test.cjs.js.map +1 -1
- package/dist/nile-accordion/nile-accordian.test.esm.js +1 -1
- package/dist/nile-accordion/nile-accordion.cjs.js +1 -1
- package/dist/nile-accordion/nile-accordion.cjs.js.map +1 -1
- package/dist/nile-accordion/nile-accordion.css.cjs.js +1 -1
- package/dist/nile-accordion/nile-accordion.css.cjs.js.map +1 -1
- package/dist/nile-accordion/nile-accordion.css.esm.js +54 -23
- package/dist/nile-accordion/nile-accordion.esm.js +9 -8
- package/dist/nile-avatar/nile-avatar.test.cjs.js +1 -1
- package/dist/nile-avatar/nile-avatar.test.cjs.js.map +1 -1
- package/dist/nile-avatar/nile-avatar.test.esm.js +11 -1
- package/dist/nile-code-editor/nile-code-editor.css.cjs.js +1 -1
- package/dist/nile-code-editor/nile-code-editor.css.cjs.js.map +1 -1
- package/dist/nile-code-editor/nile-code-editor.css.esm.js +3 -3
- package/dist/src/nile-accordion/nile-accordian.test.js +24 -29
- package/dist/src/nile-accordion/nile-accordian.test.js.map +1 -1
- package/dist/src/nile-accordion/nile-accordion.css.js +53 -22
- package/dist/src/nile-accordion/nile-accordion.css.js.map +1 -1
- package/dist/src/nile-accordion/nile-accordion.d.ts +31 -16
- package/dist/src/nile-accordion/nile-accordion.js +68 -34
- package/dist/src/nile-accordion/nile-accordion.js.map +1 -1
- package/dist/src/nile-avatar/nile-avatar.test.d.ts +1 -0
- package/dist/src/nile-avatar/nile-avatar.test.js +64 -30
- package/dist/src/nile-avatar/nile-avatar.test.js.map +1 -1
- package/dist/src/nile-code-editor/nile-code-editor.css.js +3 -3
- package/dist/src/nile-code-editor/nile-code-editor.css.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/nile-accordion/nile-accordian.test.ts +30 -57
- package/src/nile-accordion/nile-accordion.css.ts +53 -22
- package/src/nile-accordion/nile-accordion.ts +61 -33
- package/src/nile-avatar/nile-avatar.test.ts +84 -33
- package/src/nile-code-editor/nile-code-editor.css.ts +3 -3
- package/vscode-html-custom-data.json +82 -43
package/package.json
CHANGED
@@ -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<
|
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<
|
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('.
|
41
|
-
|
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('.
|
55
|
-
|
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
|
-
.
|
34
|
-
border:
|
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
|
-
.
|
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
|
-
.
|
65
|
+
.accordian__header {
|
45
66
|
display: flex;
|
67
|
+
gap:12px;
|
46
68
|
align-items: center;
|
47
69
|
border-radius: inherit;
|
48
|
-
|
70
|
+
font-weight:500;
|
71
|
+
padding: var(--accordian-heading-padding);
|
49
72
|
user-select: none;
|
50
73
|
cursor: pointer;
|
51
74
|
}
|
52
75
|
|
53
|
-
.
|
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
|
-
.
|
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
|
-
.
|
93
|
+
.accordian--disabled .accordian__header {
|
63
94
|
cursor: not-allowed;
|
64
95
|
}
|
65
96
|
|
66
|
-
.
|
97
|
+
.accordian--disabled .accordian__header:focus-visible {
|
67
98
|
outline: none;
|
68
99
|
box-shadow: none;
|
69
100
|
}
|
70
101
|
|
71
|
-
.
|
102
|
+
.accordian__summary {
|
72
103
|
flex: 1 1 auto;
|
73
104
|
display: flex;
|
74
105
|
align-items: center;
|
75
106
|
}
|
76
107
|
|
77
|
-
.
|
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
|
-
.
|
115
|
+
.accordian--open .accordian__summary-icon {
|
85
116
|
rotate: 90deg;
|
86
117
|
}
|
87
118
|
|
88
|
-
.
|
89
|
-
|
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
|
-
.
|
124
|
+
.accordian__body {
|
98
125
|
overflow: hidden;
|
99
126
|
}
|
100
127
|
|
101
|
-
.
|
128
|
+
.accordian__content {
|
102
129
|
display: block;
|
103
|
-
padding:
|
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
|
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
|
34
|
-
* @slot summary - The
|
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
|
39
|
-
* @event nile-after-show - Emitted after the
|
40
|
-
* @event nile-hide - Emitted when the
|
41
|
-
* @event nile-after-hide - Emitted after the
|
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
|
47
|
+
* @csspart content - The accordian content.
|
48
48
|
*
|
49
|
-
* @animation
|
50
|
-
* @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('.
|
57
|
-
@query('.
|
58
|
-
@query('.
|
59
|
-
@query('.
|
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
|
63
|
-
* can use the `show()` and `hide()` methods and this attribute will reflect the
|
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
|
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, '
|
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, '
|
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
|
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
|
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 =
|
188
|
+
const isRtl = true;
|
171
189
|
|
172
190
|
return html`
|
173
191
|
<div
|
174
192
|
part="base"
|
175
193
|
class=${classMap({
|
176
|
-
|
177
|
-
'
|
178
|
-
'
|
179
|
-
'
|
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="
|
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="
|
218
|
+
<slot name="summary" part="summary" class="accordian__summary">${this.summary}</slot>
|
195
219
|
|
196
|
-
<span part="summary-icon" class="
|
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="
|
207
|
-
<slot part="content" id="content"
|
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('
|
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('
|
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
|
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>(
|
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
|
14
|
-
const el = await fixture<NileAvatar>(
|
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
|
24
|
-
const el = await fixture<NileAvatar>(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
expect(
|
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="
|
35
|
-
const
|
36
|
-
expect(
|
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
|
42
|
-
expect(
|
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>(
|
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('
|
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
|
+
});
|