@hubspot/ui-extensions 0.9.8 → 0.10.1

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 (30) hide show
  1. package/dist/__tests__/{experimental → crm}/hooks/useAssociations.spec.js +123 -4
  2. package/dist/__tests__/{experimental → crm}/hooks/useCrmProperties.spec.js +17 -3
  3. package/dist/__tests__/{experimental/crm → crm/utils}/fetchAssociations.spec.js +1 -1
  4. package/dist/__tests__/{experimental/crm → crm/utils}/fetchCrmProperties.spec.js +1 -1
  5. package/dist/__tests__/test-d/extension-points.test-d.d.ts +1 -0
  6. package/dist/__tests__/test-d/extension-points.test-d.js +29 -0
  7. package/dist/coreComponents.d.ts +12 -0
  8. package/dist/coreComponents.js +8 -0
  9. package/dist/{experimental → crm}/hooks/useAssociations.d.ts +5 -2
  10. package/dist/{experimental → crm}/hooks/useAssociations.js +7 -9
  11. package/dist/{experimental → crm}/hooks/useCrmProperties.d.ts +1 -3
  12. package/dist/{experimental → crm}/hooks/useCrmProperties.js +4 -8
  13. package/dist/crm/index.d.ts +2 -0
  14. package/dist/crm/index.js +2 -0
  15. package/dist/experimental/index.d.ts +3 -27
  16. package/dist/experimental/index.js +3 -15
  17. package/dist/experimental/types.d.ts +1 -31
  18. package/dist/experimental/types.js +4 -0
  19. package/dist/pages/home/components.d.ts +28 -0
  20. package/dist/pages/home/components.js +20 -0
  21. package/dist/types.d.ts +79 -1
  22. package/package.json +10 -4
  23. /package/dist/__tests__/{experimental → crm}/hooks/useAssociations.spec.d.ts +0 -0
  24. /package/dist/__tests__/{experimental → crm}/hooks/useCrmProperties.spec.d.ts +0 -0
  25. /package/dist/__tests__/{experimental/crm → crm/utils}/fetchAssociations.spec.d.ts +0 -0
  26. /package/dist/__tests__/{experimental/crm → crm/utils}/fetchCrmProperties.spec.d.ts +0 -0
  27. /package/dist/{experimental/crm → crm/utils}/fetchAssociations.d.ts +0 -0
  28. /package/dist/{experimental/crm → crm/utils}/fetchAssociations.js +0 -0
  29. /package/dist/{experimental/crm → crm/utils}/fetchCrmProperties.d.ts +0 -0
  30. /package/dist/{experimental/crm → crm/utils}/fetchCrmProperties.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import { renderHook, waitFor } from '@testing-library/react';
2
- import { useAssociations } from '../../../experimental/hooks/useAssociations';
3
- import { fetchAssociations } from '../../../experimental/crm/fetchAssociations';
2
+ import { useAssociations } from '../../../crm/hooks/useAssociations';
3
+ import { fetchAssociations } from '../../../crm/utils/fetchAssociations';
4
4
  // Mock the logger module
5
5
  jest.mock('../../../logger', () => ({
6
6
  logger: {
@@ -11,8 +11,8 @@ jest.mock('../../../logger', () => ({
11
11
  },
12
12
  }));
13
13
  // Mock the fetchAssociations function, keep utility functions
14
- jest.mock('../../../experimental/crm/fetchAssociations', () => ({
15
- ...jest.requireActual('../../../experimental/crm/fetchAssociations'),
14
+ jest.mock('../../../crm/utils/fetchAssociations', () => ({
15
+ ...jest.requireActual('../../../crm/utils/fetchAssociations'),
16
16
  fetchAssociations: jest.fn(),
17
17
  }));
18
18
  // Get reference to the mocked function
@@ -313,6 +313,125 @@ describe('useAssociations with Pagination', () => {
313
313
  expect(result.current.isLoading).toBe(false);
314
314
  });
315
315
  });
316
+ it('should handle null config gracefully', async () => {
317
+ mockFetchAssociations.mockResolvedValue({
318
+ data: {
319
+ results: [],
320
+ hasMore: false,
321
+ nextOffset: 0,
322
+ },
323
+ cleanup: jest.fn(),
324
+ });
325
+ // @ts-expect-error - intentional type issue to check the failure state
326
+ const { result } = renderHook(() => useAssociations(null));
327
+ await waitFor(() => {
328
+ expect(result.current.isLoading).toBe(false);
329
+ expect(result.current.results).toEqual([]);
330
+ expect(result.current.pagination.pageSize).toBe(10); // DEFAULT_PAGE_SIZE
331
+ });
332
+ expect(mockFetchAssociations).toHaveBeenCalledWith(expect.objectContaining({
333
+ toObjectType: undefined,
334
+ properties: undefined,
335
+ pageLength: 10,
336
+ offset: undefined,
337
+ }), expect.any(Object));
338
+ });
339
+ it('should handle undefined config gracefully', async () => {
340
+ mockFetchAssociations.mockResolvedValue({
341
+ data: {
342
+ results: [],
343
+ hasMore: false,
344
+ nextOffset: 0,
345
+ },
346
+ cleanup: jest.fn(),
347
+ });
348
+ // @ts-expect-error - intentional type issue to check the failure state
349
+ const { result } = renderHook(() => useAssociations(undefined));
350
+ await waitFor(() => {
351
+ expect(result.current.isLoading).toBe(false);
352
+ expect(result.current.results).toEqual([]);
353
+ expect(result.current.pagination.pageSize).toBe(10); // DEFAULT_PAGE_SIZE
354
+ });
355
+ expect(mockFetchAssociations).toHaveBeenCalledWith(expect.objectContaining({
356
+ toObjectType: undefined,
357
+ properties: undefined,
358
+ pageLength: 10,
359
+ offset: undefined,
360
+ }), expect.any(Object));
361
+ });
362
+ it('should handle empty config object gracefully', async () => {
363
+ mockFetchAssociations.mockResolvedValue({
364
+ data: {
365
+ results: [],
366
+ hasMore: false,
367
+ nextOffset: 0,
368
+ },
369
+ cleanup: jest.fn(),
370
+ });
371
+ // @ts-expect-error - intentional type issue to check the failure state
372
+ const { result } = renderHook(() => useAssociations({}));
373
+ await waitFor(() => {
374
+ expect(result.current.isLoading).toBe(false);
375
+ expect(result.current.results).toEqual([]);
376
+ expect(result.current.pagination.pageSize).toBe(10); // DEFAULT_PAGE_SIZE
377
+ });
378
+ expect(mockFetchAssociations).toHaveBeenCalledWith(expect.objectContaining({
379
+ toObjectType: undefined,
380
+ properties: undefined,
381
+ pageLength: 10,
382
+ offset: undefined,
383
+ }), expect.any(Object));
384
+ });
385
+ it('should handle config with missing toObjectType', async () => {
386
+ mockFetchAssociations.mockResolvedValue({
387
+ data: {
388
+ results: [],
389
+ hasMore: false,
390
+ nextOffset: 0,
391
+ },
392
+ cleanup: jest.fn(),
393
+ });
394
+ const { result } = renderHook(() =>
395
+ // @ts-expect-error - intentional type issue to check the failure state
396
+ useAssociations({
397
+ properties: ['firstname'],
398
+ pageLength: 5,
399
+ }));
400
+ await waitFor(() => {
401
+ expect(result.current.isLoading).toBe(false);
402
+ expect(result.current.pagination.pageSize).toBe(5);
403
+ });
404
+ expect(mockFetchAssociations).toHaveBeenCalledWith(expect.objectContaining({
405
+ toObjectType: undefined,
406
+ properties: ['firstname'],
407
+ pageLength: 5,
408
+ offset: undefined,
409
+ }), expect.any(Object));
410
+ });
411
+ it('should handle config with missing properties', async () => {
412
+ mockFetchAssociations.mockResolvedValue({
413
+ data: {
414
+ results: [],
415
+ hasMore: false,
416
+ nextOffset: 0,
417
+ },
418
+ cleanup: jest.fn(),
419
+ });
420
+ const { result } = renderHook(() => useAssociations({
421
+ toObjectType: '0-1',
422
+ pageLength: 8,
423
+ }));
424
+ await waitFor(() => {
425
+ expect(result.current.isLoading).toBe(false);
426
+ expect(result.current.pagination.pageSize).toBe(8);
427
+ });
428
+ expect(mockFetchAssociations).toHaveBeenCalledWith(expect.objectContaining({
429
+ toObjectType: '0-1',
430
+ properties: undefined,
431
+ pageLength: 8,
432
+ offset: undefined,
433
+ }), expect.any(Object));
434
+ });
316
435
  });
317
436
  describe('options handling', () => {
318
437
  it('should pass formatting options to fetchAssociations', async () => {
@@ -1,5 +1,5 @@
1
1
  import { renderHook, waitFor } from '@testing-library/react';
2
- import { useCrmProperties } from '../../../experimental/hooks/useCrmProperties';
2
+ import { useCrmProperties } from '../../../crm/hooks/useCrmProperties';
3
3
  // Mock the logger module
4
4
  jest.mock('../../../logger', () => ({
5
5
  logger: {
@@ -10,9 +10,9 @@ jest.mock('../../../logger', () => ({
10
10
  },
11
11
  }));
12
12
  // Mock the fetchCrmProperties module
13
- jest.mock('../../../experimental/crm/fetchCrmProperties');
13
+ jest.mock('../../../crm/utils/fetchCrmProperties');
14
14
  const mockFetchCrmProperties = jest.fn();
15
- import * as fetchCrmPropertiesModule from '../../../experimental/crm/fetchCrmProperties';
15
+ import * as fetchCrmPropertiesModule from '../../../crm/utils/fetchCrmProperties';
16
16
  fetchCrmPropertiesModule.fetchCrmProperties = mockFetchCrmProperties;
17
17
  describe('useCrmProperties', () => {
18
18
  let originalError;
@@ -239,4 +239,18 @@ describe('useCrmProperties', () => {
239
239
  expect(mockCleanup1).toHaveBeenCalledTimes(1);
240
240
  });
241
241
  });
242
+ it('should handle non-array propertyNames without crashing during stabilization', async () => {
243
+ const errorMessage = 'Properties must be an array of strings';
244
+ mockFetchCrmProperties.mockRejectedValue(new Error(errorMessage));
245
+ const nonArrayPropertyNames = 2;
246
+ const { result } = renderHook(() =>
247
+ // @ts-expect-error - we are deliberately testing a bad type
248
+ useCrmProperties(nonArrayPropertyNames));
249
+ await waitFor(() => {
250
+ expect(result.current.error).toBeInstanceOf(Error);
251
+ expect(result.current.error?.message).toBe(errorMessage);
252
+ expect(result.current.isLoading).toBe(false);
253
+ });
254
+ expect(mockFetchCrmProperties).toHaveBeenCalledWith(2, expect.any(Function), {});
255
+ });
242
256
  });
@@ -8,7 +8,7 @@ Object.defineProperty(global, 'self', {
8
8
  value: mockSelf,
9
9
  writable: true,
10
10
  });
11
- import { fetchAssociations, calculatePaginationFlags, } from '../../../experimental/crm/fetchAssociations';
11
+ import { fetchAssociations, calculatePaginationFlags, } from '../../../crm/utils/fetchAssociations';
12
12
  describe('fetchAssociations', () => {
13
13
  // Helper functions
14
14
  const createMockResponse = (data, overrides = {}) => ({
@@ -8,7 +8,7 @@ Object.defineProperty(global, 'self', {
8
8
  value: mockSelf,
9
9
  writable: true,
10
10
  });
11
- import { fetchCrmProperties } from '../../../experimental/crm/fetchCrmProperties';
11
+ import { fetchCrmProperties } from '../../../crm/utils/fetchCrmProperties';
12
12
  const DEFAULT_OPTIONS = {};
13
13
  describe('fetchCrmProperties', () => {
14
14
  beforeEach(() => {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { expectAssignable } from 'tsd';
2
+ // Test app home extension point
3
+ expectAssignable({
4
+ actions: {
5
+ addAlert: () => { },
6
+ copyTextToClipboard: async () => { },
7
+ closeOverlay: () => { },
8
+ reloadPage: () => { },
9
+ openIframeModal: () => { },
10
+ },
11
+ context: {
12
+ user: {
13
+ id: 123,
14
+ emails: ['test@example.com'],
15
+ email: 'test@example.com',
16
+ firstName: 'John',
17
+ lastName: 'Doe',
18
+ roles: [],
19
+ teams: [],
20
+ permissions: [],
21
+ },
22
+ portal: {
23
+ id: 456,
24
+ timezone: 'America/New_York',
25
+ },
26
+ location: 'home',
27
+ },
28
+ customComponents: {},
29
+ });
@@ -830,3 +830,15 @@ export declare const CurrencyInput: "CurrencyInput" & {
830
830
  readonly props?: types.InlineProps | undefined;
831
831
  readonly children?: true | undefined;
832
832
  } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"Inline", types.InlineProps, true>>;
833
+ /**
834
+ * The `AutoGrid` component renders a responsive grid layout that automatically adjusts the number of columns based on available space. Use this component to create flexible grid layouts for cards, tiles, or other content.
835
+ *
836
+ * **Links:**
837
+ *
838
+ * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/simple-grid Docs}
839
+ */
840
+ export declare const AutoGrid: "AutoGrid" & {
841
+ readonly type?: "AutoGrid" | undefined;
842
+ readonly props?: types.AutoGridProps | undefined;
843
+ readonly children?: true | undefined;
844
+ } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"AutoGrid", types.AutoGridProps, true>>;
@@ -558,3 +558,11 @@ export const CurrencyInput = createRemoteReactComponent('CurrencyInput');
558
558
  *
559
559
  * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/inline Docs}
560
560
  */ export const Inline = createRemoteReactComponent('Inline');
561
+ /**
562
+ * The `AutoGrid` component renders a responsive grid layout that automatically adjusts the number of columns based on available space. Use this component to create flexible grid layouts for cards, tiles, or other content.
563
+ *
564
+ * **Links:**
565
+ *
566
+ * - {@link https://developers.hubspot.com/docs/reference/ui-components/standard-components/simple-grid Docs}
567
+ */
568
+ export const AutoGrid = createRemoteReactComponent('AutoGrid');
@@ -1,5 +1,5 @@
1
- import { type FetchAssociationsRequest, type AssociationResult } from '../crm/fetchAssociations';
2
- import { type FetchCrmPropertiesOptions } from '../crm/fetchCrmProperties';
1
+ import { type FetchAssociationsRequest, type AssociationResult } from '../utils/fetchAssociations';
2
+ import { type FetchCrmPropertiesOptions } from '../utils/fetchCrmProperties';
3
3
  export interface UseAssociationsOptions {
4
4
  propertiesToFormat?: 'all' | string[];
5
5
  formattingOptions?: FetchCrmPropertiesOptions['formattingOptions'];
@@ -19,4 +19,7 @@ export interface UseAssociationsResult {
19
19
  isLoading: boolean;
20
20
  pagination: UseAssociationsPagination;
21
21
  }
22
+ /**
23
+ * A hook to fetch and manage associations between CRM objects with pagination support.
24
+ */
22
25
  export declare function useAssociations(config: Omit<FetchAssociationsRequest, 'offset'>, options?: UseAssociationsOptions): UseAssociationsResult;
@@ -1,6 +1,5 @@
1
1
  import { useEffect, useReducer, useMemo, useRef, useCallback } from 'react';
2
- import { logger } from '../../logger';
3
- import { fetchAssociations, DEFAULT_PAGE_SIZE, calculatePaginationFlags, } from '../crm/fetchAssociations';
2
+ import { fetchAssociations, DEFAULT_PAGE_SIZE, calculatePaginationFlags, } from '../utils/fetchAssociations';
4
3
  function createInitialState(pageSize) {
5
4
  return {
6
5
  results: [],
@@ -80,13 +79,12 @@ function associationsReducer(state, action) {
80
79
  }
81
80
  }
82
81
  const DEFAULT_OPTIONS = {};
82
+ /**
83
+ * A hook to fetch and manage associations between CRM objects with pagination support.
84
+ */
83
85
  export function useAssociations(config, options = DEFAULT_OPTIONS) {
84
- const pageSize = config.pageLength ?? DEFAULT_PAGE_SIZE;
86
+ const pageSize = config?.pageLength ?? DEFAULT_PAGE_SIZE;
85
87
  const [state, dispatch] = useReducer(associationsReducer, useMemo(() => createInitialState(pageSize), [pageSize]));
86
- // Log experimental warning once on mount
87
- useEffect(() => {
88
- logger.warn('useAssociations is an experimental hook and might change or be removed in the future.');
89
- }, []);
90
88
  /**
91
89
  * HOOK OPTIMIZATION:
92
90
  *
@@ -141,8 +139,8 @@ export function useAssociations(config, options = DEFAULT_OPTIONS) {
141
139
  dispatch({ type: 'FETCH_START' });
142
140
  // Build request using current offset token
143
141
  const request = {
144
- toObjectType: stableConfig.toObjectType,
145
- properties: stableConfig.properties,
142
+ toObjectType: stableConfig?.toObjectType,
143
+ properties: stableConfig?.properties,
146
144
  pageLength: pageSize,
147
145
  offset: state.currentOffset,
148
146
  };
@@ -1,4 +1,4 @@
1
- import { type FetchCrmPropertiesOptions } from '../crm/fetchCrmProperties';
1
+ import { type FetchCrmPropertiesOptions } from '../utils/fetchCrmProperties';
2
2
  export interface CrmPropertiesState {
3
3
  properties: Record<string, string | null>;
4
4
  error: Error | null;
@@ -6,7 +6,5 @@ export interface CrmPropertiesState {
6
6
  }
7
7
  /**
8
8
  * A hook for using and managing CRM properties.
9
- *
10
- * @experimental This hook is experimental and might change or be removed in future versions.
11
9
  */
12
10
  export declare function useCrmProperties(propertyNames: string[], options?: FetchCrmPropertiesOptions): CrmPropertiesState;
@@ -1,6 +1,5 @@
1
1
  import { useEffect, useReducer, useMemo, useRef } from 'react';
2
- import { logger } from '../../logger';
3
- import { fetchCrmProperties, } from '../crm/fetchCrmProperties';
2
+ import { fetchCrmProperties, } from '../utils/fetchCrmProperties';
4
3
  const initialState = {
5
4
  properties: {},
6
5
  error: null,
@@ -35,15 +34,9 @@ function crmPropertiesReducer(state, action) {
35
34
  const DEFAULT_OPTIONS = {};
36
35
  /**
37
36
  * A hook for using and managing CRM properties.
38
- *
39
- * @experimental This hook is experimental and might change or be removed in future versions.
40
37
  */
41
38
  export function useCrmProperties(propertyNames, options = DEFAULT_OPTIONS) {
42
39
  const [state, dispatch] = useReducer(crmPropertiesReducer, initialState);
43
- // Log experimental warning once on mount
44
- useEffect(() => {
45
- logger.warn('useCrmProperties is an experimental hook and might change or be removed in the future.');
46
- }, []);
47
40
  /**
48
41
  * HOOK OPTIMIZATION:
49
42
  *
@@ -56,6 +49,9 @@ export function useCrmProperties(propertyNames, options = DEFAULT_OPTIONS) {
56
49
  const lastOptionsRef = useRef();
57
50
  const lastOptionsKeyRef = useRef();
58
51
  const stablePropertyNames = useMemo(() => {
52
+ if (!Array.isArray(propertyNames)) {
53
+ return propertyNames;
54
+ }
59
55
  const sortedNames = [...propertyNames].sort();
60
56
  const propertyNamesKey = JSON.stringify(sortedNames);
61
57
  if (propertyNamesKey === lastPropertyNamesKeyRef.current) {
@@ -1,2 +1,4 @@
1
1
  import { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions } from './components';
2
2
  export { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions, };
3
+ export { useCrmProperties } from './hooks/useCrmProperties';
4
+ export { useAssociations } from './hooks/useAssociations';
package/dist/crm/index.js CHANGED
@@ -1,2 +1,4 @@
1
1
  import { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions, } from './components';
2
2
  export { CrmPropertyList, CrmAssociationTable, CrmDataHighlight, CrmReport, CrmAssociationPivot, CrmAssociationPropertyList, CrmAssociationStageTracker, CrmSimpleDeadline, CrmStageTracker, CrmStatistics, CrmActionButton, CrmActionLink, CrmCardActions, };
3
+ export { useCrmProperties } from './hooks/useCrmProperties';
4
+ export { useAssociations } from './hooks/useAssociations';
@@ -1,7 +1,7 @@
1
1
  import type * as types from '../types';
2
2
  import type * as experimentalTypes from './types';
3
- export { useCrmProperties } from './hooks/useCrmProperties';
4
- export { useAssociations } from './hooks/useAssociations';
3
+ export { useCrmProperties } from '../crm/hooks/useCrmProperties';
4
+ export { useAssociations } from '../crm/hooks/useAssociations';
5
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. */
6
6
  declare const Iframe: "Iframe" & {
7
7
  readonly type?: "Iframe" | undefined;
@@ -27,12 +27,6 @@ declare const Center: "Center" & {
27
27
  readonly children?: true | undefined;
28
28
  } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"Center", experimentalTypes.CenterProps, true>>;
29
29
  /** @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. */
30
- declare const SimpleGrid: "SimpleGrid" & {
31
- readonly type?: "SimpleGrid" | undefined;
32
- readonly props?: experimentalTypes.SimpleGridProps | undefined;
33
- readonly children?: true | undefined;
34
- } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"SimpleGrid", experimentalTypes.SimpleGridProps, true>>;
35
- /** @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. */
36
30
  declare const Grid: "Grid" & {
37
31
  readonly type?: "Grid" | undefined;
38
32
  readonly props?: experimentalTypes.GridProps | undefined;
@@ -78,22 +72,4 @@ declare const FileInput: "FileInput" & {
78
72
  readonly props?: experimentalTypes.FileInputProps | undefined;
79
73
  readonly children?: true | undefined;
80
74
  } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"FileInput", experimentalTypes.FileInputProps, true>>;
81
- /** @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. */
82
- declare const HeaderActions: "HeaderActions" & {
83
- readonly type?: "HeaderActions" | undefined;
84
- readonly props?: experimentalTypes.HeaderActionsProps | undefined;
85
- readonly children?: true | undefined;
86
- } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"HeaderActions", experimentalTypes.HeaderActionsProps, true>>;
87
- /** @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. */
88
- declare const PrimaryHeaderActionButton: "PrimaryHeaderActionButton" & {
89
- readonly type?: "PrimaryHeaderActionButton" | undefined;
90
- readonly props?: experimentalTypes.HeaderActionButtonProps | undefined;
91
- readonly children?: true | undefined;
92
- } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"PrimaryHeaderActionButton", experimentalTypes.HeaderActionButtonProps, true>>;
93
- /** @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. */
94
- declare const SecondaryHeaderActionButton: "SecondaryHeaderActionButton" & {
95
- readonly type?: "SecondaryHeaderActionButton" | undefined;
96
- readonly props?: experimentalTypes.HeaderActionButtonProps | undefined;
97
- readonly children?: true | undefined;
98
- } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"SecondaryHeaderActionButton", experimentalTypes.HeaderActionButtonProps, true>>;
99
- export { Iframe, MediaObject, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, };
75
+ export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, };
@@ -1,6 +1,6 @@
1
1
  import { createRemoteReactComponent } from '@remote-ui/react';
2
- export { useCrmProperties } from './hooks/useCrmProperties';
3
- export { useAssociations } from './hooks/useAssociations';
2
+ export { useCrmProperties } from '../crm/hooks/useCrmProperties';
3
+ export { useAssociations } from '../crm/hooks/useAssociations';
4
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. */
5
5
  const Iframe = createRemoteReactComponent('Iframe');
6
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. */
@@ -12,8 +12,6 @@ const Stack2 = createRemoteReactComponent('Stack2');
12
12
  /** @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. */
13
13
  const Center = createRemoteReactComponent('Center');
14
14
  /** @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. */
15
- const SimpleGrid = createRemoteReactComponent('SimpleGrid');
16
- /** @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. */
17
15
  const Grid = createRemoteReactComponent('Grid');
18
16
  /** @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. */
19
17
  const GridItem = createRemoteReactComponent('GridItem');
@@ -35,14 +33,4 @@ const ExpandableText = createRemoteReactComponent('ExpandableText');
35
33
  */
36
34
  const Popover = createRemoteReactComponent('Popover');
37
35
  const FileInput = createRemoteReactComponent('FileInput');
38
- /** @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. */
39
- const HeaderActions = createRemoteReactComponent('HeaderActions');
40
- /** @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. */
41
- const PrimaryHeaderActionButton = createRemoteReactComponent('PrimaryHeaderActionButton', {
42
- fragmentProps: ['overlay'],
43
- });
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 SecondaryHeaderActionButton = createRemoteReactComponent('SecondaryHeaderActionButton', {
46
- fragmentProps: ['overlay'],
47
- });
48
- export { Iframe, MediaObject, Stack2, Center, SimpleGrid, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, HeaderActions, PrimaryHeaderActionButton, SecondaryHeaderActionButton, };
36
+ export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, };
@@ -48,15 +48,6 @@ export interface MediaObjectProps {
48
48
  itemLeft?: RemoteFragment;
49
49
  itemRight?: RemoteFragment;
50
50
  }
51
- /**
52
- * @ignore
53
- * @experimental do not use in production
54
- */
55
- export interface SimpleGridProps extends BaseLayout {
56
- minColumnWidth: number | string;
57
- gap?: AllDistances;
58
- children?: ReactNode;
59
- }
60
51
  export interface GridProps {
61
52
  justify?: FlexJustify;
62
53
  align?: FlexAlign;
@@ -238,29 +229,8 @@ export interface BaseInputProps<T = string, V = string> {
238
229
  */
239
230
  onFocus?: (value: V) => void;
240
231
  }
232
+ export {};
241
233
  /**
242
234
  * @ignore
243
235
  * @experimental do not use in production
244
236
  */
245
- /**
246
- * @ignore
247
- * @experimental do not use in production
248
- */
249
- export interface HeaderActionsProps {
250
- children: ReactNode;
251
- }
252
- /**
253
- * @ignore
254
- * @experimental do not use in production
255
- */
256
- export interface HeaderActionButtonProps {
257
- onClick?: ReactionsHandler<ExtensionEvent>;
258
- href?: string | {
259
- url: string;
260
- external?: boolean;
261
- };
262
- disabled?: boolean;
263
- children: ReactNode;
264
- overlay?: RemoteFragment;
265
- }
266
- export {};
@@ -1 +1,5 @@
1
1
  export {};
2
+ /**
3
+ * @ignore
4
+ * @experimental do not use in production
5
+ */
@@ -0,0 +1,28 @@
1
+ import type * as types from '../../types';
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?: types.HeaderActionsProps | undefined;
9
+ readonly children?: true | undefined;
10
+ } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"HeaderActions", types.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?: types.HeaderActionButtonProps | undefined;
18
+ readonly children?: true | undefined;
19
+ } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"PrimaryHeaderActionButton", types.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?: types.HeaderActionButtonProps | undefined;
27
+ readonly children?: true | undefined;
28
+ } & import("@remote-ui/react").ReactComponentTypeFromRemoteComponentType<import("@remote-ui/types").RemoteComponentType<"SecondaryHeaderActionButton", types.HeaderActionButtonProps, true>>;
@@ -0,0 +1,20 @@
1
+ import { createRemoteReactComponent } from '@remote-ui/react';
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 const HeaderActions = createRemoteReactComponent('HeaderActions');
7
+ /**
8
+ * 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.
9
+ *
10
+ */
11
+ export const PrimaryHeaderActionButton = createRemoteReactComponent('PrimaryHeaderActionButton', {
12
+ fragmentProps: ['overlay'],
13
+ });
14
+ /**
15
+ * 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.
16
+ *
17
+ */
18
+ export const SecondaryHeaderActionButton = createRemoteReactComponent('SecondaryHeaderActionButton', {
19
+ fragmentProps: ['overlay'],
20
+ });
package/dist/types.d.ts CHANGED
@@ -239,6 +239,7 @@ export interface UiePlatformActions {
239
239
  copyTextToClipboard: Clipboard['writeText'];
240
240
  closeOverlay: CloseOverlayAction;
241
241
  reloadPage: ReloadPageAction;
242
+ openIframeModal: OpenIframeModalAction;
242
243
  }
243
244
  /**
244
245
  * The props type for {@link !components.Accordion}.
@@ -309,6 +310,42 @@ export interface AlertProps {
309
310
  */
310
311
  variant?: 'info' | 'warning' | 'success' | 'error' | 'danger' | 'tip';
311
312
  }
313
+ /**
314
+ * The props type for {@link !components.HeaderActions}.
315
+ *
316
+ * @category Component Props
317
+ */
318
+ export interface HeaderActionsProps {
319
+ /**
320
+ * Sets the content that will render inside the HeaderActions. This prop is passed implicitly by providing sub-components.
321
+ */
322
+ children: ReactNode;
323
+ }
324
+ /**
325
+ * The props type for {@link !components.HeaderActions}.
326
+ *
327
+ * @category Component Props
328
+ */
329
+ export interface HeaderActionButtonProps extends OverlayComponentProps {
330
+ /**
331
+ * A function that will be invoked when the button is clicked. Do not use this function for submitting a form; use Form's `onSubmit` function instead.
332
+ *
333
+ * @event
334
+ */
335
+ onClick?: ReactionsHandler<ExtensionEvent>;
336
+ /**
337
+ * A URL that will be opened when the button is clicked. If the value is a URL external to `hubspot.com` it will be opened in a new tab.
338
+ */
339
+ href?: HrefProp;
340
+ /**
341
+ * Determines whether or not the button should be disabled.
342
+ */
343
+ disabled?: boolean;
344
+ /**
345
+ * Sets the content that will render inside the HeaderActionButton. This prop is passed implicitly by providing sub-components.
346
+ */
347
+ children: ReactNode;
348
+ }
312
349
  /**
313
350
  * The props type for {@link !components.ButtonRow}.
314
351
  *
@@ -683,6 +720,7 @@ export interface DropdownProps {
683
720
  */
684
721
  disabled?: boolean;
685
722
  }
723
+ export type EmptyStateImageName = 'addOnReporting' | 'announcement' | 'api' | 'automatedTesting' | 'beta' | 'building' | 'callingSetUp' | 'companies' | 'components' | 'cone' | 'contacts' | 'contentStrategy' | 'customObjects' | 'customerExperience' | 'customerSupport' | 'deals' | 'developerSecurityUpdate' | 'electronicSignature' | 'electronicSignatureEmptyState' | 'emailConfirmation' | 'emptyStateCharts' | 'idea' | 'integrations' | 'leads' | 'lock' | 'meetings' | 'missedGoal' | 'multipleObjects' | 'object' | 'productsShoppingCart' | 'registration' | 'sandboxAddOn' | 'social' | 'store' | 'storeDisabled' | 'successfullyConnectedEmail' | 'target' | 'task' | 'tickets' | 'voteAndSearch';
686
724
  /**
687
725
  * The props type for {@link !components.EmptyState}.
688
726
  *
@@ -725,6 +763,12 @@ export interface EmptyStateProps {
725
763
  * @defaultValue `250`
726
764
  */
727
765
  imageWidth?: number;
766
+ /**
767
+ * The name of the image to display.
768
+ *
769
+ * @defaultValue `"emptyStateCharts"`
770
+ */
771
+ imageName?: EmptyStateImageName;
728
772
  }
729
773
  /**
730
774
  * The props type for {@link !components.ErrorState}.
@@ -1411,6 +1455,38 @@ export interface InlineProps {
1411
1455
  */
1412
1456
  gap?: AllDistances;
1413
1457
  }
1458
+ /**
1459
+ * The props type for {@link !components.AutoGrid}.
1460
+ *
1461
+ * @category Component Props
1462
+ */
1463
+ export interface AutoGridProps {
1464
+ /**
1465
+ * Sets the width of each column in the grid.
1466
+ * When `flexible` is true, columns will be at least this width (or collapse to container width if smaller), then expand equally to fill available space.
1467
+ * When `flexible` is false, columns are exactly this width.
1468
+ */
1469
+ columnWidth: number;
1470
+ /**
1471
+ * Sets the spacing between grid items.
1472
+ *
1473
+ * @defaultValue `"flush"`
1474
+ */
1475
+ gap?: AllDistances;
1476
+ /**
1477
+ * Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components.
1478
+ *
1479
+ */
1480
+ children?: ReactNode;
1481
+ /**
1482
+ * Whether columns should expand to fill available space.
1483
+ * When true, columns will be at least `columnWidth` but grow equally to fill the container.
1484
+ * When false, columns are exactly `columnWidth`.
1485
+ *
1486
+ * @defaultValue `false`
1487
+ */
1488
+ flexible?: boolean;
1489
+ }
1414
1490
  /**
1415
1491
  * The props type for {@link !components.Link}.
1416
1492
  *
@@ -2569,10 +2645,12 @@ export interface AppContext {
2569
2645
  appName?: string;
2570
2646
  cardTitle?: string;
2571
2647
  }
2648
+ type ProfileVariableValue = string | number | bigint | boolean;
2572
2649
  /** @ignore */
2573
2650
  export interface BaseContext {
2574
2651
  user: UserContext;
2575
2652
  portal: PortalContext;
2653
+ variables?: Record<string, ProfileVariableValue>;
2576
2654
  }
2577
2655
  /** @ignore */
2578
2656
  export interface CrmContext extends BaseContext {
@@ -2923,7 +3001,7 @@ export interface SettingsExtensionPoint extends ExtensionPointContract {
2923
3001
  }
2924
3002
  /** @ignore */
2925
3003
  export interface AppHomeExtensionPoint extends ExtensionPointContract {
2926
- actions: AppHomeActions;
3004
+ actions: AppHomeActions & UiePlatformActions;
2927
3005
  context: AppHomeContext;
2928
3006
  }
2929
3007
  export interface ExampleCrmComponentProps {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/ui-extensions",
3
- "version": "0.9.8",
3
+ "version": "0.10.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,8 +11,9 @@
11
11
  "watch": "npm run clean && tsc --watch",
12
12
  "prepare": "npm run build",
13
13
  "lint": "echo 'No linter configured for @hubspot/ui-extensions'",
14
- "test": "jest",
15
- "jest": "jest --watch"
14
+ "test": "jest && tsd",
15
+ "jest": "jest --watch",
16
+ "test:types": "tsd"
16
17
  },
17
18
  "files": [
18
19
  "dist"
@@ -23,6 +24,7 @@
23
24
  "exports": {
24
25
  ".": "./dist/index.js",
25
26
  "./crm": "./dist/crm/index.js",
27
+ "./pages/home": "./dist/pages/home/components.js",
26
28
  "./experimental": "./dist/experimental/index.js",
27
29
  "./experimental/testing": "./dist/experimental/testing/index.js",
28
30
  "./experimental/testing/jest-matchers": "./dist/experimental/testing/jest/matchers/index.js"
@@ -64,7 +66,11 @@
64
66
  "react-dom": "^18.2.0",
65
67
  "react-reconciler": "^0.29.0",
66
68
  "ts-jest": "^29.1.1",
69
+ "tsd": "^0.33.0",
67
70
  "typescript": "5.0.4"
68
71
  },
69
- "gitHead": "68d176099343d6d202421ec241c131f1d07562a8"
72
+ "tsd": {
73
+ "directory": "src/__tests__/test-d"
74
+ },
75
+ "gitHead": "d3300e12daf61bc1931da0e79d62aede9675a750"
70
76
  }