@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 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.8.5-alpha1",
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
- onescape={onInputEscape}
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
- onescape={onInputEscape}
53
+ onclear={onInputEscape}
56
54
  onsubmit={onInputSubmit}
57
- onfocus={openPopover}
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.closePopover();
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.openPopover();
127
+ this.openDropdown();
128
128
  } else {
129
- this.closePopover();
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 openPopover() {
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 closePopover() {
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.closePopover();
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-shortcut {
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
- <div class="shortcut" if:true={shortcutKey}>
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.shortcutKey && "has-shortcut",
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._value = (<HTMLInputElement>e.target).value || "";
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.onSubmit();
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
- dispatchSubmit() {
193
- this.dispatchEvent(new CustomEvent("submit", { detail: this.value }));
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
- onSubmit = async () => {
213
+ submit = async () => {
197
214
  if (this.inputInvalid) {
198
215
  return;
199
216
  }
200
- this.dispatchSubmit();
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.dispatchEvent(new CustomEvent("escape"));
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.onSubmit();
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) {
@@ -15,7 +15,6 @@
15
15
  placeholder={inputPlaceholder}
16
16
  submit-label={inputSubmitLabel}
17
17
  onsubmit={onSubmit}
18
- onenter={onSubmit}
19
18
  size={size}
20
19
  ></dx-input>
21
20
  </div>
@@ -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
- "button > dx-tree-tile"
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
- "button > dx-tree-tile"
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 button: HTMLElement = component.shadowRoot.querySelector(
284
- "button"
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(button, GTM_EVENT_NAME, {
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 button: HTMLElement = component.shadowRoot.querySelector(
332
- "button"
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
- <button if:false={href} onclick={onButtonClick} aria-label={treeNode.label}>
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 this._treeNode.link && this._treeNode.link.href;
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 onButtonClick(event: Event): void {
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 tileDiv: HTMLElement = component.shadowRoot.querySelector(
28
- "div.tile"
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(tileDiv.classList).toHaveLength(1);
32
- expect(tileDiv).not.toBeNull();
33
- assertLevelAndLabel(tileDiv);
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 tileDiv: HTMLElement = component.shadowRoot.querySelector(
55
- "div.tile.tile-root"
55
+ const tileAnchor: HTMLElement = component.shadowRoot.querySelector(
56
+ "a.tile.tile-root"
56
57
  );
57
- expect(tileDiv).not.toBeNull();
58
- expect(tileDiv.classList).toHaveLength(2);
59
- assertLevelAndLabel(tileDiv);
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 tileDiv: HTMLElement = component.shadowRoot.querySelector(
83
- "div.tile.tile-with-children"
83
+ const tileAnchor: HTMLElement = component.shadowRoot.querySelector(
84
+ "a.tile.tile-with-children"
84
85
  );
85
86
 
86
- expect(tileDiv).not.toBeNull();
87
- expect(tileDiv.classList).toHaveLength(2);
88
- assertLevelAndLabel(tileDiv);
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 tileDiv: HTMLElement = component.shadowRoot.querySelector(
111
- "div.tile.tile-selected"
111
+ const tileAnchor: HTMLElement = component.shadowRoot.querySelector(
112
+ "a.tile.tile-selected"
112
113
  );
113
- expect(tileDiv).not.toBeNull();
114
- expect(tileDiv.classList).toHaveLength(2);
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
- <div class={tileClass}>
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
- </div>
23
+ </a>
24
24
  </template>
@@ -18,6 +18,7 @@ export default class TreeTile extends LightningElement {
18
18
  @api isExpanded = false;
19
19
  @api hasChildren = false;
20
20
  @api isSelected = false;
21
+ @api href!: string;
21
22
 
22
23
  get tileAriaLevel() {
23
24
  return this.treeNode.level + 2;