@kubex/zinc 1.1.63 → 1.1.64
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/dist/custom-elements.json +928 -3
- package/dist/vscode.html-custom-data.json +47 -0
- package/dist/web-types.json +109 -1
- package/dist/zn.d.ts +152 -0
- package/dist/zn.min.js +523 -418
- package/docs/pages/components/remarkd-editor.md +103 -0
- package/package.json +3 -2
- package/src/components/file/file.component.ts +16 -1
- package/src/components/query-builder/query-builder.component.ts +14 -6
- package/src/components/remarkd-editor/index.ts +12 -0
- package/src/components/remarkd-editor/remarkd-editor.component.ts +752 -0
- package/src/components/remarkd-editor/remarkd-editor.scss +241 -0
- package/src/components/remarkd-editor/remarkd-editor.test.ts +147 -0
- package/src/components/tabs/tabs.component.ts +9 -4
- package/src/components/tabs/tabs.test.ts +42 -1
- package/src/zinc.ts +1 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
@use "../../wc";
|
|
2
|
+
// Remarkd's official output styles, scoped under .remarkd-rendered.
|
|
3
|
+
@use "../../../node_modules/remarkd-js/docs/assets/remarkd-output";
|
|
4
|
+
|
|
5
|
+
:host {
|
|
6
|
+
display: block;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.remarkd-editor {
|
|
10
|
+
position: relative;
|
|
11
|
+
border: 1px solid rgb(var(--zn-border-color));
|
|
12
|
+
border-radius: var(--zn-border-radius);
|
|
13
|
+
background-color: rgba(var(--zn-panel), var(--zn-panel-opacity));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.remarkd-editor__toolbar {
|
|
17
|
+
display: flex;
|
|
18
|
+
gap: 2px;
|
|
19
|
+
flex-wrap: wrap;
|
|
20
|
+
padding: var(--zn-spacing-x-small);
|
|
21
|
+
border-bottom: 1px solid rgb(var(--zn-border-color));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.remarkd-editor__body {
|
|
25
|
+
padding: var(--zn-spacing-large) var(--zn-spacing-large) var(--zn-spacing-large) 88px;
|
|
26
|
+
min-height: 10rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.remarkd-editor--disabled {
|
|
30
|
+
opacity: 0.6;
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.remarkd-editor__block {
|
|
35
|
+
position: relative;
|
|
36
|
+
border-radius: var(--zn-border-radius);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.remarkd-editor__actions {
|
|
40
|
+
position: absolute;
|
|
41
|
+
left: -80px;
|
|
42
|
+
top: 0;
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: 2px;
|
|
46
|
+
opacity: 0;
|
|
47
|
+
transition: opacity 0.15s ease;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.remarkd-editor__block:hover .remarkd-editor__actions,
|
|
51
|
+
.remarkd-editor__actions:focus-within {
|
|
52
|
+
opacity: 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.remarkd-editor__drag-handle {
|
|
56
|
+
display: inline-flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
padding: 2px;
|
|
59
|
+
color: var(--zn-color-neutral-400, #9aa4b2);
|
|
60
|
+
cursor: grab;
|
|
61
|
+
touch-action: none;
|
|
62
|
+
user-select: none;
|
|
63
|
+
|
|
64
|
+
&:active {
|
|
65
|
+
cursor: grabbing;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.remarkd-editor__ghost {
|
|
70
|
+
position: fixed;
|
|
71
|
+
top: 0;
|
|
72
|
+
left: 0;
|
|
73
|
+
z-index: 20;
|
|
74
|
+
pointer-events: none;
|
|
75
|
+
opacity: 0.9;
|
|
76
|
+
background: var(--zn-color-neutral-0, white);
|
|
77
|
+
border-radius: var(--zn-border-radius);
|
|
78
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
|
79
|
+
max-width: 50%;
|
|
80
|
+
max-height: 160px;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.remarkd-editor__block--dragging {
|
|
85
|
+
opacity: 0.4;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.remarkd-editor__block--drop-before::before,
|
|
89
|
+
.remarkd-editor__block--drop-after::after {
|
|
90
|
+
content: '';
|
|
91
|
+
position: absolute;
|
|
92
|
+
left: 0;
|
|
93
|
+
right: 0;
|
|
94
|
+
height: 6px;
|
|
95
|
+
border-radius: 3px;
|
|
96
|
+
background: rgba(var(--zn-primary), 0.6);
|
|
97
|
+
pointer-events: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.remarkd-editor__block--drop-before::before {
|
|
101
|
+
top: -4px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.remarkd-editor__block--drop-after::after {
|
|
105
|
+
bottom: -4px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.remarkd-editor__rendered {
|
|
109
|
+
padding: var(--zn-spacing-x-small);
|
|
110
|
+
border-radius: var(--zn-border-radius);
|
|
111
|
+
cursor: text;
|
|
112
|
+
|
|
113
|
+
&:hover {
|
|
114
|
+
background: var(--zn-color-neutral-100);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The shell keeps the block's rendered chrome while its source is edited —
|
|
119
|
+
// remarkd hint classes (hint-note, hint-tip, …) apply their own styling here.
|
|
120
|
+
.remarkd-editor__edit-shell {
|
|
121
|
+
border: 1px solid rgb(var(--zn-border-color));
|
|
122
|
+
border-radius: var(--zn-border-radius);
|
|
123
|
+
background: var(--zn-color-neutral-0, white);
|
|
124
|
+
padding: var(--zn-spacing-x-small);
|
|
125
|
+
|
|
126
|
+
// Hint shells keep their own left accent — only tint the other edges.
|
|
127
|
+
&:focus-within {
|
|
128
|
+
border-top-color: rgb(var(--zn-primary));
|
|
129
|
+
border-right-color: rgb(var(--zn-primary));
|
|
130
|
+
border-bottom-color: rgb(var(--zn-primary));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
&:not([class*="hint-"]):focus-within {
|
|
134
|
+
border-left-color: rgb(var(--zn-primary));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.remarkd-editor__input {
|
|
139
|
+
display: block;
|
|
140
|
+
width: 100%;
|
|
141
|
+
resize: none;
|
|
142
|
+
overflow: hidden;
|
|
143
|
+
border: 0;
|
|
144
|
+
background: transparent;
|
|
145
|
+
color: inherit;
|
|
146
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
147
|
+
font-size: 0.875rem;
|
|
148
|
+
line-height: 1.5;
|
|
149
|
+
padding: 0;
|
|
150
|
+
outline: none;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Hints edit in the rendered font so they read as the same block.
|
|
154
|
+
.remarkd-editor__edit-shell[class*="hint-"] .remarkd-editor__input {
|
|
155
|
+
font: inherit;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.remarkd-editor__edit-shell--h1 .remarkd-editor__input,
|
|
159
|
+
.remarkd-editor__edit-shell--h2 .remarkd-editor__input,
|
|
160
|
+
.remarkd-editor__edit-shell--h3 .remarkd-editor__input {
|
|
161
|
+
font-family: inherit;
|
|
162
|
+
font-weight: 700;
|
|
163
|
+
line-height: 1.2;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.remarkd-editor__edit-shell--h1 .remarkd-editor__input {
|
|
167
|
+
font-size: 26px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.remarkd-editor__edit-shell--h2 .remarkd-editor__input {
|
|
171
|
+
font-size: 21px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.remarkd-editor__edit-shell--h3 .remarkd-editor__input {
|
|
175
|
+
font-size: 17px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.remarkd-editor__edit-shell--code {
|
|
179
|
+
background: var(--rmd-code-bg, #0f1626);
|
|
180
|
+
|
|
181
|
+
.remarkd-editor__input {
|
|
182
|
+
color: var(--rmd-code-text, #f5f8ff);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.remarkd-editor__add {
|
|
187
|
+
padding: var(--zn-spacing-x-small);
|
|
188
|
+
min-height: 1.8em;
|
|
189
|
+
color: var(--zn-color-neutral-400, #9aa4b2);
|
|
190
|
+
cursor: text;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.remarkd-editor__edit-wrap {
|
|
194
|
+
position: relative;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.remarkd-editor__slash-menu {
|
|
198
|
+
position: absolute;
|
|
199
|
+
top: 100%;
|
|
200
|
+
left: 0;
|
|
201
|
+
z-index: 10;
|
|
202
|
+
min-width: 12rem;
|
|
203
|
+
margin-top: 4px;
|
|
204
|
+
padding: 4px;
|
|
205
|
+
border: 1px solid rgb(var(--zn-border-color));
|
|
206
|
+
border-radius: var(--zn-border-radius);
|
|
207
|
+
background: var(--zn-color-neutral-0, white);
|
|
208
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
|
209
|
+
display: flex;
|
|
210
|
+
flex-direction: column;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.remarkd-editor__slash-item {
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: var(--zn-spacing-x-small);
|
|
217
|
+
padding: 6px 8px;
|
|
218
|
+
border: 0;
|
|
219
|
+
border-radius: var(--zn-border-radius);
|
|
220
|
+
background: none;
|
|
221
|
+
color: var(--zn-color-text, #222);
|
|
222
|
+
font: inherit;
|
|
223
|
+
font-size: 0.875rem;
|
|
224
|
+
text-align: left;
|
|
225
|
+
cursor: pointer;
|
|
226
|
+
|
|
227
|
+
&:hover,
|
|
228
|
+
&--active {
|
|
229
|
+
background: var(--zn-color-neutral-100);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.remarkd-editor__validation {
|
|
234
|
+
position: absolute;
|
|
235
|
+
width: 1px;
|
|
236
|
+
height: 1px;
|
|
237
|
+
opacity: 0;
|
|
238
|
+
pointer-events: none;
|
|
239
|
+
border: 0;
|
|
240
|
+
padding: 0;
|
|
241
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import '../../../dist/zn.min.js';
|
|
2
|
+
import {expect, fixture, html} from '@open-wc/testing';
|
|
3
|
+
import type ZnRemarkdEditor from './remarkd-editor.component';
|
|
4
|
+
|
|
5
|
+
describe('<zn-remarkd-editor>', () => {
|
|
6
|
+
it('should render a component', async () => {
|
|
7
|
+
const el = await fixture(html`
|
|
8
|
+
<zn-remarkd-editor></zn-remarkd-editor>`);
|
|
9
|
+
expect(el).to.exist;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should split the value into rendered remarkd blocks', async () => {
|
|
13
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
14
|
+
<zn-remarkd-editor value="# Title
|
|
15
|
+
|
|
16
|
+
NOTE: a note"></zn-remarkd-editor>`);
|
|
17
|
+
const blocks = el.shadowRoot!.querySelectorAll('.remarkd-editor__block');
|
|
18
|
+
expect(blocks.length).to.equal(2);
|
|
19
|
+
expect(blocks[1].innerHTML).to.contain('hint-note');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should keep fenced content as a single block', async () => {
|
|
23
|
+
const fenced = '```\n\ncode\n\n```';
|
|
24
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
25
|
+
<zn-remarkd-editor value=${fenced}></zn-remarkd-editor>`);
|
|
26
|
+
expect(el.shadowRoot!.querySelectorAll('.remarkd-editor__block').length).to.equal(1);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should swap a block to source editing on click and commit on blur', async () => {
|
|
30
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
31
|
+
<zn-remarkd-editor value="# Title"></zn-remarkd-editor>`);
|
|
32
|
+
el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
|
|
33
|
+
await el.updateComplete;
|
|
34
|
+
|
|
35
|
+
const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
|
|
36
|
+
expect(input).to.exist;
|
|
37
|
+
expect(input.value).to.equal('# Title');
|
|
38
|
+
|
|
39
|
+
input.value = '# Changed';
|
|
40
|
+
input.dispatchEvent(new Event('input', {bubbles: true}));
|
|
41
|
+
input.dispatchEvent(new Event('blur'));
|
|
42
|
+
await el.updateComplete;
|
|
43
|
+
|
|
44
|
+
expect(el.value).to.equal('# Changed');
|
|
45
|
+
expect(el.shadowRoot!.querySelector('.remarkd-editor__rendered')!.innerHTML).to.contain('Changed');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should remove a block committed as empty', async () => {
|
|
49
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
50
|
+
<zn-remarkd-editor value="# Title
|
|
51
|
+
|
|
52
|
+
Second"></zn-remarkd-editor>`);
|
|
53
|
+
el.shadowRoot!.querySelectorAll<HTMLElement>('.remarkd-editor__rendered')[1].click();
|
|
54
|
+
await el.updateComplete;
|
|
55
|
+
|
|
56
|
+
const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
|
|
57
|
+
input.value = '';
|
|
58
|
+
input.dispatchEvent(new Event('input', {bubbles: true}));
|
|
59
|
+
input.dispatchEvent(new Event('blur'));
|
|
60
|
+
await el.updateComplete;
|
|
61
|
+
|
|
62
|
+
expect(el.value).to.equal('# Title');
|
|
63
|
+
expect(el.shadowRoot!.querySelectorAll('.remarkd-editor__block').length).to.equal(1);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should render an always-visible toolbar', async () => {
|
|
67
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
68
|
+
<zn-remarkd-editor></zn-remarkd-editor>`);
|
|
69
|
+
expect(el.shadowRoot!.querySelector('.remarkd-editor__toolbar')).to.exist;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should open the slash menu when "/" starts an empty block', async () => {
|
|
73
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
74
|
+
<zn-remarkd-editor value="Hello"></zn-remarkd-editor>`);
|
|
75
|
+
el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
|
|
76
|
+
await el.updateComplete;
|
|
77
|
+
|
|
78
|
+
const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
|
|
79
|
+
input.value = '/';
|
|
80
|
+
input.dispatchEvent(new Event('input', {bubbles: true}));
|
|
81
|
+
await el.updateComplete;
|
|
82
|
+
|
|
83
|
+
expect(el.shadowRoot!.querySelector('.remarkd-editor__slash-menu')).to.exist;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should start a new block on shift+enter', async () => {
|
|
87
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
88
|
+
<zn-remarkd-editor value="First"></zn-remarkd-editor>`);
|
|
89
|
+
el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
|
|
90
|
+
await el.updateComplete;
|
|
91
|
+
|
|
92
|
+
const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
|
|
93
|
+
input.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter', shiftKey: true, bubbles: true}));
|
|
94
|
+
await el.updateComplete;
|
|
95
|
+
|
|
96
|
+
// First block committed and rendered; a fresh draft textarea sits below it.
|
|
97
|
+
expect(el.shadowRoot!.querySelectorAll('.remarkd-editor__block').length).to.equal(1);
|
|
98
|
+
const draft = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
|
|
99
|
+
expect(draft).to.exist;
|
|
100
|
+
expect(draft.value).to.equal('');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should open the image dialog from the toolbar', async () => {
|
|
104
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
105
|
+
<zn-remarkd-editor></zn-remarkd-editor>`);
|
|
106
|
+
const buttons = el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button');
|
|
107
|
+
const imageButton = buttons[buttons.length - 1];
|
|
108
|
+
imageButton.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
|
|
109
|
+
await el.updateComplete;
|
|
110
|
+
|
|
111
|
+
const dialog = el.shadowRoot!.querySelector('.remarkd-editor__image-dialog')!;
|
|
112
|
+
expect(dialog).to.exist;
|
|
113
|
+
// No attachment-url configured — the dialog falls back to a URL input.
|
|
114
|
+
expect(el.shadowRoot!.querySelector('.remarkd-editor__image-url')).to.exist;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should toggle a checkbox in the source instead of opening the editor', async () => {
|
|
118
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
119
|
+
<zn-remarkd-editor value="- [ ] First
|
|
120
|
+
- [x] Second"></zn-remarkd-editor>`);
|
|
121
|
+
const boxes = el.shadowRoot!.querySelectorAll<HTMLInputElement>('input[type="checkbox"]');
|
|
122
|
+
boxes[0].dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
|
|
123
|
+
await el.updateComplete;
|
|
124
|
+
|
|
125
|
+
expect(el.value).to.equal('- [x] First\n- [x] Second');
|
|
126
|
+
expect(el.shadowRoot!.querySelector('.remarkd-editor__input')).to.not.exist;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should emit zn-change when a block edit changes the value', async () => {
|
|
130
|
+
const el = await fixture<ZnRemarkdEditor>(html`
|
|
131
|
+
<zn-remarkd-editor value="Hello"></zn-remarkd-editor>`);
|
|
132
|
+
let fired = false;
|
|
133
|
+
el.addEventListener('zn-change', () => {
|
|
134
|
+
fired = true;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
|
|
138
|
+
await el.updateComplete;
|
|
139
|
+
const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
|
|
140
|
+
input.value = 'Hello world';
|
|
141
|
+
input.dispatchEvent(new Event('input', {bubbles: true}));
|
|
142
|
+
input.dispatchEvent(new Event('blur'));
|
|
143
|
+
await el.updateComplete;
|
|
144
|
+
|
|
145
|
+
expect(fired).to.be.true;
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -10,6 +10,11 @@ import ZincElement from '../../internal/zinc-element';
|
|
|
10
10
|
|
|
11
11
|
import styles from './tabs.scss';
|
|
12
12
|
|
|
13
|
+
// Every element that manages its own tabs (ZnTabs and its subclasses). Tab
|
|
14
|
+
// registration must stop at these so an outer container never claims a nested
|
|
15
|
+
// container's tabs.
|
|
16
|
+
const tabContainerSelector = 'zn-tabs, zn-page, zn-page-nav';
|
|
17
|
+
|
|
13
18
|
/**
|
|
14
19
|
* @summary Short summary of the component's intended use.
|
|
15
20
|
* @documentation https://zinc.style/components/tabs
|
|
@@ -228,7 +233,7 @@ export default class ZnTabs extends ZincElement {
|
|
|
228
233
|
}
|
|
229
234
|
}
|
|
230
235
|
|
|
231
|
-
for (const uriTab of deepQuerySelectorAll("[tab-uri]", this,
|
|
236
|
+
for (const uriTab of deepQuerySelectorAll("[tab-uri]", this, tabContainerSelector)) {
|
|
232
237
|
const uri: string = uriTab.getAttribute("tab-uri")!;
|
|
233
238
|
const eleTabId = this._uriToId(uri);
|
|
234
239
|
if (eleTabId === tabId) {
|
|
@@ -459,11 +464,11 @@ export default class ZnTabs extends ZincElement {
|
|
|
459
464
|
}
|
|
460
465
|
|
|
461
466
|
_registerTabs = () => {
|
|
462
|
-
deepQuerySelectorAll('[tab]', this,
|
|
467
|
+
deepQuerySelectorAll('[tab]', this, tabContainerSelector).forEach(ele => {
|
|
463
468
|
this._addTab(ele as HTMLElement);
|
|
464
469
|
});
|
|
465
470
|
|
|
466
|
-
deepQuerySelectorAll('[tab-uri]', this,
|
|
471
|
+
deepQuerySelectorAll('[tab-uri]', this, tabContainerSelector).forEach(ele => {
|
|
467
472
|
if (ele.getAttribute('tab-uri') === "") {
|
|
468
473
|
ele.setAttribute('tab', "");
|
|
469
474
|
ele.removeAttribute('tab-uri');
|
|
@@ -471,7 +476,7 @@ export default class ZnTabs extends ZincElement {
|
|
|
471
476
|
this._addTab(ele as HTMLElement);
|
|
472
477
|
});
|
|
473
478
|
|
|
474
|
-
deepQuerySelectorAll('[ref-tab-uri]', this,
|
|
479
|
+
deepQuerySelectorAll('[ref-tab-uri]', this, tabContainerSelector).forEach(ele => {
|
|
475
480
|
if (!ele.hasAttribute('ref-tab')) {
|
|
476
481
|
ele.setAttribute('ref-tab', this._uriToId(ele.getAttribute('ref-tab-uri')!));
|
|
477
482
|
ele.removeAttribute('ref-tab-uri');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
|
-
import { expect, fixture, html } from '@open-wc/testing';
|
|
2
|
+
import { aTimeout, expect, fixture, html } from '@open-wc/testing';
|
|
3
|
+
import type ZnTabs from './tabs.component';
|
|
3
4
|
|
|
4
5
|
describe('<zn-tabs>', () => {
|
|
5
6
|
it('should render a component', async () => {
|
|
@@ -7,4 +8,44 @@ describe('<zn-tabs>', () => {
|
|
|
7
8
|
|
|
8
9
|
expect(el).to.exist;
|
|
9
10
|
});
|
|
11
|
+
|
|
12
|
+
it('does not register tabs belonging to a nested zn-page', async () => {
|
|
13
|
+
const el = await fixture<ZnTabs>(html`
|
|
14
|
+
<zn-tabs>
|
|
15
|
+
<div class="outer-home">Outer Home</div>
|
|
16
|
+
<div id="outer-second">
|
|
17
|
+
<zn-page caption="Nested Page">
|
|
18
|
+
<zn-tab caption="Overview">Overview Content</zn-tab>
|
|
19
|
+
<zn-tab caption="Details">Details Content</zn-tab>
|
|
20
|
+
</zn-page>
|
|
21
|
+
</div>
|
|
22
|
+
</zn-tabs>
|
|
23
|
+
`);
|
|
24
|
+
await aTimeout(60);
|
|
25
|
+
|
|
26
|
+
el.setActiveTab('outer-second', false, false);
|
|
27
|
+
await aTimeout(20);
|
|
28
|
+
|
|
29
|
+
// A menu selection bubbling out of the page (e.g. an ellipses action menu)
|
|
30
|
+
// makes the outer tabs re-register; it must not claim the page's nav items.
|
|
31
|
+
const page = el.querySelector('zn-page')!;
|
|
32
|
+
page.dispatchEvent(new CustomEvent('zn-menu-select', { bubbles: true, composed: true }));
|
|
33
|
+
await aTimeout(250);
|
|
34
|
+
|
|
35
|
+
const navbar = page.shadowRoot!.querySelector('zn-navbar')!;
|
|
36
|
+
const navItems = Array.from(navbar.shadowRoot!.querySelectorAll<HTMLElement>('li:not(.more)'));
|
|
37
|
+
|
|
38
|
+
navItems[1].click();
|
|
39
|
+
await aTimeout(40);
|
|
40
|
+
navItems[0].click();
|
|
41
|
+
await aTimeout(40);
|
|
42
|
+
|
|
43
|
+
const outerHome = el.querySelector('.outer-home')!;
|
|
44
|
+
const outerSecond = el.querySelector('#outer-second')!;
|
|
45
|
+
expect(outerSecond.hasAttribute('selected')).to.equal(true);
|
|
46
|
+
expect(outerHome.hasAttribute('selected')).to.equal(false);
|
|
47
|
+
|
|
48
|
+
const selectedPagePanel = page.shadowRoot!.querySelector<HTMLElement>('#content > div[selected]')!;
|
|
49
|
+
expect(selectedPagePanel.id).to.equal('');
|
|
50
|
+
});
|
|
10
51
|
});
|
package/src/zinc.ts
CHANGED
|
@@ -101,6 +101,7 @@ export { default as TranslationGroup } from './components/translation-group';
|
|
|
101
101
|
export { default as OptGroup } from './components/opt-group';
|
|
102
102
|
export { default as PriorityList } from './components/priority-list';
|
|
103
103
|
export { default as MarkdownEditor } from './components/markdown-editor';
|
|
104
|
+
export { default as RemarkdEditor } from './components/remarkd-editor';
|
|
104
105
|
export { default as FlowBuilder } from './components/flow-builder';
|
|
105
106
|
export { default as FlowSteps } from './components/flow-builder/modules/flow-steps';
|
|
106
107
|
export { default as FlowStepGroup } from './components/flow-builder/modules/flow-step-group';
|