@salesforcedevs/dx-components 0.8.5-alpha1 → 0.10.1
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/LICENSE +12 -0
- package/package.json +3 -2
- package/src/modules/dx/codeBlock/codeBlock.ts +1 -6
- package/src/modules/dx/headerSearch/headerSearch.html +4 -6
- package/src/modules/dx/headerSearch/headerSearch.ts +6 -6
- package/src/modules/dx/input/__tests__/input.test.ts +45 -0
- package/src/modules/dx/input/input.css +13 -1
- package/src/modules/dx/input/input.html +9 -1
- package/src/modules/dx/input/input.stories.ts +3 -0
- package/src/modules/dx/input/input.ts +28 -17
- package/src/modules/dx/popover/popover.ts +11 -7
- package/src/modules/dx/sectionSignup/sectionSignup.html +0 -1
- package/src/modules/dx/treeItem/__tests__/treeItem.test.ts +10 -22
- package/src/modules/dx/treeItem/treeItem.html +2 -11
- package/src/modules/dx/treeItem/treeItem.ts +10 -6
- package/src/modules/dx/treeTile/__tests__/treeTile.test.ts +21 -20
- package/src/modules/dx/treeTile/treeTile.css +16 -0
- package/src/modules/dx/treeTile/treeTile.html +2 -2
- package/src/modules/dx/treeTile/treeTile.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (c) 2020, Salesforce.com, Inc.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
7
|
+
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
9
|
+
|
|
10
|
+
* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
11
|
+
|
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforcedevs/dx-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "DX Lightning web components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -28,5 +28,6 @@
|
|
|
28
28
|
"@types/classnames": "^2.2.10",
|
|
29
29
|
"@types/debounce": "^1.2.0",
|
|
30
30
|
"@types/lodash.get": "^4.4.6"
|
|
31
|
-
}
|
|
31
|
+
},
|
|
32
|
+
"gitHead": "84ec3fe64f80f136723f84c0222afe7f34c7b2a2"
|
|
32
33
|
}
|
|
@@ -20,8 +20,6 @@ export default class CodeBlock extends LightningElement {
|
|
|
20
20
|
@api title: string = "title";
|
|
21
21
|
@api sourceLink: string = "";
|
|
22
22
|
@api language: string = "";
|
|
23
|
-
// if it is true, it renders code as is coming instead of wrapping it into html/xml comments tags.
|
|
24
|
-
@api isEncoded = false;
|
|
25
23
|
|
|
26
24
|
private markupLangs = ["visualforce", "html", "xml"];
|
|
27
25
|
private _codeBlockRendered: boolean = false;
|
|
@@ -91,10 +89,7 @@ export default class CodeBlock extends LightningElement {
|
|
|
91
89
|
formatCodeBlock() {
|
|
92
90
|
const divEl = this.template.querySelector("div.code-block-content");
|
|
93
91
|
const templateEl = document.createElement("template");
|
|
94
|
-
if (
|
|
95
|
-
!this.isEncoded &&
|
|
96
|
-
this.markupLangs.includes(this.selectedLanguage.id || "")
|
|
97
|
-
) {
|
|
92
|
+
if (this.markupLangs.includes(this.selectedLanguage.id || "")) {
|
|
98
93
|
// eslint-disable-next-line
|
|
99
94
|
templateEl.innerHTML = `<pre class='codeblock'><!-- ${this.codeBlock.replace(
|
|
100
95
|
/<!--(.*?)-->/gs,
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
open={isDropdownOpen}
|
|
9
9
|
options={suggestions}
|
|
10
10
|
onrequestclose={onDropdownRequestClose}
|
|
11
|
-
onrequestopen={openPopover}
|
|
12
11
|
full-width
|
|
13
12
|
page-padding="0"
|
|
14
13
|
>
|
|
@@ -18,8 +17,9 @@
|
|
|
18
17
|
icon-size="large"
|
|
19
18
|
icon-symbol="search"
|
|
20
19
|
onchange={onInputChange}
|
|
21
|
-
|
|
20
|
+
onclear={onInputEscape}
|
|
22
21
|
onsubmit={onInputSubmit}
|
|
22
|
+
onfocus={openDropdown}
|
|
23
23
|
placeholder="Search"
|
|
24
24
|
size="override"
|
|
25
25
|
type="search"
|
|
@@ -43,18 +43,16 @@
|
|
|
43
43
|
open={isDropdownOpen}
|
|
44
44
|
placement="bottom-end"
|
|
45
45
|
onrequestclose={onDropdownRequestClose}
|
|
46
|
-
onrequestopen={openPopover}
|
|
47
46
|
offset="small"
|
|
48
47
|
full-width
|
|
49
|
-
temp-id="banana"
|
|
50
48
|
>
|
|
51
49
|
<dx-input
|
|
52
50
|
aria-label="Record Search Term"
|
|
53
51
|
icon-symbol="search"
|
|
54
52
|
onchange={onInputChange}
|
|
55
|
-
|
|
53
|
+
onclear={onInputEscape}
|
|
56
54
|
onsubmit={onInputSubmit}
|
|
57
|
-
onfocus={
|
|
55
|
+
onfocus={openDropdown}
|
|
58
56
|
placeholder="Search"
|
|
59
57
|
shortcut-key="k"
|
|
60
58
|
size="small"
|
|
@@ -70,7 +70,7 @@ export default class HeaderSearch extends LightningElement {
|
|
|
70
70
|
switch (e.detail as PopoverRequestCloseType) {
|
|
71
71
|
case "interact-outside":
|
|
72
72
|
case "keypress-escape":
|
|
73
|
-
this.
|
|
73
|
+
this.closeDropdown();
|
|
74
74
|
break;
|
|
75
75
|
default:
|
|
76
76
|
break;
|
|
@@ -124,29 +124,29 @@ export default class HeaderSearch extends LightningElement {
|
|
|
124
124
|
const input = this.template.querySelector("dx-input");
|
|
125
125
|
if (this.isMobileSearchOpen && input) {
|
|
126
126
|
input.focus();
|
|
127
|
-
this.
|
|
127
|
+
this.openDropdown();
|
|
128
128
|
} else {
|
|
129
|
-
this.
|
|
129
|
+
this.closeDropdown();
|
|
130
130
|
}
|
|
131
131
|
if (!this.isMobileSearchOpen && this.searchBox) {
|
|
132
132
|
this.searchBox.updateText("");
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
private
|
|
136
|
+
private openDropdown() {
|
|
137
137
|
this.isDropdownOpen = true;
|
|
138
138
|
this.isMobileSearchOpen = true;
|
|
139
139
|
this.reportState<boolean>(true);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
private
|
|
142
|
+
private closeDropdown() {
|
|
143
143
|
this.isDropdownOpen = false;
|
|
144
144
|
this.isMobileSearchOpen = false;
|
|
145
145
|
this.reportState<boolean>(false);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
private onInputEscape() {
|
|
149
|
-
this.
|
|
149
|
+
this.closeDropdown();
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
private reportState<T>(state: T) {
|
|
@@ -320,4 +320,49 @@ describe(TAG, () => {
|
|
|
320
320
|
|
|
321
321
|
return Promise.resolve().then(() => assertErrorMissing(element));
|
|
322
322
|
});
|
|
323
|
+
|
|
324
|
+
it("shows clear button if value is nonempty and clearable is true", () => {
|
|
325
|
+
const element = render({
|
|
326
|
+
value: "test",
|
|
327
|
+
clearable: true
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const clearButton: HTMLButtonElement = element.shadowRoot.querySelector(
|
|
331
|
+
".clear-button"
|
|
332
|
+
);
|
|
333
|
+
expect(clearButton).not.toBeNull();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("doesn't show clear button if no value", () => {
|
|
337
|
+
const element = render({
|
|
338
|
+
value: "",
|
|
339
|
+
clearable: true
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const clearButton: HTMLButtonElement = element.shadowRoot.querySelector(
|
|
343
|
+
".clear-button"
|
|
344
|
+
);
|
|
345
|
+
expect(clearButton).toBeNull();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("clears input value on click of clear button", async () => {
|
|
349
|
+
const element = render({
|
|
350
|
+
value: "test",
|
|
351
|
+
clearable: true
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const input: HTMLInputElement = element.shadowRoot.querySelector(
|
|
355
|
+
"input"
|
|
356
|
+
);
|
|
357
|
+
expect(input.value).toEqual("test");
|
|
358
|
+
|
|
359
|
+
const clearButton: HTMLButtonElement = element.shadowRoot.querySelector(
|
|
360
|
+
".clear-button"
|
|
361
|
+
);
|
|
362
|
+
clearButton.dispatchEvent(new MouseEvent("click"));
|
|
363
|
+
|
|
364
|
+
await Promise.resolve();
|
|
365
|
+
|
|
366
|
+
expect(input.value).toEqual("");
|
|
367
|
+
});
|
|
323
368
|
});
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
flex-grow: 1;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
.input-container.has-
|
|
54
|
+
.input-container.has-floating-item {
|
|
55
55
|
padding-right: var(--dx-g-spacing-sm);
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -150,3 +150,15 @@ input::placeholder {
|
|
|
150
150
|
margin-top: var(--dx-g-spacing-2xs);
|
|
151
151
|
display: block;
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
.clear-button > dx-icon {
|
|
155
|
+
--dx-c-icon-color: var(--sds-g-gray-9);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.clear-button > dx-icon::part(svg) {
|
|
159
|
+
transition: var(--dx-g-transition-hue-1x);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.clear-button:hover > dx-icon {
|
|
163
|
+
--dx-c-icon-color: var(--dx-g-blue-vibrant-30);
|
|
164
|
+
}
|
|
@@ -25,7 +25,15 @@
|
|
|
25
25
|
type={type}
|
|
26
26
|
value={value}
|
|
27
27
|
/>
|
|
28
|
-
<
|
|
28
|
+
<button
|
|
29
|
+
class="clear-button"
|
|
30
|
+
aria-label="Clear"
|
|
31
|
+
if:true={showClearButton}
|
|
32
|
+
onclick={clear}
|
|
33
|
+
>
|
|
34
|
+
<dx-icon symbol="clear" size="medium"></dx-icon>
|
|
35
|
+
</button>
|
|
36
|
+
<div class="shortcut" if:true={showShortcutKey}>
|
|
29
37
|
<img src={shortcutImgSrc} alt={shortcutImgAlt} />
|
|
30
38
|
<span>{shortcutKey}</span>
|
|
31
39
|
</div>
|
|
@@ -19,8 +19,11 @@ export const Base = () => html`
|
|
|
19
19
|
margin-bottom: 25px;
|
|
20
20
|
margin-top: 25px;
|
|
21
21
|
`)}
|
|
22
|
+
|
|
22
23
|
<dx-input placeholder="Example"></dx-input>
|
|
23
24
|
|
|
25
|
+
<dx-input placeholder="Example Clearable" clearable="true"></dx-input>
|
|
26
|
+
|
|
24
27
|
<dx-input placeholder="Example Large" size="large"></dx-input>
|
|
25
28
|
|
|
26
29
|
<dx-input placeholder="Example with Icon" icon-symbol="search"></dx-input>
|
|
@@ -20,6 +20,7 @@ const typeValidatorMap: ValidatorMap = {
|
|
|
20
20
|
export default class Input extends LightningElement {
|
|
21
21
|
@api ariaLabel!: string;
|
|
22
22
|
@api autocomplete: string | null = null;
|
|
23
|
+
@api clearable: boolean = false;
|
|
23
24
|
@api disabled: boolean = false;
|
|
24
25
|
@api iconSize: IconSize = "medium";
|
|
25
26
|
@api iconSymbol: IconSymbol | null = null;
|
|
@@ -84,6 +85,14 @@ export default class Input extends LightningElement {
|
|
|
84
85
|
private _value: string | null = null;
|
|
85
86
|
private _loading: boolean = false;
|
|
86
87
|
|
|
88
|
+
private get showClearButton(): boolean {
|
|
89
|
+
return this.clearable && !!this.value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private get showShortcutKey(): boolean {
|
|
93
|
+
return !this.showClearButton && !!this.shortcutKey;
|
|
94
|
+
}
|
|
95
|
+
|
|
87
96
|
private get ariaKeyShortcuts() {
|
|
88
97
|
return this.shortcutKey ? `meta+${this.shortcutKey}` : null;
|
|
89
98
|
}
|
|
@@ -113,7 +122,8 @@ export default class Input extends LightningElement {
|
|
|
113
122
|
return cx(
|
|
114
123
|
"input-container",
|
|
115
124
|
this.focused && "state-focused",
|
|
116
|
-
this.
|
|
125
|
+
(this.showClearButton || this.showShortcutKey) &&
|
|
126
|
+
"has-floating-item",
|
|
117
127
|
!!this.errorMessage && "has-error"
|
|
118
128
|
);
|
|
119
129
|
}
|
|
@@ -174,45 +184,46 @@ export default class Input extends LightningElement {
|
|
|
174
184
|
if (!e || !e.target) {
|
|
175
185
|
return;
|
|
176
186
|
}
|
|
177
|
-
this.
|
|
178
|
-
this.dispatchChange();
|
|
187
|
+
this.dispatchChange((<HTMLInputElement>e.target).value || "");
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
onButtonClick(e: Event) {
|
|
182
191
|
if (!this.buttonDisabled) {
|
|
183
192
|
e.stopPropagation();
|
|
184
193
|
}
|
|
185
|
-
this.
|
|
194
|
+
this.submit();
|
|
186
195
|
}
|
|
187
196
|
|
|
188
|
-
dispatchChange() {
|
|
197
|
+
dispatchChange(value: string) {
|
|
198
|
+
this._value = value;
|
|
189
199
|
this.dispatchEvent(new CustomEvent("change", { detail: this.value }));
|
|
190
200
|
}
|
|
191
201
|
|
|
192
|
-
|
|
193
|
-
|
|
202
|
+
clear(e?: Event) {
|
|
203
|
+
if (e) {
|
|
204
|
+
e.stopPropagation();
|
|
205
|
+
}
|
|
206
|
+
this.dispatchChange("");
|
|
207
|
+
this.dispatchEvent(new CustomEvent("clear"));
|
|
208
|
+
if (this.input) {
|
|
209
|
+
this.input.blur();
|
|
210
|
+
}
|
|
194
211
|
}
|
|
195
212
|
|
|
196
|
-
|
|
213
|
+
submit = async () => {
|
|
197
214
|
if (this.inputInvalid) {
|
|
198
215
|
return;
|
|
199
216
|
}
|
|
200
|
-
this.
|
|
217
|
+
this.dispatchEvent(new CustomEvent("submit", { detail: this.value }));
|
|
201
218
|
};
|
|
202
219
|
|
|
203
220
|
private onKeyDown(e: KeyboardEvent) {
|
|
204
221
|
switch (e.key) {
|
|
205
222
|
case "Escape":
|
|
206
|
-
this.
|
|
207
|
-
if (this.input) {
|
|
208
|
-
this.input.blur();
|
|
209
|
-
this._value = "";
|
|
210
|
-
this.dispatchChange();
|
|
211
|
-
}
|
|
223
|
+
this.clear();
|
|
212
224
|
break;
|
|
213
225
|
case "Enter":
|
|
214
|
-
this.
|
|
215
|
-
this.dispatchEvent(new CustomEvent("enter"));
|
|
226
|
+
this.submit();
|
|
216
227
|
break;
|
|
217
228
|
default:
|
|
218
229
|
}
|
|
@@ -212,23 +212,27 @@ export default class Popover extends LightningElement {
|
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
private onControlClick = () => {
|
|
215
|
-
if (this.controlled) {
|
|
215
|
+
if (this.controlled && this.open) {
|
|
216
216
|
this.dispatchRequestCloseEvent("click-control");
|
|
217
217
|
return;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
if (this.controlled && !this.open) {
|
|
221
|
+
this.dispatchEvent(
|
|
222
|
+
new CustomEvent("requestopen", {
|
|
223
|
+
bubbles: true,
|
|
224
|
+
composed: true
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
220
230
|
if (this.open) {
|
|
221
231
|
this.closePopover();
|
|
222
232
|
return;
|
|
223
233
|
}
|
|
224
234
|
|
|
225
235
|
this.openPopover();
|
|
226
|
-
this.dispatchEvent(
|
|
227
|
-
new CustomEvent("requestopen", {
|
|
228
|
-
bubbles: true,
|
|
229
|
-
composed: true
|
|
230
|
-
})
|
|
231
|
-
);
|
|
232
236
|
};
|
|
233
237
|
|
|
234
238
|
private onSlotChange(e: LightningSlotElement) {
|
|
@@ -29,18 +29,10 @@ describe(TAG, () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
const link = component.shadowRoot.querySelector("a");
|
|
32
|
-
expect(link).toBeNull();
|
|
33
|
-
|
|
34
|
-
const button: HTMLElement = component.shadowRoot.querySelector(
|
|
35
|
-
"button"
|
|
36
|
-
);
|
|
37
|
-
expect(button).not.toBeNull();
|
|
38
|
-
expect(button.getAttribute("aria-label")).toBe(
|
|
39
|
-
NODE_WITH_CHILDREN.label
|
|
40
|
-
);
|
|
32
|
+
expect(link).not.toBeNull();
|
|
41
33
|
|
|
42
34
|
const tile: TreeTile = component.shadowRoot.querySelector(
|
|
43
|
-
"
|
|
35
|
+
"a > dx-tree-tile"
|
|
44
36
|
);
|
|
45
37
|
expect(tile).not.toBeNull();
|
|
46
38
|
assertTile(tile, {
|
|
@@ -66,7 +58,7 @@ describe(TAG, () => {
|
|
|
66
58
|
});
|
|
67
59
|
|
|
68
60
|
const tile: TreeTile = component.shadowRoot.querySelector(
|
|
69
|
-
"
|
|
61
|
+
"a > dx-tree-tile"
|
|
70
62
|
);
|
|
71
63
|
expect(tile).not.toBeNull();
|
|
72
64
|
assertTile(tile, {
|
|
@@ -280,14 +272,12 @@ describe(TAG, () => {
|
|
|
280
272
|
component.addEventListener("tileselected", mockTileSelected);
|
|
281
273
|
component.addEventListener("expandcollapse", mockExpandCollapse);
|
|
282
274
|
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
);
|
|
286
|
-
expect(button).not.toBeNull();
|
|
287
|
-
button.click();
|
|
275
|
+
const link: HTMLElement = component.shadowRoot.querySelector("a");
|
|
276
|
+
expect(link).not.toBeNull();
|
|
277
|
+
link.click();
|
|
288
278
|
|
|
289
279
|
expect(track).toBeCalledTimes(1);
|
|
290
|
-
expect(track).toBeCalledWith(
|
|
280
|
+
expect(track).toBeCalledWith(link, GTM_EVENT_NAME, {
|
|
291
281
|
clickText: NODE_WITH_CHILDREN.label,
|
|
292
282
|
sideNavAction: "click"
|
|
293
283
|
});
|
|
@@ -328,11 +318,9 @@ describe(TAG, () => {
|
|
|
328
318
|
);
|
|
329
319
|
expect(tile).toHaveProperty("isSelected", true);
|
|
330
320
|
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
);
|
|
334
|
-
expect(button).not.toBeNull();
|
|
335
|
-
button.click();
|
|
321
|
+
const link: HTMLElement = component.shadowRoot.querySelector("a");
|
|
322
|
+
expect(link).not.toBeNull();
|
|
323
|
+
link.click();
|
|
336
324
|
|
|
337
325
|
return Promise.resolve().then(() => {
|
|
338
326
|
expect(tile).toHaveProperty("isSelected", true);
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<dx-tree-tile
|
|
4
|
-
is-root={isRoot}
|
|
5
|
-
tree-node={treeNode}
|
|
6
|
-
is-expanded={isExpanded}
|
|
7
|
-
has-children={hasChildren}
|
|
8
|
-
is-selected={isSelected}
|
|
9
|
-
oniconclick={onIconClick}
|
|
10
|
-
></dx-tree-tile>
|
|
11
|
-
</button>
|
|
12
|
-
<a if:true={href} href={href} target={target} onclick={onLinkClick}>
|
|
2
|
+
<a href={href} target={target} onclick={onLinkClick}>
|
|
13
3
|
<dx-tree-tile
|
|
4
|
+
href={href}
|
|
14
5
|
is-root={isRoot}
|
|
15
6
|
tree-node={treeNode}
|
|
16
7
|
is-expanded={isExpanded}
|
|
@@ -39,7 +39,11 @@ export default class TreeItem extends LightningElement {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
private get href(): string | undefined {
|
|
42
|
-
return
|
|
42
|
+
return (
|
|
43
|
+
(this._treeNode.link && this._treeNode.link.href) ||
|
|
44
|
+
this._treeNode.name ||
|
|
45
|
+
""
|
|
46
|
+
);
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
private get target(): string {
|
|
@@ -62,7 +66,11 @@ export default class TreeItem extends LightningElement {
|
|
|
62
66
|
this.sendGtm(event, this.isExpanded ? "expand" : "collapse");
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
private
|
|
69
|
+
private onLinkClick(event: Event): void {
|
|
70
|
+
if (!(this._treeNode.link && this._treeNode.link.href)) {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
}
|
|
73
|
+
|
|
66
74
|
if (this.hasChildren) {
|
|
67
75
|
this.doExpand();
|
|
68
76
|
}
|
|
@@ -71,10 +79,6 @@ export default class TreeItem extends LightningElement {
|
|
|
71
79
|
this.fireEvent("tileselected", { name: this._treeNode.name });
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
private onLinkClick(event: Event): void {
|
|
75
|
-
this.sendGtm(event, "click");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
82
|
private doExpand(): void {
|
|
79
83
|
this.isExpanded = !this.isExpanded;
|
|
80
84
|
this.fireEvent("expandcollapse");
|
|
@@ -23,14 +23,15 @@ const assertLevelAndLabel = (container: HTMLElement) => {
|
|
|
23
23
|
describe(TAG, () => {
|
|
24
24
|
describe("render", () => {
|
|
25
25
|
it("should render as a leaf tile", () => {
|
|
26
|
-
const component = render({ treeNode: SINGLE_NODE });
|
|
27
|
-
const
|
|
28
|
-
"
|
|
26
|
+
const component = render({ treeNode: SINGLE_NODE, href: "/test" });
|
|
27
|
+
const tileAnchor: HTMLElement = component.shadowRoot.querySelector(
|
|
28
|
+
"a.tile"
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
-
expect(
|
|
32
|
-
expect(
|
|
33
|
-
|
|
31
|
+
expect(tileAnchor.getAttribute("href")).toBe("/test");
|
|
32
|
+
expect(tileAnchor.classList).toHaveLength(1);
|
|
33
|
+
expect(tileAnchor).not.toBeNull();
|
|
34
|
+
assertLevelAndLabel(tileAnchor);
|
|
34
35
|
|
|
35
36
|
const arrowIcon: Icon = component.shadowRoot.querySelector(
|
|
36
37
|
"dx-icon"
|
|
@@ -51,12 +52,12 @@ describe(TAG, () => {
|
|
|
51
52
|
isRoot: true,
|
|
52
53
|
hasChildren: true
|
|
53
54
|
});
|
|
54
|
-
const
|
|
55
|
-
"
|
|
55
|
+
const tileAnchor: HTMLElement = component.shadowRoot.querySelector(
|
|
56
|
+
"a.tile.tile-root"
|
|
56
57
|
);
|
|
57
|
-
expect(
|
|
58
|
-
expect(
|
|
59
|
-
assertLevelAndLabel(
|
|
58
|
+
expect(tileAnchor).not.toBeNull();
|
|
59
|
+
expect(tileAnchor.classList).toHaveLength(2);
|
|
60
|
+
assertLevelAndLabel(tileAnchor);
|
|
60
61
|
|
|
61
62
|
const arrowIcon: Icon = component.shadowRoot.querySelector(
|
|
62
63
|
"dx-icon"
|
|
@@ -79,13 +80,13 @@ describe(TAG, () => {
|
|
|
79
80
|
hasChildren: true,
|
|
80
81
|
isExpanded: true
|
|
81
82
|
});
|
|
82
|
-
const
|
|
83
|
-
"
|
|
83
|
+
const tileAnchor: HTMLElement = component.shadowRoot.querySelector(
|
|
84
|
+
"a.tile.tile-with-children"
|
|
84
85
|
);
|
|
85
86
|
|
|
86
|
-
expect(
|
|
87
|
-
expect(
|
|
88
|
-
assertLevelAndLabel(
|
|
87
|
+
expect(tileAnchor).not.toBeNull();
|
|
88
|
+
expect(tileAnchor.classList).toHaveLength(2);
|
|
89
|
+
assertLevelAndLabel(tileAnchor);
|
|
89
90
|
|
|
90
91
|
const arrowIcon: Icon = component.shadowRoot.querySelector(
|
|
91
92
|
"dx-icon"
|
|
@@ -107,11 +108,11 @@ describe(TAG, () => {
|
|
|
107
108
|
treeNode: SINGLE_NODE,
|
|
108
109
|
isSelected: true
|
|
109
110
|
});
|
|
110
|
-
const
|
|
111
|
-
"
|
|
111
|
+
const tileAnchor: HTMLElement = component.shadowRoot.querySelector(
|
|
112
|
+
"a.tile.tile-selected"
|
|
112
113
|
);
|
|
113
|
-
expect(
|
|
114
|
-
expect(
|
|
114
|
+
expect(tileAnchor).not.toBeNull();
|
|
115
|
+
expect(tileAnchor.classList).toHaveLength(2);
|
|
115
116
|
|
|
116
117
|
return expect(component).toBeAccessible();
|
|
117
118
|
});
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
font-size: var(--dx-g-text-sm);
|
|
35
35
|
font-weight: var(--dx-g-font-demi);
|
|
36
36
|
color: var(--dx-g-blue-vibrant-30);
|
|
37
|
+
display: block;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
.tile-selected {
|
|
@@ -188,3 +189,18 @@ dx-type-badge.delete {
|
|
|
188
189
|
--dx-c-type-badge-color: var(--dx-g-red-vibrant-40);
|
|
189
190
|
--dx-c-type-badge-background: var(--dx-g-hot-orange-vibrant-90);
|
|
190
191
|
}
|
|
192
|
+
|
|
193
|
+
a {
|
|
194
|
+
display: block;
|
|
195
|
+
text-decoration: none;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
a:link,
|
|
199
|
+
a:visited {
|
|
200
|
+
color: var(--dx-g-gray-10);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
a:visited.tile-root,
|
|
204
|
+
a:link.tile-root {
|
|
205
|
+
color: var(--dx-g-blue-vibrant-30);
|
|
206
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<a href={href} class={tileClass}>
|
|
3
3
|
<div class="flex indentation" aria-level={tileAriaLevel}>
|
|
4
4
|
<span class="tile-label truncate" title={treeNode.label}>
|
|
5
5
|
{treeNode.label}
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
value={methodText}
|
|
21
21
|
></dx-type-badge>
|
|
22
22
|
</div>
|
|
23
|
-
</
|
|
23
|
+
</a>
|
|
24
24
|
</template>
|