@hubspot/ui-extensions 0.11.1 → 0.11.2
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/dist/__synced__/experimental/types.synced.d.ts +3 -4
- package/dist/__synced__/remoteComponents.synced.d.ts +152 -70
- package/dist/__synced__/remoteComponents.synced.js +98 -0
- package/dist/__synced__/types/components/button.synced.d.ts +6 -0
- package/dist/__tests__/crm/hooks/useAssociations.spec.js +33 -29
- package/dist/__tests__/crm/hooks/useCrmProperties.spec.js +19 -18
- package/dist/__tests__/crm/utils/fetchAssociations.spec.js +8 -7
- package/dist/__tests__/crm/utils/fetchCrmProperties.spec.js +34 -33
- package/dist/crm/index.d.ts +1 -1
- package/dist/crm/index.js +1 -1
- package/dist/experimental/index.d.ts +1 -1
- package/dist/experimental/index.js +1 -1
- package/dist/experimental/testing/__tests__/debug.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/debug.spec.js +43 -0
- package/dist/experimental/testing/__tests__/find.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/find.spec.js +33 -0
- package/dist/experimental/testing/__tests__/findAll.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/findAll.spec.js +12 -0
- package/dist/experimental/testing/__tests__/findAllChildren.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/findAllChildren.spec.js +48 -0
- package/dist/experimental/testing/__tests__/findChild.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/findChild.spec.js +29 -0
- package/dist/experimental/testing/__tests__/fragments.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/fragments.spec.js +59 -0
- package/dist/experimental/testing/__tests__/invalid-components.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/invalid-components.spec.js +88 -0
- package/dist/experimental/testing/__tests__/isMatch.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/isMatch.spec.js +60 -0
- package/dist/experimental/testing/__tests__/maybeFind.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/maybeFind.spec.js +58 -0
- package/dist/experimental/testing/__tests__/maybeFindChild.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/maybeFindChild.spec.js +65 -0
- package/dist/experimental/testing/__tests__/trigger.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/trigger.spec.js +40 -0
- package/dist/experimental/testing/__tests__/type-utils.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/type-utils.spec.js +163 -0
- package/dist/experimental/testing/__tests__/waitFor.spec.d.ts +1 -0
- package/dist/experimental/testing/__tests__/waitFor.spec.js +55 -0
- package/dist/experimental/testing/index.d.ts +3 -0
- package/dist/experimental/testing/index.js +3 -0
- package/dist/experimental/testing/internal/convert.d.ts +10 -0
- package/dist/experimental/testing/internal/convert.js +131 -0
- package/dist/experimental/testing/internal/debug.d.ts +1 -1
- package/dist/experimental/testing/internal/debug.js +10 -1
- package/dist/experimental/testing/internal/document.d.ts +14 -0
- package/dist/experimental/testing/internal/document.js +37 -0
- package/dist/experimental/testing/internal/errors.d.ts +12 -0
- package/dist/experimental/testing/internal/errors.js +18 -0
- package/dist/experimental/testing/internal/match.d.ts +19 -0
- package/dist/experimental/testing/internal/match.js +42 -0
- package/dist/experimental/testing/internal/query.js +1 -19
- package/dist/experimental/testing/internal/utils/promise-utils.d.ts +14 -0
- package/dist/experimental/testing/internal/utils/promise-utils.js +14 -0
- package/dist/experimental/testing/render.d.ts +9 -0
- package/dist/experimental/testing/render.js +155 -0
- package/dist/experimental/testing/types.d.ts +1 -0
- package/dist/pages/home/index.d.ts +1 -1
- package/dist/pages/home/index.js +1 -1
- package/package.json +11 -13
- package/dist/__synced__/appHomeRemoteComponents.synced.d.ts +0 -28
- package/dist/__synced__/appHomeRemoteComponents.synced.js +0 -21
- package/dist/__synced__/crmRemoteComponents.synced.d.ts +0 -66
- package/dist/__synced__/crmRemoteComponents.synced.js +0 -15
- package/dist/__synced__/experimentalRemoteComponents.synced.d.ts +0 -94
- package/dist/__synced__/experimentalRemoteComponents.synced.js +0 -56
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { act as reactAct } from 'react';
|
|
2
|
+
import { InvalidComponentsError } from './errors';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a rendered document.
|
|
5
|
+
*
|
|
6
|
+
* @param options Options for creating the document.
|
|
7
|
+
* @returns A rendered document.
|
|
8
|
+
*/
|
|
9
|
+
export const createDocument = (options) => {
|
|
10
|
+
const { getLatestRootNode } = options;
|
|
11
|
+
const invalidComponentNames = new Set();
|
|
12
|
+
const document = {
|
|
13
|
+
rootNode: null,
|
|
14
|
+
batchUpdate: (run) => {
|
|
15
|
+
reactAct(run);
|
|
16
|
+
const latestRootNode = getLatestRootNode();
|
|
17
|
+
if (latestRootNode.document.hasInvalidComponentNames()) {
|
|
18
|
+
throw new InvalidComponentsError(latestRootNode);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
addInvalidComponentName: (name) => {
|
|
22
|
+
invalidComponentNames.add(name);
|
|
23
|
+
},
|
|
24
|
+
getInvalidComponentNames: () => {
|
|
25
|
+
if (invalidComponentNames.size === 0) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
const invalidComponentNamesArray = [...invalidComponentNames];
|
|
29
|
+
invalidComponentNamesArray.sort();
|
|
30
|
+
return invalidComponentNamesArray;
|
|
31
|
+
},
|
|
32
|
+
hasInvalidComponentNames: () => {
|
|
33
|
+
return invalidComponentNames.size > 0;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
return document;
|
|
37
|
+
};
|
|
@@ -42,3 +42,15 @@ export declare class FindInvalidComponentError extends Error {
|
|
|
42
42
|
findMethodName: string;
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Thrown when a fragment prop array is detected in the rendered output.
|
|
47
|
+
*/
|
|
48
|
+
export declare class InvalidFragmentPropArrayError extends Error {
|
|
49
|
+
constructor(componentName: string, propName: string);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when a waitFor() check doesn't pass within the timeout period.
|
|
53
|
+
*/
|
|
54
|
+
export declare class WaitForTimeoutError extends Error {
|
|
55
|
+
constructor(timeout: number);
|
|
56
|
+
}
|
|
@@ -50,3 +50,21 @@ export class FindInvalidComponentError extends Error {
|
|
|
50
50
|
this.name = 'FindInvalidComponentError';
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Thrown when a fragment prop array is detected in the rendered output.
|
|
55
|
+
*/
|
|
56
|
+
export class InvalidFragmentPropArrayError extends Error {
|
|
57
|
+
constructor(componentName, propName) {
|
|
58
|
+
super(`Invalid rendered output. Invalid fragment prop array ${propName} for component ${componentName}. Arrays are not allowed for fragment props.`);
|
|
59
|
+
this.name = 'InvalidFragmentPropArrayError';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Thrown when a waitFor() check doesn't pass within the timeout period.
|
|
64
|
+
*/
|
|
65
|
+
export class WaitForTimeoutError extends Error {
|
|
66
|
+
constructor(timeout) {
|
|
67
|
+
super(`waitFor() failed. Timeout of ${timeout}ms exceeded waiting for check to pass`);
|
|
68
|
+
this.name = 'WaitForTimeoutError';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HubSpotReactComponent, UnknownComponentProps } from '../../../__synced__/types/shared.synced';
|
|
2
|
+
import { ElementMatcher, RenderedElementNode, RenderedNode } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Checks if the element matches the matcher.
|
|
5
|
+
*
|
|
6
|
+
* @param element The element to check.
|
|
7
|
+
* @param matcher An optional matcher to filter the elements.
|
|
8
|
+
* @returns `true` if the element matches the matcher, `false` otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export declare const checkElementMatches: <TProps extends UnknownComponentProps>(element: RenderedElementNode<TProps>, matcher?: ElementMatcher<TProps> | undefined) => boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if the node matches the component and matcher.
|
|
13
|
+
*
|
|
14
|
+
* @param node The node to check.
|
|
15
|
+
* @param component The component to check.
|
|
16
|
+
* @param matcher An optional matcher to filter the elements.
|
|
17
|
+
* @returns Whether the node matches the component and matcher.
|
|
18
|
+
*/
|
|
19
|
+
export declare const isMatch: <TComponentProps extends UnknownComponentProps>(node: RenderedNode | null | undefined, component: HubSpotReactComponent<TComponentProps>, matcher?: ElementMatcher<TComponentProps> | undefined) => node is RenderedElementNode<TComponentProps>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RenderedNodeType, } from '../types';
|
|
2
|
+
import { __hubSpotComponentRegistry } from '../../../__synced__/remoteComponents.synced';
|
|
3
|
+
/**
|
|
4
|
+
* Checks if the element matches the matcher.
|
|
5
|
+
*
|
|
6
|
+
* @param element The element to check.
|
|
7
|
+
* @param matcher An optional matcher to filter the elements.
|
|
8
|
+
* @returns `true` if the element matches the matcher, `false` otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export const checkElementMatches = (element, matcher) => {
|
|
11
|
+
// If no matcher is provided, the element matches
|
|
12
|
+
if (!matcher) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
// Check if the matcher is a predicate function
|
|
16
|
+
if (typeof matcher === 'function') {
|
|
17
|
+
return matcher(element);
|
|
18
|
+
}
|
|
19
|
+
// Check if all the props in the matcher match the props in the element
|
|
20
|
+
return Object.keys(matcher).every((key) => element.props[key] === matcher[key]);
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Checks if the node matches the component and matcher.
|
|
24
|
+
*
|
|
25
|
+
* @param node The node to check.
|
|
26
|
+
* @param component The component to check.
|
|
27
|
+
* @param matcher An optional matcher to filter the elements.
|
|
28
|
+
* @returns Whether the node matches the component and matcher.
|
|
29
|
+
*/
|
|
30
|
+
export const isMatch = (node, component, matcher) => {
|
|
31
|
+
if (node == null) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (node.nodeType !== RenderedNodeType.Element) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const targetComponentName = __hubSpotComponentRegistry.getComponentName(component);
|
|
38
|
+
if (node.name !== targetComponentName) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return checkElementMatches(node, matcher);
|
|
42
|
+
};
|
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
import { __hubSpotComponentRegistry } from '../../../__synced__/remoteComponents.synced';
|
|
2
2
|
import { isRenderedElementNode, isRenderedFragmentNode } from '../type-utils';
|
|
3
3
|
import { ComponentNotFoundError, FindInvalidComponentError, InvalidComponentsError, } from './errors';
|
|
4
|
-
|
|
5
|
-
* Checks if the element matches the matcher.
|
|
6
|
-
*
|
|
7
|
-
* @param element The element to check.
|
|
8
|
-
* @param matcher An optional matcher to filter the elements.
|
|
9
|
-
* @returns `true` if the element matches the matcher, `false` otherwise.
|
|
10
|
-
*/
|
|
11
|
-
const checkElementMatches = (element, matcher) => {
|
|
12
|
-
// If no matcher is provided, the element matches
|
|
13
|
-
if (!matcher) {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
// Check if the matcher is a predicate function
|
|
17
|
-
if (typeof matcher === 'function') {
|
|
18
|
-
return matcher(element);
|
|
19
|
-
}
|
|
20
|
-
// Check if all the props in the matcher match the props in the element
|
|
21
|
-
return Object.keys(matcher).every((key) => element.props[key] === matcher[key]);
|
|
22
|
-
};
|
|
4
|
+
import { checkElementMatches } from './match';
|
|
23
5
|
const addMatchToFindResult = (findResult, match, options) => {
|
|
24
6
|
if (options.findFirstOnly) {
|
|
25
7
|
findResult.match = match;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A deferred promise that can be resolved or rejected externally.
|
|
3
|
+
*/
|
|
4
|
+
export interface Deferred<T = unknown> {
|
|
5
|
+
promise: Promise<T>;
|
|
6
|
+
resolve: (value: T) => void;
|
|
7
|
+
reject: (error: unknown) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Creates a deferred promise that can be resolved or rejected externally.
|
|
11
|
+
*
|
|
12
|
+
* @returns A deferred promise object.
|
|
13
|
+
*/
|
|
14
|
+
export declare const createDeferred: <T = unknown>() => Deferred<T>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a deferred promise that can be resolved or rejected externally.
|
|
3
|
+
*
|
|
4
|
+
* @returns A deferred promise object.
|
|
5
|
+
*/
|
|
6
|
+
export const createDeferred = () => {
|
|
7
|
+
let resolve;
|
|
8
|
+
let reject;
|
|
9
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
10
|
+
resolve = _resolve;
|
|
11
|
+
reject = _reject;
|
|
12
|
+
});
|
|
13
|
+
return { promise, resolve: resolve, reject: reject };
|
|
14
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type RenderResult } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a UI extension component using React so that assertions can be made against the rendered output.
|
|
5
|
+
*
|
|
6
|
+
* @param node The React node to render.
|
|
7
|
+
* @returns A render result object that can be used to query the rendered DOM.
|
|
8
|
+
*/
|
|
9
|
+
export declare const render: (node: ReactNode) => RenderResult;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { createRemoteRoot } from '@remote-ui/core';
|
|
2
|
+
import { createRoot as createReactRoot } from '@remote-ui/react';
|
|
3
|
+
import { convertRemoteRoot } from './internal/convert';
|
|
4
|
+
import { createDocument } from './internal/document';
|
|
5
|
+
import { InvalidComponentsError, WaitForTimeoutError } from './internal/errors';
|
|
6
|
+
import { isMatch } from './internal/match';
|
|
7
|
+
import { find, findAll, findAllChildren, findChild, maybeFind, maybeFindChild, } from './internal/query';
|
|
8
|
+
import { createRootNode } from './internal/root';
|
|
9
|
+
import { createDeferred } from './internal/utils/promise-utils';
|
|
10
|
+
const DEFAULT_WAIT_FOR_TIMEOUT_IN_MS = 1000;
|
|
11
|
+
const DEFAULT_WAIT_FOR_OPTIONS = {
|
|
12
|
+
timeoutInMs: DEFAULT_WAIT_FOR_TIMEOUT_IN_MS,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Renders a UI extension component using React so that assertions can be made against the rendered output.
|
|
16
|
+
*
|
|
17
|
+
* @param node The React node to render.
|
|
18
|
+
* @returns A render result object that can be used to query the rendered DOM.
|
|
19
|
+
*/
|
|
20
|
+
export const render = (node) => {
|
|
21
|
+
let dirty = true;
|
|
22
|
+
let waitForChecksQueued = false;
|
|
23
|
+
let waitForList = [];
|
|
24
|
+
const runWaitForChecks = () => {
|
|
25
|
+
waitForChecksQueued = false;
|
|
26
|
+
waitForList = waitForList.filter((waitFor) => {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
28
|
+
const renderedRootNode = getLatestRootNode();
|
|
29
|
+
if (renderedRootNode.document.hasInvalidComponentNames()) {
|
|
30
|
+
// Reject the waitFor promise if we detect invalid components in the rendered output.
|
|
31
|
+
waitFor.deferred.reject(new InvalidComponentsError(renderedRootNode));
|
|
32
|
+
if (waitFor.setTimeoutId) {
|
|
33
|
+
// Clear the timeout so that we don't reject the promise if the check passes before the timeout expires.
|
|
34
|
+
// Technically, calling reject on a promise that has already been resolved is a no-op.
|
|
35
|
+
clearTimeout(waitFor.setTimeoutId);
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
waitFor.check(); // Run the user provided check function.
|
|
41
|
+
if (waitFor.setTimeoutId) {
|
|
42
|
+
// Clear the timeout so that we don't reject the promise if the check passes before the timeout expires.
|
|
43
|
+
// Technically, calling reject on a promise that has already been resolved is a no-op.
|
|
44
|
+
clearTimeout(waitFor.setTimeoutId);
|
|
45
|
+
}
|
|
46
|
+
waitFor.deferred.resolve(); // Resolve the promise that was originally returned by the waitFor function.
|
|
47
|
+
return false; // Remove the waitFor from the list since the check passed and we resolved the promise
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return true; // Keep the waitFor in the list since the check failed and we didn't resolve the promise.
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* This function will be invoked any time there are changes to the remote DOM.
|
|
56
|
+
* We don't really care what the changes were because we are not actually sending them
|
|
57
|
+
* to a host page. We are only interested in the fact that the DOM has been updated
|
|
58
|
+
* so that we can know that we need rebuild our own tree of rendered nodes.
|
|
59
|
+
*/
|
|
60
|
+
const remoteChannel = () => {
|
|
61
|
+
dirty = true;
|
|
62
|
+
if (waitForList.length > 0) {
|
|
63
|
+
// Even though @remote-ui/core is notifying us that the DOM has been updated, it hasn't actually
|
|
64
|
+
// applied the DOMs to the remote DOM representation. Therefore, we need to queue a microtask to
|
|
65
|
+
// run the waitFor checks at the end of the event loop to give the DOM a chance to be updated...
|
|
66
|
+
if (!waitForChecksQueued) {
|
|
67
|
+
waitForChecksQueued = true;
|
|
68
|
+
queueMicrotask(runWaitForChecks);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const remoteRoot = createRemoteRoot(remoteChannel);
|
|
73
|
+
const reactRoot = createReactRoot(remoteRoot);
|
|
74
|
+
reactRoot.render(node);
|
|
75
|
+
remoteRoot.mount();
|
|
76
|
+
let maybeRenderedRootNode;
|
|
77
|
+
/**
|
|
78
|
+
* Returns the latest rendered root node. If the root node has not been rendered yet, it will be rendered.
|
|
79
|
+
* If there were no changes to the remote DOM then the previously rendered root node will be returned.
|
|
80
|
+
*/
|
|
81
|
+
const getLatestRootNode = () => {
|
|
82
|
+
if (maybeRenderedRootNode && !dirty) {
|
|
83
|
+
// Return the previously rendered root node if there were no changes to the remote DOM.
|
|
84
|
+
return maybeRenderedRootNode;
|
|
85
|
+
}
|
|
86
|
+
// Create a new document for the next rendered DOM tree
|
|
87
|
+
const document = createDocument({
|
|
88
|
+
getLatestRootNode,
|
|
89
|
+
});
|
|
90
|
+
const nextRootNode = createRootNode(document);
|
|
91
|
+
document.rootNode = nextRootNode;
|
|
92
|
+
// Convert the tree of remote nodes to a tree of rendered nodes.
|
|
93
|
+
convertRemoteRoot(nextRootNode, remoteRoot);
|
|
94
|
+
maybeRenderedRootNode = nextRootNode;
|
|
95
|
+
dirty = false;
|
|
96
|
+
return maybeRenderedRootNode;
|
|
97
|
+
};
|
|
98
|
+
const initialRenderedRootNode = getLatestRootNode();
|
|
99
|
+
if (initialRenderedRootNode.document.hasInvalidComponentNames()) {
|
|
100
|
+
throw new InvalidComponentsError(initialRenderedRootNode);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
find: (component, matcher) => {
|
|
104
|
+
return find(getLatestRootNode(), component, matcher);
|
|
105
|
+
},
|
|
106
|
+
findAll: (component, matcher) => {
|
|
107
|
+
return findAll(getLatestRootNode(), component, matcher);
|
|
108
|
+
},
|
|
109
|
+
findChild: (component, matcher) => {
|
|
110
|
+
return findChild(getLatestRootNode(), component, matcher);
|
|
111
|
+
},
|
|
112
|
+
findAllChildren: (component, matcher) => {
|
|
113
|
+
return findAllChildren(getLatestRootNode(), component, matcher);
|
|
114
|
+
},
|
|
115
|
+
maybeFindChild: (component, matcher) => {
|
|
116
|
+
return maybeFindChild(getLatestRootNode(), component, matcher);
|
|
117
|
+
},
|
|
118
|
+
maybeFind: (component, matcher) => {
|
|
119
|
+
return maybeFind(getLatestRootNode(), component, matcher);
|
|
120
|
+
},
|
|
121
|
+
waitFor: (check, options = DEFAULT_WAIT_FOR_OPTIONS) => {
|
|
122
|
+
const { timeoutInMs = DEFAULT_WAIT_FOR_TIMEOUT_IN_MS } = options;
|
|
123
|
+
try {
|
|
124
|
+
check();
|
|
125
|
+
// Nothing to wait for since the check passed! Resolve the promise immediately.
|
|
126
|
+
return Promise.resolve();
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
// If the check failed then we need to put it in the waitForList so that we
|
|
130
|
+
// can check it again later. We return a promise that will be resolved when
|
|
131
|
+
// the provided check passes.
|
|
132
|
+
}
|
|
133
|
+
const deferred = createDeferred();
|
|
134
|
+
const waitFor = { check, deferred, setTimeoutId: null };
|
|
135
|
+
waitForList.push(waitFor);
|
|
136
|
+
if (timeoutInMs > 0) {
|
|
137
|
+
waitFor.setTimeoutId = setTimeout(() => {
|
|
138
|
+
// Remove the wait for from the list since the timeout expired and we rejected the promise.
|
|
139
|
+
waitForList = waitForList.filter((currentWaitFor) => currentWaitFor !== waitFor);
|
|
140
|
+
deferred.reject(new WaitForTimeoutError(timeoutInMs));
|
|
141
|
+
}, timeoutInMs);
|
|
142
|
+
}
|
|
143
|
+
return deferred.promise;
|
|
144
|
+
},
|
|
145
|
+
debugLog: (label) => {
|
|
146
|
+
return getLatestRootNode().debugLog(label);
|
|
147
|
+
},
|
|
148
|
+
getRootNode: () => {
|
|
149
|
+
return getLatestRootNode();
|
|
150
|
+
},
|
|
151
|
+
isMatch: (targetNode, component, matcher) => {
|
|
152
|
+
return isMatch(targetNode, component, matcher);
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
};
|
|
@@ -235,6 +235,7 @@ export interface RenderResult extends CommonQueryMethods {
|
|
|
235
235
|
* @returns The root node.
|
|
236
236
|
*/
|
|
237
237
|
getRootNode: () => RenderedRootNode;
|
|
238
|
+
isMatch: <TComponentProps extends UnknownComponentProps>(node: RenderedNode | null | undefined, component: HubSpotReactComponent<TComponentProps>, matcher?: ElementMatcher<TComponentProps>) => node is RenderedElementNode<TComponentProps>;
|
|
238
239
|
}
|
|
239
240
|
export type RenderedNode = RenderedElementNode<any> | RenderedRootNode | RenderedFragmentNode | RenderedTextNode;
|
|
240
241
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, } from '../../__synced__/
|
|
1
|
+
export { HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, } from '../../__synced__/remoteComponents.synced';
|
package/dist/pages/home/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, } from '../../__synced__/
|
|
1
|
+
export { HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, } from '../../__synced__/remoteComponents.synced';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/ui-extensions",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
"watch": "npm run clean && tsc --watch",
|
|
13
13
|
"prepare": "npm run build",
|
|
14
14
|
"lint": "echo 'No linter configured for @hubspot/ui-extensions'",
|
|
15
|
-
"test": "
|
|
16
|
-
"
|
|
15
|
+
"test": "vitest run && tsd",
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"test:watch": "vitest",
|
|
17
18
|
"test:types": "tsd"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
@@ -26,7 +27,8 @@
|
|
|
26
27
|
".": "./dist/index.js",
|
|
27
28
|
"./crm": "./dist/crm/index.js",
|
|
28
29
|
"./pages/home": "./dist/pages/home/index.js",
|
|
29
|
-
"./experimental": "./dist/experimental/index.js"
|
|
30
|
+
"./experimental": "./dist/experimental/index.js",
|
|
31
|
+
"./experimental/testing": "./dist/experimental/testing/index.js"
|
|
30
32
|
},
|
|
31
33
|
"license": "MIT",
|
|
32
34
|
"dependencies": {
|
|
@@ -51,28 +53,24 @@
|
|
|
51
53
|
},
|
|
52
54
|
"typescript": {
|
|
53
55
|
"optional": true
|
|
54
|
-
},
|
|
55
|
-
"jest": {
|
|
56
|
-
"optional": true
|
|
57
56
|
}
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
60
59
|
"@testing-library/dom": "^10.4.0",
|
|
61
60
|
"@testing-library/react": "^14.1.2",
|
|
62
|
-
"@types/jest": "^29.5.13",
|
|
63
61
|
"@types/node": "^20.11.0",
|
|
64
62
|
"@types/react": "^18.3.1",
|
|
65
63
|
"@types/react-dom": "^18.3.1",
|
|
66
|
-
"
|
|
67
|
-
"
|
|
64
|
+
"@vitest/coverage-v8": "2.1.8",
|
|
65
|
+
"jsdom": "26.1.0",
|
|
68
66
|
"react-dom": "18.3.1",
|
|
69
67
|
"react-reconciler": "^0.29.0",
|
|
70
|
-
"ts-jest": "^29.1.1",
|
|
71
68
|
"tsd": "^0.33.0",
|
|
72
|
-
"typescript": "5.0.4"
|
|
69
|
+
"typescript": "5.0.4",
|
|
70
|
+
"vitest": "2.1.9"
|
|
73
71
|
},
|
|
74
72
|
"tsd": {
|
|
75
73
|
"directory": "src/__tests__/test-d"
|
|
76
74
|
},
|
|
77
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "6545e14f68b8927c84d75ae45f287a43f19a0bb2"
|
|
78
76
|
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type * as componentTypes from './types/components/index.synced';
|
|
2
|
-
/**
|
|
3
|
-
* The `HeaderActions` component renders a container for action buttons in the app home header. It accepts `PrimaryHeaderActionButton` and `SecondaryHeaderActionButton` as children.
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
export declare const HeaderActions: "HeaderActions" & {
|
|
7
|
-
readonly type?: "HeaderActions" | undefined;
|
|
8
|
-
readonly props?: componentTypes.HeaderActionsProps | undefined;
|
|
9
|
-
readonly children?: true | undefined;
|
|
10
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"HeaderActions", componentTypes.HeaderActionsProps, true>>;
|
|
11
|
-
/**
|
|
12
|
-
* The `PrimaryHeaderActionButton` component renders a primary action button in the app home header. This button is styled as the main call-to-action and only one should be used per `HeaderActions` container.
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
export declare const PrimaryHeaderActionButton: "PrimaryHeaderActionButton" & {
|
|
16
|
-
readonly type?: "PrimaryHeaderActionButton" | undefined;
|
|
17
|
-
readonly props?: componentTypes.HeaderActionButtonProps | undefined;
|
|
18
|
-
readonly children?: true | undefined;
|
|
19
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"PrimaryHeaderActionButton", componentTypes.HeaderActionButtonProps, true>>;
|
|
20
|
-
/**
|
|
21
|
-
* The `SecondaryHeaderActionButton` component renders a secondary action button in the app home header. Multiple secondary actions can be used and they will be grouped appropriately in the header.
|
|
22
|
-
*
|
|
23
|
-
*/
|
|
24
|
-
export declare const SecondaryHeaderActionButton: "SecondaryHeaderActionButton" & {
|
|
25
|
-
readonly type?: "SecondaryHeaderActionButton" | undefined;
|
|
26
|
-
readonly props?: componentTypes.HeaderActionButtonProps | undefined;
|
|
27
|
-
readonly children?: true | undefined;
|
|
28
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"SecondaryHeaderActionButton", componentTypes.HeaderActionButtonProps, true>>;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Do not manually update this file, changes will be autogenerated by our scripts based on: ui-extensions-remote-renderer/static/js/appHomeRemoteComponents.ts
|
|
2
|
-
import { createRemoteReactComponent } from '@remote-ui/react';
|
|
3
|
-
/**
|
|
4
|
-
* The `HeaderActions` component renders a container for action buttons in the app home header. It accepts `PrimaryHeaderActionButton` and `SecondaryHeaderActionButton` as children.
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
export const HeaderActions = createRemoteReactComponent('HeaderActions');
|
|
8
|
-
/**
|
|
9
|
-
* The `PrimaryHeaderActionButton` component renders a primary action button in the app home header. This button is styled as the main call-to-action and only one should be used per `HeaderActions` container.
|
|
10
|
-
*
|
|
11
|
-
*/
|
|
12
|
-
export const PrimaryHeaderActionButton = createRemoteReactComponent('PrimaryHeaderActionButton', {
|
|
13
|
-
fragmentProps: ['overlay'],
|
|
14
|
-
});
|
|
15
|
-
/**
|
|
16
|
-
* The `SecondaryHeaderActionButton` component renders a secondary action button in the app home header. Multiple secondary actions can be used and they will be grouped appropriately in the header.
|
|
17
|
-
*
|
|
18
|
-
*/
|
|
19
|
-
export const SecondaryHeaderActionButton = createRemoteReactComponent('SecondaryHeaderActionButton', {
|
|
20
|
-
fragmentProps: ['overlay'],
|
|
21
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import * as crmTypes from './types/crm.synced';
|
|
2
|
-
export declare const CrmPropertyList: "CrmPropertyList" & {
|
|
3
|
-
readonly type?: "CrmPropertyList" | undefined;
|
|
4
|
-
readonly props?: crmTypes.CrmPropertyListProps | undefined;
|
|
5
|
-
readonly children?: true | undefined;
|
|
6
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmPropertyList", crmTypes.CrmPropertyListProps, true>>;
|
|
7
|
-
export declare const CrmAssociationTable: "CrmAssociationTable" & {
|
|
8
|
-
readonly type?: "CrmAssociationTable" | undefined;
|
|
9
|
-
readonly props?: crmTypes.CrmAssociationTableProps | undefined;
|
|
10
|
-
readonly children?: true | undefined;
|
|
11
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmAssociationTable", crmTypes.CrmAssociationTableProps, true>>;
|
|
12
|
-
export declare const CrmDataHighlight: "CrmDataHighlight" & {
|
|
13
|
-
readonly type?: "CrmDataHighlight" | undefined;
|
|
14
|
-
readonly props?: crmTypes.CrmDataHighlightProps | undefined;
|
|
15
|
-
readonly children?: true | undefined;
|
|
16
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmDataHighlight", crmTypes.CrmDataHighlightProps, true>>;
|
|
17
|
-
export declare const CrmReport: "CrmReport" & {
|
|
18
|
-
readonly type?: "CrmReport" | undefined;
|
|
19
|
-
readonly props?: crmTypes.CrmReportProps | undefined;
|
|
20
|
-
readonly children?: true | undefined;
|
|
21
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmReport", crmTypes.CrmReportProps, true>>;
|
|
22
|
-
export declare const CrmAssociationPivot: "CrmAssociationPivot" & {
|
|
23
|
-
readonly type?: "CrmAssociationPivot" | undefined;
|
|
24
|
-
readonly props?: crmTypes.CrmAssociationPivotProps | undefined;
|
|
25
|
-
readonly children?: true | undefined;
|
|
26
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmAssociationPivot", crmTypes.CrmAssociationPivotProps, true>>;
|
|
27
|
-
export declare const CrmAssociationPropertyList: "CrmAssociationPropertyList" & {
|
|
28
|
-
readonly type?: "CrmAssociationPropertyList" | undefined;
|
|
29
|
-
readonly props?: crmTypes.CrmAssociationPropertyListProps | undefined;
|
|
30
|
-
readonly children?: true | undefined;
|
|
31
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmAssociationPropertyList", crmTypes.CrmAssociationPropertyListProps, true>>;
|
|
32
|
-
export declare const CrmAssociationStageTracker: "CrmAssociationStageTracker" & {
|
|
33
|
-
readonly type?: "CrmAssociationStageTracker" | undefined;
|
|
34
|
-
readonly props?: crmTypes.CrmAssociationStageTrackerProps | undefined;
|
|
35
|
-
readonly children?: true | undefined;
|
|
36
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmAssociationStageTracker", crmTypes.CrmAssociationStageTrackerProps, true>>;
|
|
37
|
-
export declare const CrmSimpleDeadline: "CrmSimpleDeadline" & {
|
|
38
|
-
readonly type?: "CrmSimpleDeadline" | undefined;
|
|
39
|
-
readonly props?: crmTypes.CrmSimpleDeadlineProps | undefined;
|
|
40
|
-
readonly children?: true | undefined;
|
|
41
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmSimpleDeadline", crmTypes.CrmSimpleDeadlineProps, true>>;
|
|
42
|
-
export declare const CrmStageTracker: "CrmStageTracker" & {
|
|
43
|
-
readonly type?: "CrmStageTracker" | undefined;
|
|
44
|
-
readonly props?: crmTypes.CrmStageTrackerProps | undefined;
|
|
45
|
-
readonly children?: true | undefined;
|
|
46
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmStageTracker", crmTypes.CrmStageTrackerProps, true>>;
|
|
47
|
-
export declare const CrmStatistics: "CrmStatistics" & {
|
|
48
|
-
readonly type?: "CrmStatistics" | undefined;
|
|
49
|
-
readonly props?: crmTypes.CrmStatisticsProps | undefined;
|
|
50
|
-
readonly children?: true | undefined;
|
|
51
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmStatistics", crmTypes.CrmStatisticsProps, true>>;
|
|
52
|
-
export declare const CrmActionButton: "CrmActionButton" & {
|
|
53
|
-
readonly type?: "CrmActionButton" | undefined;
|
|
54
|
-
readonly props?: crmTypes.CrmActionButtonProps | undefined;
|
|
55
|
-
readonly children?: true | undefined;
|
|
56
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmActionButton", crmTypes.CrmActionButtonProps, true>>;
|
|
57
|
-
export declare const CrmActionLink: "CrmActionLink" & {
|
|
58
|
-
readonly type?: "CrmActionLink" | undefined;
|
|
59
|
-
readonly props?: crmTypes.CrmActionLinkProps | undefined;
|
|
60
|
-
readonly children?: true | undefined;
|
|
61
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmActionLink", crmTypes.CrmActionLinkProps, true>>;
|
|
62
|
-
export declare const CrmCardActions: "CrmCardActions" & {
|
|
63
|
-
readonly type?: "CrmCardActions" | undefined;
|
|
64
|
-
readonly props?: crmTypes.CrmCardActionsProps | undefined;
|
|
65
|
-
readonly children?: true | undefined;
|
|
66
|
-
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CrmCardActions", crmTypes.CrmCardActionsProps, true>>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Do not manually update this file, changes will be autogenerated by our scripts based on: ui-extensions-remote-renderer/static/js/crmRemoteComponents.ts
|
|
2
|
-
import { createRemoteReactComponent } from '@remote-ui/react';
|
|
3
|
-
export const CrmPropertyList = createRemoteReactComponent('CrmPropertyList');
|
|
4
|
-
export const CrmAssociationTable = createRemoteReactComponent('CrmAssociationTable');
|
|
5
|
-
export const CrmDataHighlight = createRemoteReactComponent('CrmDataHighlight');
|
|
6
|
-
export const CrmReport = createRemoteReactComponent('CrmReport');
|
|
7
|
-
export const CrmAssociationPivot = createRemoteReactComponent('CrmAssociationPivot');
|
|
8
|
-
export const CrmAssociationPropertyList = createRemoteReactComponent('CrmAssociationPropertyList');
|
|
9
|
-
export const CrmAssociationStageTracker = createRemoteReactComponent('CrmAssociationStageTracker');
|
|
10
|
-
export const CrmSimpleDeadline = createRemoteReactComponent('CrmSimpleDeadline');
|
|
11
|
-
export const CrmStageTracker = createRemoteReactComponent('CrmStageTracker');
|
|
12
|
-
export const CrmStatistics = createRemoteReactComponent('CrmStatistics');
|
|
13
|
-
export const CrmActionButton = createRemoteReactComponent('CrmActionButton');
|
|
14
|
-
export const CrmActionLink = createRemoteReactComponent('CrmActionLink');
|
|
15
|
-
export const CrmCardActions = createRemoteReactComponent('CrmCardActions');
|