@salesforcedevs/arch-components 1.20.17-alpha9 → 1.25.1-alpha

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 (157) hide show
  1. package/lwc.config.json +42 -3
  2. package/package.json +34 -45
  3. package/src/assets/css/arch-variables.css +512 -0
  4. package/src/modules/arch/badge/badge.css +22 -0
  5. package/src/modules/arch/badge/badge.html +5 -0
  6. package/src/modules/arch/badge/badge.ts +9 -0
  7. package/src/modules/arch/button/button.css +1 -0
  8. package/src/modules/arch/button/button.html +20 -0
  9. package/src/modules/arch/button/button.ts +67 -0
  10. package/src/modules/arch/buttonLink/buttonLink.css +1 -0
  11. package/src/modules/arch/buttonLink/buttonLink.html +19 -0
  12. package/src/modules/arch/buttonLink/buttonLink.stories.js +34 -0
  13. package/src/modules/arch/buttonLink/buttonLink.ts +8 -0
  14. package/src/modules/arch/buttonStyles/buttonStyles.css +220 -0
  15. package/src/modules/arch/card/card.css +128 -0
  16. package/src/modules/arch/card/card.html +85 -0
  17. package/src/modules/arch/card/card.ts +277 -0
  18. package/src/modules/arch/cardBase/cardBase.css +11 -0
  19. package/src/modules/arch/cardBase/cardBase.html +2 -0
  20. package/src/modules/arch/cardGridA/cardGridA.css +11 -0
  21. package/src/modules/arch/cardGridA/cardGridA.html +21 -0
  22. package/src/modules/arch/cardGridA/cardGridA.stories.js +118 -0
  23. package/src/modules/arch/cardGridA/cardGridA.ts +24 -0
  24. package/src/modules/arch/cardGridC/cardGridC.css +24 -0
  25. package/src/modules/arch/cardGridC/cardGridC.html +22 -0
  26. package/src/modules/arch/cardGridC/cardGridC.stories.js +51 -0
  27. package/src/modules/arch/cardGridC/cardGridC.ts +11 -0
  28. package/src/modules/arch/cardGridD/cardGridD.css +17 -0
  29. package/src/modules/arch/cardGridD/cardGridD.html +20 -0
  30. package/src/modules/arch/cardGridD/cardGridD.stories.js +43 -0
  31. package/src/modules/arch/cardGridD/cardGridD.ts +7 -0
  32. package/src/modules/arch/cardNew/cardNew.css +31 -0
  33. package/src/modules/arch/cardNew/cardNew.html +32 -0
  34. package/src/modules/arch/cardNew/cardNew.ts +66 -0
  35. package/src/modules/arch/children/children.html +2 -0
  36. package/src/modules/arch/children/children.ts +31 -0
  37. package/src/modules/arch/color/color.ts +59 -0
  38. package/src/modules/arch/content/__fixtures__/index.ts +884 -0
  39. package/src/modules/arch/content/content.css +643 -0
  40. package/src/modules/arch/content/content.html +65 -0
  41. package/src/modules/arch/content/content.stories.js +21 -0
  42. package/src/modules/arch/content/content.ts +169 -0
  43. package/src/modules/arch/contentIcon/contentIcon.css +48 -0
  44. package/src/modules/arch/contentIcon/contentIcon.html +15 -0
  45. package/src/modules/arch/contentIcon/contentIcon.stories.js +130 -0
  46. package/src/modules/arch/contentIcon/contentIcon.ts +68 -0
  47. package/src/{common → modules/arch}/context/context.ts +21 -19
  48. package/src/modules/arch/contextAdapter/constants.ts +1 -0
  49. package/src/modules/arch/contextAdapter/contextAdapter.ts +54 -0
  50. package/src/modules/arch/debounce/debounce.ts +32 -0
  51. package/src/modules/arch/dialog/dialog.ts +154 -0
  52. package/src/modules/arch/dialogStyles/dialogStyles.css +90 -0
  53. package/src/modules/arch/effectAdapter/effectAdapter.html +1 -0
  54. package/src/modules/arch/effectAdapter/effectAdapter.ts +28 -0
  55. package/src/modules/arch/explorer/explorer.css +301 -0
  56. package/src/modules/arch/explorer/explorer.html +418 -0
  57. package/src/modules/arch/explorer/explorer.ts +718 -0
  58. package/src/modules/arch/explorer/types.d.ts +60 -0
  59. package/src/modules/arch/fetch/fetch.ts +55 -0
  60. package/src/modules/arch/footerMfe/footerMfe.html +3 -0
  61. package/src/modules/arch/footerMfe/footerMfe.ts +19 -0
  62. package/src/modules/arch/gallery/gallery.css +365 -0
  63. package/src/modules/arch/gallery/gallery.html +71 -0
  64. package/src/modules/arch/gallery/gallery.ts +366 -0
  65. package/src/modules/arch/gallery/types.d.ts +35 -0
  66. package/src/modules/arch/heading/heading.css +1 -0
  67. package/src/modules/arch/heading/heading.html +9 -0
  68. package/src/modules/arch/heading/heading.ts +36 -0
  69. package/src/modules/arch/helpers/helpers.ts +141 -0
  70. package/src/modules/arch/heroA/heroA.css +116 -0
  71. package/src/modules/arch/heroA/heroA.html +28 -0
  72. package/src/modules/arch/heroA/heroA.stories.js +60 -0
  73. package/src/modules/arch/heroA/heroA.ts +53 -0
  74. package/src/modules/arch/heroB/heroB.css +79 -0
  75. package/src/modules/arch/heroB/heroB.html +27 -0
  76. package/src/modules/arch/heroB/heroB.stories.js +55 -0
  77. package/src/modules/arch/heroB/heroB.ts +26 -0
  78. package/src/modules/arch/i18n/i18n.ts +78 -0
  79. package/src/modules/arch/icon/icon.css +28 -0
  80. package/src/modules/arch/icon/icon.html +17 -0
  81. package/src/modules/arch/icon/icon.stories.js +26 -0
  82. package/src/modules/arch/icon/icon.ts +92 -0
  83. package/src/modules/arch/instrumentation/instrumentation.css +1 -0
  84. package/src/modules/arch/instrumentation/instrumentation.html +1 -0
  85. package/src/modules/arch/instrumentation/instrumentation.ts +113 -0
  86. package/src/modules/arch/labels/helpers.ts +25 -0
  87. package/src/modules/arch/labels/pointHelpers.ts +47 -0
  88. package/src/modules/arch/labels/timeHelpers.ts +182 -0
  89. package/src/modules/arch/labels/types.d.ts +5 -0
  90. package/src/modules/arch/logger/logger.ts +33 -0
  91. package/src/modules/arch/menu/menu.ts +260 -0
  92. package/src/modules/arch/overflow/overflow.ts +71 -0
  93. package/src/modules/arch/page/page.css +3 -0
  94. package/src/modules/arch/page/page.html +3 -0
  95. package/src/modules/arch/page/page.stories.js +19 -0
  96. package/src/modules/arch/page/page.ts +3 -0
  97. package/src/modules/arch/pageHeaderA/pageHeaderA.css +82 -0
  98. package/src/modules/arch/pageHeaderA/pageHeaderA.html +24 -0
  99. package/src/modules/arch/pageHeaderA/pageHeaderA.stories.js +25 -0
  100. package/src/modules/arch/pageHeaderA/pageHeaderA.ts +51 -0
  101. package/src/modules/arch/pill/pill.css +70 -0
  102. package/src/modules/arch/pill/pill.html +17 -0
  103. package/src/modules/arch/pill/pill.ts +34 -0
  104. package/src/modules/arch/polling-request.ts +97 -0
  105. package/src/modules/arch/reflectedElement/reflectedElement.html +2 -0
  106. package/src/{common → modules/arch}/reflectedElement/reflectedElement.ts +5 -3
  107. package/src/modules/arch/reset/reset.css +39 -0
  108. package/src/modules/arch/searchList/searchList.css +120 -0
  109. package/src/modules/arch/searchList/searchList.html +42 -0
  110. package/src/modules/arch/searchList/searchList.ts +53 -0
  111. package/src/modules/arch/sectionA/sectionA.css +64 -0
  112. package/src/modules/arch/sectionA/sectionA.html +21 -0
  113. package/src/modules/arch/sectionA/sectionA.stories.js +25 -0
  114. package/src/modules/arch/sectionA/sectionA.ts +27 -0
  115. package/src/modules/arch/select/select.css +40 -0
  116. package/src/modules/arch/select/select.html +24 -0
  117. package/src/modules/arch/select/select.ts +64 -0
  118. package/src/modules/arch/socialShare/socialShare.css +50 -0
  119. package/src/modules/arch/socialShare/socialShare.html +56 -0
  120. package/src/modules/arch/socialShare/socialShare.ts +29 -0
  121. package/src/modules/arch/spinner/spinner.css +195 -0
  122. package/src/modules/arch/spinner/spinner.html +9 -0
  123. package/src/modules/arch/spinner/spinner.ts +15 -0
  124. package/src/modules/arch/styles/styles.css +24 -0
  125. package/src/modules/arch/summary/summary.css +134 -0
  126. package/src/modules/arch/summary/summary.html +71 -0
  127. package/src/modules/arch/summary/summary.stories.js +163 -0
  128. package/src/modules/arch/summary/summary.ts +96 -0
  129. package/src/modules/arch/tab/tab.css +3 -0
  130. package/src/modules/arch/tab/tab.html +5 -0
  131. package/src/modules/arch/tab/tab.ts +46 -0
  132. package/src/modules/arch/tabset/tabset.css +112 -0
  133. package/src/modules/arch/tabset/tabset.html +62 -0
  134. package/src/modules/arch/tabset/tabset.ts +244 -0
  135. package/src/modules/arch/testutils.ts +118 -0
  136. package/src/modules/arch/threeCardGrid/threeCardGrid.css +6 -0
  137. package/src/modules/arch/threeCardGrid/threeCardGrid.html +5 -0
  138. package/src/modules/arch/threeCardGrid/threeCardGrid.ts +3 -0
  139. package/src/modules/arch/track/track.ts +23 -0
  140. package/src/modules/arch/trailhead.ts +120 -0
  141. package/src/modules/arch/types.d.ts +1 -0
  142. package/src/modules/arch/useEffectAttr.ts +16 -0
  143. package/src/modules/arch/utils/utils.ts +20 -0
  144. package/src/modules/arch/withState.ts +21 -0
  145. package/src/modules/arch/xsfMfeEvents/xsfMfeEvents.html +1 -0
  146. package/src/modules/arch/xsfMfeEvents/xsfMfeEvents.ts +47 -0
  147. package/src/common/effectAdapter/__tests__/effectAdapter.test.ts +0 -12
  148. package/src/common/effectAdapter/effectAdapter.ts +0 -18
  149. package/src/common/reflectedElement/__tests__/modules/test/select/select.html +0 -3
  150. package/src/common/reflectedElement/__tests__/modules/test/select/select.ts +0 -7
  151. package/src/common/reflectedElement/__tests__/modules/test/selectTransform/selectTransform.html +0 -3
  152. package/src/common/reflectedElement/__tests__/modules/test/selectTransform/selectTransform.ts +0 -18
  153. package/src/common/reflectedElement/__tests__/reflectedElement.test.ts +0 -75
  154. package/src/common/slot/__tests__/slot.test.ts +0 -96
  155. package/src/common/slot/slot.ts +0 -20
  156. /package/src/{common → modules/arch}/context/context.html +0 -0
  157. /package/src/{common/effectAdapter/effectAdapter.html → modules/arch/contextAdapter/contextAdapter.html} +0 -0
@@ -0,0 +1,244 @@
1
+ import { LightningElement, api, track } from "lwc";
2
+ import classnames from "classnames";
3
+ import { TabElement } from "./../tab/tab";
4
+ import { Menu } from "arch/menu";
5
+ import { calculateOverflow } from "arch/overflow";
6
+
7
+ export type Tab = TabElement & {
8
+ active: boolean;
9
+ className: string;
10
+ hidden: boolean;
11
+ labelId: string | null;
12
+ onselect: () => void;
13
+ onmenuselect: () => void;
14
+ overflow: boolean;
15
+ tabindex: number;
16
+ width: number;
17
+ };
18
+
19
+ export default class extends LightningElement {
20
+ @track private tabs: Tab[] = [];
21
+
22
+ private get activeTab() {
23
+ return this.tabs.find((t) => t.active) || null;
24
+ }
25
+
26
+ private _activeTabValue: string | null = null;
27
+
28
+ @api
29
+ get activeTabValue() {
30
+ return this._activeTabValue;
31
+ }
32
+
33
+ set activeTabValue(newValue) {
34
+ this.setActiveTabValueInternal(newValue);
35
+ }
36
+
37
+ private setActiveTabValueInternal(newValue: string | null) {
38
+ // Already selected, do nothing
39
+ if (!newValue || this._activeTabValue === newValue) {
40
+ return;
41
+ }
42
+
43
+ // Unload previously active tab
44
+ if (this.activeTab) {
45
+ this.activeTab.unload();
46
+ }
47
+
48
+ // Update tab properties
49
+ this._activeTabValue = newValue;
50
+
51
+ this.tabs = this.tabs.map((t) => ({
52
+ ...t,
53
+ ...this.calculatedTabProperties(t.value === newValue)
54
+ }));
55
+
56
+ // Load the new tab
57
+ if (this.activeTab) {
58
+ this.activeTab.load();
59
+ }
60
+ }
61
+
62
+ private hasOverflow: boolean = false;
63
+
64
+ private menu = new Menu({
65
+ onChange: () => {},
66
+ shadowRoot: this.template
67
+ });
68
+
69
+ connectedCallback() {
70
+ window.addEventListener("resize", this.handleWindowResize);
71
+ }
72
+
73
+ renderedCallback() {
74
+ // Set the first tab as active by default
75
+ if (!this.activeTabValue && this.tabs.length) {
76
+ this.setActiveTabValueInternal(this.tabs[0].value);
77
+ }
78
+
79
+ // Repaint
80
+ requestAnimationFrame(this.detectOverflow.bind(this));
81
+ this.menu.renderedCallback();
82
+ }
83
+
84
+ disconnectedCallback() {
85
+ window.removeEventListener("resize", this.handleWindowResize);
86
+ this.menu.disconnectedCallback();
87
+ }
88
+
89
+ // TODO i18n
90
+ private get labels() {
91
+ return {
92
+ overflowButton: "More"
93
+ };
94
+ }
95
+
96
+ private get isOverflowHidden() {
97
+ return !this.hasOverflow;
98
+ }
99
+
100
+ private calculatedTabProperties(isActive: boolean) {
101
+ return {
102
+ active: isActive,
103
+ className: classnames("tab", {
104
+ "tab-active": isActive
105
+ }),
106
+ labelId: isActive ? "active-tab" : null,
107
+ hidden: !isActive,
108
+ tabindex: isActive ? 0 : -1
109
+ };
110
+ }
111
+
112
+ // Detect if tabs need to be placed in an overflow menu
113
+ private detectOverflow() {
114
+ const tabs = Array.from(
115
+ this.template.querySelectorAll('.tab[role="presentation"]')
116
+ ) as HTMLElement[];
117
+
118
+ // Update tab.width
119
+ tabs.forEach((t) => {
120
+ const value = t.querySelector("a")!.getAttribute("data-value");
121
+ const tab = this.tabs.find((tabItem) => tabItem.value === value);
122
+ if (tab) {
123
+ const { paddingLeft, width, paddingRight } =
124
+ getComputedStyle(t);
125
+ tab.width =
126
+ parseInt(paddingLeft, 10) +
127
+ parseInt(width, 10) +
128
+ parseInt(paddingRight, 10);
129
+ }
130
+ });
131
+
132
+ // Calculate which items need to be placed in overflow
133
+ const { overflowItems } = calculateOverflow(
134
+ this.tabs,
135
+ this.template.querySelector(".tablist")!.getBoundingClientRect()
136
+ .width,
137
+ this.template
138
+ .querySelector(".overflow-tab")!
139
+ .getBoundingClientRect().width
140
+ );
141
+
142
+ this.hasOverflow = !!overflowItems.length;
143
+
144
+ // Update tab.overflow
145
+ this.tabs.forEach((i) => {
146
+ i.overflow = !!overflowItems.find((o) => o.value === i.value);
147
+ });
148
+ }
149
+
150
+ private handleWindowResize = () => {
151
+ this.detectOverflow();
152
+
153
+ if (this.menu) {
154
+ this.menu.close();
155
+ }
156
+ };
157
+
158
+ private handleTabRegister(e: { detail: TabElement }) {
159
+ const element = e.detail;
160
+
161
+ const tab: Tab = {
162
+ ...this.calculatedTabProperties(
163
+ element.value === this.activeTabValue
164
+ ),
165
+ ...element,
166
+ onselect: () => {
167
+ this.setActiveTabValueInternal(element.value);
168
+ },
169
+ onmenuselect: () => {
170
+ this.setActiveTabValueInternal(element.value);
171
+
172
+ // Re-focus the menu button
173
+ this.template.querySelector('[data-menu="trigger"]')!.focus();
174
+
175
+ if (this.menu) {
176
+ this.menu.close();
177
+ }
178
+ },
179
+ overflow: false,
180
+ width: 0
181
+ };
182
+
183
+ this.tabs.push(tab);
184
+
185
+ // Load the tab if it's currently active
186
+ if (element.value === this.activeTabValue) {
187
+ tab.load();
188
+ }
189
+ }
190
+
191
+ private handleKeyDown(e: KeyboardEvent) {
192
+ switch (e.code) {
193
+ // Cycle through tabs
194
+ case "ArrowLeft":
195
+ case "ArrowRight": {
196
+ e.preventDefault();
197
+ e.stopPropagation();
198
+
199
+ const increment = e.code === "ArrowLeft" ? -1 : 1;
200
+ const visibleTabs = this.tabs.filter((t) => !t.overflow);
201
+ const currentFocusedIndex = visibleTabs.findIndex(
202
+ (tabItem) => tabItem.value === this.activeTabValue
203
+ );
204
+
205
+ let newIndex = currentFocusedIndex + increment;
206
+ if (newIndex < 0) {
207
+ newIndex = visibleTabs.length - 1;
208
+ }
209
+ if (newIndex + 1 > visibleTabs.length) {
210
+ newIndex = 0;
211
+ }
212
+
213
+ // Set the active tabpanel and focus the tab
214
+ const newValue = visibleTabs[newIndex].value;
215
+ this.setActiveTabValueInternal(newValue);
216
+ this.template
217
+ .querySelector(`a[data-value=${newValue}]`)!
218
+ .focus();
219
+ break;
220
+ }
221
+ case "ArrowDown":
222
+ // Focus the tabpanel
223
+ this.template.querySelector('[role="tabpanel"]')!.focus();
224
+ break;
225
+ case "Enter":
226
+ case "Space": {
227
+ e.preventDefault();
228
+ e.stopPropagation();
229
+
230
+ const element = e.target as HTMLElement;
231
+
232
+ if (element && element.nodeName === "A") {
233
+ const newValue = element.getAttribute("data-value");
234
+ if (newValue) {
235
+ this.setActiveTabValueInternal(newValue);
236
+ }
237
+ }
238
+ break;
239
+ }
240
+ default:
241
+ break;
242
+ }
243
+ }
244
+ }
@@ -0,0 +1,118 @@
1
+ import { html, render, TemplateResult } from 'lit-html';
2
+ import { querySelectorAll, querySelector } from 'kagekiri';
3
+ // @ts-ignore we are using a beta feature "setFeatureFlagForTest" that tsc doesn't like
4
+ import { createElement, LightningElement, setFeatureFlagForTest } from 'lwc';
5
+
6
+ export { html, render };
7
+
8
+ export function nextTick() {
9
+ return new Promise((resolve) => {
10
+ process.nextTick(resolve);
11
+ });
12
+ }
13
+
14
+ export function $(selector: string, element: HTMLElement) {
15
+ return element.shadowRoot!.querySelectorAll(selector);
16
+ }
17
+
18
+ export function $testid(value: string, element: HTMLElement) {
19
+ return $(`[data-testid="${value}"]`, element);
20
+ }
21
+
22
+ export function renderIntoBody(template: TemplateResult) {
23
+ const element = document.createElement('div');
24
+ document.body.append(element);
25
+ render(template, element);
26
+ return element.children[0] as HTMLElement;
27
+ }
28
+
29
+ export async function renderIntoBodyAndWait(
30
+ template: TemplateResult,
31
+ ready:
32
+ | ((params: {
33
+ querySelector: typeof querySelector;
34
+ querySelectorAll: typeof querySelectorAll;
35
+ }) => boolean)
36
+ | string
37
+ ) {
38
+ const element = renderIntoBody(template);
39
+ await new Promise((resolve) => {
40
+ const check = () => {
41
+ const isReady =
42
+ typeof ready === 'string'
43
+ ? querySelector(ready, element) !== null
44
+ : ready({ querySelector, querySelectorAll });
45
+ if (isReady) {
46
+ resolve();
47
+ } else {
48
+ requestAnimationFrame(check);
49
+ }
50
+ };
51
+ requestAnimationFrame(check);
52
+ });
53
+ return element;
54
+ }
55
+
56
+ // LWC Component Testing Utilities
57
+ export const renderLightningElement = (
58
+ element: LightningElement,
59
+ properties = {}
60
+ ) => {
61
+ Object.assign(element, properties);
62
+ document.body.appendChild(element as unknown as Node);
63
+ return element;
64
+ };
65
+
66
+ export const createRenderComponent = (
67
+ tag: string,
68
+ LWC: new () => LightningElement
69
+ ) => {
70
+ setFeatureFlagForTest('ENABLE_LIGHT_DOM_COMPONENTS', true);
71
+ return (properties = {}) => {
72
+ const component = createElement(tag, {
73
+ is: LWC
74
+ });
75
+
76
+ renderLightningElement(component, properties);
77
+ return component;
78
+ };
79
+ };
80
+
81
+ export const mockSlottedElement = <T extends HTMLElement>(
82
+ element: any,
83
+ elementType = 'button',
84
+ slotName?: string
85
+ ): { slottedElement: T; slot: HTMLSlotElement } => {
86
+ const slot: HTMLSlotElement = element.shadowRoot.querySelector(
87
+ `slot${slotName ? `[name='${slotName}']` : ''}`
88
+ );
89
+ expect(slot).not.toBeNull();
90
+
91
+ const slottedElement = document.createElement(elementType) as T;
92
+ slot.assignedElements = jest.fn(() => [slottedElement]);
93
+ slot.dispatchEvent(new Event('slotchange'));
94
+
95
+ return { slottedElement, slot };
96
+ };
97
+
98
+ const wait = () => new Promise((res) => setTimeout(res, 0));
99
+
100
+ const renderAllShadowDoms = (element: {
101
+ shadowRoot: { children: HTMLElement };
102
+ }) => {
103
+ if (element.shadowRoot) {
104
+ return element.shadowRoot.children;
105
+ }
106
+
107
+ return element;
108
+ };
109
+
110
+ export const toSnapshot = async <T extends Object>(
111
+ renderFn: Function,
112
+ props?: T
113
+ ) => {
114
+ const element = renderFn(props);
115
+ await wait();
116
+ const doc = renderAllShadowDoms(element);
117
+ return expect(doc).toMatchSnapshot();
118
+ };
@@ -0,0 +1,6 @@
1
+ /* Override the default grid template columns at tablet resolution, forcing 3 columns */
2
+ @media screen and (min-width: 640px) {
3
+ dx-grid::part(container) {
4
+ grid-template-columns: repeat(3, 1fr);
5
+ }
6
+ }
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <dx-grid columns="three">
3
+ <slot></slot>
4
+ </dx-grid>
5
+ </template>
@@ -0,0 +1,3 @@
1
+ import { LightningElement } from 'lwc';
2
+
3
+ export default class ThreeCardGrid extends LightningElement {}
@@ -0,0 +1,23 @@
1
+ type TrackingPayload = {
2
+ label?: string;
3
+ ctaType?: string;
4
+ };
5
+
6
+ export const track = (
7
+ element: HTMLElement,
8
+ eventName: string,
9
+ payload: TrackingPayload
10
+ ) => {
11
+ const trackEvent = new CustomEvent('trailhead_track', {
12
+ bubbles: true,
13
+ composed: true,
14
+ detail: {
15
+ eventName,
16
+ payload: {
17
+ ...payload,
18
+ ...{ pageLocation: element.tagName }
19
+ }
20
+ }
21
+ });
22
+ element.dispatchEvent(trackEvent);
23
+ };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * @file Utilites for interacting with the Trailhead platform.
3
+ */
4
+ import { fetch, fetchJSON, postJSON, csrfHeader } from './fetch/fetch';
5
+ import { PollingRequest, PollingRequestInit } from './polling-request';
6
+ import logger from './logger/logger';
7
+ import { Org } from '../modules/th/orgPicker/types';
8
+
9
+ const CSRF_WARNING =
10
+ 'A CSRF token required for this request. Set it using fromTrailhead.configure().';
11
+
12
+ const DEFAULT_BASE_URL = '';
13
+
14
+ type TrailheadAPIClientInit = {
15
+ baseUrl?: string;
16
+ csrfToken?: string;
17
+ };
18
+
19
+ /**
20
+ * Client for interacting with Trailhead via API requests
21
+ * @example
22
+ * const fromTrailhead = new TrailheadAPIClient({ csrfToken: "XXX" });
23
+ * fromTrailhead.get("/user_orgs");
24
+ */
25
+ export class TrailheadAPIClient {
26
+ baseUrl: string = DEFAULT_BASE_URL;
27
+ csrfToken: string | undefined;
28
+
29
+ constructor(args: TrailheadAPIClientInit = {}) {
30
+ this.configure(args);
31
+ }
32
+
33
+ configure({
34
+ baseUrl = process.env.API_HOST,
35
+ csrfToken
36
+ }: TrailheadAPIClientInit) {
37
+ this.baseUrl = baseUrl || DEFAULT_BASE_URL;
38
+ this.csrfToken = csrfToken;
39
+ }
40
+
41
+ async get<T>(path: string) {
42
+ return fetchJSON<T>(this.makeUrl(path));
43
+ }
44
+
45
+ async post<T>(path: string, body?: object) {
46
+ this.checkRequest();
47
+ return postJSON<T>(this.makeUrl(path), body, {
48
+ headers: { ...csrfHeader(this.csrfToken) }
49
+ });
50
+ }
51
+
52
+ async patch<T>(path: string, body?: object) {
53
+ this.checkRequest();
54
+ return postJSON<T>(this.makeUrl(path), body, {
55
+ method: 'PATCH',
56
+ headers: {
57
+ ...csrfHeader(this.csrfToken)
58
+ }
59
+ });
60
+ }
61
+
62
+ async delete<T>(path: string) {
63
+ this.checkRequest();
64
+ return fetchJSON<T>(this.makeUrl(path), {
65
+ method: 'DELETE',
66
+ headers: { ...csrfHeader(this.csrfToken) }
67
+ });
68
+ }
69
+
70
+ async poll<T>(args: PollingRequestInit<T>) {
71
+ return new PollingRequest<T>(args);
72
+ }
73
+
74
+ makeUrl(path: string) {
75
+ return [this.baseUrl, path]
76
+ .map((s) => s.replace(/^\/|\/$/g, ''))
77
+ .join('/');
78
+ }
79
+
80
+ private checkRequest() {
81
+ if (!this.csrfToken) {
82
+ logger.warn(CSRF_WARNING);
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Determine if an org is reachable (after creation) by
89
+ * requesting open endpoints. The org is reachable if the
90
+ * user's client resolves the freshly propagated DNS entries.
91
+ */
92
+ export async function isOrgReachable(org: Org): Promise<boolean> {
93
+ const subdomain = org.instance_url.split('.')[0];
94
+
95
+ const responses = await Promise.all([
96
+ fetch(
97
+ `${subdomain}.my.salesforce.com/.well-known/openid-configuration`
98
+ ),
99
+ fetch(
100
+ `${subdomain}--c.documentforce.com/.well-known/openid-configuration`,
101
+ {
102
+ mode: 'no-cors'
103
+ }
104
+ ),
105
+ fetch(
106
+ `${subdomain}--th-con-app.visualforce.com/.well-known/openid-configuration`,
107
+ {
108
+ mode: 'no-cors'
109
+ }
110
+ ),
111
+ fetch(
112
+ `${subdomain}.lightning.force.com/.well-known/openid-configuration`,
113
+ {
114
+ mode: 'no-cors'
115
+ }
116
+ )
117
+ ]).catch(() => Promise.resolve([{ status: 404 }]));
118
+
119
+ return responses.every((r) => r.status === 200 || r.status === 0);
120
+ }
@@ -0,0 +1 @@
1
+ export type BooleanAttr = boolean | string | null;
@@ -0,0 +1,16 @@
1
+ export function useEffectAttr(fn: Function, deps: Array<string>, context: any) {
2
+ const prevValues = new Map<string, any>();
3
+ return () => {
4
+ const prev = deps.map((key) => prevValues.get(key));
5
+ const next = deps.map((key) => context[key]);
6
+ for (const i in prev) {
7
+ if (next[i] !== prev[i]) {
8
+ fn.call(context);
9
+ break;
10
+ }
11
+ }
12
+ for (const k of deps) {
13
+ prevValues.set(k, context[k]);
14
+ }
15
+ };
16
+ }
@@ -0,0 +1,20 @@
1
+ export function waitForPageSettled() {
2
+ return new Promise((resolve) => {
3
+ let prevH: number = 0;
4
+ const prevY = window.scrollY;
5
+ function check() {
6
+ const nextH = window.innerHeight + document.body.scrollHeight;
7
+ const nextY = window.scrollY;
8
+ const settled = nextH === prevH;
9
+ if (settled) {
10
+ if (nextY === prevY) {
11
+ resolve();
12
+ }
13
+ return;
14
+ }
15
+ prevH = nextH;
16
+ requestAnimationFrame(check);
17
+ }
18
+ requestAnimationFrame(check);
19
+ });
20
+ }
@@ -0,0 +1,21 @@
1
+ import { directive, NodePart } from 'lit-html';
2
+
3
+ export type WithStateState = any;
4
+ export type WithStateSetState = (state: any) => void;
5
+
6
+ /**
7
+ * lit-html directive for creating thin, stateful renderings
8
+ * @example
9
+ * html`${withState((state, setState) => html`state.msg`, { msg: 'hello!' })}`
10
+ */
11
+ export default directive((render, initialState = {}) => (part: NodePart) => {
12
+ let prevState = initialState;
13
+
14
+ function setState(nextState: WithStateState) {
15
+ part.setValue(render({ ...prevState, ...nextState }, setState));
16
+ part.commit();
17
+ prevState = { ...prevState, ...nextState };
18
+ }
19
+
20
+ part.setValue(render(initialState, setState));
21
+ });
@@ -0,0 +1 @@
1
+ <template></template>
@@ -0,0 +1,47 @@
1
+ import { LightningElement } from 'lwc';
2
+ import { track } from 'arch/instrumentation';
3
+
4
+ type EventHandler = (event: Event) => void;
5
+
6
+ export default class XsfMfeEvents extends LightningElement {
7
+ connectedCallback() {
8
+ document.addEventListener(
9
+ 'www_spasearch',
10
+ this.handleSearch as EventHandler
11
+ );
12
+ document.addEventListener('www_track', this.handleTrack as EventHandler);
13
+ document.addEventListener('toggle_global_nav', this.handleToggleGlobalNav as EventHandler);
14
+ }
15
+
16
+ disconnectedCallback() {
17
+ document.removeEventListener(
18
+ 'www_spasearch',
19
+ this.handleSearch as EventHandler
20
+ );
21
+ document.removeEventListener('www_track', this.handleTrack as EventHandler);
22
+ document.removeEventListener('toggle_global_nav', this.handleToggleGlobalNav as EventHandler);
23
+ }
24
+
25
+ handleSearch = ({ target, type, detail }: CustomEvent) => {
26
+ const searchTerm = detail?.searchTerm;
27
+ track(target, type, { searchTerm });
28
+ if (searchTerm) {
29
+ window.location.href = `/search/?keywords=${encodeURIComponent(
30
+ searchTerm
31
+ )}`;
32
+ }
33
+ };
34
+
35
+ handleTrack = ({ target, detail }: CustomEvent) => {
36
+ const { event: eventName, ...payload } = detail;
37
+ track(target as EventTarget, eventName, payload);
38
+ };
39
+
40
+ handleToggleGlobalNav = ({ detail }: CustomEvent) => {
41
+ const isVisible = detail;
42
+ document.documentElement?.style.setProperty(
43
+ '--dx-g-global-header-nav-row-count',
44
+ isVisible ? '2' : '1'
45
+ );
46
+ };
47
+ }
@@ -1,12 +0,0 @@
1
- import { EffectAdapter } from '../effectAdapter';
2
-
3
- describe('EffectAdapter', () => {
4
- it('calls the handler', async () => {
5
- let value = Symbol();
6
- let setValue = jest.fn();
7
- let adapter = new EffectAdapter(setValue);
8
- let config = { value: value };
9
- adapter.update(config);
10
- expect(setValue).toHaveBeenCalledWith(config);
11
- });
12
- });
@@ -1,18 +0,0 @@
1
- import { WireAdapter } from 'lwc';
2
-
3
- export type EffectAdapterConfig<T> = {};
4
-
5
- export class EffectAdapter<T>
6
- implements
7
- WireAdapter<EffectAdapterConfig<T>, EffectAdapterConfig<T>, void>
8
- {
9
- constructor(private setValue: (value: EffectAdapterConfig<T>) => void) {}
10
-
11
- connect() {}
12
-
13
- disconnect() {}
14
-
15
- update(config: EffectAdapterConfig<T>) {
16
- this.setValue(config);
17
- }
18
- }
@@ -1,3 +0,0 @@
1
- <template>
2
- <select lwc:dom="manual"></select>
3
- </template>
@@ -1,7 +0,0 @@
1
- import { ReflectedElement } from 'common/reflectedElement';
2
-
3
- export default class extends ReflectedElement {
4
- get contentElement() {
5
- return this.template.querySelector('select') as HTMLSelectElement;
6
- }
7
- }
@@ -1,3 +0,0 @@
1
- <template>
2
- <select lwc:dom="manual"></select>
3
- </template>