@salesforcedevs/dx-components 0.21.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/lwc.config.json +2 -0
  2. package/package.json +2 -2
  3. package/src/assets/svg/slack-cta-blog-background-left.svg +14 -0
  4. package/src/assets/svg/slack-cta-blog-background-right.svg +11 -0
  5. package/src/assets/svg/slack-cta-podcast-background-left.svg +28 -0
  6. package/src/assets/svg/slack-cta-podcast-background-right.svg +38 -0
  7. package/src/assets/svg/slack-logo.svg +6 -0
  8. package/src/modules/dx/cardExpanded/__tests__/cardExpanded.test.ts +193 -0
  9. package/src/modules/dx/cardExpanded/__tests__/mockProps.ts +21 -0
  10. package/src/modules/dx/cardExpanded/cardExpanded.css +49 -0
  11. package/src/modules/dx/cardExpanded/cardExpanded.html +37 -0
  12. package/src/modules/dx/cardExpanded/cardExpanded.stories.ts +59 -0
  13. package/src/modules/dx/cardExpanded/cardExpanded.ts +45 -0
  14. package/src/modules/dx/cardMinimal/__tests__/cardMinimal.test.ts +87 -0
  15. package/src/modules/dx/cardMinimal/__tests__/mockProps.ts +10 -0
  16. package/src/modules/dx/cardMinimal/cardMinimal.css +31 -0
  17. package/src/modules/dx/cardMinimal/cardMinimal.html +13 -0
  18. package/src/modules/dx/cardMinimal/cardMinimal.stories.ts +32 -0
  19. package/src/modules/dx/cardMinimal/cardMinimal.ts +3 -0
  20. package/src/modules/dx/codeBlock/__tests__/codeBlock.test.ts +0 -4
  21. package/src/modules/dx/grid/__tests__/grid.test.ts +2 -7
  22. package/src/modules/dx/headerSearch/__tests__/headerSearch.test.ts +0 -1
  23. package/src/modules/dx/imageAndLabel/imageAndLabel.html +14 -2
  24. package/src/modules/dx/popover/__tests__/popover.test.ts +26 -37
  25. package/src/modules/dx/sidebar/__tests__/sidebar.test.ts +0 -4
  26. package/src/modules/dx/sidebarOld/__tests__/sidebarOld.test.ts +0 -4
  27. package/src/modules/dx/sidebarSearch/sidebarSearch.html +2 -2
  28. package/src/modules/dx/slackAdd/__tests__/slackAdd.test.ts +52 -0
  29. package/src/modules/dx/slackAdd/slackAdd.css +93 -0
  30. package/src/modules/dx/slackAdd/slackAdd.html +17 -0
  31. package/src/modules/dx/slackAdd/slackAdd.stories.ts +25 -0
  32. package/src/modules/dx/slackAdd/slackAdd.ts +13 -0
  33. package/src/modules/dx/socials/__tests__/socials.test.ts +12 -22
  34. package/src/modules/dx/topics/__tests__/mockProps.ts +3 -0
  35. package/src/modules/dx/topics/__tests__/topics.test.ts +31 -0
  36. package/src/modules/dx/topics/topics.css +25 -0
  37. package/src/modules/dx/topics/topics.html +12 -0
  38. package/src/modules/dx/topics/topics.ts +25 -0
  39. package/src/modules/dx/tree/__tests__/tree.test.ts +17 -28
  40. package/src/modules/dx/treeItem/__tests__/treeItem.test.ts +36 -59
  41. package/src/modules/dx/typeBadge/typeBadge.css +1 -1
  42. package/src/modules/helpers/text/text.css +7 -0
  43. package/src/modules/utils/archiveCard/archiveCard.ts +35 -0
  44. package/src/modules/utils/normalizers/normalizers.ts +2 -2
@@ -0,0 +1,31 @@
1
+ @import "helpers/reset";
2
+ @import "helpers/text";
3
+
4
+ :host {
5
+ display: block;
6
+ }
7
+
8
+ a {
9
+ --dx-c-max-lines: var(--dx-c-title-max-lines, 3);
10
+
11
+ color: var(--dx-g-blue-vibrant-50);
12
+ flex-grow: 1;
13
+ font-family: var(--dx-g-font-sans);
14
+ font-size: var(--dx-g-text-sm);
15
+ font-weight: var(--dx-g-font-demi);
16
+ }
17
+
18
+ a:hover {
19
+ color: var(--dx-g-blue-vibrant-40);
20
+ }
21
+
22
+ a + * {
23
+ margin-left: var(--dx-g-spacing-sm);
24
+ }
25
+
26
+ .card-container {
27
+ align-items: center;
28
+ border-bottom: 1px solid var(--sds-g-gray-4);
29
+ display: flex;
30
+ padding: var(--dx-g-spacing-smd) 0;
31
+ }
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <div class="card-container">
3
+ <a class="dx-text-truncate" href={href} target={target}>
4
+ <h4>{title}</h4>
5
+ </a>
6
+ <dx-type-badge
7
+ color={badgeColor}
8
+ size={badgeSize}
9
+ type="content-type"
10
+ value={contentType}
11
+ ></dx-type-badge>
12
+ </div>
13
+ </template>
@@ -0,0 +1,32 @@
1
+ import { html } from "lit-html";
2
+ import { blogPostMock, podcastEpisodeMock } from "./__tests__/mockProps";
3
+
4
+ export default {
5
+ title: "dx/dx-card-minimal",
6
+ component: "dx-card-minimal"
7
+ };
8
+
9
+ export const BlogPost = (args: { title: string; href: string }) => html`
10
+ <dx-card-minimal
11
+ title="${args.title}"
12
+ href="${args.href}"
13
+ ></dx-card-minimal>
14
+ `;
15
+
16
+ BlogPost.args = {
17
+ title: blogPostMock.title,
18
+ href: blogPostMock.href
19
+ };
20
+
21
+ export const PodcastEpisode = (args: { title: string; href: string }) => html`
22
+ <dx-card-minimal
23
+ title="${args.title}"
24
+ href="${args.href}"
25
+ content-type="${podcastEpisodeMock.contentType}"
26
+ ></dx-card-minimal>
27
+ `;
28
+
29
+ PodcastEpisode.args = {
30
+ title: podcastEpisodeMock.title,
31
+ href: podcastEpisodeMock.href
32
+ };
@@ -0,0 +1,3 @@
1
+ import { ArchiveCard } from "utils/archiveCard";
2
+
3
+ export default class MinimalCard extends ArchiveCard {}
@@ -25,10 +25,6 @@ describe(TAG, () => {
25
25
  const oldNavigator = window.navigator;
26
26
  const oldError = console.error;
27
27
 
28
- afterEach(() => {
29
- jest.clearAllMocks();
30
- });
31
-
32
28
  afterAll(() => {
33
29
  Object.assign(window.navigator, oldNavigator);
34
30
  Object.assign(console.error, oldError);
@@ -5,10 +5,6 @@ const TAG = "dx-grid";
5
5
  const render = createRenderComponent(TAG, Grid);
6
6
 
7
7
  describe(TAG, () => {
8
- afterEach(() => {
9
- jest.clearAllMocks();
10
- });
11
-
12
8
  [
13
9
  [4, "four", "dynamic"],
14
10
  [8, "four", "dynamic"],
@@ -21,9 +17,8 @@ describe(TAG, () => {
21
17
  ].forEach(([numElements, calculatedColumns, columns]) =>
22
18
  it(`choses ${calculatedColumns} columns when there are ${numElements} elements`, () => {
23
19
  const element = render({ columns });
24
- const slot: HTMLSlotElement = element.shadowRoot.querySelector(
25
- "slot"
26
- );
20
+ const slot: HTMLSlotElement =
21
+ element.shadowRoot.querySelector("slot");
27
22
  const div = document.createElement("div");
28
23
  slot.assignedElements = jest
29
24
  .fn()
@@ -89,7 +89,6 @@ describe(TAG, () => {
89
89
  });
90
90
 
91
91
  afterEach(() => {
92
- jest.clearAllMocks();
93
92
  subscribeCallback = null;
94
93
  });
95
94
 
@@ -1,10 +1,22 @@
1
1
  <template>
2
2
  <div if:false={href} class="container">
3
- <img class={imgClass} src={imgSrc} alt={imgAlt} part="image" />
3
+ <img
4
+ if:true={imgSrc}
5
+ class={imgClass}
6
+ src={imgSrc}
7
+ alt={imgAlt}
8
+ part="image"
9
+ />
4
10
  <span class="dx-text-label-1-dark" part="label">{label}</span>
5
11
  </div>
6
12
  <a if:true={href} href={href} class="container">
7
- <img class={imgClass} src={imgSrc} alt={imgAlt} part="image" />
13
+ <img
14
+ if:true={imgSrc}
15
+ class={imgClass}
16
+ src={imgSrc}
17
+ alt={imgAlt}
18
+ part="image"
19
+ />
8
20
  <span class="dx-text-label-1-dark" part="label">{label}</span>
9
21
  </a>
10
22
  </template>
@@ -16,10 +16,6 @@ describe(TAG, () => {
16
16
  global.console = { ...global.console, error: jest.fn() };
17
17
  });
18
18
 
19
- afterEach(() => {
20
- jest.clearAllMocks();
21
- });
22
-
23
19
  afterAll(() => {
24
20
  global.console = originalConsole;
25
21
  });
@@ -29,15 +25,13 @@ describe(TAG, () => {
29
25
 
30
26
  expect(console.error).not.toBeCalled();
31
27
 
32
- const menuEl: HTMLElement = component.shadowRoot.querySelector(
33
- ".popover-container"
34
- );
28
+ const menuEl: HTMLElement =
29
+ component.shadowRoot.querySelector(".popover-container");
35
30
  expect(menuEl).not.toBeNull();
36
31
  expect(menuEl.classList).toHaveLength(1);
37
32
 
38
- const popoverDiv: HTMLElement = menuEl.querySelector(
39
- "div.popover"
40
- )!;
33
+ const popoverDiv: HTMLElement =
34
+ menuEl.querySelector("div.popover")!;
41
35
  expect(popoverDiv).not.toBeNull();
42
36
  expect(popoverDiv.classList).toHaveLength(1);
43
37
 
@@ -47,15 +41,13 @@ describe(TAG, () => {
47
41
  it("renders with api properties", () => {
48
42
  const component = render(mockProps);
49
43
  expect(console.error).not.toBeCalled();
50
- const menuEl: HTMLElement = component.shadowRoot.querySelector(
51
- ".popover-container"
52
- );
44
+ const menuEl: HTMLElement =
45
+ component.shadowRoot.querySelector(".popover-container");
53
46
  expect(menuEl).not.toBeNull();
54
47
  expect(menuEl.classList).toHaveLength(1);
55
48
 
56
- const popoverDiv: HTMLElement = menuEl.querySelector(
57
- "div.popover"
58
- )!;
49
+ const popoverDiv: HTMLElement =
50
+ menuEl.querySelector("div.popover")!;
59
51
  expect(popoverDiv).not.toBeNull();
60
52
  expect(popoverDiv.classList).toHaveLength(4);
61
53
  expect(popoverDiv.classList).toContain(
@@ -74,9 +66,8 @@ describe(TAG, () => {
74
66
 
75
67
  expect(console.error).not.toBeCalled();
76
68
 
77
- const popoverDiv: HTMLElement = component.shadowRoot.querySelector(
78
- "div.popover"
79
- );
69
+ const popoverDiv: HTMLElement =
70
+ component.shadowRoot.querySelector("div.popover");
80
71
  expect(popoverDiv.classList).toHaveLength(2);
81
72
  expect(popoverDiv.classList).toContain("popover-fullwidth");
82
73
  });
@@ -89,9 +80,8 @@ describe(TAG, () => {
89
80
  'The "width" and "full-width" properties cannot be provided simultaneously.'
90
81
  );
91
82
 
92
- const popoverDiv: HTMLElement = component.shadowRoot.querySelector(
93
- "div.popover"
94
- );
83
+ const popoverDiv: HTMLElement =
84
+ component.shadowRoot.querySelector("div.popover");
95
85
  expect(popoverDiv.style).not.toBeNull();
96
86
  expect(popoverDiv.classList).toHaveLength(5);
97
87
  expect(popoverDiv.classList).toContain(
@@ -120,9 +110,8 @@ describe(TAG, () => {
120
110
 
121
111
  it("doesn't render menu content before interaction", async () => {
122
112
  const component = render();
123
- const content = component.shadowRoot.querySelector(
124
- ".popover-content"
125
- );
113
+ const content =
114
+ component.shadowRoot.querySelector(".popover-content");
126
115
 
127
116
  expect(content).toBeNull();
128
117
 
@@ -130,9 +119,8 @@ describe(TAG, () => {
130
119
 
131
120
  await Promise.resolve();
132
121
 
133
- const contentAfterOpen = component.shadowRoot.querySelector(
134
- ".popover-content"
135
- );
122
+ const contentAfterOpen =
123
+ component.shadowRoot.querySelector(".popover-content");
136
124
 
137
125
  expect(contentAfterOpen).not.toBeNull();
138
126
 
@@ -145,9 +133,8 @@ describe(TAG, () => {
145
133
 
146
134
  const renderComponent = createRenderComponent(TAG, Popover);
147
135
  const component = renderComponent();
148
- const content = component.shadowRoot.querySelector(
149
- ".popover-content"
150
- );
136
+ const content =
137
+ component.shadowRoot.querySelector(".popover-content");
151
138
 
152
139
  expect(content).not.toBeNull();
153
140
  });
@@ -183,9 +170,12 @@ describe(TAG, () => {
183
170
  it("doesn't fire open if passed control is disabled", () => {
184
171
  const element = render();
185
172
 
186
- const { slot: controlSlot, slottedElement } = mockSlottedElement<
187
- HTMLButtonElement
188
- >(element, "button", "control");
173
+ const { slot: controlSlot, slottedElement } =
174
+ mockSlottedElement<HTMLButtonElement>(
175
+ element,
176
+ "button",
177
+ "control"
178
+ );
189
179
  expect(controlSlot.assignedElements).toBeCalledTimes(1);
190
180
  expect(slottedElement.ariaHasPopup).toBe("true");
191
181
  expect(slottedElement.style.cursor).toBe("cursor");
@@ -237,9 +227,8 @@ describe(TAG, () => {
237
227
  const closeEvent = jest.fn();
238
228
  component.addEventListener("close", closeEvent);
239
229
 
240
- const popoverDiv: HTMLElement = component.shadowRoot.querySelector(
241
- ".popover"
242
- );
230
+ const popoverDiv: HTMLElement =
231
+ component.shadowRoot.querySelector(".popover");
243
232
  expect(popoverDiv).not.toBeNull();
244
233
 
245
234
  expect(component.open).toBe(true);
@@ -25,10 +25,6 @@ const GTM_EVENT_NAME = "custEv_leftNavLinkClick";
25
25
  const TOGGLE_BUTTON_LABEL = "Toggle Sidebar";
26
26
 
27
27
  describe(TAG, () => {
28
- afterEach(() => {
29
- jest.clearAllMocks();
30
- });
31
-
32
28
  describe("desktop", () => {
33
29
  beforeEach(() => {
34
30
  unmockQueryParameter();
@@ -19,10 +19,6 @@ const GTM_EVENT_NAME = "custEv_leftNavLinkClick";
19
19
  const TOGGLE_BUTTON_LABEL = "Toggle Sidebar";
20
20
 
21
21
  describe(TAG, () => {
22
- afterEach(() => {
23
- jest.clearAllMocks();
24
- });
25
-
26
22
  describe("desktop", () => {
27
23
  describe("component render", () => {
28
24
  it("should render as expanded with all the elements that sidebar includes", () => {
@@ -10,9 +10,9 @@
10
10
  <dx-input
11
11
  class="search-box"
12
12
  type="search"
13
- placeholder="Search this list..."
13
+ placeholder="Search"
14
14
  role="search"
15
- aria-label="Content Search"
15
+ aria-label="Search in this guide"
16
16
  icon-symbol="search"
17
17
  size="small"
18
18
  value={value}
@@ -0,0 +1,52 @@
1
+ import slackAdd from "dx/slackAdd";
2
+ import { createRenderComponent } from "utils/tests";
3
+
4
+ const TAG = "dx-slack-add";
5
+ const render = createRenderComponent(TAG, slackAdd);
6
+
7
+ describe(TAG, () => {
8
+ it("renders the button linking to slack", () => {
9
+ const element = render();
10
+
11
+ const slackBtn = element.shadowRoot.querySelectorAll(".slack-btn a");
12
+ expect(slackBtn).not.toBe(undefined);
13
+ return expect(slackBtn.length).toBe(1);
14
+ });
15
+
16
+ it("renders a button meeting all accesibilty requirements", () => {
17
+ const element = render();
18
+ return expect(element).toBeAccessible();
19
+ });
20
+
21
+ it("renders the inner button text correctly", () => {
22
+ const element = render({
23
+ innerText: "foobar"
24
+ });
25
+
26
+ const header = element.shadowRoot
27
+ .querySelectorAll(".container")
28
+ .item(0);
29
+ expect(header.textContent).toBe("Follow along with Slackfoobar");
30
+
31
+ const slackBtn = element.shadowRoot
32
+ .querySelectorAll(".slack-btn a")
33
+ .item(0);
34
+ return expect(slackBtn.textContent).toBe("foobar");
35
+ });
36
+
37
+ it("renders the header text correctly", () => {
38
+ const element = render({
39
+ headerText: "foobar"
40
+ });
41
+
42
+ const slackBtn = element.shadowRoot
43
+ .querySelectorAll(".slack-btn a")
44
+ .item(0);
45
+ expect(slackBtn.textContent).toBe("Add to Slack");
46
+
47
+ const header = element.shadowRoot
48
+ .querySelectorAll(".container")
49
+ .item(0);
50
+ return expect(header.textContent).toBe("foobarAdd to Slack");
51
+ });
52
+ });
@@ -0,0 +1,93 @@
1
+ @import "helpers/reset";
2
+ @import "helpers/text";
3
+
4
+ .container {
5
+ background-repeat: no-repeat;
6
+ display: flex;
7
+ flex-direction: column;
8
+ flex-wrap: wrap;
9
+ align-items: center;
10
+ justify-content: center;
11
+ text-align: center;
12
+ }
13
+
14
+ .slack-cta-button {
15
+ align-items: center;
16
+ background-color: #fff;
17
+ border: 2px solid rgb(1, 118, 211);
18
+ border-radius: 4px;
19
+ display: inline-flex;
20
+ height: 48px;
21
+ justify-content: center;
22
+ text-decoration: none;
23
+ padding: 10px;
24
+ color: rgb(1, 118, 211);
25
+ font-size: 14px;
26
+ font-weight: 500;
27
+ letter-spacing: 0;
28
+ line-height: 20px;
29
+ }
30
+
31
+ .podcast {
32
+ background-image: url("/assets/svg/slack-cta-podcast-background-left.svg"),
33
+ url("/assets/svg/slack-cta-podcast-background-right.svg");
34
+ background-size: 40% 40%;
35
+ background-position: 5% 25%, 95% 75%;
36
+ }
37
+
38
+ .blog {
39
+ background-image: url("/assets/svg/slack-cta-blog-background-left.svg"),
40
+ url("/assets/svg/slack-cta-blog-background-right.svg");
41
+ background-position: 20% 25%, 80% 75%;
42
+ }
43
+
44
+ .dx-text-heading-4 {
45
+ font-size: var(--dx-g-text-lg);
46
+ max-width: 40%;
47
+ }
48
+
49
+ @media screen and (max-width: 768px) {
50
+ .container {
51
+ background-position: -100% 25%, 180% 75%;
52
+ }
53
+
54
+ .podcast.container {
55
+ background-size: 75% 60%;
56
+ }
57
+
58
+ .blog.container {
59
+ background-size: 75% 20%;
60
+ }
61
+
62
+ .dx-text-heading-4 {
63
+ max-width: 28ex;
64
+ font-size: var(--dx-g-text-lg);
65
+ }
66
+
67
+ .slack-btn a {
68
+ display: inline-flex;
69
+ margin: 4px auto;
70
+ }
71
+
72
+ .slack-btn img {
73
+ height: 20px;
74
+ padding-right: 10px;
75
+ }
76
+ }
77
+
78
+ @media screen and (min-width: 768px) {
79
+ .container {
80
+ min-height: 242px;
81
+ padding-top: 22px;
82
+ }
83
+
84
+ .slack-btn a {
85
+ display: inline-flex;
86
+ margin: 22px 13px 8px 13px;
87
+ }
88
+
89
+ .slack-btn img {
90
+ height: 20px;
91
+ padding-right: 10px;
92
+ }
93
+ }
@@ -0,0 +1,17 @@
1
+ <template>
2
+ <div class={containerClass}>
3
+ <h2 class="dx-text-heading-4">{headerText}</h2>
4
+ <div class="slack-btn">
5
+ <a
6
+ class="slack-cta-button"
7
+ href="https://slack.com/oauth/v2/authorize?scope=chat%3Awrite%2Ccommands&amp;user_scope=&amp;redirect_uri=https%3A%2F%2Fdsc-slack-app.herokuapp.com%2Fslack%2Foauth_redirect&amp;client_id=1814240140212.2864842743411"
8
+ >
9
+ <img
10
+ src="/assets/svg/slack-logo.svg"
11
+ alt="install slack extension"
12
+ />
13
+ {innerText}
14
+ </a>
15
+ </div>
16
+ </div>
17
+ </template>
@@ -0,0 +1,25 @@
1
+ export default {
2
+ title: "dx/dx-slack-add",
3
+ component: "dx-slack-add"
4
+ };
5
+
6
+ const createStyles = () => `
7
+ <style>
8
+ h2 {
9
+ font-weight: bold;
10
+ font-size: 24px;
11
+ margin: 10px 0 30px 0;
12
+ }
13
+ </style>
14
+ `;
15
+
16
+ export const Base = () => `
17
+ ${createStyles()}
18
+ <h2>Add to Slack</h2>
19
+
20
+ <dx-slack-add header-text="Get notified of new episodes with the new Salesforce Developers Slack app" background="podcast"></dx-slack-add>
21
+
22
+ <dx-slack-add header-text="Get the latest Salesforce Developer posts exactly how you want." inner-text="Send posts to Slack" background="blog"></dx-slack-add>
23
+
24
+ <dx-slack-add header-text="Get notified of new events with the new Salesforce Developers Slack app"></dx-slack-add>
25
+ `;
@@ -0,0 +1,13 @@
1
+ import { LightningElement, api } from "lwc";
2
+ import { SlackCtaBackgroundVariant } from "typings/custom";
3
+ import cx from "classnames";
4
+
5
+ export default class SlackAdd extends LightningElement {
6
+ @api headerText?: string = "Follow along with Slack";
7
+ @api innerText?: string = "Add to Slack";
8
+ @api background?: SlackCtaBackgroundVariant;
9
+
10
+ get containerClass() {
11
+ return cx("container", this.background);
12
+ }
13
+ }
@@ -14,10 +14,6 @@ describe(TAG, () => {
14
14
  global.console = { ...global.console, error: jest.fn() };
15
15
  });
16
16
 
17
- afterEach(() => {
18
- jest.clearAllMocks();
19
- });
20
-
21
17
  afterAll(() => {
22
18
  global.console = originalConsole;
23
19
  });
@@ -25,9 +21,8 @@ describe(TAG, () => {
25
21
  it("renders dx-icon-badge for each social link", () => {
26
22
  const component = render();
27
23
 
28
- const links: Array<HTMLAnchorElement> = component.shadowRoot.querySelectorAll(
29
- "a"
30
- );
24
+ const links: Array<HTMLAnchorElement> =
25
+ component.shadowRoot.querySelectorAll("a");
31
26
  expect(links).toHaveLength(3);
32
27
 
33
28
  const [facebookLink, twitterLink, linkedinLink] = links;
@@ -50,9 +45,8 @@ describe(TAG, () => {
50
45
  expect(linkedinLink.target).toBe("_blank");
51
46
  expect(linkedinLink.title).toBe("Linkedin");
52
47
 
53
- const iconBadge: Array<IconBadge> = component.shadowRoot.querySelectorAll(
54
- "dx-icon-badge"
55
- );
48
+ const iconBadge: Array<IconBadge> =
49
+ component.shadowRoot.querySelectorAll("dx-icon-badge");
56
50
  expect(iconBadge).toHaveLength(3);
57
51
 
58
52
  const [facebookIcon, twitterIcon, linkedinIcon] = iconBadge;
@@ -66,9 +60,8 @@ describe(TAG, () => {
66
60
  expect(linkedinIcon.sprite).toBe("brand");
67
61
  expect(linkedinIcon.symbol).toBe("linkedin-in");
68
62
 
69
- const detailSection = component.shadowRoot.querySelector(
70
- ".details-section"
71
- );
63
+ const detailSection =
64
+ component.shadowRoot.querySelector(".details-section");
72
65
  expect(detailSection).toBeNull();
73
66
 
74
67
  return expect(component).toBeAccessible();
@@ -78,9 +71,8 @@ describe(TAG, () => {
78
71
  const expectedTarget = "_self";
79
72
  const component = render({ linksTarget: expectedTarget });
80
73
 
81
- const links: Array<HTMLAnchorElement> = component.shadowRoot.querySelectorAll(
82
- "a"
83
- );
74
+ const links: Array<HTMLAnchorElement> =
75
+ component.shadowRoot.querySelectorAll("a");
84
76
  expect(links).toHaveLength(3);
85
77
  links.forEach(({ target }) => expect(target).toBe(expectedTarget));
86
78
 
@@ -93,9 +85,8 @@ describe(TAG, () => {
93
85
  const testTarget = "not_valid_at_all";
94
86
  const component = render({ linksTarget: testTarget });
95
87
 
96
- const links: Array<HTMLAnchorElement> = component.shadowRoot.querySelectorAll(
97
- "a"
98
- );
88
+ const links: Array<HTMLAnchorElement> =
89
+ component.shadowRoot.querySelectorAll("a");
99
90
  expect(links).toHaveLength(3);
100
91
  links.forEach(({ target }) => expect(target).toBe("_blank"));
101
92
 
@@ -110,9 +101,8 @@ describe(TAG, () => {
110
101
  it("renders details section", () => {
111
102
  const component = render({ details: "Test|Another Test" });
112
103
 
113
- const details: Array<HTMLElement> = component.shadowRoot.querySelectorAll(
114
- ".details-section span"
115
- );
104
+ const details: Array<HTMLElement> =
105
+ component.shadowRoot.querySelectorAll(".details-section span");
116
106
  expect(details).toHaveLength(2);
117
107
 
118
108
  const [testDetail, anotherTestDetail] = details;
@@ -0,0 +1,3 @@
1
+ export default {
2
+ topics: ["mobile", "performance"]
3
+ };
@@ -0,0 +1,31 @@
1
+ import Topics from "dx/topics";
2
+ import { createRenderComponent } from "utils/tests";
3
+ import mockProps from "./mockProps";
4
+
5
+ const TAG = "dx-topics";
6
+ const render = createRenderComponent(TAG, Topics);
7
+ describe(TAG, () => {
8
+ it("renders", () => {
9
+ const component = render(mockProps);
10
+ expect(component.topics).toEqual(mockProps.topics);
11
+
12
+ const spans: Array<HTMLElement> =
13
+ component.shadowRoot.querySelectorAll("span");
14
+ expect(spans).toHaveLength(3);
15
+
16
+ const [expectedFirst, expectedSecond] = mockProps.topics;
17
+ const [first, dot, second] = spans;
18
+ expect(first.textContent).toBe(expectedFirst);
19
+ expect(second.textContent).toBe(expectedSecond);
20
+ expect(dot.classList).toContain("dot-separator");
21
+
22
+ return expect(component).toBeAccessible();
23
+ });
24
+
25
+ it("doesn't render any span if list is empty", () => {
26
+ const component = render();
27
+ const spans: Array<HTMLElement> =
28
+ component.shadowRoot.querySelectorAll("span");
29
+ expect(spans).toHaveLength(0);
30
+ });
31
+ });
@@ -0,0 +1,25 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ .container {
6
+ align-items: center;
7
+ color: var(--dx-g-blue-vibrant-20);
8
+ font-size: var(--dx-g-text-xs);
9
+ font-family: var(--dx-g-font-display);
10
+ font-weight: var(--dx-g-font-demi);
11
+ display: flex;
12
+ gap: var(--dx-g-spacing-sm);
13
+ letter-spacing: 1px;
14
+ text-transform: uppercase;
15
+ }
16
+
17
+ .dot-separator {
18
+ --size: 4px;
19
+
20
+ height: var(--size);
21
+ width: var(--size);
22
+ background-color: var(--dx-g-blue-vibrant-20);
23
+ border-radius: 100%;
24
+ margin-bottom: 3px;
25
+ }