@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,99 +0,0 @@
1
- /* eslint-disable formatjs/no-literal-string-in-object */
2
- /* eslint-disable formatjs/no-literal-string-in-jsx */
3
- import { toDataTable } from "src/utils";
4
- import type { APIResponseItem, DataSourceConfig } from "../data_source";
5
- import { DataAPIError, DataSourceHandler } from "../data_source";
6
-
7
- // Mock dependencies
8
- jest.mock("src/utils", () => ({
9
- toDataTable: jest.fn().mockReturnValue({ rows: [] }),
10
- }));
11
-
12
- describe("DataSourceHandler", () => {
13
- // Test interface
14
- interface TestConfig extends DataSourceConfig {
15
- testField: string;
16
- }
17
-
18
- interface TestResponse extends APIResponseItem {
19
- name: string;
20
- }
21
-
22
- // Test data
23
- const mockConfig: TestConfig = {
24
- schema: "test/v1",
25
- testField: "value",
26
- };
27
-
28
- const mockColumns = [
29
- {
30
- label: "ID",
31
- getValue: (item: TestResponse) => `ID ${item.id}`,
32
- toCell: jest.fn().mockReturnValue({ type: "string", value: "id-123" }),
33
- },
34
- {
35
- label: "Name",
36
- getValue: "name" as const,
37
- toCell: jest.fn().mockReturnValue({ type: "string", value: "Test Name" }),
38
- },
39
- ];
40
-
41
- const mockFetchData = jest.fn();
42
- const mockSelectionPage = jest.fn().mockReturnValue(<div>Selection</div>);
43
- const mockConfigPage = jest.fn().mockReturnValue(<div>Config</div>);
44
-
45
- let handler: DataSourceHandler<TestConfig, TestResponse>;
46
-
47
- beforeEach(() => {
48
- jest.clearAllMocks();
49
- handler = new DataSourceHandler<TestConfig, TestResponse>(
50
- mockConfig,
51
- mockColumns,
52
- mockFetchData,
53
- mockSelectionPage,
54
- mockConfigPage,
55
- );
56
- });
57
-
58
- test("creates handler with correct schema", () => {
59
- expect(handler.schema).toBe("test/v1");
60
- expect(handler.sourceConfig).toEqual(mockConfig);
61
- });
62
-
63
- test("matchSource returns true for matching schema", () => {
64
- const result = handler.matchSource({ schema: "test/v1" });
65
- expect(result).toBe(true);
66
- });
67
-
68
- test("matchSource returns false for non-matching schema", () => {
69
- const result = handler.matchSource({ schema: "wrong/v1" });
70
- expect(result).toBe(false);
71
- });
72
-
73
- test("fetchAndBuildTable calls fetchData with correct parameters", async () => {
74
- const mockData = [{ id: "id-123", name: "Test Item" }];
75
- mockFetchData.mockResolvedValueOnce(mockData);
76
-
77
- const authToken = "test-token";
78
- const rowLimit = 10;
79
- const signal = new AbortController().signal;
80
-
81
- await handler.fetchAndBuildTable(mockConfig, authToken, rowLimit, signal);
82
-
83
- expect(mockFetchData).toHaveBeenCalledWith(
84
- mockConfig,
85
- authToken,
86
- rowLimit,
87
- signal,
88
- );
89
- expect(toDataTable).toHaveBeenCalledWith(mockData, mockColumns, rowLimit);
90
- });
91
-
92
- test("fetchAndBuildTable throws DataAPIError when fetchData fails", async () => {
93
- mockFetchData.mockRejectedValueOnce(new Error("Network error"));
94
-
95
- await expect(
96
- handler.fetchAndBuildTable(mockConfig, "token", 10, undefined),
97
- ).rejects.toThrow(DataAPIError);
98
- });
99
- });
@@ -1,15 +0,0 @@
1
- import { Alert } from "@canva/app-ui-kit";
2
- import { useAppContext } from "src/context";
3
-
4
- export const AppError = () => {
5
- const { appError, setAppError } = useAppContext();
6
- if (!appError) {
7
- return null;
8
- }
9
-
10
- return (
11
- <Alert tone="critical" onDismiss={() => setAppError("")}>
12
- {appError}
13
- </Alert>
14
- );
15
- };
@@ -1,26 +0,0 @@
1
- import { Button, Rows } from "@canva/app-ui-kit";
2
- import { useIntl } from "react-intl";
3
- import { useAppContext } from "src/context";
4
-
5
- export const Footer = () => {
6
- const { isAuthenticated, logout } = useAppContext();
7
- const intl = useIntl();
8
-
9
- return (
10
- <Rows spacing="1u">
11
- {isAuthenticated && (
12
- <Button
13
- variant="tertiary"
14
- onClick={async () => {
15
- logout();
16
- }}
17
- >
18
- {intl.formatMessage({
19
- defaultMessage: "Log Out",
20
- description: "Button for logging out of the data source",
21
- })}
22
- </Button>
23
- )}
24
- </Rows>
25
- );
26
- };
@@ -1,40 +0,0 @@
1
- import {
2
- ArrowLeftIcon,
3
- Box,
4
- Button,
5
- Column,
6
- Columns,
7
- Title,
8
- } from "@canva/app-ui-kit";
9
- import { useNavigate } from "react-router-dom";
10
- import { Paths } from "src/routes/paths";
11
-
12
- export const Header = ({
13
- title,
14
- showBack,
15
- }: {
16
- title: string;
17
- showBack: boolean;
18
- }) => {
19
- const navigate = useNavigate();
20
- return (
21
- <Box paddingBottom="1u">
22
- <Columns spacing="0" alignY="center" align="start">
23
- {showBack && (
24
- <Column width="content">
25
- <Button
26
- onClick={() => navigate(Paths.DATA_SOURCE_SELECTION)}
27
- icon={ArrowLeftIcon}
28
- variant="tertiary"
29
- />
30
- </Column>
31
- )}
32
- <Column>
33
- <Title size="small" lineClamp={1}>
34
- {title}
35
- </Title>
36
- </Column>
37
- </Columns>
38
- </Box>
39
- );
40
- };
@@ -1,3 +0,0 @@
1
- export * from "./app_error";
2
- export * from "./footer";
3
- export * from "./header";
@@ -1,95 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
-
3
- export const ownershipFilter = defineMessages({
4
- label: {
5
- defaultMessage: "Filter by Ownership",
6
- description: "Label for a select control to filter designs by ownership",
7
- },
8
- description: {
9
- defaultMessage:
10
- "Filter the list of designs based on the user's ownership of the designs",
11
- description: "Subtitle for a select control to filter designs by ownership",
12
- },
13
- any: {
14
- defaultMessage: "Any (default)",
15
- description: "Default option for ownership filter",
16
- },
17
- owned: {
18
- defaultMessage: "Owned by me",
19
- description: "Option for ownership filter",
20
- },
21
- shared: {
22
- defaultMessage: "Shared with me",
23
- description: "Option for ownership filter",
24
- },
25
- });
26
-
27
- export const datasetFilter = defineMessages({
28
- label: {
29
- defaultMessage: "Filter by Dataset",
30
- description: "Label for a select control to filter designs by dataset",
31
- },
32
- description: {
33
- defaultMessage:
34
- "Filter the list of brand templates based on the dataset definitions",
35
- description: "Subtitle for a select control to filter designs by dataset",
36
- },
37
- any: {
38
- defaultMessage: "Any (default)",
39
- description: "Default option for dataset filter",
40
- },
41
- nonEmpty: {
42
- defaultMessage: "Templates with one or more data fields defined",
43
- description: "Option for dataset filter",
44
- },
45
- });
46
-
47
- export const sortOrderField = defineMessages({
48
- label: {
49
- defaultMessage: "Sort by",
50
- description: "Label for a select control to sort designs",
51
- },
52
- description: {
53
- defaultMessage: "Sort the list of designs",
54
- description: "Subtitle for a select control to sort designs",
55
- },
56
- relevance: {
57
- defaultMessage: "Relevance (default)",
58
- description: "Default option for sort order",
59
- },
60
- modifiedDesc: {
61
- defaultMessage: "Last modified - descending",
62
- description: "Option for sort order",
63
- },
64
- modifiedAsc: {
65
- defaultMessage: "Last modified - ascending",
66
- description: "Option for sort order",
67
- },
68
- titleDesc: {
69
- defaultMessage: "Title - descending",
70
- description: "Option for sort order",
71
- },
72
- titleAsc: {
73
- defaultMessage: "Title - ascending",
74
- description: "Option for sort order",
75
- },
76
- });
77
-
78
- export const filterMenu = defineMessages({
79
- search: {
80
- defaultMessage: "Search",
81
- description: "Label for a search input field",
82
- },
83
- clear: {
84
- defaultMessage: "Clear all",
85
- description: "Label for a button to clear all filters",
86
- },
87
- apply: {
88
- defaultMessage: "Apply",
89
- description: "Label for a button to apply filters",
90
- },
91
- count: {
92
- defaultMessage: "Filter count",
93
- description: "Label for the number of active filters",
94
- },
95
- });
@@ -1,109 +0,0 @@
1
- import {
2
- Badge,
3
- Box,
4
- Button,
5
- Column,
6
- Columns,
7
- Flyout,
8
- Rows,
9
- SearchInputMenu,
10
- SlidersIcon,
11
- } from "@canva/app-ui-kit";
12
- import type { ReactNode } from "react";
13
- import { useState } from "react";
14
- import { useIntl } from "react-intl";
15
- import { filterMenu } from "./messages";
16
-
17
- interface SearchFilterProps {
18
- value: string;
19
- onChange: (value: string) => void;
20
- filterCount: number;
21
- resetFilters: () => void;
22
- children?: ReactNode;
23
- }
24
-
25
- export const SearchFilter = ({
26
- value,
27
- onChange,
28
- filterCount,
29
- resetFilters,
30
- children,
31
- }: SearchFilterProps) => {
32
- const intl = useIntl();
33
- const [triggerRef, setTriggerRef] = useState<HTMLDivElement | null>(null);
34
- const [isFilterMenuOpen, setIsFilterMenuOpen] = useState(false);
35
-
36
- const onFilterClick = () => {
37
- setIsFilterMenuOpen(!isFilterMenuOpen);
38
- };
39
-
40
- const filterButton = (
41
- <Button
42
- size="small"
43
- variant="tertiary"
44
- icon={SlidersIcon}
45
- onClick={onFilterClick}
46
- />
47
- );
48
- return (
49
- <>
50
- <Box paddingStart="0.5u">
51
- <SearchInputMenu
52
- value={value}
53
- placeholder={intl.formatMessage(filterMenu.search)}
54
- onChange={(value) => onChange(value)}
55
- onClear={() => onChange("")}
56
- ref={setTriggerRef}
57
- end={
58
- filterCount === 0 ? (
59
- filterButton
60
- ) : (
61
- <Badge
62
- tone="assist"
63
- wrapInset="0"
64
- shape="circle"
65
- text={filterCount.toString()}
66
- ariaLabel={intl.formatMessage(filterMenu.count)}
67
- >
68
- {filterButton}
69
- </Badge>
70
- )
71
- }
72
- />
73
- </Box>
74
- <Flyout
75
- open={isFilterMenuOpen}
76
- onRequestClose={() => setIsFilterMenuOpen(false)}
77
- width="trigger"
78
- trigger={triggerRef}
79
- placement="bottom-center"
80
- footer={
81
- <Box padding="2u" background="surface">
82
- <Columns spacing="1u">
83
- <Column>
84
- <Button variant="secondary" onClick={resetFilters} stretch>
85
- {intl.formatMessage(filterMenu.clear)}
86
- </Button>
87
- </Column>
88
- <Column>
89
- <Button
90
- variant="primary"
91
- onClick={() => {
92
- setIsFilterMenuOpen(false);
93
- }}
94
- stretch
95
- >
96
- {intl.formatMessage(filterMenu.apply)}
97
- </Button>
98
- </Column>
99
- </Columns>
100
- </Box>
101
- }
102
- >
103
- <Box padding="2u">
104
- <Rows spacing="2u">{children}</Rows>
105
- </Box>
106
- </Flyout>
107
- </>
108
- );
109
- };
@@ -1,26 +0,0 @@
1
- import type { SelectOption } from "@canva/app-ui-kit";
2
- import { FormField, Select } from "@canva/app-ui-kit";
3
-
4
- interface SelectFieldProps {
5
- value: string;
6
- onChange: (value: string) => void;
7
- label: string;
8
- options: SelectOption<string>[];
9
- }
10
-
11
- export const SelectField = ({
12
- value,
13
- onChange,
14
- label,
15
- options,
16
- }: SelectFieldProps) => {
17
- return (
18
- <FormField
19
- label={label}
20
- value={value}
21
- control={(props) => (
22
- <Select {...props} options={options} onChange={onChange} />
23
- )}
24
- />
25
- );
26
- };
@@ -1,125 +0,0 @@
1
- import type { RenderSelectionUiRequest } from "@canva/intents/data";
2
- import type { AccessTokenResponse } from "@canva/user";
3
- import { auth } from "@canva/user";
4
- import type { JSX, ReactNode } from "react";
5
- import { createContext, useCallback, useState } from "react";
6
- import type { APIResponseItem, DataSourceHandler } from "src/api/data_source";
7
- import { type DataSourceConfig } from "src/api/data_source";
8
-
9
- export interface AppContextType {
10
- appError: string;
11
- setAppError: (value: string) => void;
12
- request: RenderSelectionUiRequest;
13
- isAuthenticated: boolean;
14
- accessToken: AccessTokenResponse | undefined;
15
- setAccessToken: (token: AccessTokenResponse | undefined) => void;
16
- oauth: ReturnType<typeof auth.initOauth>;
17
- logout: () => Promise<void>;
18
- dataSourceHandler?: DataSourceHandler<DataSourceConfig, APIResponseItem>;
19
- setDataSourceHandler: (
20
- value: DataSourceHandler<DataSourceConfig, APIResponseItem>,
21
- ) => void;
22
-
23
- dataSourceConfig?: DataSourceConfig;
24
- setDataSourceConfig: (value: DataSourceConfig) => void;
25
- loadDataSource: (title: string, source: DataSourceConfig) => Promise<void>;
26
- }
27
-
28
- export const AppContext = createContext<AppContextType>({
29
- appError: "",
30
- setAppError: () => {},
31
- request: {} as RenderSelectionUiRequest,
32
- isAuthenticated: false,
33
- accessToken: undefined,
34
- setAccessToken: () => {},
35
- oauth: auth.initOauth(),
36
- logout: async () => {},
37
- dataSourceHandler: {} as DataSourceHandler<DataSourceConfig, APIResponseItem>,
38
- setDataSourceHandler: () => {},
39
- dataSourceConfig: {} as DataSourceConfig,
40
- setDataSourceConfig: () => {},
41
- loadDataSource: async () => {},
42
- });
43
-
44
- /**
45
- * Provides application-wide state and methods using React Context.
46
- * @param {object} props - The props object.
47
- * @param {ReactNode} props.children - The children components wrapped by the provider.
48
- * @returns {JSX.Element} The provider component.
49
- * @description This provider component wraps the entire application to provide application-wide state and methods using React Context.
50
- * It manages state related to app errors, filter parameters, and authentication.
51
- * It exposes these state values and setter methods to its child components via the AppContext.
52
- * For more information on React Context, refer to the official React documentation: {@link https://react.dev/learn/passing-data-deeply-with-context}.
53
- */
54
- export const ContextProvider = ({
55
- renderSelectionUiRequest,
56
- children,
57
- }: {
58
- renderSelectionUiRequest: RenderSelectionUiRequest;
59
- children: ReactNode;
60
- }): JSX.Element => {
61
- const [appError, setAppError] = useState<string>("");
62
- const [request] = useState<RenderSelectionUiRequest>(
63
- renderSelectionUiRequest,
64
- );
65
-
66
- // authentication
67
- const [accessToken, setAccessToken] = useState<
68
- AccessTokenResponse | undefined
69
- >(undefined);
70
- const oauth = auth.initOauth();
71
- const isAuthenticated = !!accessToken;
72
-
73
- // data handlers
74
- const [dataSourceHandler, setDataSourceHandler] =
75
- useState<DataSourceHandler<DataSourceConfig, APIResponseItem>>();
76
- const [dataSourceConfig, setDataSourceConfig] = useState<DataSourceConfig>();
77
-
78
- // data connection
79
- const loadDataSource = useCallback(
80
- async (title: string, source: DataSourceConfig) => {
81
- const result = await request.updateDataRef({
82
- title,
83
- source: JSON.stringify(source),
84
- });
85
- if (result.status === "remote_request_failed") {
86
- setAppError(`Failed to load data source: uanble to connect to the API`);
87
- } else if (result.status === "app_error") {
88
- setAppError(
89
- `Failed to load data source: ${result.message || result.status}`,
90
- );
91
- } else {
92
- setAppError("");
93
- }
94
- },
95
- [request],
96
- );
97
-
98
- const logout = useCallback(async () => {
99
- try {
100
- setAccessToken(undefined);
101
- await oauth.deauthorize();
102
- setAccessToken(null);
103
- } catch (error) {
104
- setAppError(error instanceof Error ? error.message : "Logout failed");
105
- }
106
- }, [oauth]);
107
-
108
- const value: AppContextType = {
109
- appError,
110
- setAppError,
111
- request,
112
- isAuthenticated,
113
- accessToken,
114
- setAccessToken,
115
- oauth,
116
- logout,
117
- dataSourceHandler,
118
- setDataSourceHandler,
119
- dataSourceConfig,
120
- setDataSourceConfig,
121
- loadDataSource,
122
- };
123
-
124
- return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
125
- };
@@ -1,2 +0,0 @@
1
- export * from "./app_context";
2
- export * from "./use_app_context";
@@ -1,17 +0,0 @@
1
- import { useContext } from "react";
2
- import type { AppContextType } from "./app_context";
3
- import { AppContext } from "./app_context";
4
-
5
- /**
6
- * A custom React hook to access the application-wide state and methods provided by the ContextProvider using React Context.
7
- * @returns {AppContextType} - An object containing application-wide state and methods.
8
- * @throws {Error} - Throws an error if used outside the context of a ContextProvider.
9
- * @description This hook allows components to access the application-wide state and methods provided by the ContextProvider using React Context. It retrieves the context value using the useContext hook and ensures that the context is available. If used outside the context of an ContextProvider, it throws an error instructing developers to use it within an ContextProvider.
10
- */
11
- export const useAppContext = (): AppContextType => {
12
- const context = useContext(AppContext);
13
- if (!context) {
14
- throw new Error("useAppContext must be used within a ContextProvider");
15
- }
16
- return context;
17
- };
@@ -1,4 +0,0 @@
1
- import { prepareDataConnector } from "@canva/intents/data";
2
- import dataConnector from "./intents/data_connector";
3
-
4
- prepareDataConnector(dataConnector);
@@ -1,20 +0,0 @@
1
- import { AppI18nProvider } from "@canva/app-i18n-kit";
2
- import { AppUiProvider } from "@canva/app-ui-kit";
3
- import type { RenderSelectionUiRequest } from "@canva/intents/data";
4
- import { ErrorBoundary } from "react-error-boundary";
5
- import { createHashRouter, RouterProvider } from "react-router-dom";
6
- import { ContextProvider } from "../../context";
7
- import { ErrorPage } from "../../pages";
8
- import { routes } from "../../routes";
9
-
10
- export const App = ({ request }: { request: RenderSelectionUiRequest }) => (
11
- <AppI18nProvider>
12
- <AppUiProvider>
13
- <ErrorBoundary fallback={<ErrorPage />}>
14
- <ContextProvider renderSelectionUiRequest={request}>
15
- <RouterProvider router={createHashRouter(routes)} />
16
- </ContextProvider>
17
- </ErrorBoundary>
18
- </AppUiProvider>
19
- </AppI18nProvider>
20
- );
@@ -1,70 +0,0 @@
1
- import { LoadingIndicator } from "@canva/app-ui-kit";
2
- import { useEffect } from "react";
3
- import { useNavigate } from "react-router-dom";
4
- import type {
5
- APIResponseItem,
6
- DataSourceConfig,
7
- DataSourceHandler,
8
- } from "../../api";
9
- import { DATA_SOURCES } from "../../api/data_sources";
10
- import { useAppContext } from "../../context";
11
- import { Paths } from "../../routes/paths";
12
- import {
13
- isDataRefEmpty,
14
- isLaunchedWithError,
15
- isOutdatedSource,
16
- } from "../../utils/data_params";
17
-
18
- const parseDataSource = (source: string) => {
19
- try {
20
- return source ? JSON.parse(source) : undefined;
21
- } catch {
22
- return undefined;
23
- }
24
- };
25
-
26
- export const Entrypoint = () => {
27
- const navigate = useNavigate();
28
- const context = useAppContext();
29
- const { request, setAppError, setDataSourceHandler } = context;
30
-
31
- useEffect(() => {
32
- // if the app was loaded with a populated data ref, we should reload the previous state.
33
- // otherwise, if this is a first launch or there is an error, we should navigate to the first screen for a user - selecting a data source
34
- let navigateTo: Paths | undefined;
35
-
36
- if (isDataRefEmpty(request)) {
37
- // probably a first time launch - the user will need to select a data source
38
- navigateTo = Paths.DATA_SOURCE_SELECTION;
39
- } else if (isOutdatedSource(request) || isLaunchedWithError(request)) {
40
- // the configured source does not match the expected data source types
41
- // so prompt the user to reconfigure the data source
42
- setAppError("The data source configuration needs to be updated.");
43
- navigateTo = Paths.DATA_SOURCE_SELECTION;
44
- } else {
45
- // there is a data ref, so we should parse it and navigate to the appropriate page
46
- const dataRef = request.invocationContext.dataSourceRef;
47
- const parsedSource = parseDataSource(dataRef?.source ?? "");
48
-
49
- if (parsedSource) {
50
- const dataHandler = DATA_SOURCES.find((handler) =>
51
- handler.matchSource(parsedSource),
52
- );
53
- if (dataHandler) {
54
- setDataSourceHandler(
55
- dataHandler as unknown as DataSourceHandler<
56
- DataSourceConfig,
57
- APIResponseItem
58
- >,
59
- );
60
- dataHandler.sourceConfig = parsedSource;
61
- navigateTo = Paths.DATA_SOURCE_CONFIG;
62
- }
63
- }
64
- }
65
-
66
- navigate(navigateTo || Paths.DATA_SOURCE_SELECTION);
67
- }, [request]);
68
-
69
- return <LoadingIndicator />;
70
- };
@@ -1,21 +0,0 @@
1
- import { Box, Rows } from "@canva/app-ui-kit";
2
- import { Outlet } from "react-router-dom";
3
- import * as styles from "styles/components.css";
4
- import { AppError } from "../../components";
5
-
6
- export const Home = () => (
7
- <div className={styles.scrollContainer}>
8
- <Box
9
- justifyContent="center"
10
- width="full"
11
- alignItems="start"
12
- display="flex"
13
- height="full"
14
- >
15
- <Rows spacing="3u">
16
- <AppError />
17
- <Outlet />
18
- </Rows>
19
- </Box>
20
- </div>
21
- );