@hubspot/ui-extensions 0.12.4 → 0.13.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 (85) hide show
  1. package/dist/__generated__/version.d.ts +2 -0
  2. package/dist/__generated__/version.js +4 -0
  3. package/dist/__tests__/crm/hooks/useAssociations.spec.js +79 -0
  4. package/dist/__tests__/crm/utils/fetchAssociations.spec.js +0 -10
  5. package/dist/__tests__/experimental/hooks/useCrmSearch.spec.js +586 -0
  6. package/dist/__tests__/experimental/hooks/utils/fetchCrmSearch.spec.js +217 -0
  7. package/dist/__tests__/hooks/useDebounce.spec.js +123 -0
  8. package/dist/__tests__/hooks/utils/useFetchLifecycle.spec.js +324 -0
  9. package/dist/__tests__/internal/hook-utils.spec.js +17 -0
  10. package/dist/__tests__/test-d/extension-points.test-d.js +1 -0
  11. package/dist/crm/hooks/useAssociations.d.ts +3 -3
  12. package/dist/crm/hooks/useAssociations.js +55 -139
  13. package/dist/crm/hooks/useCrmProperties.js +29 -125
  14. package/dist/crm/utils/fetchAssociations.js +0 -3
  15. package/dist/experimental/hooks/useCrmSearch.d.ts +2 -0
  16. package/dist/experimental/hooks/useCrmSearch.js +206 -0
  17. package/dist/experimental/hooks/utils/fetchCrmSearch.d.ts +6 -0
  18. package/dist/experimental/hooks/utils/fetchCrmSearch.js +39 -0
  19. package/dist/experimental/index.d.ts +1 -0
  20. package/dist/experimental/index.js +1 -0
  21. package/dist/hooks/useDebounce.d.ts +19 -0
  22. package/dist/hooks/useDebounce.js +32 -0
  23. package/dist/hooks/utils/useFetchLifecycle.d.ts +35 -0
  24. package/dist/hooks/utils/useFetchLifecycle.js +103 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +1 -0
  27. package/dist/internal/hook-utils.d.ts +6 -0
  28. package/dist/internal/hook-utils.js +16 -1
  29. package/dist/{experimental/pages → pages}/components/page-routes.d.ts +1 -2
  30. package/dist/{experimental/pages → pages}/components/page-routes.js +0 -4
  31. package/dist/{experimental/pages → pages}/create-page-router.d.ts +1 -3
  32. package/dist/{experimental/pages → pages}/create-page-router.js +1 -3
  33. package/dist/{experimental/pages → pages}/create-page-router.test.js +2 -2
  34. package/dist/{experimental/pages → pages}/hooks.d.ts +0 -1
  35. package/dist/{experimental/pages → pages}/hooks.js +0 -1
  36. package/dist/pages/index.d.ts +6 -1
  37. package/dist/pages/index.js +4 -1
  38. package/dist/{experimental/pages → pages}/internal/page-router-internal-types.d.ts +1 -1
  39. package/dist/pages/internal/useAppPageLocation.d.ts +1 -0
  40. package/dist/{experimental/pages → pages}/internal/useAppPageLocation.js +2 -2
  41. package/dist/{experimental/pages → pages}/types.d.ts +1 -2
  42. package/dist/pages/types.js +1 -0
  43. package/dist/shared/types/actions.d.ts +12 -2
  44. package/dist/shared/types/components/table.d.ts +16 -0
  45. package/dist/shared/types/context.d.ts +6 -0
  46. package/dist/shared/types/crm.d.ts +4 -0
  47. package/dist/shared/types/experimental.d.ts +1 -1
  48. package/dist/shared/types/extension-points.d.ts +9 -3
  49. package/dist/shared/types/extension-points.js +1 -0
  50. package/dist/shared/types/hooks.d.ts +72 -0
  51. package/dist/shared/types/hooks.js +1 -0
  52. package/dist/shared/types/shared.d.ts +8 -0
  53. package/dist/shared/types/worker-globals.d.ts +5 -0
  54. package/dist/testing/__tests__/createRenderer.spec.js +1 -1
  55. package/dist/testing/internal/mocks/index.d.ts +1 -1
  56. package/dist/testing/internal/mocks/mock-extension-point-api.js +21 -1
  57. package/dist/testing/internal/mocks/mock-hooks.js +26 -0
  58. package/dist/testing/internal/types-internal.d.ts +2 -0
  59. package/dist/testing/types.d.ts +11 -1
  60. package/dist/utils/pagination.d.ts +0 -9
  61. package/package.json +3 -2
  62. package/dist/experimental/pages/index.d.ts +0 -6
  63. package/dist/experimental/pages/index.js +0 -4
  64. package/dist/experimental/pages/internal/useAppPageLocation.d.ts +0 -1
  65. package/dist/shared/types/pages/app-pages-types.d.ts +0 -75
  66. package/dist/shared/types/pages/components/page-routes.d.ts +0 -115
  67. package/dist/shared/types/pages/index.d.ts +0 -1
  68. package/dist/shared/types/pages.d.ts +0 -1
  69. /package/dist/{experimental/pages/create-page-router.test.d.ts → __tests__/experimental/hooks/useCrmSearch.spec.d.ts} +0 -0
  70. /package/dist/{experimental/pages/internal/trie-router.test.d.ts → __tests__/experimental/hooks/utils/fetchCrmSearch.spec.d.ts} +0 -0
  71. /package/dist/{experimental/pages/types.js → __tests__/hooks/useDebounce.spec.d.ts} +0 -0
  72. /package/dist/{shared/types/pages.js → __tests__/hooks/utils/useFetchLifecycle.spec.d.ts} +0 -0
  73. /package/dist/{shared/types/pages/app-pages-types.js → __tests__/internal/hook-utils.spec.d.ts} +0 -0
  74. /package/dist/{experimental/pages → pages}/components/index.d.ts +0 -0
  75. /package/dist/{experimental/pages → pages}/components/index.js +0 -0
  76. /package/dist/{shared/types/pages/components/page-routes.js → pages/create-page-router.test.d.ts} +0 -0
  77. /package/dist/{experimental/pages → pages}/internal/app-page-route-context.d.ts +0 -0
  78. /package/dist/{experimental/pages → pages}/internal/app-page-route-context.js +0 -0
  79. /package/dist/{experimental/pages → pages}/internal/convert-page-routes-react-elements.d.ts +0 -0
  80. /package/dist/{experimental/pages → pages}/internal/convert-page-routes-react-elements.js +0 -0
  81. /package/dist/{experimental/pages → pages}/internal/page-router-internal-types.js +0 -0
  82. /package/dist/{experimental/pages → pages}/internal/trie-router.d.ts +0 -0
  83. /package/dist/{experimental/pages → pages}/internal/trie-router.js +0 -0
  84. /package/dist/{shared/types/pages/index.js → pages/internal/trie-router.test.d.ts} +0 -0
  85. /package/dist/{experimental/pages → pages}/internal/trie-router.test.js +0 -0
@@ -0,0 +1,39 @@
1
+ import { getWorkerGlobals } from "../../../internal/global-utils.js";
2
+ function isCrmSearchResponse(data) {
3
+ const d = data;
4
+ if (d === null ||
5
+ typeof d !== 'object' ||
6
+ !Array.isArray(d.results) ||
7
+ typeof d.total !== 'number' ||
8
+ typeof d.hasMore !== 'boolean') {
9
+ return false;
10
+ }
11
+ return d.results.every((result) => result !== null &&
12
+ typeof result === 'object' &&
13
+ typeof result.objectId === 'number' &&
14
+ result.properties !== null &&
15
+ typeof result.properties === 'object');
16
+ }
17
+ export const fetchCrmSearch = async (request, options) => {
18
+ let response;
19
+ let result;
20
+ try {
21
+ response = await getWorkerGlobals().hsWorkerAPI.fetchCrmSearch(request, options);
22
+ result = await response.json();
23
+ }
24
+ catch (error) {
25
+ throw error instanceof Error
26
+ ? error
27
+ : new Error('Failed to fetch CRM search results: Unknown error');
28
+ }
29
+ if (result.error) {
30
+ throw new Error(result.error);
31
+ }
32
+ if (!isCrmSearchResponse(result.data)) {
33
+ throw new Error('Invalid response format');
34
+ }
35
+ return {
36
+ data: result.data,
37
+ cleanup: result.cleanup || (() => { }),
38
+ };
39
+ };
@@ -1,4 +1,5 @@
1
1
  export { useCrmProperties } from '../crm/hooks/useCrmProperties.ts';
2
2
  export { useAssociations } from '../crm/hooks/useAssociations.ts';
3
+ export { useCrmSearch } from './hooks/useCrmSearch.ts';
3
4
  export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, } from '../shared/remoteComponents.tsx';
4
5
  export type * from '../shared/types/experimental.ts';
@@ -1,3 +1,4 @@
1
1
  export { useCrmProperties } from "../crm/hooks/useCrmProperties.js";
2
2
  export { useAssociations } from "../crm/hooks/useAssociations.js";
3
+ export { useCrmSearch } from "./hooks/useCrmSearch.js";
3
4
  export { Iframe, MediaObject, Stack2, Center, GridItem, Grid, SettingsView, ExpandableText, Popover, FileInput, } from "../shared/remoteComponents.js";
@@ -0,0 +1,19 @@
1
+ import type { JsonSerializable } from '../shared/types/shared.ts';
2
+ /**
3
+ * Returns a debounced version of `value` that only updates after the value
4
+ * has stopped changing for `delayMs` milliseconds (default: 300ms).
5
+ *
6
+ * Useful for delaying expensive operations (e.g. search requests) until the
7
+ * user has finished typing.
8
+ *
9
+ * Object and array values are compared by deep equality (via `JSON.stringify`),
10
+ * so values must be JSON-serializable. Functions, Symbols, BigInts, and
11
+ * circular references are not supported.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const [searchText, setSearchText] = useState('');
16
+ * const debouncedQuery = useDebounce(searchText, 200);
17
+ * ```
18
+ */
19
+ export declare function useDebounce<T extends JsonSerializable>(value: T, delayMs?: number): T;
@@ -0,0 +1,32 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useStableValue } from "../internal/hook-utils.js";
3
+ /**
4
+ * Returns a debounced version of `value` that only updates after the value
5
+ * has stopped changing for `delayMs` milliseconds (default: 300ms).
6
+ *
7
+ * Useful for delaying expensive operations (e.g. search requests) until the
8
+ * user has finished typing.
9
+ *
10
+ * Object and array values are compared by deep equality (via `JSON.stringify`),
11
+ * so values must be JSON-serializable. Functions, Symbols, BigInts, and
12
+ * circular references are not supported.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const [searchText, setSearchText] = useState('');
17
+ * const debouncedQuery = useDebounce(searchText, 200);
18
+ * ```
19
+ */
20
+ export function useDebounce(value, delayMs = 300) {
21
+ const stableValue = useStableValue(value);
22
+ const [debouncedValue, setDebouncedValue] = useState(stableValue);
23
+ useEffect(() => {
24
+ const timer = setTimeout(() => {
25
+ setDebouncedValue(stableValue);
26
+ }, delayMs);
27
+ return () => {
28
+ clearTimeout(timer);
29
+ };
30
+ }, [stableValue, delayMs]);
31
+ return debouncedValue;
32
+ }
@@ -0,0 +1,35 @@
1
+ export interface FetchSignal {
2
+ cancelled: boolean;
3
+ isRefetch: boolean;
4
+ }
5
+ export interface FetchResult<TData> {
6
+ data: TData;
7
+ cleanup?: () => void;
8
+ }
9
+ export interface FetchContext {
10
+ isRefetch: boolean;
11
+ }
12
+ export interface FetchLifecycleCallbacks<TData> {
13
+ onStart: (context: FetchContext) => void;
14
+ onSuccess: (data: TData, context: FetchContext) => void;
15
+ onError: (error: Error, context: FetchContext) => void;
16
+ }
17
+ export interface UseFetchLifecycleOptions<TData> {
18
+ fetchFn: (signal: FetchSignal) => Promise<FetchResult<TData>>;
19
+ callbacks: FetchLifecycleCallbacks<TData>;
20
+ deps: React.DependencyList;
21
+ defaultErrorMessage?: string;
22
+ }
23
+ export interface UseFetchLifecycleResult {
24
+ refetch: () => Promise<void>;
25
+ }
26
+ /**
27
+ * Shared fetch/refetch/cleanup/abort lifecycle for data hooks.
28
+ *
29
+ * Manages:
30
+ * - Initial fetch on mount and when deps change
31
+ * - Cancellation of in-flight fetches on unmount or deps change
32
+ * - Refetch with abort signal and cleanup tracking
33
+ * - Error normalization
34
+ */
35
+ export declare function useFetchLifecycle<TData>({ fetchFn, callbacks, deps, defaultErrorMessage, }: UseFetchLifecycleOptions<TData>): UseFetchLifecycleResult;
@@ -0,0 +1,103 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ function normalizeError(err, defaultMessage) {
3
+ return err instanceof Error ? err : new Error(defaultMessage);
4
+ }
5
+ /**
6
+ * Shared fetch/refetch/cleanup/abort lifecycle for data hooks.
7
+ *
8
+ * Manages:
9
+ * - Initial fetch on mount and when deps change
10
+ * - Cancellation of in-flight fetches on unmount or deps change
11
+ * - Refetch with abort signal and cleanup tracking
12
+ * - Error normalization
13
+ */
14
+ export function useFetchLifecycle({ fetchFn, callbacks, deps, defaultErrorMessage = 'An error occurred', }) {
15
+ // Store latest versions in refs to avoid stale closures in refetch
16
+ const fetchFnRef = useRef(fetchFn);
17
+ fetchFnRef.current = fetchFn;
18
+ const callbacksRef = useRef(callbacks);
19
+ callbacksRef.current = callbacks;
20
+ const defaultErrorMessageRef = useRef(defaultErrorMessage);
21
+ defaultErrorMessageRef.current = defaultErrorMessage;
22
+ // Track in-flight refetch to support cancellation
23
+ const refetchAbortRef = useRef(null);
24
+ // Track refetch cleanup function to prevent memory leaks
25
+ const refetchCleanupRef = useRef(null);
26
+ useEffect(() => {
27
+ let cancelled = false;
28
+ let cleanup = null;
29
+ const signal = { cancelled: false, isRefetch: false };
30
+ const fetchData = async () => {
31
+ const context = { isRefetch: false };
32
+ try {
33
+ callbacksRef.current.onStart(context);
34
+ const result = await fetchFnRef.current(signal);
35
+ if (!cancelled) {
36
+ callbacksRef.current.onSuccess(result.data, context);
37
+ cleanup = result.cleanup ?? null;
38
+ }
39
+ }
40
+ catch (err) {
41
+ if (!cancelled) {
42
+ callbacksRef.current.onError(normalizeError(err, defaultErrorMessageRef.current), context);
43
+ }
44
+ }
45
+ };
46
+ fetchData();
47
+ return () => {
48
+ cancelled = true;
49
+ signal.cancelled = true;
50
+ // Call cleanup function to release resources
51
+ if (cleanup) {
52
+ cleanup();
53
+ }
54
+ // Clean up any active refetch subscription
55
+ if (refetchCleanupRef.current) {
56
+ refetchCleanupRef.current();
57
+ refetchCleanupRef.current = null;
58
+ }
59
+ };
60
+ }, deps);
61
+ const refetch = useCallback(async () => {
62
+ // Cancel any in-flight refetch
63
+ if (refetchAbortRef.current) {
64
+ refetchAbortRef.current.cancelled = true;
65
+ }
66
+ // Clean up old refetch subscription to prevent memory leaks
67
+ if (refetchCleanupRef.current) {
68
+ refetchCleanupRef.current();
69
+ refetchCleanupRef.current = null;
70
+ }
71
+ // Create new abort signal for this refetch
72
+ const abortSignal = { cancelled: false, isRefetch: true };
73
+ refetchAbortRef.current = abortSignal;
74
+ const context = { isRefetch: true };
75
+ try {
76
+ callbacksRef.current.onStart(context);
77
+ const result = await fetchFnRef.current(abortSignal);
78
+ if (!abortSignal.cancelled) {
79
+ callbacksRef.current.onSuccess(result.data, context);
80
+ // Store cleanup for next refetch or unmount
81
+ refetchCleanupRef.current = result.cleanup ?? null;
82
+ }
83
+ else {
84
+ // If cancelled, clean up immediately
85
+ if (result.cleanup) {
86
+ result.cleanup();
87
+ }
88
+ }
89
+ }
90
+ catch (err) {
91
+ if (!abortSignal.cancelled) {
92
+ callbacksRef.current.onError(normalizeError(err, defaultErrorMessageRef.current), context);
93
+ }
94
+ }
95
+ finally {
96
+ // Clear the abort ref if this is still the current refetch
97
+ if (refetchAbortRef.current === abortSignal) {
98
+ refetchAbortRef.current = null;
99
+ }
100
+ }
101
+ }, []);
102
+ return { refetch };
103
+ }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export { hubspot } from './hubspot.ts';
3
3
  export { logger } from './logger.ts';
4
4
  export * from './shared/types/index.ts';
5
5
  export { Accordion, Alert, AutoGrid, BarChart, Box, Button, ButtonRow, Card, Checkbox, CurrencyInput, DateInput, DescriptionList, DescriptionListItem, Divider, Dropdown, EmptyState, ErrorState, Flex, Form, Heading, Icon, Illustration, Image, Inline, Input, LineChart, Link, List, LoadingButton, LoadingSpinner, Modal, ModalBody, ModalFooter, MultiSelect, NumberInput, Panel, PanelBody, PanelFooter, PanelSection, ProgressBar, RadioButton, ScoreCircle, SearchInput, Select, Spacer, Stack, Statistics, StatisticsItem, StatisticsTrend, StatusTag, StepIndicator, StepperInput, Tab, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Tag, Text, TextArea, Textarea, Tile, TimeInput, Toggle, ToggleGroup, Tooltip, } from './shared/remoteComponents.tsx';
6
+ export { useDebounce } from './hooks/useDebounce.ts';
6
7
  export { useExtensionContext } from './hooks/useExtensionContext.tsx';
7
8
  export { useExtensionActions } from './hooks/useExtensionActions.tsx';
8
9
  export { useExtensionApi } from './hooks/useExtensionApi.tsx';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export { hubspot } from "./hubspot.js";
4
4
  export { logger } from "./logger.js";
5
5
  export * from "./shared/types/index.js";
6
6
  export { Accordion, Alert, AutoGrid, BarChart, Box, Button, ButtonRow, Card, Checkbox, CurrencyInput, DateInput, DescriptionList, DescriptionListItem, Divider, Dropdown, EmptyState, ErrorState, Flex, Form, Heading, Icon, Illustration, Image, Inline, Input, LineChart, Link, List, LoadingButton, LoadingSpinner, Modal, ModalBody, ModalFooter, MultiSelect, NumberInput, Panel, PanelBody, PanelFooter, PanelSection, ProgressBar, RadioButton, ScoreCircle, SearchInput, Select, Spacer, Stack, Statistics, StatisticsItem, StatisticsTrend, StatusTag, StepIndicator, StepperInput, Tab, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Tag, Text, TextArea, Textarea, Tile, TimeInput, Toggle, ToggleGroup, Tooltip, } from "./shared/remoteComponents.js";
7
+ export { useDebounce } from "./hooks/useDebounce.js";
7
8
  export { useExtensionContext } from "./hooks/useExtensionContext.js";
8
9
  export { useExtensionActions } from "./hooks/useExtensionActions.js";
9
10
  export { useExtensionApi } from "./hooks/useExtensionApi.js";
@@ -1,3 +1,4 @@
1
+ import type { JsonSerializable } from '../shared/types/shared.ts';
1
2
  import type { RendererMocksContext } from '../testing/internal/mocks/index.ts';
2
3
  import type { RendererMocks } from '../testing/types.ts';
3
4
  type AnyFunction = (...args: any[]) => any;
@@ -24,4 +25,9 @@ export declare function useMocksContext(): RendererMocksContext | null;
24
25
  * @returns The children wrapped in the Mocks context provider.
25
26
  */
26
27
  export declare const MocksContextProvider: import("react").Provider<RendererMocksContext | null>;
28
+ /**
29
+ * Stabilizes a value's reference identity across re-renders using deep
30
+ * comparison via JSON.stringify.
31
+ */
32
+ export declare function useStableValue<T extends JsonSerializable>(value: T): T;
27
33
  export {};
@@ -1,4 +1,4 @@
1
- import { createContext, useContext } from 'react';
1
+ import { createContext, useContext, useRef } from 'react';
2
2
  const ReactRenderMocksContext = createContext(null);
3
3
  /**
4
4
  * Creates a mock-aware hook function that can be used to mock the original hook function.
@@ -40,3 +40,18 @@ export function useMocksContext() {
40
40
  * @returns The children wrapped in the Mocks context provider.
41
41
  */
42
42
  export const MocksContextProvider = ReactRenderMocksContext.Provider;
43
+ /**
44
+ * Stabilizes a value's reference identity across re-renders using deep
45
+ * comparison via JSON.stringify.
46
+ */
47
+ export function useStableValue(value) {
48
+ const stableRef = useRef({
49
+ key: JSON.stringify(value),
50
+ value,
51
+ });
52
+ const key = JSON.stringify(value);
53
+ if (key !== stableRef.current.key) {
54
+ stableRef.current = { key, value };
55
+ }
56
+ return stableRef.current.value;
57
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ComponentType, ReactNode } from 'react';
2
- import type { EmptyProps } from '../../../shared/types/shared.ts';
2
+ import type { EmptyProps } from '../../shared/types/shared.ts';
3
3
  /**
4
4
  * The props type for the layout component of [PageRoutes](https://developers.hubspot.com/docs/apps/developer-platform/add-features/ui-extensibility/ui-components/app-page-components/page-routes#pageroutes).
5
5
  */
@@ -72,7 +72,6 @@ export interface PageRoutesProps {
72
72
  *
73
73
  * See [PageRoutes](https://developers.hubspot.com/docs/apps/developer-platform/add-features/ui-extensibility/ui-components/app-page-components/page-routes#pageroutes) for more information.
74
74
  *
75
- * @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.
76
75
  */
77
76
  export declare function PageRoutes(__props: PageRoutesProps): null;
78
77
  export declare namespace PageRoutes {
@@ -18,7 +18,6 @@ class PageRoutesRenderError extends Error {
18
18
  *
19
19
  * See [PageRoutes](https://developers.hubspot.com/docs/apps/developer-platform/add-features/ui-extensibility/ui-components/app-page-components/page-routes#pageroutes) for more information.
20
20
  *
21
- * @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.
22
21
  */
23
22
  export function PageRoutes(__props) {
24
23
  throw new PageRoutesRenderError('PageRoutes');
@@ -32,7 +31,6 @@ export function PageRoutes(__props) {
32
31
  * <PageRoutes.IndexRoute component={HomePage} />
33
32
  * ```
34
33
  *
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
34
  */
37
35
  PageRoutes.IndexRoute = function IndexRoute(__props) {
38
36
  throw new PageRoutesRenderError('PageRoutes.IndexRoute');
@@ -45,7 +43,6 @@ PageRoutes.IndexRoute = function IndexRoute(__props) {
45
43
  * ```tsx
46
44
  * <PageRoutes.Route path="/docs" component={DocsPage} />
47
45
  * ```
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
46
  */
50
47
  PageRoutes.Route = function Route(__props) {
51
48
  throw new PageRoutesRenderError('PageRoutes.Route');
@@ -59,7 +56,6 @@ PageRoutes.Route = function Route(__props) {
59
56
  * <PageRoutes.AnyRoute component={NotFoundPage} />
60
57
  * ```
61
58
  *
62
- * @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.
63
59
  */
64
60
  PageRoutes.AnyRoute = function AnyRoute(__props) {
65
61
  throw new PageRoutesRenderError('PageRoutes.AnyRoute');
@@ -1,5 +1,5 @@
1
1
  import { type ReactElement } from 'react';
2
- import type { ReactFragmentProps } from '../../shared/types/index.ts';
2
+ import type { ReactFragmentProps } from '../shared/types/index.ts';
3
3
  import type { PageRoutesProps } from './components/page-routes.ts';
4
4
  import type { PageRouterComponent } from './types.ts';
5
5
  /**
@@ -29,7 +29,5 @@ import type { PageRouterComponent } from './types.ts';
29
29
  *
30
30
  * @param routes The routes to render.
31
31
  * @returns The page router component.
32
- *
33
- * @experimental This function is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates.
34
32
  */
35
33
  export declare const createPageRouter: (reactPageRoutesElement: ReactElement<PageRoutesProps> | ReactElement<ReactFragmentProps>) => PageRouterComponent;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from 'react';
3
- import { EmptyState, Text } from "../../shared/remoteComponents.js";
3
+ import { EmptyState, Text } from "../shared/remoteComponents.js";
4
4
  import { AppPageRouteProvider } from "./internal/app-page-route-context.js";
5
5
  import { convertReactPageRoutesElement } from "./internal/convert-page-routes-react-elements.js";
6
6
  import { RouteNodeType, } from "./internal/page-router-internal-types.js";
@@ -69,8 +69,6 @@ const addRoutes = (trieRouter, seenRouteIds, routeNode, parentPath, parentLayout
69
69
  *
70
70
  * @param routes The routes to render.
71
71
  * @returns The page router component.
72
- *
73
- * @experimental This function is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates.
74
72
  */
75
73
  export const createPageRouter = (reactPageRoutesElement) => {
76
74
  // Convert the React element to a route node and add them to a trie router.
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { describe, expect, it } from 'vitest';
3
- import { Box, Text } from "../../index.js";
4
- import { createRenderer } from "../../testing/index.js";
3
+ import { Box, Text } from "../index.js";
4
+ import { createRenderer } from "../testing/index.js";
5
5
  import { PageRoutes } from "./components/page-routes.js";
6
6
  import { createPageRouter } from "./create-page-router.js";
7
7
  import { usePageRoute } from "./hooks.js";
@@ -3,6 +3,5 @@ import type { MatchedPageRoute } from './types.ts';
3
3
  * The hook that provides the current page route to the component.
4
4
  *
5
5
  * @returns The current page route.
6
- * @experimental This hook is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates.
7
6
  */
8
7
  export declare const usePageRoute: () => MatchedPageRoute;
@@ -4,7 +4,6 @@ import { AppPageRouteContext } from "./internal/app-page-route-context.js";
4
4
  * The hook that provides the current page route to the component.
5
5
  *
6
6
  * @returns The current page route.
7
- * @experimental This hook is experimental. Avoid using it in production due to potential breaking changes. Your feedback is valuable for improvements. Stay tuned for updates.
8
7
  */
9
8
  export const usePageRoute = () => {
10
9
  const matchedPageRoute = useContext(AppPageRouteContext);
@@ -1 +1,6 @@
1
- export { PageBreadcrumbs } from '../shared/remoteComponents.tsx';
1
+ export * from './components/index.ts';
2
+ export * from './create-page-router.tsx';
3
+ export * from './hooks.ts';
4
+ export type * from './types.ts';
5
+ export type * from '../shared/types/pages/components/index.ts';
6
+ export { PageBreadcrumbs, PageHeader, PageLink, PageTitle, } from '../shared/remoteComponents.tsx';
@@ -1 +1,4 @@
1
- export { PageBreadcrumbs } from "../shared/remoteComponents.js";
1
+ export * from "./components/index.js";
2
+ export * from "./create-page-router.js";
3
+ export * from "./hooks.js";
4
+ export { PageBreadcrumbs, PageHeader, PageLink, PageTitle, } from "../shared/remoteComponents.js";
@@ -1,5 +1,5 @@
1
1
  import type { ComponentType, ReactNode } from 'react';
2
- import type { EmptyProps } from '../../../shared/types/shared.ts';
2
+ import type { EmptyProps } from '../../shared/types/shared.ts';
3
3
  import type { PageRoutesLayoutComponent } from '../components/page-routes.ts';
4
4
  export interface PageRoutesLayoutProps {
5
5
  children: ReactNode;
@@ -0,0 +1 @@
1
+ export declare const useAppPageLocation: () => import("../../index.ts").AppPageLocation;
@@ -1,5 +1,5 @@
1
- import { getWorkerGlobals } from "../../../internal/global-utils.js";
2
- import { useMocksContext } from "../../../internal/hook-utils.js";
1
+ import { getWorkerGlobals } from "../../internal/global-utils.js";
2
+ import { useMocksContext } from "../../internal/hook-utils.js";
3
3
  export const useAppPageLocation = () => {
4
4
  const mocksContext = useMocksContext();
5
5
  if (!mocksContext) {
@@ -1,5 +1,5 @@
1
1
  import type { ComponentType } from 'react';
2
- import type { EmptyProps } from '../../shared/types/shared.ts';
2
+ import type { EmptyProps } from '../shared/types/shared.ts';
3
3
  /**
4
4
  * Represents the current page route that has been matched by the page router.
5
5
  */
@@ -24,6 +24,5 @@ export type PageRouterProps = EmptyProps;
24
24
  /**
25
25
  * The component type for the page router that handles rendering matched routes.
26
26
  *
27
- * @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.
28
27
  */
29
28
  export type PageRouterComponent = ComponentType<PageRouterProps>;
@@ -0,0 +1 @@
1
+ export {};
@@ -31,6 +31,11 @@ export type onCrmPropertiesUpdateAction = (properties: string[] | '*', callback:
31
31
  /** @ignore */
32
32
  export type CloseOverlayAction = (id: string) => void;
33
33
  /** @ignore */
34
+ export type NavigateToPageAction = (options: {
35
+ to: string;
36
+ params?: Record<string, string>;
37
+ }) => void;
38
+ /** @ignore */
34
39
  export interface CrmHostActions {
35
40
  addAlert: AddAlertAction;
36
41
  reloadPage: ReloadPageAction;
@@ -48,10 +53,15 @@ export interface AppHomeActions {
48
53
  addAlert: AddAlertAction;
49
54
  }
50
55
  /** @ignore */
56
+ export interface PagesActions {
57
+ addAlert: AddAlertAction;
58
+ }
59
+ /** @ignore */
51
60
  export interface UiePlatformActions {
52
- copyTextToClipboard: Clipboard['writeText'];
53
61
  closeOverlay: CloseOverlayAction;
54
- reloadPage: ReloadPageAction;
62
+ copyTextToClipboard: Clipboard['writeText'];
63
+ navigateToPage: NavigateToPageAction;
55
64
  openIframeModal: OpenIframeModalAction;
65
+ reloadPage: ReloadPageAction;
56
66
  }
57
67
  export {};
@@ -70,6 +70,14 @@ export interface SortedTableHeaderProps extends BaseTableHeaderProps {
70
70
  * @category Component Props
71
71
  */
72
72
  export type TableHeaderProps = SortedTableHeaderProps | UnSortedTableHeaderProps;
73
+ /**
74
+ * Controls row height and vertical padding for every cell in the table.
75
+ *
76
+ * - `'default'`: generous row height and padding around content.
77
+ * - `'condensed'`: moderate row height and padding around content.
78
+ * - `'compact'`: no min row height, minimal padding. Rows collapse to content height.
79
+ */
80
+ export type TableDensity = 'default' | 'condensed' | 'compact';
73
81
  interface BaseTableProps extends BaseComponentProps {
74
82
  /**
75
83
  * When set to `false`, the table will not include borders.
@@ -83,6 +91,14 @@ interface BaseTableProps extends BaseComponentProps {
83
91
  * @defaultValue `false`
84
92
  */
85
93
  flush?: boolean;
94
+ /**
95
+ * Sets row height and vertical padding for all header, body, and footer cells.
96
+ * Use `'condensed'` for denser data views and `'compact'` when rows should be
97
+ * sized only to their content.
98
+ *
99
+ * @defaultValue `'default'`
100
+ */
101
+ density?: TableDensity;
86
102
  /**
87
103
  * Sets the content that will render inside the component. This prop is passed implicitly by providing sub-components. The children should be one of Table's subcomponents.
88
104
  */
@@ -18,6 +18,7 @@ export interface UserContext {
18
18
  roles: string[];
19
19
  teams: Team[];
20
20
  locale?: string;
21
+ language?: string;
21
22
  permissions: Permission[];
22
23
  }
23
24
  /** @ignore */
@@ -60,6 +61,11 @@ export interface AppHomeContext extends BaseContext {
60
61
  extension?: AppContext;
61
62
  }
62
63
  /** @ignore */
64
+ export interface PagesContext extends BaseContext {
65
+ location: 'pages';
66
+ extension?: AppContext;
67
+ }
68
+ /** @ignore */
63
69
  export interface GenericContext extends BaseContext {
64
70
  location: 'uie.playground.middle';
65
71
  }
@@ -372,6 +372,10 @@ export interface CrmActionLinkProps extends BaseActionComponent {
372
372
  * @defaultValue `"primary"`
373
373
  */
374
374
  variant?: 'primary' | 'light' | 'dark' | 'destructive';
375
+ /**
376
+ * A function that will be invoked when the link is clicked. It receives no arguments and its return value is ignored.
377
+ */
378
+ onClick?: () => void;
375
379
  }
376
380
  export interface ActionLibraryButtonCardActionConfig<SpecificActionType extends ActionType = ActionType> {
377
381
  /**
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { ReactionsHandler } from './reactions.ts';
3
3
  import type { AllDistances, BaseComponentProps, ExtensionEvent } from './shared.ts';
4
+ export type { CrmSearchFilter, CrmSearchFilterGroup, CrmSearchResponse, CrmSearchResultItem, CrmSearchSort, FetchCrmSearchRequest, UseCrmSearchOptions, UseCrmSearchResult, FormattingOptions, } from './hooks.ts';
4
5
  /**
5
6
  * @ignore
6
7
  * @experimental do not use in production
@@ -233,4 +234,3 @@ export interface BaseInputProps<T = string, V = string> {
233
234
  */
234
235
  onFocus?: (value: V) => void;
235
236
  }
236
- export {};
@@ -1,6 +1,6 @@
1
1
  import type { ComponentType } from 'react';
2
- import type { AppHomeActions, CrmHostActions, SettingsActions, UiePlatformActions } from './actions.ts';
3
- import type { AppHomeContext, BaseContext, CrmContext, GenericContext, SettingsContext } from './context.ts';
2
+ import type { AppHomeActions, CrmHostActions, PagesActions, SettingsActions, UiePlatformActions } from './actions.ts';
3
+ import type { AppHomeContext, BaseContext, CrmContext, GenericContext, PagesContext, SettingsContext } from './context.ts';
4
4
  import type { CrmActionButtonProps, CrmActionLinkProps, CrmAssociationPivotProps, CrmAssociationPropertyListProps, CrmAssociationStageTrackerProps, CrmAssociationTableProps, CrmCardActionsProps, CrmDataHighlightProps, CrmPropertyListProps, CrmRelativeTimelineProps, CrmReportProps, CrmSimpleDeadlineProps, CrmStageTrackerProps, CrmStatisticsProps } from './crm.ts';
5
5
  import type { ServerlessFuncRunner } from './http-requests.ts';
6
6
  import type { TypesOfReadOnlyArray } from './shared.ts';
@@ -75,6 +75,11 @@ export interface AppHomeExtensionPoint extends ExtensionPointContract {
75
75
  actions: AppHomeActions & UiePlatformActions;
76
76
  context: AppHomeContext;
77
77
  }
78
+ /** @ignore */
79
+ export interface PagesExtensionPoint extends ExtensionPointContract {
80
+ actions: PagesActions & UiePlatformActions;
81
+ context: PagesContext;
82
+ }
78
83
  export interface ExampleCrmComponentProps {
79
84
  name: string;
80
85
  size: 'sm' | 'md' | 'lg';
@@ -90,7 +95,7 @@ interface RemotePlaygroundExtensionPoint extends ExtensionPointContract {
90
95
  ExampleCrmComponent: ComponentType<ExampleCrmComponentProps>;
91
96
  };
92
97
  }
93
- export declare const EXTENSION_POINT_LOCATIONS: readonly ["crm.preview", "crm.record.sidebar", "crm.record.tab", "helpdesk.sidebar", "uie.playground.middle", "settings", "home"];
98
+ export declare const EXTENSION_POINT_LOCATIONS: readonly ["crm.preview", "crm.record.sidebar", "crm.record.tab", "helpdesk.sidebar", "uie.playground.middle", "settings", "home", "pages"];
94
99
  export type ExtensionPointLocation = TypesOfReadOnlyArray<typeof EXTENSION_POINT_LOCATIONS>;
95
100
  /** @ignore */
96
101
  interface LocationToExtensionPoint {
@@ -101,6 +106,7 @@ interface LocationToExtensionPoint {
101
106
  'helpdesk.sidebar': StandardCrmExtensionPoint;
102
107
  settings: SettingsExtensionPoint;
103
108
  home: AppHomeExtensionPoint;
109
+ pages: PagesExtensionPoint;
104
110
  }
105
111
  /**
106
112
  * While this resolves to the same result as LocationToExtensionPoint, it ensures that every location
@@ -6,4 +6,5 @@ export const EXTENSION_POINT_LOCATIONS = [
6
6
  'uie.playground.middle',
7
7
  'settings',
8
8
  'home',
9
+ 'pages',
9
10
  ];