@hubspot/ui-extensions 0.9.2 → 0.9.4
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/__tests__/experimental/crm/fetchAssociations.spec.d.ts +1 -0
- package/dist/__tests__/experimental/crm/fetchAssociations.spec.js +396 -0
- package/dist/__tests__/experimental/crm/fetchCrmProperties.spec.js +180 -14
- package/dist/__tests__/experimental/hooks/useAssociations.spec.d.ts +1 -0
- package/dist/__tests__/experimental/hooks/useAssociations.spec.js +368 -0
- package/dist/__tests__/experimental/hooks/useCrmProperties.spec.js +151 -4
- package/dist/experimental/crm/fetchAssociations.d.ts +29 -0
- package/dist/experimental/crm/fetchAssociations.js +42 -0
- package/dist/experimental/crm/fetchCrmProperties.d.ts +23 -2
- package/dist/experimental/crm/fetchCrmProperties.js +22 -16
- package/dist/experimental/hooks/useAssociations.d.ts +10 -0
- package/dist/experimental/hooks/useAssociations.js +116 -0
- package/dist/experimental/hooks/useCrmProperties.d.ts +3 -2
- package/dist/experimental/hooks/useCrmProperties.js +95 -31
- package/dist/experimental/index.d.ts +34 -1
- package/dist/experimental/index.js +19 -4
- package/dist/experimental/types.d.ts +212 -6
- package/dist/types.d.ts +15 -1
- package/dist/types.js +1 -0
- package/package.json +2 -2
|
@@ -4,29 +4,35 @@ function isCrmPropertiesResponse(data) {
|
|
|
4
4
|
// Confirm the data is a defined object
|
|
5
5
|
data === null ||
|
|
6
6
|
typeof data !== 'object' ||
|
|
7
|
-
// Confirm all keys and values are strings
|
|
8
|
-
!Object.keys(data).every((key) => typeof key === 'string' &&
|
|
7
|
+
// Confirm all keys and values are strings, or null
|
|
8
|
+
!Object.keys(data).every((key) => typeof key === 'string' &&
|
|
9
|
+
(typeof data[key] === 'string' || data[key] === null))) {
|
|
9
10
|
return false;
|
|
10
11
|
}
|
|
11
12
|
return true;
|
|
12
13
|
}
|
|
13
|
-
export const fetchCrmProperties = async (propertyNames, propertiesUpdatedCallback) => {
|
|
14
|
+
export const fetchCrmProperties = async (propertyNames, propertiesUpdatedCallback, options) => {
|
|
15
|
+
let response;
|
|
16
|
+
let result;
|
|
14
17
|
try {
|
|
15
18
|
// eslint-disable-next-line hubspot-dev/no-confusing-browser-globals
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
throw new Error(`Failed to fetch CRM properties: ${response.statusText}`);
|
|
19
|
-
}
|
|
20
|
-
const data = await response.json();
|
|
21
|
-
if (!isCrmPropertiesResponse(data)) {
|
|
22
|
-
throw new Error('Invalid response format');
|
|
23
|
-
}
|
|
24
|
-
return data;
|
|
19
|
+
response = await self.fetchCrmProperties(propertyNames, propertiesUpdatedCallback, options);
|
|
20
|
+
result = await response.json();
|
|
25
21
|
}
|
|
26
22
|
catch (error) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
// Only handle network/parsing errors, not our validation errors
|
|
24
|
+
throw error instanceof Error
|
|
25
|
+
? error
|
|
26
|
+
: new Error('Failed to fetch CRM properties: Unknown error');
|
|
31
27
|
}
|
|
28
|
+
if (result.error) {
|
|
29
|
+
throw new Error(result.error);
|
|
30
|
+
}
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new Error(`Failed to fetch CRM properties: ${response.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
if (!isCrmPropertiesResponse(result.data)) {
|
|
35
|
+
throw new Error('Invalid response format');
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
32
38
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type FetchAssociationsRequest, type AssociationsResponse } from '../crm/fetchAssociations';
|
|
2
|
+
import { type FetchCrmPropertiesOptions } from '../crm/fetchCrmProperties';
|
|
3
|
+
export interface AssociationsState {
|
|
4
|
+
results: AssociationsResponse['results'];
|
|
5
|
+
error: Error | null;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
hasMore: boolean;
|
|
8
|
+
nextOffset: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function useAssociations(request: FetchAssociationsRequest, options?: FetchCrmPropertiesOptions): AssociationsState;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useEffect, useReducer, useMemo, useRef } from 'react';
|
|
2
|
+
import { logger } from '../../logger';
|
|
3
|
+
import { fetchAssociations, } from '../crm/fetchAssociations';
|
|
4
|
+
const initialState = {
|
|
5
|
+
results: [],
|
|
6
|
+
error: null,
|
|
7
|
+
isLoading: true,
|
|
8
|
+
hasMore: false,
|
|
9
|
+
nextOffset: 0,
|
|
10
|
+
};
|
|
11
|
+
function associationsReducer(state, action) {
|
|
12
|
+
switch (action.type) {
|
|
13
|
+
case 'FETCH_START':
|
|
14
|
+
return {
|
|
15
|
+
...state,
|
|
16
|
+
isLoading: true,
|
|
17
|
+
error: null,
|
|
18
|
+
};
|
|
19
|
+
case 'FETCH_SUCCESS':
|
|
20
|
+
return {
|
|
21
|
+
...state,
|
|
22
|
+
isLoading: false,
|
|
23
|
+
results: action.payload.results,
|
|
24
|
+
hasMore: action.payload.hasMore,
|
|
25
|
+
nextOffset: action.payload.nextOffset,
|
|
26
|
+
error: null,
|
|
27
|
+
};
|
|
28
|
+
case 'FETCH_ERROR':
|
|
29
|
+
return {
|
|
30
|
+
...state,
|
|
31
|
+
isLoading: false,
|
|
32
|
+
error: action.payload,
|
|
33
|
+
results: [],
|
|
34
|
+
hasMore: false,
|
|
35
|
+
nextOffset: 0,
|
|
36
|
+
};
|
|
37
|
+
default:
|
|
38
|
+
return state;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const DEFAULT_OPTIONS = {};
|
|
42
|
+
export function useAssociations(request, options = DEFAULT_OPTIONS) {
|
|
43
|
+
const [state, dispatch] = useReducer(associationsReducer, initialState);
|
|
44
|
+
// Log experimental warning once on mount
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
logger.warn('useAssociations is an experimental hook and might change or be removed in the future.');
|
|
47
|
+
}, []);
|
|
48
|
+
/**
|
|
49
|
+
* HOOK OPTIMIZATION:
|
|
50
|
+
*
|
|
51
|
+
* Create stable references for request and options to prevent unnecessary re-renders and API calls.
|
|
52
|
+
* Then, external developers can pass inline objects without worrying about memoization
|
|
53
|
+
* We handle the deep equality comparison ourselves, and return the same object reference when content is equivalent.
|
|
54
|
+
*/
|
|
55
|
+
const lastRequestRef = useRef();
|
|
56
|
+
const lastRequestKeyRef = useRef();
|
|
57
|
+
const lastOptionsRef = useRef();
|
|
58
|
+
const lastOptionsKeyRef = useRef();
|
|
59
|
+
const stableRequest = useMemo(() => {
|
|
60
|
+
const requestKey = JSON.stringify(request);
|
|
61
|
+
if (requestKey === lastRequestKeyRef.current) {
|
|
62
|
+
return lastRequestRef.current;
|
|
63
|
+
}
|
|
64
|
+
lastRequestKeyRef.current = requestKey;
|
|
65
|
+
lastRequestRef.current = request;
|
|
66
|
+
return request;
|
|
67
|
+
}, [request]);
|
|
68
|
+
const stableOptions = useMemo(() => {
|
|
69
|
+
const optionsKey = JSON.stringify(options);
|
|
70
|
+
if (optionsKey === lastOptionsKeyRef.current) {
|
|
71
|
+
return lastOptionsRef.current;
|
|
72
|
+
}
|
|
73
|
+
lastOptionsKeyRef.current = optionsKey;
|
|
74
|
+
lastOptionsRef.current = options;
|
|
75
|
+
return options;
|
|
76
|
+
}, [options]);
|
|
77
|
+
// Fetch the associations
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
let cancelled = false;
|
|
80
|
+
let cleanup = null;
|
|
81
|
+
const fetchData = async () => {
|
|
82
|
+
try {
|
|
83
|
+
dispatch({ type: 'FETCH_START' });
|
|
84
|
+
const result = await fetchAssociations(stableRequest, stableOptions);
|
|
85
|
+
if (!cancelled) {
|
|
86
|
+
dispatch({
|
|
87
|
+
type: 'FETCH_SUCCESS',
|
|
88
|
+
payload: {
|
|
89
|
+
results: result.data.results,
|
|
90
|
+
hasMore: result.data.hasMore,
|
|
91
|
+
nextOffset: result.data.nextOffset,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
cleanup = result.cleanup;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
if (!cancelled) {
|
|
99
|
+
const errorData = err instanceof Error
|
|
100
|
+
? err
|
|
101
|
+
: new Error('Failed to fetch associations');
|
|
102
|
+
dispatch({ type: 'FETCH_ERROR', payload: errorData });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
fetchData();
|
|
107
|
+
return () => {
|
|
108
|
+
cancelled = true;
|
|
109
|
+
// Call cleanup function to release resources
|
|
110
|
+
if (cleanup) {
|
|
111
|
+
cleanup();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}, [stableRequest, stableOptions]);
|
|
115
|
+
return state;
|
|
116
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type FetchCrmPropertiesOptions } from '../crm/fetchCrmProperties';
|
|
1
2
|
export interface CrmPropertiesState {
|
|
2
|
-
properties: Record<string, string>;
|
|
3
|
+
properties: Record<string, string | null>;
|
|
3
4
|
error: Error | null;
|
|
4
5
|
isLoading: boolean;
|
|
5
6
|
}
|
|
@@ -8,4 +9,4 @@ export interface CrmPropertiesState {
|
|
|
8
9
|
*
|
|
9
10
|
* @experimental This hook is experimental and might change or be removed in future versions.
|
|
10
11
|
*/
|
|
11
|
-
export declare function useCrmProperties(propertyNames: string[]): CrmPropertiesState;
|
|
12
|
+
export declare function useCrmProperties(propertyNames: string[], options?: FetchCrmPropertiesOptions): CrmPropertiesState;
|
|
@@ -1,49 +1,113 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useReducer, useMemo, useRef } from 'react';
|
|
2
2
|
import { logger } from '../../logger';
|
|
3
|
-
import { fetchCrmProperties } from '../crm/fetchCrmProperties';
|
|
3
|
+
import { fetchCrmProperties, } from '../crm/fetchCrmProperties';
|
|
4
|
+
const initialState = {
|
|
5
|
+
properties: {},
|
|
6
|
+
error: null,
|
|
7
|
+
isLoading: true,
|
|
8
|
+
};
|
|
9
|
+
function crmPropertiesReducer(state, action) {
|
|
10
|
+
switch (action.type) {
|
|
11
|
+
case 'FETCH_START':
|
|
12
|
+
return {
|
|
13
|
+
...state,
|
|
14
|
+
isLoading: true,
|
|
15
|
+
error: null,
|
|
16
|
+
};
|
|
17
|
+
case 'FETCH_SUCCESS':
|
|
18
|
+
return {
|
|
19
|
+
...state,
|
|
20
|
+
isLoading: false,
|
|
21
|
+
properties: action.payload,
|
|
22
|
+
error: null,
|
|
23
|
+
};
|
|
24
|
+
case 'FETCH_ERROR':
|
|
25
|
+
return {
|
|
26
|
+
...state,
|
|
27
|
+
isLoading: false,
|
|
28
|
+
error: action.payload,
|
|
29
|
+
properties: {},
|
|
30
|
+
};
|
|
31
|
+
default:
|
|
32
|
+
return state;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const DEFAULT_OPTIONS = {};
|
|
4
36
|
/**
|
|
5
37
|
* A hook for using and managing CRM properties.
|
|
6
38
|
*
|
|
7
39
|
* @experimental This hook is experimental and might change or be removed in future versions.
|
|
8
40
|
*/
|
|
9
|
-
export function useCrmProperties(propertyNames) {
|
|
10
|
-
const [
|
|
11
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
12
|
-
const [error, setError] = useState(null);
|
|
41
|
+
export function useCrmProperties(propertyNames, options = DEFAULT_OPTIONS) {
|
|
42
|
+
const [state, dispatch] = useReducer(crmPropertiesReducer, initialState);
|
|
13
43
|
// Log experimental warning once on mount
|
|
14
44
|
useEffect(() => {
|
|
15
45
|
logger.warn('useCrmProperties is an experimental hook and might change or be removed in the future.');
|
|
16
46
|
}, []);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
47
|
+
/**
|
|
48
|
+
* HOOK OPTIMIZATION:
|
|
49
|
+
*
|
|
50
|
+
* Create stable references for propertyNames and options to prevent unnecessary re-renders and API calls.
|
|
51
|
+
* Then, external developers can pass inline arrays/objects without worrying about memoization
|
|
52
|
+
* We handle the deep equality comparison ourselves, and return the same object reference when content is equivalent.
|
|
53
|
+
*/
|
|
54
|
+
const lastPropertyNamesRef = useRef();
|
|
55
|
+
const lastPropertyNamesKeyRef = useRef();
|
|
56
|
+
const lastOptionsRef = useRef();
|
|
57
|
+
const lastOptionsKeyRef = useRef();
|
|
58
|
+
const stablePropertyNames = useMemo(() => {
|
|
59
|
+
const sortedNames = [...propertyNames].sort();
|
|
60
|
+
const propertyNamesKey = JSON.stringify(sortedNames);
|
|
61
|
+
if (propertyNamesKey === lastPropertyNamesKeyRef.current) {
|
|
62
|
+
return lastPropertyNamesRef.current;
|
|
63
|
+
}
|
|
64
|
+
lastPropertyNamesKeyRef.current = propertyNamesKey;
|
|
65
|
+
lastPropertyNamesRef.current = sortedNames;
|
|
66
|
+
return sortedNames;
|
|
67
|
+
}, [propertyNames]);
|
|
68
|
+
const stableOptions = useMemo(() => {
|
|
69
|
+
const optionsKey = JSON.stringify(options);
|
|
70
|
+
if (optionsKey === lastOptionsKeyRef.current) {
|
|
71
|
+
return lastOptionsRef.current;
|
|
72
|
+
}
|
|
73
|
+
lastOptionsKeyRef.current = optionsKey;
|
|
74
|
+
lastOptionsRef.current = options;
|
|
75
|
+
return options;
|
|
76
|
+
}, [options]);
|
|
20
77
|
// Fetch the properties
|
|
21
78
|
useEffect(() => {
|
|
22
|
-
|
|
79
|
+
let cancelled = false;
|
|
80
|
+
let cleanup = null;
|
|
81
|
+
const fetchData = async () => {
|
|
23
82
|
try {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
83
|
+
dispatch({ type: 'FETCH_START' });
|
|
84
|
+
const result = await fetchCrmProperties(stablePropertyNames, (data) => {
|
|
85
|
+
if (!cancelled) {
|
|
86
|
+
dispatch({ type: 'FETCH_SUCCESS', payload: data });
|
|
87
|
+
}
|
|
88
|
+
}, stableOptions);
|
|
89
|
+
if (!cancelled) {
|
|
90
|
+
dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
|
|
91
|
+
cleanup = result.cleanup;
|
|
92
|
+
}
|
|
27
93
|
}
|
|
28
94
|
catch (err) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
95
|
+
if (!cancelled) {
|
|
96
|
+
const errorData = err instanceof Error
|
|
97
|
+
? err
|
|
98
|
+
: new Error('Failed to fetch CRM properties');
|
|
99
|
+
dispatch({ type: 'FETCH_ERROR', payload: errorData });
|
|
100
|
+
}
|
|
34
101
|
}
|
|
35
|
-
|
|
36
|
-
|
|
102
|
+
};
|
|
103
|
+
fetchData();
|
|
104
|
+
return () => {
|
|
105
|
+
cancelled = true;
|
|
106
|
+
// Call cleanup function to release RPC resources
|
|
107
|
+
if (cleanup) {
|
|
108
|
+
cleanup();
|
|
37
109
|
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
setIsLoading(false);
|
|
42
|
-
});
|
|
43
|
-
}, [propertyNames]);
|
|
44
|
-
return {
|
|
45
|
-
properties,
|
|
46
|
-
error,
|
|
47
|
-
isLoading,
|
|
48
|
-
};
|
|
110
|
+
};
|
|
111
|
+
}, [stablePropertyNames, stableOptions]);
|
|
112
|
+
return state;
|
|
49
113
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as types from '../types';
|
|
2
2
|
import type * as experimentalTypes from './types';
|
|
3
3
|
export { useCrmProperties } from './hooks/useCrmProperties';
|
|
4
|
+
export { useAssociations } from './hooks/useAssociations';
|
|
4
5
|
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
5
6
|
declare const Iframe: "Iframe" & {
|
|
6
7
|
readonly type?: "Iframe" | undefined;
|
|
@@ -83,4 +84,36 @@ declare const FileInput: "FileInput" & {
|
|
|
83
84
|
readonly props?: experimentalTypes.FileInputProps | undefined;
|
|
84
85
|
readonly children?: true | undefined;
|
|
85
86
|
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"FileInput", experimentalTypes.FileInputProps, true>>;
|
|
86
|
-
|
|
87
|
+
/**
|
|
88
|
+
* The TimeInput component renders an input field where a user can select a time.
|
|
89
|
+
*/
|
|
90
|
+
declare const TimeInput: "TimeInput" & {
|
|
91
|
+
readonly type?: "TimeInput" | undefined;
|
|
92
|
+
readonly props?: experimentalTypes.TimeInputProps | undefined;
|
|
93
|
+
readonly children?: true | undefined;
|
|
94
|
+
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"TimeInput", experimentalTypes.TimeInputProps, true>>;
|
|
95
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
96
|
+
declare const CurrencyInput: "CurrencyInput" & {
|
|
97
|
+
readonly type?: "CurrencyInput" | undefined;
|
|
98
|
+
readonly props?: experimentalTypes.CurrencyInputProps | undefined;
|
|
99
|
+
readonly children?: true | undefined;
|
|
100
|
+
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"CurrencyInput", experimentalTypes.CurrencyInputProps, true>>;
|
|
101
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
102
|
+
declare const HeaderActions: "HeaderActions" & {
|
|
103
|
+
readonly type?: "HeaderActions" | undefined;
|
|
104
|
+
readonly props?: experimentalTypes.HeaderActionsProps | undefined;
|
|
105
|
+
readonly children?: true | undefined;
|
|
106
|
+
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"HeaderActions", experimentalTypes.HeaderActionsProps, true>>;
|
|
107
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
108
|
+
declare const PrimaryHeaderActionButton: "PrimaryHeaderActionButton" & {
|
|
109
|
+
readonly type?: "PrimaryHeaderActionButton" | undefined;
|
|
110
|
+
readonly props?: experimentalTypes.HeaderActionButtonProps | undefined;
|
|
111
|
+
readonly children?: true | undefined;
|
|
112
|
+
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"PrimaryHeaderActionButton", experimentalTypes.HeaderActionButtonProps, true>>;
|
|
113
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
114
|
+
declare const SecondaryHeaderActionButton: "SecondaryHeaderActionButton" & {
|
|
115
|
+
readonly type?: "SecondaryHeaderActionButton" | undefined;
|
|
116
|
+
readonly props?: experimentalTypes.HeaderActionButtonProps | undefined;
|
|
117
|
+
readonly children?: true | undefined;
|
|
118
|
+
} & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"SecondaryHeaderActionButton", experimentalTypes.HeaderActionButtonProps, true>>;
|
|
119
|
+
export { Iframe, MediaObject, Inline, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, TimeInput, CurrencyInput, HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRemoteReactComponent } from '@remote-ui/react';
|
|
2
2
|
export { useCrmProperties } from './hooks/useCrmProperties';
|
|
3
|
+
export { useAssociations } from './hooks/useAssociations';
|
|
3
4
|
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
4
5
|
const Iframe = createRemoteReactComponent('Iframe');
|
|
5
6
|
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
@@ -34,8 +35,22 @@ const ExpandableText = createRemoteReactComponent('ExpandableText');
|
|
|
34
35
|
*
|
|
35
36
|
* - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/popover Popover Docs}
|
|
36
37
|
*/
|
|
37
|
-
const Popover = createRemoteReactComponent('Popover'
|
|
38
|
-
fragmentProps: ['header', 'body', 'footer'],
|
|
39
|
-
});
|
|
38
|
+
const Popover = createRemoteReactComponent('Popover');
|
|
40
39
|
const FileInput = createRemoteReactComponent('FileInput');
|
|
41
|
-
|
|
40
|
+
/**
|
|
41
|
+
* The TimeInput component renders an input field where a user can select a time.
|
|
42
|
+
*/
|
|
43
|
+
const TimeInput = createRemoteReactComponent('TimeInput');
|
|
44
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
45
|
+
const CurrencyInput = createRemoteReactComponent('CurrencyInput');
|
|
46
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
47
|
+
const HeaderActions = createRemoteReactComponent('HeaderActions');
|
|
48
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
49
|
+
const PrimaryHeaderActionButton = createRemoteReactComponent('PrimaryHeaderActionButton', {
|
|
50
|
+
fragmentProps: ['overlay'],
|
|
51
|
+
});
|
|
52
|
+
/** @experimental This component is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates. */
|
|
53
|
+
const SecondaryHeaderActionButton = createRemoteReactComponent('SecondaryHeaderActionButton', {
|
|
54
|
+
fragmentProps: ['overlay'],
|
|
55
|
+
});
|
|
56
|
+
export { Iframe, MediaObject, Inline, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, TimeInput, CurrencyInput, HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, };
|
|
@@ -127,17 +127,39 @@ export interface ExpandableTextProps {
|
|
|
127
127
|
* @experimental do not use in production
|
|
128
128
|
*/
|
|
129
129
|
export interface PopoverProps {
|
|
130
|
+
/**
|
|
131
|
+
* A unique ID for the popover. Used to identify the popover in the overlay system.
|
|
132
|
+
*
|
|
133
|
+
*/
|
|
134
|
+
id: string;
|
|
135
|
+
/**
|
|
136
|
+
* The content to render inside the popover.
|
|
137
|
+
*/
|
|
130
138
|
children: ReactNode;
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
/**
|
|
140
|
+
* The placement of the popover.
|
|
141
|
+
*
|
|
142
|
+
* @defaultValue `top`
|
|
143
|
+
*/
|
|
133
144
|
placement?: 'left' | 'right' | 'top' | 'bottom';
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
/**
|
|
146
|
+
* The variant of the popover.
|
|
147
|
+
*
|
|
148
|
+
* @defaultValue `default`
|
|
149
|
+
*/
|
|
137
150
|
variant?: 'default' | 'shepherd' | 'longform';
|
|
151
|
+
/**
|
|
152
|
+
* If set to `true`, will show the close button in the popover. PopoverHeader required to display close button.
|
|
153
|
+
*
|
|
154
|
+
* @defaultValue `false`
|
|
155
|
+
*/
|
|
138
156
|
showCloseButton?: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* The size of the arrow in the popover. If set to `none`, the arrow will not be displayed.
|
|
159
|
+
*
|
|
160
|
+
* @defaultValue `small`
|
|
161
|
+
*/
|
|
139
162
|
arrowSize?: 'none' | 'small' | 'medium';
|
|
140
|
-
onClick?: ReactionsHandler<ExtensionEvent>;
|
|
141
163
|
}
|
|
142
164
|
export interface FileInputProps {
|
|
143
165
|
value?: File | {
|
|
@@ -146,4 +168,188 @@ export interface FileInputProps {
|
|
|
146
168
|
name: string;
|
|
147
169
|
onChange: (event: any) => void;
|
|
148
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* @ignore
|
|
173
|
+
* @experimental do not use in production
|
|
174
|
+
*/
|
|
175
|
+
export interface BaseTime {
|
|
176
|
+
/** The hour for the time (0 to 23) in 24-hour format (e.g. 0 = 12:00 AM, 9 = 9:00 AM, 15 = 3:00 PM). */
|
|
177
|
+
hours: number;
|
|
178
|
+
/** The minutes for the time (0 to 59). */
|
|
179
|
+
minutes: number;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Generic collection of props for all inputs (experimental version)
|
|
183
|
+
* @internal
|
|
184
|
+
* */
|
|
185
|
+
export interface BaseInputProps<T = string, V = string> {
|
|
186
|
+
/**
|
|
187
|
+
* The label text to display for the form input element.
|
|
188
|
+
*/
|
|
189
|
+
label: string;
|
|
190
|
+
/**
|
|
191
|
+
* The unique identifier for the input element, this could be thought of as the HTML5 [Input element's name attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name).
|
|
192
|
+
*/
|
|
193
|
+
name: string;
|
|
194
|
+
/**
|
|
195
|
+
* The value of the input.
|
|
196
|
+
*/
|
|
197
|
+
value?: T;
|
|
198
|
+
/**
|
|
199
|
+
* Determines if the required indicator should be displayed.
|
|
200
|
+
*
|
|
201
|
+
* @defaultValue `false`
|
|
202
|
+
*/
|
|
203
|
+
required?: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Determines if the field is editable or not.
|
|
206
|
+
*
|
|
207
|
+
* @defaultValue `false`
|
|
208
|
+
*/
|
|
209
|
+
readOnly?: boolean;
|
|
210
|
+
/**
|
|
211
|
+
* Instructional message to display to the user to help understand the purpose of the input.
|
|
212
|
+
*/
|
|
213
|
+
description?: string;
|
|
214
|
+
/**
|
|
215
|
+
* Text that will appear in a tooltip next to the input label.
|
|
216
|
+
*/
|
|
217
|
+
tooltip?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Text that appears in the input when it has no value set.
|
|
220
|
+
*/
|
|
221
|
+
placeholder?: string;
|
|
222
|
+
/**
|
|
223
|
+
* If set to `true`, `validationMessage` is displayed as an error message, if it was provided. The input will also render its error state to let the user know there is an error. If set to `false`, `validationMessage` is displayed as a success message.
|
|
224
|
+
*
|
|
225
|
+
* @defaultValue `false`
|
|
226
|
+
*/
|
|
227
|
+
error?: boolean;
|
|
228
|
+
/**
|
|
229
|
+
* The value of the input on the first render.
|
|
230
|
+
*/
|
|
231
|
+
defaultValue?: T;
|
|
232
|
+
/**
|
|
233
|
+
* The text to show under the input for error or success validations.
|
|
234
|
+
*/
|
|
235
|
+
validationMessage?: string;
|
|
236
|
+
/**
|
|
237
|
+
* A callback function that is invoked when the value is committed. Currently these times are `onBlur` of the input and when the user submits the form.
|
|
238
|
+
*
|
|
239
|
+
* @event
|
|
240
|
+
*/
|
|
241
|
+
onChange?: (value: V) => void;
|
|
242
|
+
/**
|
|
243
|
+
* A function that is called and passed the value every time the field is edited by the user. It is recommended that you do not use this value to update state, that is what `onChange` should be used for. Instead this should be used for validation.
|
|
244
|
+
*
|
|
245
|
+
* @event
|
|
246
|
+
*/
|
|
247
|
+
onInput?: (value: V) => void;
|
|
248
|
+
/**
|
|
249
|
+
* A function that is called and passed the value every time the field loses focus.
|
|
250
|
+
*
|
|
251
|
+
* @event
|
|
252
|
+
*/
|
|
253
|
+
onBlur?: (value: V) => void;
|
|
254
|
+
/**
|
|
255
|
+
* A function that is called and passed the value every time the field gets focused.
|
|
256
|
+
*
|
|
257
|
+
* @event
|
|
258
|
+
*/
|
|
259
|
+
onFocus?: (value: V) => void;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* @ignore
|
|
263
|
+
* @experimental do not use in production
|
|
264
|
+
*
|
|
265
|
+
* The values used to invoke events on the TimeInput component
|
|
266
|
+
*/
|
|
267
|
+
export interface TimeInputEventsPayload extends BaseTime {
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* @internal
|
|
271
|
+
* @ignore
|
|
272
|
+
* */
|
|
273
|
+
type BaseTimeInputForTime = Omit<BaseInputProps<BaseTime | null, TimeInputEventsPayload>, 'onInput' | 'placeholder' | 'onChange'>;
|
|
274
|
+
/**
|
|
275
|
+
* @ignore
|
|
276
|
+
* @experimental do not use in production
|
|
277
|
+
*/
|
|
278
|
+
export interface TimeInputProps extends BaseTimeInputForTime {
|
|
279
|
+
/**
|
|
280
|
+
* A callback function that is invoked when the value is changed.
|
|
281
|
+
*
|
|
282
|
+
* @event
|
|
283
|
+
*/
|
|
284
|
+
onChange?: (value: TimeInputEventsPayload) => void;
|
|
285
|
+
/**
|
|
286
|
+
* Sets the earliest time that will be valid.
|
|
287
|
+
*/
|
|
288
|
+
min?: BaseTime;
|
|
289
|
+
/**
|
|
290
|
+
* Sets the latest time that will be valid.
|
|
291
|
+
*/
|
|
292
|
+
max?: BaseTime;
|
|
293
|
+
/**
|
|
294
|
+
* Sets the interval (in minutes) between the dropdown options.
|
|
295
|
+
*
|
|
296
|
+
* @defaultValue `30`
|
|
297
|
+
*/
|
|
298
|
+
interval?: number;
|
|
299
|
+
/**
|
|
300
|
+
* Sets the timezone that the component will display alongside times in the TimePicker. This will not adjust the available valid inputs.
|
|
301
|
+
*
|
|
302
|
+
*/
|
|
303
|
+
timezone?: 'userTz' | 'portalTz';
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* @ignore
|
|
307
|
+
* @experimental do not use in production
|
|
308
|
+
*/
|
|
309
|
+
type BaseInputForNumber = Omit<BaseInputProps<number, number>, 'onInput'>;
|
|
310
|
+
/**
|
|
311
|
+
* @ignore
|
|
312
|
+
* @experimental do not use in production
|
|
313
|
+
*/
|
|
314
|
+
export interface CurrencyInputProps extends BaseInputForNumber {
|
|
315
|
+
/**
|
|
316
|
+
* ISO 4217 currency code (e.g., "USD", "EUR", "JPY")
|
|
317
|
+
* @defaultValue "USD"
|
|
318
|
+
*/
|
|
319
|
+
currency?: string;
|
|
320
|
+
/**
|
|
321
|
+
* Sets the number of decimal places for the currency
|
|
322
|
+
* If not provided, defaults to currency-specific precision
|
|
323
|
+
*/
|
|
324
|
+
precision?: number;
|
|
325
|
+
/**
|
|
326
|
+
* Sets the lower bound of the input
|
|
327
|
+
*/
|
|
328
|
+
min?: number;
|
|
329
|
+
/**
|
|
330
|
+
* Sets the upper bound of the input
|
|
331
|
+
*/
|
|
332
|
+
max?: number;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* @ignore
|
|
336
|
+
* @experimental do not use in production
|
|
337
|
+
*/
|
|
338
|
+
export interface HeaderActionsProps {
|
|
339
|
+
children: ReactNode;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* @ignore
|
|
343
|
+
* @experimental do not use in production
|
|
344
|
+
*/
|
|
345
|
+
export interface HeaderActionButtonProps {
|
|
346
|
+
onClick?: ReactionsHandler<ExtensionEvent>;
|
|
347
|
+
href?: string | {
|
|
348
|
+
url: string;
|
|
349
|
+
external?: boolean;
|
|
350
|
+
};
|
|
351
|
+
disabled?: boolean;
|
|
352
|
+
children: ReactNode;
|
|
353
|
+
overlay?: RemoteFragment;
|
|
354
|
+
}
|
|
149
355
|
export {};
|