@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.
- package/lwc.config.json +2 -0
- package/package.json +2 -2
- package/src/assets/svg/slack-cta-blog-background-left.svg +14 -0
- package/src/assets/svg/slack-cta-blog-background-right.svg +11 -0
- package/src/assets/svg/slack-cta-podcast-background-left.svg +28 -0
- package/src/assets/svg/slack-cta-podcast-background-right.svg +38 -0
- package/src/assets/svg/slack-logo.svg +6 -0
- package/src/modules/dx/cardExpanded/__tests__/cardExpanded.test.ts +193 -0
- package/src/modules/dx/cardExpanded/__tests__/mockProps.ts +21 -0
- package/src/modules/dx/cardExpanded/cardExpanded.css +49 -0
- package/src/modules/dx/cardExpanded/cardExpanded.html +37 -0
- package/src/modules/dx/cardExpanded/cardExpanded.stories.ts +59 -0
- package/src/modules/dx/cardExpanded/cardExpanded.ts +45 -0
- package/src/modules/dx/cardMinimal/__tests__/cardMinimal.test.ts +87 -0
- package/src/modules/dx/cardMinimal/__tests__/mockProps.ts +10 -0
- package/src/modules/dx/cardMinimal/cardMinimal.css +31 -0
- package/src/modules/dx/cardMinimal/cardMinimal.html +13 -0
- package/src/modules/dx/cardMinimal/cardMinimal.stories.ts +32 -0
- package/src/modules/dx/cardMinimal/cardMinimal.ts +3 -0
- package/src/modules/dx/codeBlock/__tests__/codeBlock.test.ts +0 -4
- package/src/modules/dx/grid/__tests__/grid.test.ts +2 -7
- package/src/modules/dx/headerSearch/__tests__/headerSearch.test.ts +0 -1
- package/src/modules/dx/imageAndLabel/imageAndLabel.html +14 -2
- package/src/modules/dx/popover/__tests__/popover.test.ts +26 -37
- package/src/modules/dx/sidebar/__tests__/sidebar.test.ts +0 -4
- package/src/modules/dx/sidebarOld/__tests__/sidebarOld.test.ts +0 -4
- package/src/modules/dx/sidebarSearch/sidebarSearch.html +2 -2
- package/src/modules/dx/slackAdd/__tests__/slackAdd.test.ts +52 -0
- package/src/modules/dx/slackAdd/slackAdd.css +93 -0
- package/src/modules/dx/slackAdd/slackAdd.html +17 -0
- package/src/modules/dx/slackAdd/slackAdd.stories.ts +25 -0
- package/src/modules/dx/slackAdd/slackAdd.ts +13 -0
- package/src/modules/dx/socials/__tests__/socials.test.ts +12 -22
- package/src/modules/dx/topics/__tests__/mockProps.ts +3 -0
- package/src/modules/dx/topics/__tests__/topics.test.ts +31 -0
- package/src/modules/dx/topics/topics.css +25 -0
- package/src/modules/dx/topics/topics.html +12 -0
- package/src/modules/dx/topics/topics.ts +25 -0
- package/src/modules/dx/tree/__tests__/tree.test.ts +17 -28
- package/src/modules/dx/treeItem/__tests__/treeItem.test.ts +36 -59
- package/src/modules/dx/typeBadge/typeBadge.css +1 -1
- package/src/modules/helpers/text/text.css +7 -0
- package/src/modules/utils/archiveCard/archiveCard.ts +35 -0
- 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
|
+
};
|
|
@@ -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 =
|
|
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()
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div if:false={href} class="container">
|
|
3
|
-
<img
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 } =
|
|
187
|
-
HTMLButtonElement
|
|
188
|
-
|
|
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 =
|
|
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
|
|
13
|
+
placeholder="Search"
|
|
14
14
|
role="search"
|
|
15
|
-
aria-label="
|
|
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&user_scope=&redirect_uri=https%3A%2F%2Fdsc-slack-app.herokuapp.com%2Fslack%2Foauth_redirect&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> =
|
|
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> =
|
|
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 =
|
|
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> =
|
|
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> =
|
|
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> =
|
|
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,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
|
+
}
|