@hubspot/ui-extensions 0.8.53 → 0.8.55

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.
@@ -24,8 +24,8 @@ describe('fetchCrmProperties', () => {
24
24
  };
25
25
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
26
26
  const propertyNames = ['firstname', 'lastname'];
27
- const result = await fetchCrmProperties(propertyNames);
28
- expect(mockFetchCrmProperties).toHaveBeenCalledWith(propertyNames);
27
+ const result = await fetchCrmProperties(propertyNames, jest.fn());
28
+ expect(mockFetchCrmProperties).toHaveBeenCalledWith(propertyNames, expect.any(Function));
29
29
  expect(result).toEqual({
30
30
  firstname: 'Test value for firstname',
31
31
  lastname: 'Test value for lastname',
@@ -38,12 +38,12 @@ describe('fetchCrmProperties', () => {
38
38
  };
39
39
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
40
40
  const propertyNames = ['firstname'];
41
- await expect(fetchCrmProperties(propertyNames)).rejects.toThrow('Failed to fetch CRM properties: Not Found');
41
+ await expect(fetchCrmProperties(propertyNames, jest.fn())).rejects.toThrow('Failed to fetch CRM properties: Not Found');
42
42
  });
43
43
  it('throws an error when fetch fails', async () => {
44
44
  mockFetchCrmProperties.mockRejectedValue(new Error('Network error'));
45
45
  const propertyNames = ['firstname'];
46
- await expect(fetchCrmProperties(propertyNames)).rejects.toThrow('Network error');
46
+ await expect(fetchCrmProperties(propertyNames, jest.fn())).rejects.toThrow('Network error');
47
47
  });
48
48
  it('throws an error if the response is not an object', async () => {
49
49
  const mockApiResponse = 'Invalid response';
@@ -53,6 +53,31 @@ describe('fetchCrmProperties', () => {
53
53
  };
54
54
  mockFetchCrmProperties.mockResolvedValue(mockResponse);
55
55
  const propertyNames = ['firstname'];
56
- await expect(fetchCrmProperties(propertyNames)).rejects.toThrow('Invalid response format');
56
+ await expect(fetchCrmProperties(propertyNames, jest.fn())).rejects.toThrow('Invalid response format');
57
+ });
58
+ it('passes the propertiesUpdatedCallback and allows it to be called', async () => {
59
+ let capturedCallback;
60
+ const mockApiResponse = {
61
+ firstname: 'Initial',
62
+ lastname: 'Initial',
63
+ };
64
+ const mockResponse = {
65
+ ok: true,
66
+ json: jest.fn().mockResolvedValue(mockApiResponse),
67
+ };
68
+ mockFetchCrmProperties.mockImplementation((propertyNames, callback) => {
69
+ capturedCallback = callback;
70
+ return Promise.resolve(mockResponse);
71
+ });
72
+ const propertyNames = ['firstname', 'lastname'];
73
+ const mockCallback = jest.fn();
74
+ await fetchCrmProperties(propertyNames, mockCallback);
75
+ expect(typeof capturedCallback).toBe('function');
76
+ // Simulate the callback being called with new properties
77
+ const updatedProps = { firstname: 'Updated', lastname: 'Updated' };
78
+ if (capturedCallback) {
79
+ capturedCallback(updatedProps);
80
+ }
81
+ expect(mockCallback).toHaveBeenCalledWith(updatedProps);
57
82
  });
58
83
  });
@@ -68,4 +68,28 @@ describe('useCrmProperties', () => {
68
68
  expect(result.current.isLoading).toBe(false);
69
69
  });
70
70
  });
71
+ it('should update properties when propertiesUpdatedCallback is called', async () => {
72
+ // Capture the callback so we can simulate an external update to CRM properties
73
+ let capturedCallback;
74
+ mockFetchCrmProperties.mockImplementation((_propertyNames, propertiesUpdatedCallback) => {
75
+ capturedCallback = propertiesUpdatedCallback;
76
+ return { firstname: 'Initial', lastname: 'Initial' };
77
+ });
78
+ const propertyNames = ['firstname', 'lastname'];
79
+ const { result } = renderHook(() => useCrmProperties(propertyNames));
80
+ await waitFor(() => {
81
+ expect(result.current.properties).toEqual({
82
+ firstname: 'Initial',
83
+ lastname: 'Initial',
84
+ });
85
+ expect(result.current.isLoading).toBe(false);
86
+ });
87
+ const updatedProperties = { firstname: 'Updated', lastname: 'Updated' };
88
+ await waitFor(() => {
89
+ if (capturedCallback) {
90
+ capturedCallback(updatedProperties);
91
+ expect(result.current.properties).toEqual(updatedProperties);
92
+ }
93
+ });
94
+ });
71
95
  });
@@ -733,7 +733,7 @@ export declare const LineChart: "LineChart" & {
733
733
  *
734
734
  * **Links:**
735
735
  * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/tabs Documentation}
736
- * - {@link https://github.com/HubSpot/ui-extensions-examples/tree/main/tabs-example Tabs Example}
736
+ * - {@link https://github.com/hubspotdev/uie-tabbed-product-carousel Tabs Example}
737
737
  */
738
738
  export declare const Tabs: "Tabs" & {
739
739
  readonly type?: "Tabs" | undefined;
@@ -752,7 +752,7 @@ export declare const Tabs: "Tabs" & {
752
752
  *
753
753
  * **Links:**
754
754
  * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/tabs Documentation}
755
- * - {@link https://github.com/HubSpot/ui-extensions-examples/tree/main/tabs-example Tabs Example}
755
+ * - {@link https://github.com/hubspotdev/uie-tabbed-product-carousel Tabs Example}
756
756
  */
757
757
  export declare const Tab: "Tab" & {
758
758
  readonly type?: "Tab" | undefined;
@@ -493,7 +493,7 @@ export const LineChart = createRemoteReactComponent('LineChart');
493
493
  *
494
494
  * **Links:**
495
495
  * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/tabs Documentation}
496
- * - {@link https://github.com/HubSpot/ui-extensions-examples/tree/main/tabs-example Tabs Example}
496
+ * - {@link https://github.com/hubspotdev/uie-tabbed-product-carousel Tabs Example}
497
497
  */
498
498
  export const Tabs = createRemoteReactComponent('Tabs');
499
499
  /**
@@ -508,7 +508,7 @@ export const Tabs = createRemoteReactComponent('Tabs');
508
508
  *
509
509
  * **Links:**
510
510
  * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/tabs Documentation}
511
- * - {@link https://github.com/HubSpot/ui-extensions-examples/tree/main/tabs-example Tabs Example}
511
+ * - {@link https://github.com/hubspotdev/uie-tabbed-product-carousel Tabs Example}
512
512
  */
513
513
  export const Tab = createRemoteReactComponent('Tab');
514
514
  /**
@@ -1,4 +1,4 @@
1
1
  export type CrmPropertiesResponse = {
2
2
  [key: string]: string;
3
3
  };
4
- export declare const fetchCrmProperties: (propertyNames: string[]) => Promise<Record<string, string>>;
4
+ export declare const fetchCrmProperties: (propertyNames: string[], propertiesUpdatedCallback: (properties: Record<string, string>) => void) => Promise<Record<string, string>>;
@@ -10,10 +10,10 @@ function isCrmPropertiesResponse(data) {
10
10
  }
11
11
  return true;
12
12
  }
13
- export const fetchCrmProperties = async (propertyNames) => {
13
+ export const fetchCrmProperties = async (propertyNames, propertiesUpdatedCallback) => {
14
14
  try {
15
15
  // eslint-disable-next-line hubspot-dev/no-confusing-browser-globals
16
- const response = await self.fetchCrmProperties(propertyNames);
16
+ const response = await self.fetchCrmProperties(propertyNames, propertiesUpdatedCallback);
17
17
  if (!response.ok) {
18
18
  throw new Error(`Failed to fetch CRM properties: ${response.statusText}`);
19
19
  }
@@ -14,11 +14,14 @@ export function useCrmProperties(propertyNames) {
14
14
  useEffect(() => {
15
15
  logger.warn('useCrmProperties is an experimental hook and might change or be removed in the future.');
16
16
  }, []);
17
+ const propertiesUpdatedCallback = (newProperties) => {
18
+ setProperties(newProperties);
19
+ };
17
20
  // Fetch the properties
18
21
  useEffect(() => {
19
22
  (async () => {
20
23
  try {
21
- const propertyData = await fetchCrmProperties(propertyNames);
24
+ const propertyData = await fetchCrmProperties(propertyNames, propertiesUpdatedCallback);
22
25
  setProperties(propertyData);
23
26
  setError(null);
24
27
  }
@@ -66,4 +66,16 @@ declare const ExpandableText: "ExpandableText" & {
66
66
  readonly props?: experimentalTypes.ExpandableTextProps | undefined;
67
67
  readonly children?: true | undefined;
68
68
  } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"ExpandableText", experimentalTypes.ExpandableTextProps, true>>;
69
- export { Iframe, MediaObject, Inline, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, };
69
+ /**
70
+ * The `Popover` component renders a popover overlay that can contain other components.
71
+ *
72
+ * **Links:**
73
+ *
74
+ * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/popover Popover Docs}
75
+ */
76
+ declare const Popover: "Popover" & {
77
+ readonly type?: "Popover" | undefined;
78
+ readonly props?: experimentalTypes.PopoverProps | undefined;
79
+ readonly children?: true | undefined;
80
+ } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"Popover", experimentalTypes.PopoverProps, true>>;
81
+ export { Iframe, MediaObject, Inline, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, Popover, };
@@ -27,4 +27,14 @@ const SettingsView = createRemoteReactComponent('SettingsView');
27
27
  * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/expandable-text ExpandableText Docs}
28
28
  */
29
29
  const ExpandableText = createRemoteReactComponent('ExpandableText');
30
- export { Iframe, MediaObject, Inline, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, };
30
+ /**
31
+ * The `Popover` component renders a popover overlay that can contain other components.
32
+ *
33
+ * **Links:**
34
+ *
35
+ * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/popover Popover Docs}
36
+ */
37
+ const Popover = createRemoteReactComponent('Popover', {
38
+ fragmentProps: ['header', 'body', 'footer'],
39
+ });
40
+ export { Iframe, MediaObject, Inline, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, Popover, };
@@ -122,4 +122,21 @@ export interface ExpandableTextProps {
122
122
  collapseButtonText?: string;
123
123
  expanded?: boolean;
124
124
  }
125
+ /**
126
+ * @ignore
127
+ * @experimental do not use in production
128
+ */
129
+ export interface PopoverProps {
130
+ children: ReactNode;
131
+ open?: boolean;
132
+ onOpenChange?: () => void;
133
+ placement?: 'left' | 'right' | 'top' | 'bottom';
134
+ header?: ReactNode;
135
+ body?: ReactNode;
136
+ footer?: ReactNode;
137
+ variant?: 'default' | 'shepherd' | 'longform';
138
+ showCloseButton?: boolean;
139
+ arrowSize?: 'none' | 'small' | 'medium';
140
+ onClick?: ReactionsHandler<ExtensionEvent>;
141
+ }
125
142
  export {};
package/dist/types.d.ts CHANGED
@@ -227,6 +227,10 @@ export interface CrmHostActions {
227
227
  onCrmPropertiesUpdate: onCrmPropertiesUpdateAction;
228
228
  }
229
229
  /** @ignore */
230
+ export interface SettingsActions {
231
+ addAlert: AddAlertAction;
232
+ }
233
+ /** @ignore */
230
234
  export interface UiePlatformActions {
231
235
  copyTextToClipboard: Clipboard['writeText'];
232
236
  closeOverlay: CloseOverlayAction;
@@ -1345,7 +1349,7 @@ export interface ListProps {
1345
1349
  */
1346
1350
  export interface LoadingSpinnerProps {
1347
1351
  /**
1348
- * The text that displays next to the spinner.
1352
+ * The text that displays next to the spinner. Note: the label is not shown by default, you must set `showLabel` to `true` to display the label.
1349
1353
  *
1350
1354
  */
1351
1355
  label: string;
@@ -1821,7 +1825,9 @@ export interface TableElementProps {
1821
1825
  * @interface
1822
1826
  */
1823
1827
  export type BaseTableHeaderProps = TableElementProps & AlignmentProps & WidthProps;
1824
- export type TableCellProps = TableElementProps & AlignmentProps & WidthProps;
1828
+ export type TableCellProps = TableElementProps & AlignmentProps & WidthProps & {
1829
+ colSpan?: number;
1830
+ };
1825
1831
  /**
1826
1832
  * The props type for {@link !components.TableHeader} when sorted === never.
1827
1833
  *
@@ -2045,6 +2051,18 @@ export interface TextFormatOptions {
2045
2051
  * @defaultValue `"none"`
2046
2052
  */
2047
2053
  lineDecoration?: 'none' | 'underline' | 'strikethrough';
2054
+ /**
2055
+ * Controls the capitalization of text.
2056
+ *
2057
+ * - `none`: No capitalization changes (default)
2058
+ * - `uppercase`: Transforms all characters to uppercase
2059
+ * - `lowercase`: Transforms all characters to lowercase
2060
+ * - `capitalize`: Capitalizes the first letter of each word
2061
+ * - `sentenceCase`: Capitalizes the first letter of the text and makes the rest lowercase
2062
+ *
2063
+ * @defaultValue `"none"`
2064
+ */
2065
+ textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize' | 'sentenceCase';
2048
2066
  }
2049
2067
  export interface TruncateOptions {
2050
2068
  maxWidth?: number;
@@ -2750,7 +2768,7 @@ export interface CrmSidebarExtensionPoint extends ExtensionPointContract {
2750
2768
  }
2751
2769
  /** @ignore */
2752
2770
  export interface SettingsExtensionPoint extends ExtensionPointContract {
2753
- actions: UiePlatformActions;
2771
+ actions: SettingsActions & UiePlatformActions;
2754
2772
  context: SettingsContext;
2755
2773
  }
2756
2774
  export interface ExampleCrmComponentProps {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/ui-extensions",
3
- "version": "0.8.53",
3
+ "version": "0.8.55",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -66,5 +66,5 @@
66
66
  "ts-jest": "^29.1.1",
67
67
  "typescript": "5.0.4"
68
68
  },
69
- "gitHead": "2bad593e0b073687424cd1d365f68849fb448940"
69
+ "gitHead": "93b19d28469b6b8ccf6f757ab301634fcc6ef993"
70
70
  }