@canva/cli 1.19.0 → 1.21.0

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 (231) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -9
  3. package/cli.js +625 -626
  4. package/package.json +2 -3
  5. package/templates/base/backend/base_backend/create.ts +0 -114
  6. package/templates/base/backend/database/database.ts +0 -42
  7. package/templates/base/backend/routers/auth.ts +0 -288
  8. package/templates/base/declarations/declarations.d.ts +0 -29
  9. package/templates/base/eslint.config.mjs +0 -14
  10. package/templates/base/package.json +0 -91
  11. package/templates/base/scripts/copy_env.ts +0 -13
  12. package/templates/base/scripts/ssl/ssl.ts +0 -131
  13. package/templates/base/scripts/start/app_runner.ts +0 -223
  14. package/templates/base/scripts/start/context.ts +0 -171
  15. package/templates/base/scripts/start/start.ts +0 -46
  16. package/templates/base/scripts/start.tests.ts +0 -61
  17. package/templates/base/styles/components.css +0 -38
  18. package/templates/base/tsconfig.json +0 -56
  19. package/templates/base/webpack.config.ts +0 -247
  20. package/templates/common/.env.template +0 -6
  21. package/templates/common/.gitignore.template +0 -8
  22. package/templates/common/.nvmrc +0 -1
  23. package/templates/common/.prettierrc +0 -21
  24. package/templates/common/LICENSE.md +0 -48
  25. package/templates/common/README.md +0 -179
  26. package/templates/common/jest.config.mjs +0 -35
  27. package/templates/common/jest.setup.ts +0 -35
  28. package/templates/content_publisher/README.md +0 -58
  29. package/templates/content_publisher/canva-app.json +0 -17
  30. package/templates/content_publisher/declarations/declarations.d.ts +0 -29
  31. package/templates/content_publisher/eslint.config.mjs +0 -14
  32. package/templates/content_publisher/package.json +0 -90
  33. package/templates/content_publisher/scripts/copy_env.ts +0 -13
  34. package/templates/content_publisher/scripts/ssl/ssl.ts +0 -131
  35. package/templates/content_publisher/scripts/start/app_runner.ts +0 -223
  36. package/templates/content_publisher/scripts/start/context.ts +0 -171
  37. package/templates/content_publisher/scripts/start/start.ts +0 -46
  38. package/templates/content_publisher/src/index.tsx +0 -4
  39. package/templates/content_publisher/src/intents/content_publisher/index.tsx +0 -107
  40. package/templates/content_publisher/src/intents/content_publisher/post_preview.tsx +0 -240
  41. package/templates/content_publisher/src/intents/content_publisher/preview_ui.tsx +0 -62
  42. package/templates/content_publisher/src/intents/content_publisher/settings_ui.tsx +0 -81
  43. package/templates/content_publisher/src/intents/content_publisher/types.ts +0 -29
  44. package/templates/content_publisher/styles/components.css +0 -38
  45. package/templates/content_publisher/styles/preview_ui.css +0 -49
  46. package/templates/content_publisher/tsconfig.json +0 -56
  47. package/templates/content_publisher/webpack.config.ts +0 -247
  48. package/templates/dam/backend/routers/dam.ts +0 -108
  49. package/templates/dam/backend/server.ts +0 -65
  50. package/templates/dam/canva-app.json +0 -25
  51. package/templates/dam/declarations/declarations.d.ts +0 -29
  52. package/templates/dam/eslint.config.mjs +0 -14
  53. package/templates/dam/package.json +0 -97
  54. package/templates/dam/scripts/copy_env.ts +0 -13
  55. package/templates/dam/scripts/ssl/ssl.ts +0 -131
  56. package/templates/dam/scripts/start/app_runner.ts +0 -223
  57. package/templates/dam/scripts/start/context.ts +0 -171
  58. package/templates/dam/scripts/start/start.ts +0 -46
  59. package/templates/dam/src/index.tsx +0 -4
  60. package/templates/dam/src/intents/design_editor/adapter.ts +0 -44
  61. package/templates/dam/src/intents/design_editor/app.tsx +0 -35
  62. package/templates/dam/src/intents/design_editor/config.ts +0 -220
  63. package/templates/dam/src/intents/design_editor/index.css +0 -3
  64. package/templates/dam/src/intents/design_editor/index.tsx +0 -25
  65. package/templates/dam/tsconfig.json +0 -56
  66. package/templates/dam/utils/backend/base_backend/create.ts +0 -114
  67. package/templates/dam/webpack.config.ts +0 -247
  68. package/templates/data_connector/README.md +0 -84
  69. package/templates/data_connector/canva-app.json +0 -21
  70. package/templates/data_connector/declarations/declarations.d.ts +0 -29
  71. package/templates/data_connector/eslint.config.mjs +0 -14
  72. package/templates/data_connector/package.json +0 -92
  73. package/templates/data_connector/scripts/copy_env.ts +0 -13
  74. package/templates/data_connector/scripts/ssl/ssl.ts +0 -131
  75. package/templates/data_connector/scripts/start/app_runner.ts +0 -223
  76. package/templates/data_connector/scripts/start/context.ts +0 -171
  77. package/templates/data_connector/scripts/start/start.ts +0 -46
  78. package/templates/data_connector/src/api/connect_client.ts +0 -6
  79. package/templates/data_connector/src/api/data_source.ts +0 -97
  80. package/templates/data_connector/src/api/data_sources/designs.tsx +0 -296
  81. package/templates/data_connector/src/api/data_sources/index.ts +0 -4
  82. package/templates/data_connector/src/api/data_sources/templates.tsx +0 -328
  83. package/templates/data_connector/src/api/fetch_data_table.ts +0 -55
  84. package/templates/data_connector/src/api/index.ts +0 -4
  85. package/templates/data_connector/src/api/oauth.ts +0 -8
  86. package/templates/data_connector/src/api/tests/data_source.test.tsx +0 -99
  87. package/templates/data_connector/src/components/app_error.tsx +0 -15
  88. package/templates/data_connector/src/components/footer.tsx +0 -26
  89. package/templates/data_connector/src/components/header.tsx +0 -40
  90. package/templates/data_connector/src/components/index.ts +0 -3
  91. package/templates/data_connector/src/components/inputs/messages.tsx +0 -95
  92. package/templates/data_connector/src/components/inputs/search_filter.tsx +0 -109
  93. package/templates/data_connector/src/components/inputs/select_field.tsx +0 -26
  94. package/templates/data_connector/src/context/app_context.tsx +0 -125
  95. package/templates/data_connector/src/context/index.ts +0 -2
  96. package/templates/data_connector/src/context/use_app_context.ts +0 -17
  97. package/templates/data_connector/src/index.tsx +0 -4
  98. package/templates/data_connector/src/intents/data_connector/app.tsx +0 -20
  99. package/templates/data_connector/src/intents/data_connector/entrypoint.tsx +0 -70
  100. package/templates/data_connector/src/intents/data_connector/home.tsx +0 -21
  101. package/templates/data_connector/src/intents/data_connector/index.tsx +0 -56
  102. package/templates/data_connector/src/pages/data_source_config.tsx +0 -9
  103. package/templates/data_connector/src/pages/error.tsx +0 -37
  104. package/templates/data_connector/src/pages/index.ts +0 -4
  105. package/templates/data_connector/src/pages/login.tsx +0 -145
  106. package/templates/data_connector/src/pages/select_source.tsx +0 -24
  107. package/templates/data_connector/src/routes/index.ts +0 -2
  108. package/templates/data_connector/src/routes/paths.ts +0 -7
  109. package/templates/data_connector/src/routes/protected_route.tsx +0 -26
  110. package/templates/data_connector/src/routes/routes.tsx +0 -42
  111. package/templates/data_connector/src/utils/data_params.ts +0 -17
  112. package/templates/data_connector/src/utils/data_table.ts +0 -116
  113. package/templates/data_connector/src/utils/fetch_result.ts +0 -36
  114. package/templates/data_connector/src/utils/index.ts +0 -2
  115. package/templates/data_connector/src/utils/tests/data_table.test.ts +0 -133
  116. package/templates/data_connector/styles/components.css +0 -38
  117. package/templates/data_connector/tsconfig.json +0 -56
  118. package/templates/data_connector/webpack.config.ts +0 -247
  119. package/templates/gen_ai/README.md +0 -27
  120. package/templates/gen_ai/backend/routers/image.ts +0 -232
  121. package/templates/gen_ai/backend/server.ts +0 -65
  122. package/templates/gen_ai/canva-app.json +0 -25
  123. package/templates/gen_ai/declarations/declarations.d.ts +0 -29
  124. package/templates/gen_ai/eslint.config.mjs +0 -14
  125. package/templates/gen_ai/package.json +0 -101
  126. package/templates/gen_ai/scripts/copy_env.ts +0 -13
  127. package/templates/gen_ai/scripts/ssl/ssl.ts +0 -131
  128. package/templates/gen_ai/scripts/start/app_runner.ts +0 -223
  129. package/templates/gen_ai/scripts/start/context.ts +0 -171
  130. package/templates/gen_ai/scripts/start/start.ts +0 -46
  131. package/templates/gen_ai/src/api/api.ts +0 -194
  132. package/templates/gen_ai/src/api/index.ts +0 -1
  133. package/templates/gen_ai/src/components/app_error.tsx +0 -18
  134. package/templates/gen_ai/src/components/footer.messages.ts +0 -48
  135. package/templates/gen_ai/src/components/footer.tsx +0 -156
  136. package/templates/gen_ai/src/components/image_grid.tsx +0 -103
  137. package/templates/gen_ai/src/components/index.ts +0 -7
  138. package/templates/gen_ai/src/components/loading_results.tsx +0 -169
  139. package/templates/gen_ai/src/components/prompt_input.messages.ts +0 -14
  140. package/templates/gen_ai/src/components/prompt_input.tsx +0 -154
  141. package/templates/gen_ai/src/components/remaining_credits.tsx +0 -84
  142. package/templates/gen_ai/src/components/report_box.tsx +0 -54
  143. package/templates/gen_ai/src/components/tests/remaining_credit.tests.tsx +0 -47
  144. package/templates/gen_ai/src/config.ts +0 -21
  145. package/templates/gen_ai/src/context/app_context.tsx +0 -153
  146. package/templates/gen_ai/src/context/context.messages.ts +0 -30
  147. package/templates/gen_ai/src/context/index.ts +0 -2
  148. package/templates/gen_ai/src/context/use_app_context.ts +0 -17
  149. package/templates/gen_ai/src/index.tsx +0 -4
  150. package/templates/gen_ai/src/intents/design_editor/app.tsx +0 -19
  151. package/templates/gen_ai/src/intents/design_editor/home.tsx +0 -13
  152. package/templates/gen_ai/src/intents/design_editor/index.tsx +0 -17
  153. package/templates/gen_ai/src/pages/error.tsx +0 -41
  154. package/templates/gen_ai/src/pages/generate.tsx +0 -9
  155. package/templates/gen_ai/src/pages/index.ts +0 -3
  156. package/templates/gen_ai/src/pages/results.tsx +0 -31
  157. package/templates/gen_ai/src/routes/index.ts +0 -1
  158. package/templates/gen_ai/src/routes/paths.ts +0 -4
  159. package/templates/gen_ai/src/routes/routes.tsx +0 -24
  160. package/templates/gen_ai/src/utils/index.ts +0 -1
  161. package/templates/gen_ai/src/utils/obscenity_filter.ts +0 -33
  162. package/templates/gen_ai/styles/components.css +0 -38
  163. package/templates/gen_ai/styles/utils.css +0 -3
  164. package/templates/gen_ai/tsconfig.json +0 -56
  165. package/templates/gen_ai/utils/backend/base_backend/create.ts +0 -114
  166. package/templates/gen_ai/webpack.config.ts +0 -247
  167. package/templates/hello_world/canva-app.json +0 -21
  168. package/templates/hello_world/declarations/declarations.d.ts +0 -29
  169. package/templates/hello_world/eslint.config.mjs +0 -14
  170. package/templates/hello_world/package.json +0 -90
  171. package/templates/hello_world/scripts/copy_env.ts +0 -13
  172. package/templates/hello_world/scripts/ssl/ssl.ts +0 -131
  173. package/templates/hello_world/scripts/start/app_runner.ts +0 -223
  174. package/templates/hello_world/scripts/start/context.ts +0 -171
  175. package/templates/hello_world/scripts/start/start.ts +0 -46
  176. package/templates/hello_world/src/index.tsx +0 -4
  177. package/templates/hello_world/src/intents/design_editor/app.tsx +0 -86
  178. package/templates/hello_world/src/intents/design_editor/index.tsx +0 -25
  179. package/templates/hello_world/src/intents/design_editor/tests/__snapshots__/app.tests.tsx.snap +0 -45
  180. package/templates/hello_world/src/intents/design_editor/tests/app.tests.tsx +0 -92
  181. package/templates/hello_world/styles/components.css +0 -38
  182. package/templates/hello_world/tsconfig.json +0 -56
  183. package/templates/hello_world/webpack.config.ts +0 -247
  184. package/templates/mls/README.md +0 -81
  185. package/templates/mls/canva-app.json +0 -25
  186. package/templates/mls/declarations/declarations.d.ts +0 -29
  187. package/templates/mls/eslint.config.mjs +0 -14
  188. package/templates/mls/jest.config.mjs +0 -36
  189. package/templates/mls/jest.setup.ts +0 -39
  190. package/templates/mls/package.json +0 -117
  191. package/templates/mls/scripts/copy_env.ts +0 -13
  192. package/templates/mls/scripts/ssl/ssl.ts +0 -131
  193. package/templates/mls/scripts/start/app_runner.ts +0 -223
  194. package/templates/mls/scripts/start/context.ts +0 -171
  195. package/templates/mls/scripts/start/start.ts +0 -46
  196. package/templates/mls/src/__tests__/app.tests.tsx +0 -11
  197. package/templates/mls/src/__tests__/office_selection_page.tests.tsx +0 -72
  198. package/templates/mls/src/__tests__/utils.tsx +0 -19
  199. package/templates/mls/src/adapter.ts +0 -126
  200. package/templates/mls/src/components/agent/agent_card.tsx +0 -57
  201. package/templates/mls/src/components/agent/agent_grid.tsx +0 -37
  202. package/templates/mls/src/components/agent/agent_list.tsx +0 -17
  203. package/templates/mls/src/components/agent/agent_search_filters.tsx +0 -88
  204. package/templates/mls/src/components/breadcrumb/breadcrumb.tsx +0 -40
  205. package/templates/mls/src/components/listing/listing_card.tsx +0 -64
  206. package/templates/mls/src/components/listing/listing_grid.tsx +0 -37
  207. package/templates/mls/src/components/listing/listing_list.tsx +0 -21
  208. package/templates/mls/src/components/listing/listing_search_filters.tsx +0 -145
  209. package/templates/mls/src/components/placeholders/placeholders.tsx +0 -65
  210. package/templates/mls/src/data.ts +0 -359
  211. package/templates/mls/src/index.tsx +0 -4
  212. package/templates/mls/src/intents/design_editor/app.tsx +0 -44
  213. package/templates/mls/src/intents/design_editor/index.tsx +0 -25
  214. package/templates/mls/src/pages/agent_details_page/agent_details_page.tsx +0 -175
  215. package/templates/mls/src/pages/list_page/agent_tab_panel.tsx +0 -126
  216. package/templates/mls/src/pages/list_page/list_page.tsx +0 -67
  217. package/templates/mls/src/pages/list_page/listing_tab_panel.tsx +0 -135
  218. package/templates/mls/src/pages/listing_details_page/listing_details_page.tsx +0 -418
  219. package/templates/mls/src/pages/loading_page/loading_page.tsx +0 -152
  220. package/templates/mls/src/pages/office_selection_page/office_selection_page.tsx +0 -144
  221. package/templates/mls/src/real_estate.type.ts +0 -44
  222. package/templates/mls/src/util/use_add_element.tsx +0 -62
  223. package/templates/mls/src/util/use_drag_element.tsx +0 -68
  224. package/templates/mls/styles/components.css +0 -38
  225. package/templates/mls/tsconfig.json +0 -54
  226. package/templates/mls/webpack.config.ts +0 -248
  227. package/templates/optional/.cursor/mcp.json +0 -8
  228. package/templates/optional/.vscode/extensions.json +0 -6
  229. package/templates/optional/.vscode/mcp.json +0 -9
  230. package/templates/optional/AGENTS.md +0 -154
  231. package/templates/optional/CLAUDE.md +0 -154
@@ -1,56 +0,0 @@
1
- import "@canva/app-ui-kit/styles.css";
2
- import type {
3
- DataConnectorIntent,
4
- GetDataTableRequest,
5
- GetDataTableResponse,
6
- RenderSelectionUiRequest,
7
- } from "@canva/intents/data";
8
- import { auth } from "@canva/user";
9
- import { createRoot } from "react-dom/client";
10
- import { buildDataTableResult, scope } from "../../api";
11
- import { App } from "./app";
12
-
13
- const dataConnector: DataConnectorIntent = {
14
- /**
15
- * Fetches structured data from an external source.
16
- *
17
- * This action is called in two scenarios:
18
- *
19
- * - During data selection to preview data before import (when {@link RenderSelectionUiRequest.updateDataRef} is called).
20
- * - When refreshing previously imported data (when the user requests an update).
21
- *
22
- * @param params - Parameters for the data fetching operation.
23
- * @returns A promise resolving to either a successful result with data or an error.
24
- */
25
- getDataTable: async (
26
- params: GetDataTableRequest,
27
- ): Promise<GetDataTableResponse> => {
28
- const oauth = auth.initOauth();
29
- const token = await oauth.getAccessToken({ scope });
30
- return buildDataTableResult(params, token?.token);
31
- },
32
-
33
- /**
34
- * Renders a UI component for selecting and configuring data from external sources.
35
- * This UI should allow users to browse data sources, apply filters, and select data.
36
- * When selection is complete, the implementation must call the `updateDataRef`
37
- * callback provided in the params to preview and confirm the data selection.
38
- *
39
- * @param request - parameters that provide context and configuration for the data selection UI.
40
- * Contains invocation context, size limits, and the updateDataRef callback
41
- */
42
- renderSelectionUi: async (request: RenderSelectionUiRequest) => {
43
- function render() {
44
- const root = createRoot(document.getElementById("root") as Element);
45
- root.render(<App request={request} />);
46
- }
47
-
48
- render();
49
-
50
- if (module.hot) {
51
- module.hot.accept("./app", render);
52
- module.hot.accept("../../api", render);
53
- }
54
- },
55
- };
56
- export default dataConnector;
@@ -1,9 +0,0 @@
1
- import { useAppContext } from "src/context";
2
-
3
- export const DataSourceConfig = () => {
4
- const { dataSourceHandler } = useAppContext();
5
- if (!dataSourceHandler) {
6
- return undefined; // should be impossible
7
- }
8
- return dataSourceHandler.configPage(dataSourceHandler.sourceConfig);
9
- };
@@ -1,37 +0,0 @@
1
- import { Button, Rows, Text } from "@canva/app-ui-kit";
2
- import { FormattedMessage, useIntl } from "react-intl";
3
- import { useNavigate } from "react-router-dom";
4
- import { Paths } from "src/routes/paths";
5
- import * as styles from "styles/components.css";
6
-
7
- /**
8
- * Bare bones Error Page, please add relevant information and behavior that your app requires.
9
- */
10
- export const ErrorPage = () => {
11
- const navigate = useNavigate();
12
- const intl = useIntl();
13
-
14
- const onClick = () => {
15
- navigate(Paths.ENTRYPOINT);
16
- };
17
-
18
- return (
19
- <div className={styles.scrollContainer}>
20
- <Rows spacing="2u">
21
- <Text>
22
- <FormattedMessage
23
- defaultMessage="Something went wrong."
24
- description="A message to indicate that something went wrong, but no more information is available"
25
- />
26
- </Text>
27
- <Button variant="primary" onClick={onClick} stretch={true}>
28
- {intl.formatMessage({
29
- defaultMessage: "Start over",
30
- description:
31
- "A button label to clear the error and the prompt and start again",
32
- })}
33
- </Button>
34
- </Rows>
35
- </div>
36
- );
37
- };
@@ -1,4 +0,0 @@
1
- export * from "./error";
2
- export * from "./login";
3
- export * from "./select_source";
4
- export * from "./data_source_config";
@@ -1,145 +0,0 @@
1
- import {
2
- Box,
3
- Button,
4
- LoadingIndicator,
5
- Rows,
6
- Text,
7
- Title,
8
- } from "@canva/app-ui-kit";
9
- import { useCallback, useEffect, useState } from "react";
10
- import { defineMessages, FormattedMessage, useIntl } from "react-intl";
11
- import { useNavigate } from "react-router-dom";
12
- import { scope } from "src/api";
13
- import { Header } from "src/components";
14
- import { Paths } from "src/routes/paths";
15
- import * as styles from "styles/components.css";
16
- import { useAppContext } from "../context";
17
-
18
- export const Login = () => {
19
- const intl = useIntl();
20
- const navigate = useNavigate();
21
- const { oauth, setAccessToken, isAuthenticated } = useAppContext();
22
-
23
- const [error, setError] = useState<string | null>(null);
24
- const [loading, setLoading] = useState(true);
25
-
26
- useEffect(() => {
27
- // Redirect if already authenticated
28
- if (isAuthenticated) {
29
- navigate(Paths.ENTRYPOINT);
30
- return;
31
- }
32
-
33
- // check if the user is already authenticated
34
- retrieveAndSetToken();
35
- }, [isAuthenticated]);
36
-
37
- const authorize = useCallback(async () => {
38
- setLoading(true);
39
- setError(null);
40
- try {
41
- await oauth.requestAuthorization({ scope });
42
- await retrieveAndSetToken();
43
- } catch (error) {
44
- setError(error instanceof Error ? error.message : "Unknown error");
45
- setLoading(false);
46
- }
47
- }, [oauth]);
48
-
49
- // you MUST call getAccessToken every time you need a token, as the token may expire.
50
- // Canva will handle caching and refreshing the token for you.
51
- const retrieveAndSetToken = useCallback(
52
- async (forceRefresh = false) => {
53
- try {
54
- const token = await oauth.getAccessToken({ forceRefresh, scope });
55
- setAccessToken(token);
56
- setLoading(false);
57
- } catch (error) {
58
- setError(error instanceof Error ? error.message : "Unknown error");
59
- setLoading(false);
60
- }
61
- },
62
- [oauth, setAccessToken],
63
- );
64
-
65
- return (
66
- <div className={styles.scrollContainer}>
67
- <Box
68
- justifyContent="center"
69
- width="full"
70
- alignItems="center"
71
- display="flex"
72
- height="full"
73
- >
74
- {error && (
75
- <Rows spacing="2u">
76
- <Title>
77
- <FormattedMessage {...loginMessages.authorizationError} />
78
- </Title>
79
- <Text>{error}</Text>
80
- <Button variant="primary" onClick={authorize}>
81
- {intl.formatMessage(loginMessages.tryAgain)}
82
- </Button>
83
- </Rows>
84
- )}
85
- {loading && <LoadingIndicator />}
86
- {!loading && !error && (
87
- <Rows spacing="2u">
88
- <Header
89
- title={intl.formatMessage(loginMessages.signInRequired)}
90
- showBack={false}
91
- />
92
- <Text>
93
- <FormattedMessage {...loginMessages.dataConnectorsOAuth} />
94
- </Text>
95
- <Text>
96
- <FormattedMessage {...loginMessages.exampleDemonstration} />
97
- </Text>
98
- <Text>
99
- <FormattedMessage {...loginMessages.setupInstructions} />
100
- </Text>
101
- <Button variant="primary" onClick={authorize}>
102
- {intl.formatMessage(loginMessages.signIntoCanva)}
103
- </Button>
104
- </Rows>
105
- )}
106
- </Box>
107
- </div>
108
- );
109
- };
110
-
111
- const loginMessages = defineMessages({
112
- authorizationError: {
113
- defaultMessage: "Authorization error",
114
- description:
115
- "Title displayed when there is an error during OAuth authorization",
116
- },
117
- tryAgain: {
118
- defaultMessage: "Try again",
119
- description: "Button text to retry authorization after an error occurs",
120
- },
121
- signInRequired: {
122
- defaultMessage: "Sign in required",
123
- description:
124
- "Header title for the login page indicating authentication is needed",
125
- },
126
- dataConnectorsOAuth: {
127
- defaultMessage:
128
- "Data connectors can use OAuth to authenticate with other platforms.",
129
- description: "Body text shown when the user is prompted to sign in",
130
- },
131
- exampleDemonstration: {
132
- defaultMessage:
133
- "This example demonstrates how to do this with the Canva Connect API.",
134
- description: "Body text shown when the user is prompted to sign in",
135
- },
136
- setupInstructions: {
137
- defaultMessage:
138
- "For set up instructions please see the README.md in the root folder.",
139
- description: "Body text shown when the user is prompted to sign in",
140
- },
141
- signIntoCanva: {
142
- defaultMessage: "Sign into Canva",
143
- description: "Button text for initiating Canva authentication",
144
- },
145
- });
@@ -1,24 +0,0 @@
1
- import { Box, Rows } from "@canva/app-ui-kit";
2
- import { useIntl } from "react-intl";
3
- import { DATA_SOURCES } from "src/api/data_sources";
4
- import { Footer, Header } from "src/components";
5
-
6
- export const SelectSource = () => {
7
- const intl = useIntl();
8
-
9
- return (
10
- <Box paddingEnd="2u" paddingTop="2u">
11
- <Rows spacing="1u" align="start">
12
- <Header
13
- title={intl.formatMessage({
14
- defaultMessage: "What would you like to import? ",
15
- description: "The header text for the data source selection view",
16
- })}
17
- showBack={false}
18
- />
19
- {DATA_SOURCES.map((handler) => handler.selectionPage())}
20
- <Footer />
21
- </Rows>
22
- </Box>
23
- );
24
- };
@@ -1,2 +0,0 @@
1
- export * from "./routes";
2
- export * from "./protected_route";
@@ -1,7 +0,0 @@
1
- export enum Paths {
2
- ENTRYPOINT = "/",
3
- LOGIN = "/login",
4
- DATA_SOURCE_SELECTION = "/data-source-selection",
5
- DATA_SOURCE_CONFIG = "/data-source-config",
6
- ERRORS = "/errors/:retry",
7
- }
@@ -1,26 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import { useEffect } from "react";
3
- import { useNavigate } from "react-router-dom";
4
- import { Paths } from "src/routes/paths";
5
- import { useAppContext } from "../context";
6
-
7
- interface ProtectedRouteProps {
8
- children: ReactNode;
9
- }
10
-
11
- /**
12
- * A component that protects routes from unauthorized access.
13
- * If the user is not authenticated, they will be redirected to the login page.
14
- */
15
- export const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
16
- const { isAuthenticated } = useAppContext();
17
- const navigate = useNavigate();
18
-
19
- useEffect(() => {
20
- if (!isAuthenticated) {
21
- navigate(Paths.LOGIN);
22
- }
23
- }, [isAuthenticated, navigate]);
24
-
25
- return <>{children}</>;
26
- };
@@ -1,42 +0,0 @@
1
- import { Entrypoint } from "src/intents/data_connector/entrypoint";
2
- import { Home } from "src/intents/data_connector/home";
3
- import { DataSourceConfig } from "src/pages/data_source_config";
4
- import { ErrorPage } from "src/pages/error";
5
- import { Login } from "src/pages/login";
6
- import { SelectSource } from "src/pages/select_source";
7
- import { Paths } from "src/routes/paths";
8
- import { ProtectedRoute } from "./protected_route";
9
-
10
- export const routes = [
11
- {
12
- path: Paths.ENTRYPOINT,
13
- element: <Home />,
14
- errorElement: <ErrorPage />,
15
- children: [
16
- {
17
- index: true,
18
- element: <Entrypoint />,
19
- },
20
- {
21
- path: Paths.LOGIN,
22
- element: <Login />,
23
- },
24
- {
25
- path: Paths.DATA_SOURCE_SELECTION,
26
- element: (
27
- <ProtectedRoute>
28
- <SelectSource />
29
- </ProtectedRoute>
30
- ),
31
- },
32
- {
33
- path: Paths.DATA_SOURCE_CONFIG,
34
- element: (
35
- <ProtectedRoute>
36
- <DataSourceConfig />
37
- </ProtectedRoute>
38
- ),
39
- },
40
- ],
41
- },
42
- ];
@@ -1,17 +0,0 @@
1
- import type { RenderSelectionUiRequest } from "@canva/intents/data";
2
-
3
- export const isLaunchedWithError = (request: RenderSelectionUiRequest) => {
4
- return request.invocationContext.reason === "app_error";
5
- };
6
-
7
- export const isOutdatedSource = (request: RenderSelectionUiRequest) => {
8
- return request.invocationContext.reason === "outdated_source_ref";
9
- };
10
-
11
- export const isDataRefEmpty = (request: RenderSelectionUiRequest) => {
12
- return (
13
- !request?.invocationContext ||
14
- (!isLaunchedWithError(request) &&
15
- !request.invocationContext.dataSourceRef?.source)
16
- );
17
- };
@@ -1,116 +0,0 @@
1
- import type {
2
- BooleanDataTableCell,
3
- ColumnConfig,
4
- DataTable,
5
- DataTableCell,
6
- DateDataTableCell,
7
- NumberDataTableCell,
8
- StringDataTableCell,
9
- } from "@canva/intents/data";
10
- import type { APIResponseItem } from "src/api";
11
-
12
- export interface DataTableColumn<T extends APIResponseItem> {
13
- label: string;
14
- getValue: keyof T | ((result: T) => boolean | string | number | Date);
15
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
- toCell: (value: any) => DataTableCell;
17
- }
18
-
19
- export function toDataTable<T extends APIResponseItem>(
20
- apiData: T[],
21
- columns: DataTableColumn<T>[],
22
- rowLimit: number,
23
- ): DataTable {
24
- const items = apiData.slice(0, rowLimit);
25
- const dataTable: DataTable = {
26
- columnConfigs: columnConfig(columns),
27
- rows: [],
28
- };
29
- items.forEach((item) => {
30
- const cells = columns.map((column) => {
31
- const value =
32
- typeof column.getValue === "function"
33
- ? column.getValue(item)
34
- : item[column.getValue];
35
- return column.toCell(value);
36
- });
37
- dataTable.rows.push({ cells });
38
- });
39
- return dataTable;
40
- }
41
-
42
- /**
43
- * Converts an array of DataTableColumn to ColumnConfig.
44
- * @param columns Array of DataTableColumn
45
- * @returns Array of ColumnConfig
46
- */
47
- function columnConfig<T extends APIResponseItem>(
48
- columns: DataTableColumn<T>[],
49
- ): ColumnConfig[] {
50
- return columns.map((column) => ({
51
- name: column.label,
52
- type: column.toCell({} as unknown).type, // Use an empty object to infer the type
53
- }));
54
- }
55
-
56
- /**
57
- * Creates a string cell for the data table.
58
- * @param value String containing up to 10,000 characters
59
- */
60
- export function stringCell(value: string): StringDataTableCell {
61
- return {
62
- type: "string",
63
- value,
64
- };
65
- }
66
-
67
- /**
68
- * Creates a number cell for the data table.
69
- * @param value Number within range `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`
70
- * @param formatting Formatting using ISO/IEC 29500-1:2016 Office Open XML Format
71
- */
72
- export function numberCell(
73
- value: number,
74
- formatting?: string,
75
- ): NumberDataTableCell {
76
- return {
77
- type: "number",
78
- value,
79
- metadata: {
80
- formatting,
81
- },
82
- };
83
- }
84
-
85
- /**
86
- * Creates a boolean cell for the data table.
87
- * @param value Boolean value
88
- */
89
- export function booleanCell(value: boolean): BooleanDataTableCell {
90
- return {
91
- type: "boolean",
92
- value,
93
- };
94
- }
95
-
96
- /**
97
- * Creates a date cell for the data table.
98
- * @param value Number, Date or String
99
- * @description If value is a string, it will be parsed as a date. If value is a number, it will be treated as a timestamp in seconds.
100
- */
101
- export function dateCell(value: number | Date | string): DateDataTableCell {
102
- // if string, parse as date
103
- if (typeof value === "string") {
104
- value = new Date(value);
105
- }
106
-
107
- // if is date, convert to timestamp in seconds
108
- if (value instanceof Date) {
109
- value = value.valueOf() / 1000;
110
- }
111
-
112
- return {
113
- type: "date",
114
- value,
115
- };
116
- }
@@ -1,36 +0,0 @@
1
- import type {
2
- DataTable,
3
- DataTableMetadata,
4
- GetDataTableCompleted,
5
- GetDataTableError,
6
- } from "@canva/intents/data";
7
-
8
- export const completeDataTable = (
9
- dataTable: DataTable,
10
- metadata?: DataTableMetadata,
11
- ): GetDataTableCompleted => {
12
- return {
13
- status: "completed",
14
- dataTable,
15
- metadata,
16
- };
17
- };
18
-
19
- export const appError = (message?: string): GetDataTableError => {
20
- return {
21
- status: "app_error",
22
- message,
23
- };
24
- };
25
-
26
- export const outdatedSourceRef = (): GetDataTableError => {
27
- return {
28
- status: "outdated_source_ref",
29
- };
30
- };
31
-
32
- export const remoteRequestFailed = (): GetDataTableError => {
33
- return {
34
- status: "remote_request_failed",
35
- };
36
- };
@@ -1,2 +0,0 @@
1
- export * from "./data_table";
2
- export * from "./data_params";
@@ -1,133 +0,0 @@
1
- /* eslint-disable formatjs/no-literal-string-in-object */
2
- import type { APIResponseItem } from "src/api";
3
- import {
4
- booleanCell,
5
- dateCell,
6
- numberCell,
7
- stringCell,
8
- toDataTable,
9
- } from "../data_table";
10
- import type { DataTableColumn } from "../data_table";
11
-
12
- describe("data table utils", () => {
13
- interface TestItem extends APIResponseItem {
14
- id: string;
15
- name: string;
16
- count: number;
17
- active: boolean;
18
- createdAt: string;
19
- }
20
-
21
- const testItems: TestItem[] = [
22
- {
23
- id: "item1",
24
- name: "Test Item 1",
25
- count: 42,
26
- active: true,
27
- createdAt: "2023-01-01T00:00:00Z",
28
- },
29
- {
30
- id: "item2",
31
- name: "Test Item 2",
32
- count: 0,
33
- active: false,
34
- createdAt: "2023-02-15T12:30:45Z",
35
- },
36
- ];
37
-
38
- const columns: DataTableColumn<TestItem>[] = [
39
- {
40
- label: "ID",
41
- getValue: "id",
42
- toCell: stringCell,
43
- },
44
- {
45
- label: "Name",
46
- getValue: "name",
47
- toCell: stringCell,
48
- },
49
- {
50
- label: "Count",
51
- getValue: "count",
52
- toCell: numberCell,
53
- },
54
- {
55
- label: "Status",
56
- getValue: "active",
57
- toCell: booleanCell,
58
- },
59
- {
60
- label: "Created",
61
- getValue: "createdAt",
62
- toCell: dateCell,
63
- },
64
- {
65
- label: "Custom",
66
- getValue: (item) => `${item.name} (${item.count})`,
67
- toCell: stringCell,
68
- },
69
- ];
70
-
71
- test("toDataTable creates correct structure", () => {
72
- const result = toDataTable(testItems, columns, 10);
73
-
74
- expect(result.rows.length).toBe(testItems.length);
75
- expect(result.columnConfigs?.map((cell) => cell.name)).toEqual([
76
- "ID",
77
- "Name",
78
- "Count",
79
- "Status",
80
- "Created",
81
- "Custom",
82
- ]);
83
- });
84
-
85
- test("toDataTable respects row limit", () => {
86
- const result = toDataTable(testItems, columns, 1);
87
-
88
- expect(result.rows.length).toBe(1);
89
- });
90
-
91
- test("toDataTable handles function-based getValue", () => {
92
- const result = toDataTable(testItems, columns, 10);
93
-
94
- // Check the custom column values
95
- expect(result.rows[0]?.cells[5]?.value).toBe("Test Item 1 (42)");
96
- expect(result.rows[1]?.cells[5]?.value).toBe("Test Item 2 (0)");
97
- });
98
-
99
- test("cell formatter functions create correct cell structures", () => {
100
- expect(stringCell("test")).toEqual({ type: "string", value: "test" });
101
-
102
- expect(numberCell(42)).toEqual({
103
- type: "number",
104
- value: 42,
105
- metadata: { formatting: undefined },
106
- });
107
-
108
- expect(numberCell(42, "#,##0")).toEqual({
109
- type: "number",
110
- value: 42,
111
- metadata: { formatting: "#,##0" },
112
- });
113
-
114
- expect(booleanCell(true)).toEqual({ type: "boolean", value: true });
115
-
116
- // Test date with string input
117
- const dateResult1 = dateCell("2023-01-01T00:00:00Z");
118
- expect(dateResult1.type).toBe("date");
119
- expect(typeof dateResult1.value).toBe("number");
120
-
121
- // Test date with Date object input
122
- const dateObj = new Date("2023-01-01T00:00:00Z");
123
- const dateResult2 = dateCell(dateObj);
124
- expect(dateResult2.type).toBe("date");
125
- expect(typeof dateResult2.value).toBe("number");
126
-
127
- // Test date with timestamp input
128
- const timestamp = dateObj.valueOf() / 1000; // seconds
129
- const dateResult3 = dateCell(timestamp);
130
- expect(dateResult3.type).toBe("date");
131
- expect(dateResult3.value).toBe(timestamp);
132
- });
133
- });