From f731222512486ceb6fb4786d426be82c7f5f77c0 Mon Sep 17 00:00:00 2001 From: Justin Shih <36183898+Jshhhh@users.noreply.github.com> Date: Wed, 18 Oct 2023 07:28:00 -0700 Subject: [PATCH] feat: migrate auth and datastore hooks (#1107) Co-authored-by: Justin Shih --- .eslintrc.js | 1 + ...studio-ui-codegen-react-forms.test.ts.snap | 192 ++++-- ...studio-ui-codegen-react-views.test.ts.snap | 17 +- .../studio-ui-codegen-react.test.ts.snap | 75 +- .../hooks/useNavigateAction.test.tsx | 137 ---- .../hooks/useStateMutationAction.test.tsx | 65 -- .../overrides/getOverrideProps.test.ts | 59 -- .../getOverridesFromVariants.test.ts | 133 ---- .../mergeVariantsAndOverrides.test.ts | 124 ---- .../studio-ui-codegen-react-views.test.ts | 2 +- .../render-util-functions.test.ts.snap | 302 ++++++++- .../lib/imports/import-mapping.ts | 14 +- .../react-utils-studio-template-renderer.ts | 20 +- .../{utils.ts => get-error-message.ts} | 23 +- .../createDataStorePredicate.test.ts | 160 +++++ .../hooks/__tests__/test-models/models.ts | 88 +++ .../hooks/__tests__/test-models/schema.ts | 163 +++++ .../__tests__/useAuthSignoutAction.test.ts | 90 +++ .../hooks/__tests__/useDataStore.test.tsx | 329 +++++++++ .../useDataStoreDeleteAction.test.ts | 105 +++ .../useDataStoreUpdateAction.test.tsx | 153 +++++ .../__tests__/useTypeCastFields.test.tsx | 98 +++ .../hooks/createDataStorePredicate.ts | 112 +++ .../hooks/datastore-action-types.ts | 94 +++ .../lib/utils-file-functions/hooks/index.ts | 25 + .../hooks/useAuthSignoutAction.ts | 100 +++ .../hooks/useDataStoreBinding.ts | 175 +++++ .../hooks/useDataStoreCreateAction.ts | 137 ++++ .../hooks/useDataStoreDeleteAction.ts | 119 ++++ .../hooks/useDataStoreUpdateAction.ts | 164 +++++ .../hooks/useTypeCastFields.ts | 111 +++ .../lib/utils-file-functions/index.ts | 5 +- .../utils-file-functions/json-path-fetch.ts | 1 + .../utils-file-functions/storage-manager.ts | 1 + .../lib/utils-file-functions/validation.ts | 3 + packages/codegen-ui-react/package-lock.json | 639 ++++++++++-------- packages/codegen-ui-react/package.json | 8 +- .../src/ActionBindingTests.tsx | 12 +- .../src/ViewTests.tsx | 2 +- .../src/WorkflowTests.tsx | 4 +- 40 files changed, 3148 insertions(+), 914 deletions(-) delete mode 100644 packages/codegen-ui-react/lib/__tests__/hooks/useNavigateAction.test.tsx delete mode 100644 packages/codegen-ui-react/lib/__tests__/hooks/useStateMutationAction.test.tsx delete mode 100644 packages/codegen-ui-react/lib/__tests__/overrides/getOverrideProps.test.ts delete mode 100644 packages/codegen-ui-react/lib/__tests__/overrides/getOverridesFromVariants.test.ts delete mode 100644 packages/codegen-ui-react/lib/__tests__/overrides/mergeVariantsAndOverrides.test.ts rename packages/codegen-ui-react/lib/utils-file-functions/{utils.ts => get-error-message.ts} (68%) create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/createDataStorePredicate.test.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/models.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/schema.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useAuthSignoutAction.test.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStore.test.tsx create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreDeleteAction.test.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreUpdateAction.test.tsx create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useTypeCastFields.test.tsx create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/createDataStorePredicate.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/datastore-action-types.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/index.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/useAuthSignoutAction.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreBinding.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreCreateAction.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreDeleteAction.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreUpdateAction.ts create mode 100644 packages/codegen-ui-react/lib/utils-file-functions/hooks/useTypeCastFields.ts diff --git a/.eslintrc.js b/.eslintrc.js index 78b50a5d6..81a7a65ff 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,6 +24,7 @@ module.exports = { }, rules: { 'max-len': ['error', 120, 2], + 'max-classes-per-file': 'off', 'react/prop-types': 'off', '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/no-explicit-any': 'off', diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap index 331716501..c3bcb2b19 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap @@ -26993,8 +26993,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { CompositeToy, CompositeDog } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -27653,8 +27657,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Comment, Post, User as User0, Org as Org0 } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -28459,8 +28467,12 @@ import { CompositeVet, CompositeDogCompositeVet, } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -31927,8 +31939,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Org, Comment } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -32493,8 +32509,12 @@ import { CompositeVet, CompositeDogCompositeVet, } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -33638,8 +33658,12 @@ import { CPKProject, CPKTeacherCPKClass, } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -36986,8 +37010,12 @@ import { CompositeDogCompositeVet, CompositeBowl, } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -38327,8 +38355,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Dog, Owner } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -38831,8 +38863,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Dog, Owner } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -39373,8 +39409,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Owner, Dog as Dog0 } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -39878,8 +39918,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Owner, Dog as Dog0 } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -41085,8 +41129,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Tag, Post, TagPost } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -41682,8 +41730,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Member, Team as Team0 } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -42270,8 +42322,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { School, Student } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -42783,8 +42839,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Book, Author } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -43290,8 +43350,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Tag, Post, TagPost } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -43887,8 +43951,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Book, Author, Title } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -45287,8 +45355,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { School, Student } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -45856,8 +45928,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { School, Student as Student0 } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -46475,8 +46551,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Member, Team as Team0 } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -47094,8 +47174,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Tag, Post, TagPost } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -52264,8 +52348,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Member, Team as Team0 } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -53599,8 +53687,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Car, Dealership } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], @@ -54111,8 +54203,12 @@ import { useTheme, } from \\"@aws-amplify/ui-react\\"; import { Dealership, Car } from \\"../models\\"; -import { fetchByPath, getOverrideProps, validateField } from \\"./utils\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { + fetchByPath, + getOverrideProps, + useDataStoreBinding, + validateField, +} from \\"./utils\\"; import { DataStore } from \\"aws-amplify\\"; function ArrayField({ items = [], diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-views.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-views.test.ts.snap index 3239909b9..701757c7e 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-views.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-views.test.ts.snap @@ -114,12 +114,12 @@ exports[`amplify table renderer tests should generate a table element 1`] = ` exports[`amplify view renderer tests should call util file if rendered 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; -import { formatter } from \\"./utils\\"; -import { Post } from \\"../models\\"; import { createDataStorePredicate, + formatter, useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; +} from \\"./utils\\"; +import { Post } from \\"../models\\"; import { SortDirection } from \\"@aws-amplify/datastore\\"; import { Table, @@ -218,7 +218,7 @@ export default function MyPostTable(props) { exports[`amplify view renderer tests should call util file if rendered 2`] = ` "import * as React from \\"react\\"; -import { createDataStorePredicate } from \\"@aws-amplify/ui-react/internal\\"; +import { createDataStorePredicate } from \\"./utils\\"; export declare type EscapeHatchProps = { [elementHierarchy: string]: Record; } | null; @@ -296,7 +296,7 @@ export default function CustomTable(props) { exports[`amplify view renderer tests should render view with custom datastore 2`] = ` "import * as React from \\"react\\"; -import { createDataStorePredicate } from \\"@aws-amplify/ui-react/internal\\"; +import { createDataStorePredicate } from \\"./utils\\"; export declare type EscapeHatchProps = { [elementHierarchy: string]: Record; } | null; @@ -319,10 +319,7 @@ exports[`amplify view renderer tests should render view with passed in predicate "/* eslint-disable */ import * as React from \\"react\\"; import { Post } from \\"../models\\"; -import { - createDataStorePredicate, - useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; +import { createDataStorePredicate, useDataStoreBinding } from \\"./utils\\"; import { SortDirection } from \\"@aws-amplify/datastore\\"; import { Table, @@ -418,7 +415,7 @@ export default function MyPostTable(props) { exports[`amplify view renderer tests should render view with passed in predicate and sort 2`] = ` "import * as React from \\"react\\"; -import { createDataStorePredicate } from \\"@aws-amplify/ui-react/internal\\"; +import { createDataStorePredicate } from \\"./utils\\"; export declare type EscapeHatchProps = { [elementHierarchy: string]: Record; } | null; diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap index d10fa5231..be60e6b1d 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap @@ -4,10 +4,9 @@ exports[`amplify render tests actions DataStore DataStoreCreateItem 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { useDataStoreCreateAction } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreCreateAction } from \\"./utils\\"; import { Customer } from \\"../models\\"; import { schema } from \\"../models/schema\\"; -import { getOverrideProps } from \\"./utils\\"; import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -60,10 +59,9 @@ exports[`amplify render tests actions DataStore DataStoreDeleteItem 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { useDataStoreDeleteAction } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreDeleteAction } from \\"./utils\\"; import { Customer } from \\"../models\\"; import { schema } from \\"../models/schema\\"; -import { getOverrideProps } from \\"./utils\\"; import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -116,10 +114,9 @@ exports[`amplify render tests actions DataStore DataStoreUpdateItem 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { useDataStoreUpdateAction } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreUpdateAction } from \\"./utils\\"; import { Customer } from \\"../models\\"; import { schema } from \\"../models/schema\\"; -import { getOverrideProps } from \\"./utils\\"; import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -418,8 +415,7 @@ exports[`amplify render tests actions auth signs out 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { useAuthSignOutAction } from \\"@aws-amplify/ui-react/internal\\"; -import { getOverrideProps } from \\"./utils\\"; +import { getOverrideProps, useAuthSignOutAction } from \\"./utils\\"; import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -677,9 +673,10 @@ import * as React from \\"react\\"; import { User } from \\"../models\\"; import { createDataStorePredicate, + getOverrideProps, useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; -import { getOverrideProps, useStateMutationAction } from \\"./utils\\"; + useStateMutationAction, +} from \\"./utils\\"; import { Button, ButtonProps, @@ -885,13 +882,10 @@ export default function Test(props: TestProps): React.ReactElement { exports[`amplify render tests bindings auth supports auth bindings in actions 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; -import { - useAuth, - useDataStoreCreateAction, -} from \\"@aws-amplify/ui-react/internal\\"; +import { useAuth } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreCreateAction } from \\"./utils\\"; import { Customer } from \\"../models\\"; import { schema } from \\"../models/schema\\"; -import { getOverrideProps } from \\"./utils\\"; import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -1187,7 +1181,7 @@ exports[`amplify render tests collection GraphQL should render collection with d import * as React from \\"react\\"; import { UserPreferences } from \\"../API\\"; import { listUserPreferences, listUsers } from \\"../graphql/queries\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreBinding } from \\"./utils\\"; import { generateClient } from \\"aws-amplify/api\\"; import { Button, @@ -1199,7 +1193,6 @@ import { Pagination, Placeholder, } from \\"@aws-amplify/ui-react\\"; -import { getOverrideProps } from \\"./utils\\"; import { MyFlexProps } from \\"./MyFlex\\"; export type EscapeHatchProps = { @@ -1395,7 +1388,7 @@ exports[`amplify render tests collection GraphQL should render collection with d import * as React from \\"react\\"; import { UserPreferences } from \\"../API\\"; import { listUserPreferences, listUsers } from \\"../graphql/queries\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreBinding } from \\"./utils\\"; import { API } from \\"aws-amplify\\"; import { Button, @@ -1407,7 +1400,6 @@ import { Pagination, Placeholder, } from \\"@aws-amplify/ui-react\\"; -import { getOverrideProps } from \\"./utils\\"; import { MyFlexProps } from \\"./MyFlex\\"; export type EscapeHatchProps = { @@ -2036,9 +2028,8 @@ exports[`amplify render tests collection should not render nested query if the d "/* eslint-disable */ import * as React from \\"react\\"; import { Author } from \\"../models\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreBinding } from \\"./utils\\"; import AuthorProfile, { AuthorProfileProps } from \\"./AuthorProfile\\"; -import { getOverrideProps } from \\"./utils\\"; import { Collection, CollectionProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -2115,8 +2106,9 @@ import * as React from \\"react\\"; import { UserPreferences, User } from \\"../models\\"; import { createDataStorePredicate, + getOverrideProps, useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; +} from \\"./utils\\"; import { Button, ButtonProps, @@ -2125,7 +2117,6 @@ import { Flex, FlexProps, } from \\"@aws-amplify/ui-react\\"; -import { getOverrideProps } from \\"./utils\\"; import { MyFlexProps } from \\"./MyFlex\\"; export type EscapeHatchProps = { @@ -2268,8 +2259,9 @@ import * as React from \\"react\\"; import { UserPreferences, User } from \\"../models\\"; import { createDataStorePredicate, + getOverrideProps, useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; +} from \\"./utils\\"; import { SortDirection, SortPredicate } from \\"@aws-amplify/datastore\\"; import { Button, @@ -2279,7 +2271,6 @@ import { Flex, FlexProps, } from \\"@aws-amplify/ui-react\\"; -import { getOverrideProps } from \\"./utils\\"; import { MyFlexProps } from \\"./MyFlex\\"; export type EscapeHatchProps = { @@ -2403,8 +2394,9 @@ import * as React from \\"react\\"; import { UserPreferences, User } from \\"../models\\"; import { createDataStorePredicate, + getOverrideProps, useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; +} from \\"./utils\\"; import { Button, ButtonProps, @@ -2413,7 +2405,6 @@ import { Flex, FlexProps, } from \\"@aws-amplify/ui-react\\"; -import { getOverrideProps } from \\"./utils\\"; import { MyFlexProps } from \\"./MyFlex\\"; export type EscapeHatchProps = { @@ -2527,9 +2518,8 @@ exports[`amplify render tests collection should render collection with data bind "/* eslint-disable */ import * as React from \\"react\\"; import { UntitledModel } from \\"../models\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreBinding } from \\"./utils\\"; import ListingCard, { ListingCardProps } from \\"./ListingCard\\"; -import { getOverrideProps } from \\"./utils\\"; import { Collection, CollectionProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -2664,11 +2654,10 @@ exports[`amplify render tests collection should render concatenated keys if mode "/* eslint-disable */ import * as React from \\"react\\"; import { CompositePerson } from \\"../models\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreBinding } from \\"./utils\\"; import CompositePersonProfile, { CompositePersonProfileProps, } from \\"./CompositePersonProfile\\"; -import { getOverrideProps } from \\"./utils\\"; import { Collection, CollectionProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -2748,8 +2737,9 @@ import * as React from \\"react\\"; import { Flex as Flex0, FlexModel, Button as Button0 } from \\"../models\\"; import { createDataStorePredicate, + getOverrideProps, useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; +} from \\"./utils\\"; import { SortDirection, SortPredicate } from \\"@aws-amplify/datastore\\"; import { Button, @@ -2759,7 +2749,6 @@ import { Flex, FlexProps, } from \\"@aws-amplify/ui-react\\"; -import { getOverrideProps } from \\"./utils\\"; import { MyFlexProps } from \\"./MyFlex\\"; export type EscapeHatchProps = { @@ -2890,9 +2879,8 @@ exports[`amplify render tests collection should render nested query if model has "/* eslint-disable */ import * as React from \\"react\\"; import { Author } from \\"../models\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreBinding } from \\"./utils\\"; import AuthorProfile, { AuthorProfileProps } from \\"./AuthorProfile\\"; -import { getOverrideProps } from \\"./utils\\"; import { Collection, CollectionProps } from \\"@aws-amplify/ui-react\\"; export type EscapeHatchProps = { @@ -7880,14 +7868,13 @@ Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; import { User } from \\"../models\\"; -import { useDataStoreBinding } from \\"@aws-amplify/ui-react/internal\\"; +import { getOverrideProps, useDataStoreBinding } from \\"./utils\\"; import { Collection, CollectionProps, Text, TextProps, } from \\"@aws-amplify/ui-react\\"; -import { getOverrideProps } from \\"./utils\\"; import { MyTextProps } from \\"./MyText\\"; export type EscapeHatchProps = { @@ -8463,8 +8450,11 @@ exports[`amplify render tests mutations form 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { getOverrideProps, useStateMutationAction } from \\"./utils\\"; -import { useDataStoreUpdateAction } from \\"@aws-amplify/ui-react/internal\\"; +import { + getOverrideProps, + useDataStoreUpdateAction, + useStateMutationAction, +} from \\"./utils\\"; import { Customer } from \\"../models\\"; import { schema } from \\"../models/schema\\"; import { @@ -8865,12 +8855,13 @@ Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; import { User } from \\"../models\\"; +import { useAuth } from \\"@aws-amplify/ui-react/internal\\"; import { createDataStorePredicate, - useAuth, + getOverrideProps, useDataStoreBinding, -} from \\"@aws-amplify/ui-react/internal\\"; -import { getOverrideProps, useStateMutationAction } from \\"./utils\\"; + useStateMutationAction, +} from \\"./utils\\"; import { useEffect } from \\"react\\"; import { Button, diff --git a/packages/codegen-ui-react/lib/__tests__/hooks/useNavigateAction.test.tsx b/packages/codegen-ui-react/lib/__tests__/hooks/useNavigateAction.test.tsx deleted file mode 100644 index 06e977d5c..000000000 --- a/packages/codegen-ui-react/lib/__tests__/hooks/useNavigateAction.test.tsx +++ /dev/null @@ -1,137 +0,0 @@ -/* - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ -import { Hub } from 'aws-amplify'; -import { renderHook } from '@testing-library/react-hooks'; - -import { - ACTION_NAVIGATE_FINISHED, - ACTION_NAVIGATE_STARTED, - EVENT_ACTION_CORE_NAVIGATE, - UI_CHANNEL, - defaultTarget, - windowFeatures, - useNavigateAction, - UseNavigateActionOptions, - AMPLIFY_SYMBOL, -} from '../../utils-file-functions'; - -jest.mock('aws-amplify'); - -describe('useNavigateHook:', () => { - let windowSpy: jest.SpyInstance; - const url = 'https://www.amazon.com/'; - const target = '_blank'; - const anchor = '#about'; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - const testHubEventEmit = (data: UseNavigateActionOptions) => { - expect(Hub.dispatch).toHaveBeenCalledTimes(2); - expect(Hub.dispatch).toHaveBeenCalledWith( - UI_CHANNEL, - { - event: ACTION_NAVIGATE_STARTED, - data, - }, - EVENT_ACTION_CORE_NAVIGATE, - AMPLIFY_SYMBOL, - ); - expect(Hub.dispatch).toHaveBeenLastCalledWith( - UI_CHANNEL, - { - event: ACTION_NAVIGATE_FINISHED, - data, - }, - EVENT_ACTION_CORE_NAVIGATE, - AMPLIFY_SYMBOL, - ); - }; - - it('Should call window.open', () => { - windowSpy = jest.spyOn(window, 'open').mockImplementation(() => { - return window; - }); - - const config: UseNavigateActionOptions = { - type: 'url', - url, - }; - const { result } = renderHook(() => useNavigateAction(config)); - - result.current(); - expect(windowSpy).toBeCalledTimes(1); - expect(windowSpy).toBeCalledWith(url, defaultTarget, windowFeatures); - - testHubEventEmit(config); - - windowSpy.mockRestore(); - }); - - it('Should call window.open with "_blank" as target', () => { - windowSpy = jest.spyOn(window, 'open').mockImplementation(() => { - return window; - }); - - const config: UseNavigateActionOptions = { - type: 'url', - url, - target, - }; - const { result } = renderHook(() => useNavigateAction(config)); - - result.current(); - expect(windowSpy).toBeCalledTimes(1); - expect(windowSpy).toBeCalledWith(url, target, windowFeatures); - - testHubEventEmit(config); - - windowSpy.mockRestore(); - }); - - it('Should change window location hash', () => { - const config: UseNavigateActionOptions = { - type: 'anchor', - anchor, - }; - const { result } = renderHook(() => useNavigateAction(config)); - - expect(window.location.hash).toBe(''); - result.current(); - expect(window.location.hash).toBe(anchor); - - testHubEventEmit(config); - }); - - it('Should call window.reload', () => { - Object.defineProperty(window, 'location', { - value: { - reload: jest.fn(), - }, - }); - - const config: UseNavigateActionOptions = { - type: 'reload', - }; - const { result } = renderHook(() => useNavigateAction(config)); - - result.current(); - expect(window.location.reload).toBeCalledTimes(1); - - testHubEventEmit(config); - }); -}); diff --git a/packages/codegen-ui-react/lib/__tests__/hooks/useStateMutationAction.test.tsx b/packages/codegen-ui-react/lib/__tests__/hooks/useStateMutationAction.test.tsx deleted file mode 100644 index fd0b48523..000000000 --- a/packages/codegen-ui-react/lib/__tests__/hooks/useStateMutationAction.test.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ -import { Hub } from 'aws-amplify'; -import { renderHook, act } from '@testing-library/react-hooks'; - -import { - ACTION_STATE_MUTATION_FINISHED, - ACTION_STATE_MUTATION_STARTED, - EVENT_ACTION_CORE_STATE_MUTATION, - UI_CHANNEL, - useStateMutationAction, - AMPLIFY_SYMBOL, -} from '../../utils-file-functions'; - -jest.mock('aws-amplify'); - -describe('useStateMutationAction:', () => { - it('should update state correctly', () => { - const prevState = 'none'; - const newState = 'block'; - const data = { prevState, newState }; - - const { result } = renderHook(() => useStateMutationAction(prevState)); - act(() => { - const [, setNewState] = result.current; - setNewState(newState); - }); - - const [state] = result.current; - expect(state).toBe(newState); - - expect(Hub.dispatch).toHaveBeenCalledTimes(2); - expect(Hub.dispatch).toHaveBeenCalledWith( - UI_CHANNEL, - { - event: ACTION_STATE_MUTATION_STARTED, - data, - }, - EVENT_ACTION_CORE_STATE_MUTATION, - AMPLIFY_SYMBOL, - ); - expect(Hub.dispatch).toHaveBeenLastCalledWith( - UI_CHANNEL, - { - event: ACTION_STATE_MUTATION_FINISHED, - data, - }, - EVENT_ACTION_CORE_STATE_MUTATION, - AMPLIFY_SYMBOL, - ); - }); -}); diff --git a/packages/codegen-ui-react/lib/__tests__/overrides/getOverrideProps.test.ts b/packages/codegen-ui-react/lib/__tests__/overrides/getOverrideProps.test.ts deleted file mode 100644 index cf2030030..000000000 --- a/packages/codegen-ui-react/lib/__tests__/overrides/getOverrideProps.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import { getOverrideProps } from '../../utils-file-functions'; - -describe('getOverrideProps', () => { - const overrides = { - View: { - width: '436px', - padding: '0px 0px 0px 0px', - backgroundColor: 'rgba(50.36245197057724,0,251.81250303983688,1)', - overflow: 'hidden', - position: 'relative', - height: '98px', - }, - 'View.Text[0]': { - fontSize: '12px', - color: 'red', - }, - }; - - it('returns the correct overrides when path matches', () => { - const result = getOverrideProps(overrides, 'View'); - expect(result).toEqual({ - width: '436px', - padding: '0px 0px 0px 0px', - backgroundColor: 'rgba(50.36245197057724,0,251.81250303983688,1)', - overflow: 'hidden', - position: 'relative', - height: '98px', - }); - }); - - it('returns the correct overrides when path matches complex', () => { - const result = getOverrideProps(overrides, 'View.Text[0]'); - expect(result).toEqual({ - fontSize: '12px', - color: 'red', - }); - }); - - it('returns an empty object when nothing matches', () => { - const result = getOverrideProps(overrides, 'Flex'); - expect(result).toEqual({}); - }); -}); diff --git a/packages/codegen-ui-react/lib/__tests__/overrides/getOverridesFromVariants.test.ts b/packages/codegen-ui-react/lib/__tests__/overrides/getOverridesFromVariants.test.ts deleted file mode 100644 index 661326cff..000000000 --- a/packages/codegen-ui-react/lib/__tests__/overrides/getOverridesFromVariants.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -/* - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import { Variant, getOverridesFromVariants } from '../../utils-file-functions'; - -describe('getOverridesFromVariants', () => { - const variants: Variant[] = [ - { - variantValues: { - variant: 'primary', - }, - overrides: { - Button: { - fontSize: '12px', - }, - }, - }, - { - variantValues: { - variant: 'secondary', - }, - overrides: { - Button: { - fontSize: '40px', - }, - }, - }, - { - variantValues: { - variant: 'primary', - size: 'large', - }, - overrides: { - Button: { - width: '500', - }, - }, - }, - ]; - - it('should return overrides for primary variant, without optional', () => { - const selectedVariantValue = { variant: 'primary' }; - const expected = { - Button: { - fontSize: '12px', - }, - }; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); - - it('should return overrides for alternative', () => { - const selectedVariantValue = { variant: 'secondary' }; - const expected = { - Button: { - fontSize: '40px', - }, - }; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); - - it('should return overrides for multiple values, including optional', () => { - const selectedVariantValue = { variant: 'primary', size: 'large' }; - const expected = { - Button: { - width: '500', - }, - }; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); - - it('should return no overrides invalid combo', () => { - const selectedVariantValue = { variant: 'secondary', size: 'large' }; - const expected = {}; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); - - it('should return no overrides on unexpected variant parameter', () => { - const selectedVariantValue = { unexpected: 'yes' }; - const expected = {}; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); - - it('should match on expected variants even with additional props', () => { - const selectedVariantValue = { variant: 'primary', unexpected: 'yes' }; - const expected = { - Button: { - fontSize: '12px', - }, - }; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); - - it('should match on expected variants with optional even with additional props', () => { - const selectedVariantValue = { - variant: 'primary', - size: 'large', - unexpected: 'yes', - }; - const expected = { - Button: { - width: '500', - }, - }; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); - - it('should match on expected variants with undefined for optional', () => { - const selectedVariantValue = { - variant: 'primary', - size: undefined, - unexpected: 'yes', - }; - const expected = { - Button: { - fontSize: '12px', - }, - }; - expect(getOverridesFromVariants(variants, selectedVariantValue)).toEqual(expected); - }); -}); diff --git a/packages/codegen-ui-react/lib/__tests__/overrides/mergeVariantsAndOverrides.test.ts b/packages/codegen-ui-react/lib/__tests__/overrides/mergeVariantsAndOverrides.test.ts deleted file mode 100644 index d17cf11d0..000000000 --- a/packages/codegen-ui-react/lib/__tests__/overrides/mergeVariantsAndOverrides.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import { EscapeHatchProps, mergeVariantsAndOverrides } from '../../utils-file-functions'; - -describe('mergeVariantsAndOverrides', () => { - const expected: EscapeHatchProps = { - 'Flex.Button[0]': { - color: 'red', - size: 'large', - }, - 'Flex.CheckBox[1]': { - isEnabled: 'false', - size: 'large', - }, - }; - it('should return merged variants after applying overrides', () => { - const variants: EscapeHatchProps = { - 'Flex.Button[0]': { - color: 'red', - }, - 'Flex.CheckBox[1]': { - isEnabled: 'false', - size: 'small', - }, - }; - - const overrides: EscapeHatchProps = { - 'Flex.Button[0]': { - size: 'large', - }, - 'Flex.CheckBox[1]': { - size: 'large', - }, - }; - - expect(mergeVariantsAndOverrides(variants, overrides)).toEqual(expected); - }); - - it('should return merged variants when override includes new control', () => { - const variants: EscapeHatchProps = { - 'Flex.Button[0]': { - color: 'red', - size: 'large', - }, - }; - - const overrides: EscapeHatchProps = { - 'Flex.CheckBox[1]': { - isEnabled: 'false', - size: 'large', - }, - }; - - expect(mergeVariantsAndOverrides(variants, overrides)).toEqual(expected); - }); - - it('should return merged variants when all variants overridden', () => { - const variants: EscapeHatchProps = { - 'Flex.Button[0]': { - color: 'green', - size: 'small', - }, - 'Flex.CheckBox[1]': { - isEnabled: 'true', - size: 'small', - }, - }; - - const overrides: EscapeHatchProps = { - ...expected, - }; - - expect(mergeVariantsAndOverrides(variants, overrides)).toEqual(expected); - }); - - it('should return original variants when override is empty', () => { - const variants: EscapeHatchProps = { - ...expected, - }; - - const overrides: EscapeHatchProps = {}; - - expect(mergeVariantsAndOverrides(variants, overrides)).toEqual(expected); - }); - - it('should return original variants when override is null', () => { - const variants: EscapeHatchProps = { - ...expected, - }; - - // cast null as EscapeHatchProps for exhaustive test case - expect(mergeVariantsAndOverrides(variants, null as unknown as EscapeHatchProps)).toEqual(expected); - }); - - it('should return overrides when variant is null', () => { - const overrides: EscapeHatchProps = { - ...expected, - }; - - // cast null as EscapeHatchProps for exhaustive test case - expect(mergeVariantsAndOverrides(null as unknown as EscapeHatchProps, overrides)).toEqual(expected); - }); - - it('should return null when both variant & override are null', () => { - expect( - // cast null as EscapeHatchProps for exhaustive test case - mergeVariantsAndOverrides(null as unknown as EscapeHatchProps, null as unknown as EscapeHatchProps), - ).toEqual(null); - }); -}); diff --git a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-views.test.ts b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-views.test.ts index e4fcf8f04..0e69467ed 100644 --- a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-views.test.ts +++ b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-views.test.ts @@ -69,7 +69,7 @@ describe('amplify view renderer tests', () => { 'views/post-table-custom-format', 'datastore/post-ds', ); - expect(componentText.replace(/\\/g, '')).toContain(`import { formatter } from "./utils"`); + expect(componentText.replace(/\\/g, '')).toContain('formatter'); expect(componentText).toMatchSnapshot(); expect(declaration).toMatchSnapshot(); }); diff --git a/packages/codegen-ui-react/lib/__tests__/utils/__snapshots__/render-util-functions.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/utils/__snapshots__/render-util-functions.test.ts.snap index a22578d62..9fe29f1c9 100644 --- a/packages/codegen-ui-react/lib/__tests__/utils/__snapshots__/render-util-functions.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/utils/__snapshots__/render-util-functions.test.ts.snap @@ -3,7 +3,7 @@ exports[`render utils file render all 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; -import { Hub } from \\"aws-amplify\\"; +import { DataStore, Hub } from \\"aws-amplify\\"; export const UI_CHANNEL = \\"ui\\"; export const UI_EVENT_TYPE_ACTIONS = \\"actions\\"; export const CATEGORY_AUTH = \\"auth\\"; @@ -198,6 +198,306 @@ export const mergeVariantsAndOverrides = (variants, overrides) => { ...merged, }; }; +export const isErrorWithMessage = (error) => { + return ( + typeof error === \\"object\\" && + error !== null && + \\"message\\" in error && + typeof error.message === \\"string\\" + ); +}; +export const toErrorWithMessage = (maybeError) => { + if (isErrorWithMessage(maybeError)) return maybeError; + try { + return new Error(JSON.stringify(maybeError)); + } catch { + return new Error(String(maybeError)); + } +}; +export const getErrorMessage = (error) => { + return toErrorWithMessage(error).message; +}; +export const useAuthSignOutAction = (options) => async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_STARTED, + data: { options }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL + ); + await Auth.signOut(options); + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_FINISHED, + data: { options }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_FINISHED, + data: { options, errorMessage: getErrorMessage(error) }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL + ); + } +}; +export const useTypeCastFields = ({ fields, modelName, schema }) => { + return React.useMemo(() => { + if (!schema) { + return fields; + } + const castFields = {}; + Object.keys(fields).forEach((fieldName) => { + const field = fields[fieldName]; + switch (schema?.models[modelName]?.fields?.[fieldName]?.type) { + case \\"AWSTimestamp\\": + castFields[fieldName] = Number(field); + break; + case \\"Boolean\\": + castFields[fieldName] = Boolean(field); + break; + case \\"Int\\": + castFields[fieldName] = + typeof field === \\"string\\" || + (typeof field === \\"object\\" && + Object.prototype.toString.call(field) === \\"[object String]\\") + ? parseInt(field) + : field; + break; + case \\"Float\\": + castFields[fieldName] = Number(field); + break; + default: + castFields[fieldName] = field; + break; + } + }); + return castFields; + }, [fields, schema, modelName]); +}; +export const useDataStoreCreateAction = ({ + model, + fields: initialFields, + schema, +}) => { + const fields = useTypeCastFields({ + fields: initialFields, + modelName: model.name, + schema, + }); + return async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_STARTED, + data: { fields }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL + ); + const item = await DataStore.save(new model(fields)); + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_FINISHED, + data: { fields, item }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_FINISHED, + data: { + fields, + errorMessage: getErrorMessage(error), + }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL + ); + } + }; +}; +export const useDataStoreUpdateAction = ({ + fields: initialFields, + id, + model, + schema, +}) => { + const fields = useTypeCastFields({ + fields: initialFields, + modelName: model.name, + schema, + }); + return async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_STARTED, + data: { fields, id }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL + ); + const original = await DataStore.query(model, id); + if (!original) { + throw new Error(\`\${DATASTORE_QUERY_BY_ID_ERROR}: \${id}\`); + } + const item = await DataStore.save( + model.copyOf(original, (updated) => { + Object.assign(updated, fields); + }) + ); + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_FINISHED, + data: { fields, id, item }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_FINISHED, + data: { + fields, + id, + errorMessage: getErrorMessage(error), + }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL + ); + } + }; +}; +export const useDataStoreDeleteAction = + ({ model, id }) => + async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_STARTED, + data: { id }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL + ); + await DataStore.delete(model, id); + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_FINISHED, + data: { id }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_FINISHED, + data: { id, errorMessage: getErrorMessage(error) }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL + ); + } + }; +export const createDataStorePredicate = (predicateObject) => { + const { + and: groupAnd, + or: groupOr, + field, + operator, + operand, + } = predicateObject; + if (Array.isArray(groupAnd)) { + const predicates = groupAnd.map((condition) => + createDataStorePredicate(condition) + ); + return (p) => + p.and((model) => predicates.map((predicate) => predicate(model))); + } + if (Array.isArray(groupOr)) { + const predicates = groupOr.map((condition) => + createDataStorePredicate(condition) + ); + return (p) => + p.or((model) => predicates.map((predicate) => predicate(model))); + } + return (p) => { + if (!!field && !!operator && p?.[field]?.[operator]) { + return p[field][operator](operand); + } + return p; + }; +}; +export const useDataStoreCollection = ({ model, criteria, pagination }) => { + const [result, setResult] = React.useState({ + items: [], + isLoading: false, + error: undefined, + }); + const fetch = () => { + setResult({ isLoading: true, items: [] }); + const subscription = DataStore.observeQuery( + model, + criteria, + pagination + ).subscribe( + (snapshot) => setResult({ items: snapshot.items, isLoading: false }), + (error) => setResult({ items: [], error, isLoading: false }) + ); + if (subscription) { + return () => subscription.unsubscribe(); + } + }; + React.useEffect(fetch, []); + return result; +}; +export const useDataStoreItem = ({ model, id }) => { + const [item, setItem] = React.useState(); + const [isLoading, setLoading] = React.useState(false); + const [error, setError] = React.useState(); + const fetch = () => { + setLoading(true); + DataStore.query(model, id) + .then(setItem) + .catch(setError) + .finally(() => setLoading(false)); + }; + React.useEffect(fetch, []); + return { + error, + item, + isLoading, + }; +}; +export function useDataStoreBinding(props) { + return props.type === \\"record\\" + ? useDataStoreItem(props) + : useDataStoreCollection(props); +} export const validateField = (value, validations) => { for (const validation of validations) { if (value === undefined || value === \\"\\" || value === null) { diff --git a/packages/codegen-ui-react/lib/imports/import-mapping.ts b/packages/codegen-ui-react/lib/imports/import-mapping.ts index 08d63259e..874f97636 100644 --- a/packages/codegen-ui-react/lib/imports/import-mapping.ts +++ b/packages/codegen-ui-react/lib/imports/import-mapping.ts @@ -54,6 +54,7 @@ export enum ImportValue { USE_STATE = 'useState', API = 'API', HUB = 'Hub', + DATASTORE = 'DataStore', PAGINATION = 'Pagination', PLACEHOLDER = 'Placeholder', GENERATE_CLIENT = 'generateClient', @@ -62,6 +63,7 @@ export enum ImportValue { export const ImportMapping: Record = { [ImportValue.API]: ImportSource.AMPLIFY, [ImportValue.HUB]: ImportSource.AMPLIFY, + [ImportValue.DATASTORE]: ImportSource.AMPLIFY, [ImportValue.GENERATE_CLIENT]: ImportSource.AMPLIFY_API, [ImportValue.SORT_DIRECTION]: ImportSource.AMPLIFY_DATASTORE, [ImportValue.SORT_PREDICATE]: ImportSource.AMPLIFY_DATASTORE, @@ -71,13 +73,13 @@ export const ImportMapping: Record = { [ImportValue.PLACEHOLDER]: ImportSource.UI_REACT, [ImportValue.USE_BREAKPOINT_VALUE]: ImportSource.UI_REACT, [ImportValue.USE_AUTH]: ImportSource.UI_REACT_INTERNAL, - [ImportValue.CREATE_DATA_STORE_PREDICATE]: ImportSource.UI_REACT_INTERNAL, - [ImportValue.USE_DATA_STORE_BINDING]: ImportSource.UI_REACT_INTERNAL, + [ImportValue.CREATE_DATA_STORE_PREDICATE]: ImportSource.UTILS, + [ImportValue.USE_DATA_STORE_BINDING]: ImportSource.UTILS, [ImportValue.USE_NAVIGATE_ACTION]: ImportSource.UTILS, - [ImportValue.USE_DATA_STORE_CREATE_ACTION]: ImportSource.UI_REACT_INTERNAL, - [ImportValue.USE_DATA_STORE_UPDATE_ACTION]: ImportSource.UI_REACT_INTERNAL, - [ImportValue.USE_DATA_STORE_DELETE_ACTION]: ImportSource.UI_REACT_INTERNAL, - [ImportValue.USE_AUTH_SIGN_OUT_ACTION]: ImportSource.UI_REACT_INTERNAL, + [ImportValue.USE_DATA_STORE_CREATE_ACTION]: ImportSource.UTILS, + [ImportValue.USE_DATA_STORE_UPDATE_ACTION]: ImportSource.UTILS, + [ImportValue.USE_DATA_STORE_DELETE_ACTION]: ImportSource.UTILS, + [ImportValue.USE_AUTH_SIGN_OUT_ACTION]: ImportSource.UTILS, [ImportValue.USE_STATE_MUTATION_ACTION]: ImportSource.UTILS, [ImportValue.GET_OVERRIDE_PROPS]: ImportSource.UTILS, [ImportValue.MERGE_VARIANTS_OVERRIDES]: ImportSource.UTILS, diff --git a/packages/codegen-ui-react/lib/react-utils-studio-template-renderer.ts b/packages/codegen-ui-react/lib/react-utils-studio-template-renderer.ts index e9c8b1651..d5e0b292d 100644 --- a/packages/codegen-ui-react/lib/react-utils-studio-template-renderer.ts +++ b/packages/codegen-ui-react/lib/react-utils-studio-template-renderer.ts @@ -33,6 +33,14 @@ import { processFileString, validationString, findChildOverridesString, + getErrorMessageString, + useAuthSignOutActionString, + useTypeCastFieldsString, + useDataStoreCreateActionString, + useDataStoreUpdateActionString, + useDataStoreDeleteActionString, + createDataStorePredicateString, + useDataStoreBindingString, } from './utils-file-functions'; export type UtilTemplateType = 'validation' | 'formatter' | 'fetchByPath' | 'processFile'; @@ -70,7 +78,7 @@ export class ReactUtilsStudioTemplateRenderer extends StudioTemplateRenderer< renderComponentInternal() { const { printer, file } = buildPrinter(this.fileName, this.renderConfig); - this.importCollection.addMappedImport(ImportValue.HUB); + this.importCollection.addMappedImport(ImportValue.HUB, ImportValue.DATASTORE); const parsedUtils: string[] = [ constantsString, @@ -81,6 +89,14 @@ export class ReactUtilsStudioTemplateRenderer extends StudioTemplateRenderer< getOverridePropsString, getOverridesFromVariantsString, mergeVariantsAndOverridesString, + getErrorMessageString, + useAuthSignOutActionString, + useTypeCastFieldsString, + useDataStoreCreateActionString, + useDataStoreUpdateActionString, + useDataStoreDeleteActionString, + createDataStorePredicateString, + useDataStoreBindingString, ]; const utilsSet = new Set(this.utils); @@ -111,7 +127,7 @@ export class ReactUtilsStudioTemplateRenderer extends StudioTemplateRenderer< componentText += parsedUtils.join(EOL) + EOL; - const { componentText: transpliedText } = transpile(componentText, this.renderConfig); + const { componentText: transpliedText } = transpile(componentText, this.renderConfig, true); return { componentText: transpliedText, diff --git a/packages/codegen-ui-react/lib/utils-file-functions/utils.ts b/packages/codegen-ui-react/lib/utils-file-functions/get-error-message.ts similarity index 68% rename from packages/codegen-ui-react/lib/utils-file-functions/utils.ts rename to packages/codegen-ui-react/lib/utils-file-functions/get-error-message.ts index 0b0d11116..fac2d36d4 100644 --- a/packages/codegen-ui-react/lib/utils-file-functions/utils.ts +++ b/packages/codegen-ui-react/lib/utils-file-functions/get-error-message.ts @@ -19,7 +19,7 @@ type ErrorWithMessage = { message: string; }; - +/* istanbul ignore next */ export const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => { return ( typeof error === 'object' && @@ -29,6 +29,7 @@ export const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => ); }; +/* istanbul ignore next */ export const toErrorWithMessage = (maybeError: unknown): ErrorWithMessage => { if (isErrorWithMessage(maybeError)) return maybeError; @@ -44,3 +45,23 @@ export const toErrorWithMessage = (maybeError: unknown): ErrorWithMessage => { export const getErrorMessage = (error: unknown): string => { return toErrorWithMessage(error).message; }; + +export const getErrorMessageString = `export const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => { + return ( + typeof error === 'object' && + error !== null && + 'message' in error && + typeof (error as Record).message === 'string' + ); +}; +export const toErrorWithMessage = (maybeError: unknown): ErrorWithMessage => { + if (isErrorWithMessage(maybeError)) return maybeError; + try { + return new Error(JSON.stringify(maybeError)); + } catch { + return new Error(String(maybeError)); + } +}; +export const getErrorMessage = (error: unknown): string => { + return toErrorWithMessage(error).message; +};`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/createDataStorePredicate.test.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/createDataStorePredicate.test.ts new file mode 100644 index 000000000..57c0faeb0 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/createDataStorePredicate.test.ts @@ -0,0 +1,160 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { + RecursiveModelPredicate, + RecursiveModelPredicateAggregateExtender, + RecursiveModelPredicateOperator, + ValuePredicate, + PredicateInternalsKey, +} from '@aws-amplify/datastore'; + +import { createDataStorePredicate } from '../createDataStorePredicate'; +import { DataStorePredicateObject } from '../datastore-action-types'; + +type Post = { id: string; name: string; age: string }; + +const namePredicateObject = { + field: 'name', + operator: 'startsWith', + operand: 'John', +}; + +const agePredicateObject = { + field: 'age', + operator: 'gt', + operand: '25', +}; + +const booleanPredicateObject = { + field: 'isActive', + operator: 'eq', + operand: true, +}; + +const agePredicate = jest.fn(); +const booleanPredicate = jest.fn(); +const namePredicate = jest.fn(); + +const baseCondition: Omit, 'and' | 'or' | 'not'> = { + id: '' as unknown as ValuePredicate, + name: '' as unknown as ValuePredicate, + age: '' as unknown as ValuePredicate, +}; + +const ageCondition: RecursiveModelPredicate = { + ...baseCondition, + [agePredicateObject.field]: { + [agePredicateObject.operator]: agePredicate, + }, +} as RecursiveModelPredicate; + +const booleanCondition: RecursiveModelPredicate = { + ...baseCondition, + [booleanPredicateObject.field]: { + [booleanPredicateObject.operator]: booleanPredicate, + }, +} as RecursiveModelPredicate; + +const nameCondition: RecursiveModelPredicate = { + ...baseCondition, + [namePredicateObject.field]: { + [namePredicateObject.operator]: namePredicate, + }, +} as RecursiveModelPredicate; + +describe('createDataStorePredicate', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('should generate a simple predicate', () => { + const predicate = createDataStorePredicate(namePredicateObject); + + predicate(nameCondition); + expect(namePredicate).toHaveBeenCalledWith(namePredicateObject.operand); + }); + + test('should generate a simple boolean predicate', () => { + const predicate = createDataStorePredicate(booleanPredicateObject); + + predicate(booleanCondition); + expect(booleanPredicate).toHaveBeenCalledWith(booleanPredicateObject.operand); + }); + + test('should generate an `or` group predicate', () => { + const predicateObject: DataStorePredicateObject = { + or: [namePredicateObject, agePredicateObject], + }; + + const predicate = createDataStorePredicate(predicateObject); + + const or: RecursiveModelPredicateOperator = (p) => + [p(nameCondition), p(ageCondition)] as unknown as PredicateInternalsKey; + + const condition = { + or, + } as RecursiveModelPredicate; + + predicate(condition); + + expect(namePredicate).toHaveBeenCalledWith(namePredicateObject.operand); + expect(agePredicate).toHaveBeenCalledWith(agePredicateObject.operand); + }); + + test('should generate an `and` group predicate', () => { + const predicateObject: DataStorePredicateObject = { + and: [namePredicateObject, agePredicateObject], + }; + + const predicate = createDataStorePredicate(predicateObject); + + const and: RecursiveModelPredicateOperator = (p) => + [p(nameCondition), p(ageCondition)] as unknown as PredicateInternalsKey; + + const condition = { + and, + } as RecursiveModelPredicate; + + predicate(condition); + + expect(namePredicate).toHaveBeenCalledWith(namePredicateObject.operand); + expect(agePredicate).toHaveBeenCalledWith(agePredicateObject.operand); + }); + + test('should generate a nested predicate', () => { + const predicateObject: DataStorePredicateObject = { + and: [namePredicateObject, { or: [agePredicateObject] }], + }; + + const predicate = createDataStorePredicate(predicateObject); + + const or: RecursiveModelPredicateOperator = (orGroup) => + orGroup(ageCondition) as unknown as PredicateInternalsKey; + + const condition = { + and: (andGroup: RecursiveModelPredicateAggregateExtender) => + andGroup({ + ...nameCondition, + or, + } as RecursiveModelPredicate), + } as unknown as RecursiveModelPredicate; + + predicate(condition); + + expect(namePredicate).toHaveBeenCalledWith(namePredicateObject.operand); + expect(agePredicate).toHaveBeenCalledWith(agePredicateObject.operand); + }); +}); diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/models.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/models.ts new file mode 100644 index 000000000..2b3bf5a1f --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/models.ts @@ -0,0 +1,88 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { ModelInit, MutableModel } from '@aws-amplify/datastore'; + +type HomeMetaData = { + readOnlyFields: 'createdAt' | 'updatedAt'; +}; + +export declare class Home { + readonly id: string; + + readonly address?: string | null; + + readonly image_url?: string | null; + + readonly price?: number | null; + + readonly Rating?: number | null; + + readonly isAvailable?: boolean | null; + + readonly availabilityDateTime?: string | null; + + readonly availabilityDate?: string | null; + + readonly availabliltyTime?: string | null; + + readonly randomJSON?: string | null; + + readonly timestamp?: number | null; + + readonly phone?: string | null; + + readonly ipAddress?: string | null; + + readonly email?: string | null; + + readonly createdAt?: string | null; + + readonly updatedAt?: string | null; + constructor(init: ModelInit); + static copyOf( + source: Home, + mutator: (draft: MutableModel) => MutableModel | void, + ): Home; +} + +export type TodoMetaData = { + readOnlyFields: 'createdAt' | 'updatedAt'; +}; + +export class Todo { + readonly id!: string; + + readonly name: string; + + readonly description?: string; + + readonly createdAt?: string; + + readonly updatedAt?: string; + + constructor(init: ModelInit) { + this.name = init.name; + } + + static copyOf( + source: Todo, + mutator: (draft: MutableModel) => MutableModel | void, + ): Todo { + const copy = { ...source }; + mutator(copy); + return copy; + } +} diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/schema.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/schema.ts new file mode 100644 index 000000000..c5be9c547 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/test-models/schema.ts @@ -0,0 +1,163 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { Schema } from '@aws-amplify/datastore'; + +export const schema: Schema = { + codegenVersion: '3.4', + models: { + Home: { + name: 'Home', + fields: { + id: { + name: 'id', + isArray: false, + type: 'ID', + isRequired: true, + attributes: [], + }, + address: { + name: 'address', + isArray: false, + type: 'String', + isRequired: false, + attributes: [], + }, + image_url: { + name: 'image_url', + isArray: false, + type: 'String', + isRequired: false, + attributes: [], + }, + price: { + name: 'price', + isArray: false, + type: 'Float', + isRequired: false, + attributes: [], + }, + Rating: { + name: 'Rating', + isArray: false, + type: 'Int', + isRequired: false, + attributes: [], + }, + isAvailable: { + name: 'isAvailable', + isArray: false, + type: 'Boolean', + isRequired: false, + attributes: [], + }, + availabilityDateTime: { + name: 'availabilityDateTime', + isArray: false, + type: 'AWSDateTime', + isRequired: false, + attributes: [], + }, + availabilityDate: { + name: 'availabilityDate', + isArray: false, + type: 'AWSDate', + isRequired: false, + attributes: [], + }, + availabliltyTime: { + name: 'availabliltyTime', + isArray: false, + type: 'AWSTime', + isRequired: false, + attributes: [], + }, + randomJSON: { + name: 'randomJSON', + isArray: false, + type: 'AWSJSON', + isRequired: false, + attributes: [], + }, + timestamp: { + name: 'timestamp', + isArray: false, + type: 'AWSTimestamp', + isRequired: false, + attributes: [], + }, + phone: { + name: 'phone', + isArray: false, + type: 'AWSPhone', + isRequired: false, + attributes: [], + }, + ipAddress: { + name: 'ipAddress', + isArray: false, + type: 'AWSIPAddress', + isRequired: false, + attributes: [], + }, + email: { + name: 'email', + isArray: false, + type: 'AWSEmail', + isRequired: false, + attributes: [], + }, + createdAt: { + name: 'createdAt', + isArray: false, + type: 'AWSDateTime', + isRequired: false, + attributes: [], + isReadOnly: true, + }, + updatedAt: { + name: 'updatedAt', + isArray: false, + type: 'AWSDateTime', + isRequired: false, + attributes: [], + isReadOnly: true, + }, + }, + syncable: true, + pluralName: 'Homes', + attributes: [ + { + type: 'model', + properties: {}, + }, + { + type: 'auth', + properties: { + rules: [ + { + allow: 'public', + operations: ['create', 'update', 'delete', 'read'], + }, + ], + }, + }, + ], + }, + }, + enums: {}, + nonModels: {}, + version: '0b8fa51057a3db3a3632f6951db2c1f2', +}; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useAuthSignoutAction.test.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useAuthSignoutAction.test.ts new file mode 100644 index 000000000..055ba52a9 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useAuthSignoutAction.test.ts @@ -0,0 +1,90 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { Auth, Hub } from 'aws-amplify'; +import { + ACTION_AUTH_SIGNOUT_FINISHED, + ACTION_AUTH_SIGNOUT_STARTED, + EVENT_ACTION_AUTH_SIGNOUT, + UI_CHANNEL, +} from '../constants'; +import { AMPLIFY_SYMBOL } from '../../amplify-symbol'; +import { useAuthSignOutAction } from '../useAuthSignoutAction'; + +jest.mock('aws-amplify'); + +const signOutSpy = jest.spyOn(Auth, 'signOut'); +const hubDispatchSpy = jest.spyOn(Hub, 'dispatch'); + +describe('useAuthSignOutAction', () => { + beforeEach(() => jest.clearAllMocks()); + + it('should call Auth.SignOut', async () => { + const authSignOutAction = useAuthSignOutAction(); + + await authSignOutAction(); + expect(signOutSpy).toHaveBeenCalledTimes(1); + }); + + it('should call Hub with started and finished events', async () => { + const authSignOutAction = useAuthSignOutAction(); + + await authSignOutAction(); + expect(hubDispatchSpy).toHaveBeenCalledTimes(2); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { options: undefined }, + event: ACTION_AUTH_SIGNOUT_STARTED, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL, + ); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { options: undefined }, + event: ACTION_AUTH_SIGNOUT_FINISHED, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL, + ); + }); + + it('should call Hub with started and finished events with options', async () => { + const authSignOutAction = useAuthSignOutAction({ global: true }); + + await authSignOutAction(); + expect(hubDispatchSpy).toHaveBeenCalledTimes(2); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { options: { global: true } }, + event: ACTION_AUTH_SIGNOUT_STARTED, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL, + ); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { options: { global: true } }, + event: ACTION_AUTH_SIGNOUT_FINISHED, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL, + ); + }); +}); diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStore.test.tsx b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStore.test.tsx new file mode 100644 index 000000000..8c00fd9cd --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStore.test.tsx @@ -0,0 +1,329 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { DataStore, PersistentModelConstructor, SortDirection, ProducerPaginationInput } from '@aws-amplify/datastore'; +import { renderHook } from '@testing-library/react-hooks'; +import { useDataStoreBinding, useDataStoreCollection, useDataStoreItem } from '../useDataStoreBinding'; +import { Todo } from './test-models/models'; +import { createDataStorePredicate } from '../createDataStorePredicate'; + +jest.mock('@aws-amplify/datastore'); + +type FakeModel = PersistentModelConstructor; +type Callback = (x: any) => void; + +const fakeModel = { + id: 'FakeModel', +} as unknown as FakeModel; + +const nextFakeModel = { + id: 'nextFakeModel', +} as unknown as FakeModel; + +const fakeItem = { + fakeField: 'fake-value', +} as unknown as FakeModel; + +const fakePagination = { + limit: 100, + sort: (s: { rating: (s: SortDirection) => void }) => s.rating(SortDirection.ASCENDING), +} as unknown as ProducerPaginationInput; + +const fakeId = 'fake-id'; + +describe('useDataStoreCollection', () => { + afterEach(() => jest.clearAllMocks()); + + it('should return default values while data is being fetched', () => { + (DataStore.observeQuery as jest.Mock).mockImplementation(() => ({ + subscribe: () => ({ + unsubscribe: jest.fn(), + }), + })); + + const { result } = renderHook(() => + useDataStoreCollection({ + model: fakeModel, + }), + ); + + expect(result.current.isLoading).toBe(true); + expect(result.current.items).toHaveLength(0); + expect(result.current.error).toBeUndefined(); + }); + + it('should set error if DataStore.observeQuery throws an error', async () => { + const fakeError = new Error('Unexpected DataStore error'); + const fakeDataStoreObserveQuery = jest.fn(() => ({ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + subscribe: (_: any, onError: Callback) => { + setTimeout(() => onError(fakeError), 500); + return { unsubscribe: () => {} }; + }, + })); + + (DataStore.observeQuery as jest.Mock).mockImplementation(fakeDataStoreObserveQuery); + + const { result, waitForNextUpdate } = renderHook(() => + useDataStoreCollection({ + model: fakeModel, + }), + ); + + // Trigger fetch + await waitForNextUpdate(); + + // Check if error is set and loading state is back to normal + expect(result.current.error).toBe(fakeError); + expect(result.current.isLoading).toBe(false); + + // Finally, check returned items + expect(result.current.items).toHaveLength(0); + }); + + it('should return items on success', async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const fakeItems = Array.from({ length: 100 }).map((_) => fakeItem); + + const namePredicateObject = { + field: 'name', + operator: 'eq', + operand: 'fake-value', + }; + const predicate = createDataStorePredicate(namePredicateObject); + + const fakeDataStoreObserveQuery = jest.fn(() => ({ + subscribe: (onSuccess: Callback) => { + setTimeout(() => onSuccess({ items: fakeItems }), 500); + return { unsubscribe: () => {} }; + }, + })); + + (DataStore.observeQuery as jest.Mock).mockImplementation(fakeDataStoreObserveQuery); + + const { result, waitForNextUpdate } = renderHook(() => + useDataStoreCollection({ + model: fakeModel, + criteria: predicate, + pagination: fakePagination, + }), + ); + + await waitForNextUpdate(); + + expect(result.current.isLoading).toBe(false); + expect(result.current.error).toBeUndefined(); + expect(result.current.items).toBe(fakeItems); + }); + + it('should unsubscribe on unmount', () => { + const unsubscribe = jest.fn(); + + const fakeDataStoreObserveQuery = jest.fn(() => ({ + subscribe: () => ({ unsubscribe }), + })); + + (DataStore.observeQuery as jest.Mock).mockImplementation(fakeDataStoreObserveQuery); + + const { unmount } = renderHook(() => + useDataStoreCollection({ + model: fakeModel, + }), + ); + + // Force component unmount + unmount(); + + expect(unsubscribe).toHaveBeenCalled(); + }); + + it('should only call DataStore.observeQuery once', () => { + const unsubscribe = jest.fn(); + const fakeDataStoreObserveQuery = jest.fn(() => ({ + subscribe: () => ({ unsubscribe }), + })); + + (DataStore.observeQuery as jest.Mock).mockImplementation(fakeDataStoreObserveQuery); + + const { rerender } = renderHook((params) => useDataStoreCollection(params), { + initialProps: { + model: fakeModel, + }, + }); + + expect(DataStore.observeQuery).toHaveBeenCalledTimes(1); + + rerender({ model: nextFakeModel }); + + expect(DataStore.observeQuery).toHaveBeenCalledTimes(1); + }); +}); + +describe('useDataStoreItem', () => { + afterEach(() => jest.clearAllMocks()); + + it('should return default values while data is being fetched', async () => { + (DataStore.query as jest.Mock).mockResolvedValue(undefined); + + const { result, waitForNextUpdate } = renderHook(() => + useDataStoreItem({ + model: fakeModel, + id: fakeId, + }), + ); + + expect(result.current.isLoading).toBe(true); + expect(result.current.item).toBeUndefined(); + expect(result.current.error).toBeUndefined(); + + await waitForNextUpdate(); + }); + + it('should set error if DataStore.query throws an error', async () => { + const fakeError = new Error('Unexpected DataStore error'); + const fakeDataStoreQuery = jest.fn(() => Promise.reject(fakeError)); + + (DataStore.query as jest.Mock).mockImplementation(fakeDataStoreQuery); + + const { result, waitForNextUpdate } = renderHook(() => + useDataStoreItem({ + model: fakeModel, + id: fakeId, + }), + ); + + // Trigger fetch + await waitForNextUpdate(); + + // Check if error is set and loading state is back to normal + expect(result.current.error).toBe(fakeError); + expect(result.current.isLoading).toBe(false); + + // Finally, check returned item + expect(result.current.item).toBeUndefined(); + }); + + it('should return item on success', async () => { + const fakeDataStoreQuery = jest.fn(() => Promise.resolve(fakeItem)); + (DataStore.query as jest.Mock).mockImplementation(fakeDataStoreQuery); + + const { result, waitForNextUpdate } = renderHook(() => + useDataStoreItem({ + model: fakeModel, + id: fakeId, + }), + ); + + // Check if DataStore.query was invoked with expected parameters + expect(fakeDataStoreQuery).toHaveBeenCalledWith(fakeModel, fakeId); + + // Trigger fetch + await waitForNextUpdate(); + + // Check if there's no errors and loading state is back to normal + expect(result.current.error).toBeUndefined(); + expect(result.current.isLoading).toBe(false); + + // Finally, check returned item + expect(result.current.item).toBe(fakeItem); + }); + + it('should only call DataStore.query once', async () => { + const fakeDataStoreQuery = jest.fn(() => Promise.resolve(fakeItem)); + (DataStore.query as jest.Mock).mockImplementation(fakeDataStoreQuery); + + const { rerender, waitForNextUpdate } = renderHook((props) => useDataStoreItem(props), { + initialProps: { + model: fakeModel, + id: fakeId, + }, + }); + + // await fetch + await waitForNextUpdate(); + + expect(DataStore.query).toHaveBeenCalledTimes(1); + + rerender({ model: nextFakeModel, id: fakeId }); + + expect(DataStore.query).toHaveBeenCalledTimes(1); + }); +}); + +describe('useDataStoreBinding', () => { + afterEach(() => jest.clearAllMocks()); + + it('handles calls with type record in the happy path', async () => { + const fakeDataStoreQuery = jest.fn(() => Promise.resolve(fakeItem)); + (DataStore.query as jest.Mock).mockImplementation(fakeDataStoreQuery); + + const { result, waitForNextUpdate } = renderHook(() => + useDataStoreBinding({ + model: fakeModel, + id: fakeId, + type: 'record', + }), + ); + + // Check if DataStore.query was invoked with expected parameters + expect(fakeDataStoreQuery).toHaveBeenCalledWith(fakeModel, fakeId); + + // Trigger fetch + await waitForNextUpdate(); + + // Check if there's no errors and loading state is back to normal + expect(result.current.error).toBeUndefined(); + expect(result.current.isLoading).toBe(false); + + // Finally, check returned item + expect(result.current.item).toBe(fakeItem); + }); + + it('handles calls with type collection in the happy path', async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const fakeItems = Array.from({ length: 100 }).map((_) => fakeItem); + + const namePredicateObject = { + field: 'name', + operator: 'eq', + operand: 'fake-value', + }; + const predicate = createDataStorePredicate(namePredicateObject); + + const fakeDataStoreObserveQuery = jest.fn(() => ({ + subscribe: (onSuccess: Callback) => { + setTimeout(() => onSuccess({ items: fakeItems }), 500); + return { unsubscribe: () => {} }; + }, + })); + + (DataStore.observeQuery as jest.Mock).mockImplementation(fakeDataStoreObserveQuery); + + const { result, waitForNextUpdate } = renderHook(() => + useDataStoreBinding({ + model: Todo, + criteria: predicate, + pagination: fakePagination, + type: 'collection', + }), + ); + + await waitForNextUpdate(); + + expect(result.current.isLoading).toBe(false); + expect(result.current.error).toBeUndefined(); + expect(result.current.items).toBe(fakeItems); + }); +}); diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreDeleteAction.test.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreDeleteAction.test.ts new file mode 100644 index 000000000..0fb4822be --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreDeleteAction.test.ts @@ -0,0 +1,105 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { DataStore, Hub } from 'aws-amplify'; + +import { + ACTION_DATASTORE_DELETE_FINISHED, + ACTION_DATASTORE_DELETE_STARTED, + EVENT_ACTION_DATASTORE_DELETE, + UI_CHANNEL, +} from '../constants'; +import { useDataStoreDeleteAction } from '../useDataStoreDeleteAction'; +import { Todo } from './test-models/models'; +import { AMPLIFY_SYMBOL } from '../../amplify-symbol'; + +jest.mock('aws-amplify'); + +const deleteSpy = jest.spyOn(DataStore, 'delete'); +const hubDispatchSpy = jest.spyOn(Hub, 'dispatch'); + +const id = '1234'; +const dataStoreDeleteArgs = { + model: Todo, + id, +}; + +describe('useDataStoreDeleteAction', () => { + beforeEach(() => jest.clearAllMocks()); + + it('should call DataStore.delete', async () => { + const action = useDataStoreDeleteAction(dataStoreDeleteArgs); + + await action(); + expect(deleteSpy).toHaveBeenCalledTimes(1); + }); + + it('should call Hub with started and finished events', async () => { + const action = useDataStoreDeleteAction(dataStoreDeleteArgs); + + await action(); + expect(hubDispatchSpy).toHaveBeenCalledTimes(2); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { id }, + event: ACTION_DATASTORE_DELETE_STARTED, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL, + ); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { id }, + event: ACTION_DATASTORE_DELETE_FINISHED, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL, + ); + }); + + it('should call Hub with error message if DataStore.delete rejects', async () => { + const errorMessage = 'Invalid data model'; + deleteSpy.mockImplementation(() => Promise.reject(new Error(errorMessage))); + + const action = useDataStoreDeleteAction(dataStoreDeleteArgs); + + await action(); + + expect(hubDispatchSpy).toHaveBeenCalledTimes(2); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { id }, + event: ACTION_DATASTORE_DELETE_STARTED, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL, + ); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { + id, + errorMessage, + }, + event: ACTION_DATASTORE_DELETE_FINISHED, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL, + ); + }); +}); diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreUpdateAction.test.tsx b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreUpdateAction.test.tsx new file mode 100644 index 000000000..ebd505077 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useDataStoreUpdateAction.test.tsx @@ -0,0 +1,153 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { DataStore, Hub } from 'aws-amplify'; +import { renderHook } from '@testing-library/react-hooks'; + +import { + ACTION_DATASTORE_UPDATE_FINISHED, + ACTION_DATASTORE_UPDATE_STARTED, + EVENT_ACTION_DATASTORE_UPDATE, + UI_CHANNEL, +} from '../constants'; +import { useDataStoreUpdateAction } from '../useDataStoreUpdateAction'; +import { AMPLIFY_SYMBOL } from '../../amplify-symbol'; +import { Todo } from './test-models/models'; + +jest.mock('aws-amplify'); + +const name = 'milk'; +const id = '1234'; +const updateActionArgs = { + model: Todo, + id, + fields: { name }, +}; + +const saveSpy = jest.spyOn(DataStore, 'save'); +const querySpy = jest.spyOn(DataStore, 'query').mockImplementation(() => Promise.resolve([{ id, name }])); +const hubDispatchSpy = jest.spyOn(Hub, 'dispatch'); + +describe('useAuthSignOutAction', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call DataStore.save', async () => { + const { + result: { current: action }, + } = renderHook(() => useDataStoreUpdateAction(updateActionArgs)); + + await action(); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(saveSpy).toHaveBeenCalledTimes(1); + }); + + it('should call Hub with started and finished events', async () => { + const { + result: { current: action }, + } = renderHook(() => useDataStoreUpdateAction(updateActionArgs)); + + await action(); + expect(hubDispatchSpy).toHaveBeenCalledTimes(2); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { id, fields: { name } }, + event: ACTION_DATASTORE_UPDATE_STARTED, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { id, fields: { name } }, + event: ACTION_DATASTORE_UPDATE_FINISHED, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + }); + + it('should call Hub with error message if DataStore.save rejects', async () => { + const errorMessage = 'Invalid data model'; + saveSpy.mockImplementation(() => Promise.reject(new Error(errorMessage))); + querySpy.mockImplementation(() => Promise.resolve([{ id, name }])); + const { + result: { current: action }, + } = renderHook(() => useDataStoreUpdateAction(updateActionArgs)); + + await action(); + + expect(hubDispatchSpy).toHaveBeenCalledTimes(2); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { id, fields: { name } }, + event: ACTION_DATASTORE_UPDATE_STARTED, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { + id, + fields: { name }, + errorMessage, + }, + event: ACTION_DATASTORE_UPDATE_FINISHED, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + }); + + it('when original not found, should call Hub with error message', async () => { + const { + result: { current: action }, + } = renderHook(() => useDataStoreUpdateAction(updateActionArgs)); + querySpy.mockImplementationOnce( + () => Promise.resolve(undefined) as unknown as ReturnType, + ); + + await action(); + expect(hubDispatchSpy).toHaveBeenCalledTimes(2); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { id, fields: { name } }, + event: ACTION_DATASTORE_UPDATE_STARTED, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + expect(hubDispatchSpy).toHaveBeenCalledWith( + UI_CHANNEL, + { + data: { + id, + fields: { name }, + errorMessage: `Error querying datastore item by id: ${id}`, + }, + event: ACTION_DATASTORE_UPDATE_FINISHED, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + }); +}); diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useTypeCastFields.test.tsx b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useTypeCastFields.test.tsx new file mode 100644 index 000000000..973575e6e --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/__tests__/useTypeCastFields.test.tsx @@ -0,0 +1,98 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { renderHook } from '@testing-library/react-hooks'; +import { useTypeCastFields } from '../useTypeCastFields'; +import { Home } from './test-models/models'; +import { schema } from './test-models/schema'; + +jest.mock('aws-amplify'); + +const stringFields = { + address: '1234 Main St', + image_url: 'https://images.unsplash.com/photo', + price: '1.99', + Rating: '5', + isAvailable: 'true', + availabilityDateTime: '2202-03-01T13:00:00.253Z', + availabilityDate: '2048-01-01', + availabliltyTime: '13:00:00.253', + randomJSON: '{"some":"key"}', + timestamp: '31556926', + phone: '444-555-6666', + ipAddress: '192.1.1.1', + email: 'janeDoe@gmail.com', + createdAt: '2202-03-01T13:00:00.253Z', + updatedAt: '2202-03-01T13:00:00.253Z', +}; + +const fields = { + ...stringFields, + price: 1.99, + Rating: 5, + isAvailable: true, + timestamp: 31556926, +}; + +describe('useTypeCastFields', () => { + describe('when using fields with schema', () => { + const { + result: { current: convertedFields }, + } = renderHook(() => + useTypeCastFields({ + fields: stringFields, + modelName: 'Home', + schema, + }), + ); + it('should convert string with Int types to Number', () => { + expect(convertedFields.Rating).toEqual(5); + }); + + it('should convert string with Float types to Number', () => { + expect(convertedFields.price).toEqual(1.99); + }); + + it('should convert string with AWSTimestamp types to Number', () => { + expect(convertedFields.timestamp).toEqual(31556926); + }); + + // Note this would only apply if users put a Boolean datastore + // type on Field that returns string, such an TextField (input) + it('should convert string with Boolean types to Boolean', () => { + expect(convertedFields.isAvailable).toEqual(true); + }); + + // this test is mean to catch any unexpected conversions + it('should convert everything together correctly', () => { + expect(convertedFields).toEqual(fields); + }); + }); + + describe('when using strongly typed fields (no schema)', () => { + const { + result: { current }, + } = renderHook(() => + useTypeCastFields({ + fields, + modelName: 'Home', + schema: undefined, + }), + ); + it('should just return fields directly', () => { + expect(current).toBe(fields); + }); + }); +}); diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/createDataStorePredicate.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/createDataStorePredicate.ts new file mode 100644 index 000000000..61b79a719 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/createDataStorePredicate.ts @@ -0,0 +1,112 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { + AllOperators, + PersistentModel, + RecursiveModelPredicateExtender, + RecursiveModelPredicate, + RecursiveModelPredicateAggregateExtender, +} from '@aws-amplify/datastore'; +import { DataStorePredicateObject } from './datastore-action-types'; + +function isModelKey(field: unknown): field is keyof Model { + return !!field; +} + +const isOperatorKey = (operator: unknown): operator is keyof AllOperators => !!operator; + +/** + * Given an array of predicates, compose them in sequential order + */ +const mergePredicates = ( + predicates: RecursiveModelPredicateExtender[], +): RecursiveModelPredicateAggregateExtender => { + return (model) => predicates.map((predicate) => predicate(model)); +}; + +/** + * Creates a DataStore compatible predicate function from an object representation + * @internal + */ +export const createDataStorePredicate = ( + predicateObject: DataStorePredicateObject, +): RecursiveModelPredicateExtender => { + const { and: groupAnd, or: groupOr, field, operator, operand } = predicateObject; + + if (Array.isArray(groupAnd)) { + const predicates = groupAnd.map((condition) => createDataStorePredicate(condition)); + + return (p: RecursiveModelPredicate) => p.and(mergePredicates(predicates)); + } + + if (Array.isArray(groupOr)) { + const predicates = groupOr.map((condition) => createDataStorePredicate(condition)); + + return (p: RecursiveModelPredicate) => p.or(mergePredicates(predicates)); + } + + return (p: RecursiveModelPredicate) => { + if (isModelKey(field) && isOperatorKey(operator) && p?.[field]?.[operator]) { + return (p[field][operator] as Function)(operand) as RecursiveModelPredicate; + } + + return p; + }; +}; + +export const createDataStorePredicateString = `export const createDataStorePredicate = ( + predicateObject: DataStorePredicateObject +): RecursiveModelPredicateExtender => { + const { + and: groupAnd, + or: groupOr, + field, + operator, + operand, + } = predicateObject; + + if (Array.isArray(groupAnd)) { + const predicates = groupAnd.map((condition) => + createDataStorePredicate(condition) + ); + + return (p: RecursiveModelPredicate) => + p.and((model) => predicates.map((predicate) => predicate(model))); + } + + if (Array.isArray(groupOr)) { + const predicates = groupOr.map((condition) => + createDataStorePredicate(condition) + ); + + return (p: RecursiveModelPredicate) => + p.or((model) => predicates.map((predicate) => predicate(model))); + } + + return (p: RecursiveModelPredicate) => { + if ( + !!field && + !!operator && + p?.[field]?.[operator] + ) { + return (p[field][operator] as Function)( + operand + ) as RecursiveModelPredicate; + } + + return p; + }; +};`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/datastore-action-types.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/datastore-action-types.ts new file mode 100644 index 000000000..86fcfa485 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/datastore-action-types.ts @@ -0,0 +1,94 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { + PersistentModel, + PersistentModelConstructor, + ModelInit, + PersistentModelMetaData, + Schema, + IdentifierFieldOrIdentifierObject, + ProducerPaginationInput, + RecursiveModelPredicateExtender, +} from '@aws-amplify/datastore'; + +/** + * Converts a Model's field values to types supported by + * Amplify UI field components. This is required + * because Datastore Int and Float scalar types are often + * entered by users in a TextField which returns a string + */ +type ModelFields = { + // Text Field, TextArea Field, Password Field, Phone Number Field, + // Radio Group Field, Select Field return "string" + // Checkbox Field, Switch Field, Toggle Button return "boolean" + // Slider Field, Stepper Field return "number" + [Property in keyof Type]: string | number | boolean; +}; + +export type DataStoreActionFields = + | ModelInit> + | ModelFields>>; + +export interface UseDataStoreActionOptions { + model: PersistentModelConstructor; + /** + * Pass either already converted field values based on DataStore schema, + * or also pass the `schema` param have string field values + * optimistically cast to the expected type based on the `schema` + */ + fields: DataStoreActionFields; + /** + * Used to optimistically cast fields values to the + * expected value types based on the `schema` provided + */ + schema?: Schema; +} + +export type DataStoreItemProps = { + model: PersistentModelConstructor; + id: IdentifierFieldOrIdentifierObject>; +}; + +export type DataStoreCollectionProps = { + model: PersistentModelConstructor; + criteria?: RecursiveModelPredicateExtender; + pagination?: ProducerPaginationInput; +}; + +type DataStoreBaseResult = { + error?: Error; + isLoading: boolean; +}; + +export type DataStoreItemResult = DataStoreBaseResult & { item?: Model }; + +export type DataStoreCollectionResult = DataStoreBaseResult & { items: Model[] }; + +export type DataStoreBindingProps = { + type: BindingType; +} & (BindingType extends 'record' + ? DataStoreItemProps + : BindingType extends 'collection' + ? DataStoreCollectionProps + : never); + +export type DataStorePredicateObject = { + and?: DataStorePredicateObject[]; + or?: DataStorePredicateObject[]; + field?: string; + operand?: string | boolean | number; + operator?: string; +}; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/index.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/index.ts new file mode 100644 index 000000000..3977001a6 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/index.ts @@ -0,0 +1,25 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +export * from './useNavigateAction'; +export * from './useStateMutationAction'; +export * from './useAuthSignoutAction'; +export * from './useTypeCastFields'; +export * from './useDataStoreCreateAction'; +export * from './useDataStoreUpdateAction'; +export * from './useDataStoreDeleteAction'; +export * from './createDataStorePredicate'; +export * from './useDataStoreBinding'; +export * from './constants'; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/useAuthSignoutAction.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useAuthSignoutAction.ts new file mode 100644 index 000000000..371d17ab2 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useAuthSignoutAction.ts @@ -0,0 +1,100 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { SignOutOpts } from '@aws-amplify/auth/lib-esm/types/Auth'; +import { Hub, Auth } from 'aws-amplify'; +import { AMPLIFY_SYMBOL } from '../amplify-symbol'; +import { getErrorMessage } from '../get-error-message'; +import { + UI_CHANNEL, + ACTION_AUTH_SIGNOUT_STARTED, + EVENT_ACTION_AUTH_SIGNOUT, + ACTION_AUTH_SIGNOUT_FINISHED, +} from './constants'; + +export interface UseAuthSignOutAction { + (options?: SignOutOpts): () => Promise; +} + +export const useAuthSignOutAction: UseAuthSignOutAction = (options) => async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_STARTED, + data: { options }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL, + ); + + await Auth.signOut(options); + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_FINISHED, + data: { options }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL, + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_FINISHED, + data: { options, errorMessage: getErrorMessage(error) }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL, + ); + } +}; + +export const useAuthSignOutActionString = `export const useAuthSignOutAction: UseAuthSignOutAction = + (options) => async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_STARTED, + data: { options }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL + ); + + await Auth.signOut(options); + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_FINISHED, + data: { options }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_AUTH_SIGNOUT_FINISHED, + data: { options, errorMessage: getErrorMessage(error) }, + }, + EVENT_ACTION_AUTH_SIGNOUT, + AMPLIFY_SYMBOL + ); + } + };`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreBinding.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreBinding.ts new file mode 100644 index 000000000..96eceac6d --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreBinding.ts @@ -0,0 +1,175 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import * as React from 'react'; + +import { DataStore, PersistentModel } from '@aws-amplify/datastore'; +import { + DataStoreBindingProps, + DataStoreCollectionProps, + DataStoreCollectionResult, + DataStoreItemProps, + DataStoreItemResult, +} from './datastore-action-types'; + +/** + * Perform a collection query against a DataStore model + * @internal + */ +export const useDataStoreCollection = ({ + model, + criteria, + pagination, +}: DataStoreCollectionProps): DataStoreCollectionResult => { + const [result, setResult] = React.useState>({ + items: [], + isLoading: false, + error: undefined, + }); + + // eslint-disable-next-line consistent-return + const fetch = () => { + setResult({ isLoading: true, items: [] }); + + const subscription = DataStore.observeQuery(model, criteria, pagination).subscribe( + (snapshot) => setResult({ items: snapshot.items, isLoading: false }), + (error: Error) => setResult({ items: [], error, isLoading: false }), + ); + + if (subscription) { + return () => subscription.unsubscribe(); + } + }; + + React.useEffect(fetch, []); + + return result; +}; + +/** + * Perform a single record query against a DataStore model + * @internal + */ +export const useDataStoreItem = ({ + model, + id, +}: DataStoreItemProps): DataStoreItemResult => { + const [item, setItem] = React.useState(); + const [isLoading, setLoading] = React.useState(false); + const [error, setError] = React.useState(); + + const fetch = () => { + setLoading(true); + + DataStore.query(model, id) + .then(setItem) + .catch(setError) + .finally(() => setLoading(false)); + }; + React.useEffect(fetch, []); + return { + error, + item, + isLoading, + }; +}; + +/** + * Perform a query against a DataStore model + * @internal + */ +export function useDataStoreBinding( + props: DataStoreBindingProps, +): DataStoreItemResult; +export function useDataStoreBinding( + props: DataStoreBindingProps, +): DataStoreCollectionResult; +export function useDataStoreBinding( + props: DataStoreBindingProps | DataStoreBindingProps, +): DataStoreItemResult | DataStoreCollectionResult { + return props.type === 'record' ? useDataStoreItem(props) : useDataStoreCollection(props); +} + +export const useDataStoreBindingString = `export const useDataStoreCollection = ({ + model, + criteria, + pagination, +}: DataStoreCollectionProps): DataStoreCollectionResult => { + const [result, setResult] = React.useState>({ + items: [], + isLoading: false, + error: undefined, + }); + + const fetch = () => { + setResult({ isLoading: true, items: [] }); + + const subscription = DataStore.observeQuery( + model, + criteria, + pagination + ).subscribe( + (snapshot) => setResult({ items: snapshot.items, isLoading: false }), + (error: Error) => setResult({ items: [], error, isLoading: false }) + ); + + if (subscription) { + return () => subscription.unsubscribe(); + } + }; + + React.useEffect(fetch, []); + return result; +}; + +export const useDataStoreItem = ({ + model, + id, +}: DataStoreItemProps): DataStoreItemResult => { + const [item, setItem] = React.useState(); + const [isLoading, setLoading] = React.useState(false); + const [error, setError] = React.useState(); + + const fetch = () => { + setLoading(true); + + DataStore.query(model, id) + .then(setItem) + .catch(setError) + .finally(() => setLoading(false)); + }; + + React.useEffect(fetch, []); + return { + error, + item, + isLoading, + }; +}; + +export function useDataStoreBinding( + props: DataStoreBindingProps +): DataStoreItemResult; +export function useDataStoreBinding( + props: DataStoreBindingProps +): DataStoreCollectionResult; +export function useDataStoreBinding( + props: + | DataStoreBindingProps + | DataStoreBindingProps +): DataStoreItemResult | DataStoreCollectionResult { + return props.type === 'record' + ? useDataStoreItem(props) : useDataStoreCollection(props); +};`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreCreateAction.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreCreateAction.ts new file mode 100644 index 000000000..d9923a322 --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreCreateAction.ts @@ -0,0 +1,137 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { PersistentModel } from '@aws-amplify/datastore'; +import { DataStore, Hub } from 'aws-amplify'; + +import { + ACTION_DATASTORE_CREATE_FINISHED, + ACTION_DATASTORE_CREATE_STARTED, + EVENT_ACTION_DATASTORE_CREATE, + UI_CHANNEL, +} from './constants'; +import { useTypeCastFields } from './useTypeCastFields'; +import { AMPLIFY_SYMBOL } from '../amplify-symbol'; +import { getErrorMessage } from '../get-error-message'; +import { UseDataStoreActionOptions } from './datastore-action-types'; + +export type UseDataStoreCreateActionOptions = UseDataStoreActionOptions; + +/** + * Action to Create DataStore item + * @internal + */ +export const useDataStoreCreateAction = ({ + model, + fields: initialFields, + schema, +}: UseDataStoreCreateActionOptions): (() => Promise) => { + const fields = useTypeCastFields({ + fields: initialFields, + modelName: model.name, + schema, + }); + + return async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_STARTED, + data: { fields }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL, + ); + + // eslint-disable-next-line new-cap + const item = await DataStore.save(new model(fields)); + + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_FINISHED, + data: { fields, item }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL, + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_FINISHED, + data: { + fields, + errorMessage: getErrorMessage(error), + }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL, + ); + } + }; +}; + +export const useDataStoreCreateActionString = `export const useDataStoreCreateAction = ({ + model, + fields: initialFields, + schema, +}: UseDataStoreCreateActionOptions): (() => Promise) => { + const fields = useTypeCastFields({ + fields: initialFields, + modelName: model.name, + schema, + }); + + return async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_STARTED, + data: { fields }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL + ); + + const item = await DataStore.save(new model(fields)); + + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_FINISHED, + data: { fields, item }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_CREATE_FINISHED, + data: { + fields, + errorMessage: getErrorMessage(error), + }, + }, + EVENT_ACTION_DATASTORE_CREATE, + AMPLIFY_SYMBOL + ); + } + }; +};`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreDeleteAction.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreDeleteAction.ts new file mode 100644 index 000000000..0a2c4d1ee --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreDeleteAction.ts @@ -0,0 +1,119 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { + PersistentModel, + PersistentModelConstructor, + PersistentModelMetaData, + IdentifierFieldOrIdentifierObject, +} from '@aws-amplify/datastore'; +import { DataStore, Hub } from 'aws-amplify'; + +import { + ACTION_DATASTORE_DELETE_FINISHED, + ACTION_DATASTORE_DELETE_STARTED, + EVENT_ACTION_DATASTORE_DELETE, + UI_CHANNEL, +} from './constants'; +import { AMPLIFY_SYMBOL } from '../amplify-symbol'; +import { getErrorMessage } from '../get-error-message'; + +export interface UseDataStoreDeleteActionOptions { + model: PersistentModelConstructor; + id: IdentifierFieldOrIdentifierObject>; +} + +/** + * Action to Delete DataStore item + * @internal + */ +export const useDataStoreDeleteAction = + ({ model, id }: UseDataStoreDeleteActionOptions): (() => Promise) => + async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_STARTED, + data: { id }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL, + ); + + await DataStore.delete(model, id); + + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_FINISHED, + data: { id }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL, + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_FINISHED, + data: { id, errorMessage: getErrorMessage(error) }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL, + ); + } + }; + +export const useDataStoreDeleteActionString = `export const useDataStoreDeleteAction = +({ + model, + id, +}: UseDataStoreDeleteActionOptions): (() => Promise) => +async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_STARTED, + data: { id }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL + ); + + await DataStore.delete(model, id); + + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_FINISHED, + data: { id }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_DELETE_FINISHED, + data: { id, errorMessage: getErrorMessage(error) }, + }, + EVENT_ACTION_DATASTORE_DELETE, + AMPLIFY_SYMBOL + ); + } +};`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreUpdateAction.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreUpdateAction.ts new file mode 100644 index 000000000..662e99cbb --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useDataStoreUpdateAction.ts @@ -0,0 +1,164 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { IdentifierFieldOrIdentifierObject, PersistentModel, PersistentModelMetaData } from '@aws-amplify/datastore'; +import { DataStore, Hub } from 'aws-amplify'; + +import { + ACTION_DATASTORE_UPDATE_FINISHED, + ACTION_DATASTORE_UPDATE_STARTED, + DATASTORE_QUERY_BY_ID_ERROR, + EVENT_ACTION_DATASTORE_UPDATE, + UI_CHANNEL, +} from './constants'; +import { UseDataStoreActionOptions } from './datastore-action-types'; +import { useTypeCastFields } from './useTypeCastFields'; +import { AMPLIFY_SYMBOL } from '../amplify-symbol'; +import { getErrorMessage } from '../get-error-message'; + +export interface UseDataStoreUpdateActionOptions + extends UseDataStoreActionOptions { + id: IdentifierFieldOrIdentifierObject>; +} + +/** + * Action to Update DataStore item + * @internal + */ +export const useDataStoreUpdateAction = ({ + fields: initialFields, + id, + model, + schema, +}: UseDataStoreUpdateActionOptions): (() => Promise) => { + const fields = useTypeCastFields({ + fields: initialFields, + modelName: model.name, + schema, + }); + + return async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_STARTED, + data: { fields, id }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + + const original = await DataStore.query(model, id); + + if (!original) { + throw new Error(`${DATASTORE_QUERY_BY_ID_ERROR}: ${id}`); + } + + const item = await DataStore.save( + model.copyOf(original, (updated: any) => { + Object.assign(updated, fields); + }), + ); + + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_FINISHED, + data: { fields, id, item }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_FINISHED, + data: { + fields, + id, + errorMessage: getErrorMessage(error), + }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL, + ); + } + }; +}; + +export const useDataStoreUpdateActionString = `export const useDataStoreUpdateAction = ({ + fields: initialFields, + id, + model, + schema, +}: UseDataStoreUpdateActionOptions): (() => Promise) => { + const fields = useTypeCastFields({ + fields: initialFields, + modelName: model.name, + schema, + }); + + return async () => { + try { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_STARTED, + data: { fields, id }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL + ); + + const original = await DataStore.query(model, id); + + if (!original) { + throw new Error(\`\${DATASTORE_QUERY_BY_ID_ERROR}: \${id}\`); + } + + const item = await DataStore.save( + model.copyOf(original, (updated: any) => { + Object.assign(updated, fields); + }) + ); + + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_FINISHED, + data: { fields, id, item }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL + ); + } catch (error) { + Hub.dispatch( + UI_CHANNEL, + { + event: ACTION_DATASTORE_UPDATE_FINISHED, + data: { + fields, + id, + errorMessage: getErrorMessage(error), + }, + }, + EVENT_ACTION_DATASTORE_UPDATE, + AMPLIFY_SYMBOL + ); + } + }; +};`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/hooks/useTypeCastFields.ts b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useTypeCastFields.ts new file mode 100644 index 000000000..3edc2a87b --- /dev/null +++ b/packages/codegen-ui-react/lib/utils-file-functions/hooks/useTypeCastFields.ts @@ -0,0 +1,111 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import * as React from 'react'; +import { ModelInit, PersistentModel, PersistentModelMetaData, Schema } from '@aws-amplify/datastore'; + +interface UseTypeCastFieldsProps { + fields: any; + modelName: string; + schema?: Schema; +} + +type UseTypeCastFieldsReturn = ModelInit>; + +/** + * Optimistically casts field string values to types required by + * datastore based on the schema type + * @see: See https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html + */ +export const useTypeCastFields = ({ + fields, + modelName, + schema, +}: UseTypeCastFieldsProps): UseTypeCastFieldsReturn => { + return React.useMemo(() => { + if (!schema) { + return fields; + } + + const castFields: any = {}; + Object.keys(fields).forEach((fieldName: string) => { + const field = fields[fieldName]; + switch (schema?.models[modelName]?.fields?.[fieldName]?.type) { + case 'AWSTimestamp': + castFields[fieldName] = Number(field); + break; + case 'Boolean': + castFields[fieldName] = Boolean(field); + break; + case 'Int': + castFields[fieldName] = + typeof field === 'string' || + (typeof field === 'object' && Object.prototype.toString.call(field) === '[object String]') + ? parseInt(field, 10) + : field; + break; + case 'Float': + castFields[fieldName] = Number(field); + break; + default: + castFields[fieldName] = field; + break; + } + }); + + return castFields; + }, [fields, schema, modelName]); +}; + +export const useTypeCastFieldsString = `export const useTypeCastFields = ({ + fields, + modelName, + schema, +}: UseTypeCastFieldsProps): UseTypeCastFieldsReturn => { + return React.useMemo(() => { + if (!schema) { + return fields; + } + + const castFields: any = {}; + Object.keys(fields).forEach((fieldName: string) => { + const field = fields[fieldName]; + switch (schema?.models[modelName]?.fields?.[fieldName]?.type) { + case 'AWSTimestamp': + castFields[fieldName] = Number(field); + break; + case 'Boolean': + castFields[fieldName] = Boolean(field); + break; + case 'Int': + castFields[fieldName] = ( + typeof field === 'string' || + (typeof field === 'object' && + Object.prototype.toString.call(field) === '[object String]') + ) ? parseInt(field) : field; + break; + case 'Float': + castFields[fieldName] = Number(field); + break; + default: + castFields[fieldName] = field; + break; + } + }); + + return castFields; + }, [fields, schema, modelName]); +};`; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/index.ts b/packages/codegen-ui-react/lib/utils-file-functions/index.ts index 33e6445d4..6e234e6e7 100644 --- a/packages/codegen-ui-react/lib/utils-file-functions/index.ts +++ b/packages/codegen-ui-react/lib/utils-file-functions/index.ts @@ -14,11 +14,10 @@ limitations under the License. */ export * from './overrides'; -export * from './hooks/constants'; export * from './amplify-symbol'; -export * from './hooks/useNavigateAction'; -export * from './hooks/useStateMutationAction'; +export * from './hooks'; export * from './validation'; export * from './json-path-fetch'; export * from './storage-manager'; export * from './string-formatter'; +export * from './get-error-message'; diff --git a/packages/codegen-ui-react/lib/utils-file-functions/json-path-fetch.ts b/packages/codegen-ui-react/lib/utils-file-functions/json-path-fetch.ts index f27a5bbb6..0b44f42f6 100644 --- a/packages/codegen-ui-react/lib/utils-file-functions/json-path-fetch.ts +++ b/packages/codegen-ui-react/lib/utils-file-functions/json-path-fetch.ts @@ -24,6 +24,7 @@ * @param accumlator array * @returns returns value at the end of object */ +/* istanbul ignore next */ export const fetchByPath = >(input: T, path: string, accumlator: any[] = []) => { const currentPath = path.split('.'); const head = currentPath.shift(); diff --git a/packages/codegen-ui-react/lib/utils-file-functions/storage-manager.ts b/packages/codegen-ui-react/lib/utils-file-functions/storage-manager.ts index b17b91b38..c5281e275 100644 --- a/packages/codegen-ui-react/lib/utils-file-functions/storage-manager.ts +++ b/packages/codegen-ui-react/lib/utils-file-functions/storage-manager.ts @@ -17,6 +17,7 @@ import { StorageManagerProps } from '@aws-amplify/ui-react-storage'; import { STORAGE_FILE_ALGO_TYPE } from '../utils/constants'; +/* istanbul ignore next */ export const processFile = async ({ file }: Parameters>[0]) => { const fileExtension = file.name.split('.').pop(); return file diff --git a/packages/codegen-ui-react/lib/utils-file-functions/validation.ts b/packages/codegen-ui-react/lib/utils-file-functions/validation.ts index 22dee6f64..5c28bb1d4 100644 --- a/packages/codegen-ui-react/lib/utils-file-functions/validation.ts +++ b/packages/codegen-ui-react/lib/utils-file-functions/validation.ts @@ -8,6 +8,7 @@ type FieldValidationConfiguration = { validationMessage?: string; }; +/* istanbul ignore next */ export const validateField = (value: any, validations: FieldValidationConfiguration[]): ValidationResponse => { for (const validation of validations) { if (value === undefined || value === '' || value === null) { @@ -32,11 +33,13 @@ export const validateField = (value: any, validations: FieldValidationConfigurat return { hasError: false }; }; +/* istanbul ignore next */ export const parseDateValidator = (dateValidator: string) => { const isTimestamp = `${parseInt(dateValidator)}`.length === dateValidator.length; return isTimestamp ? parseInt(dateValidator) : dateValidator; }; +/* istanbul ignore next */ const checkValidation = (value: any, validation: FieldValidationConfiguration) => { if (validation.numValues?.length) { switch (validation.type) { diff --git a/packages/codegen-ui-react/package-lock.json b/packages/codegen-ui-react/package-lock.json index 52e3a25e2..d6d12a4b8 100644 --- a/packages/codegen-ui-react/package-lock.json +++ b/packages/codegen-ui-react/package-lock.json @@ -9,6 +9,7 @@ "version": "2.17.0", "license": "Apache-2.0", "dependencies": { + "@aws-amplify/codegen-ui": "2.17.0", "@typescript/vfs": "~1.3.5", "pluralize": "^8.0.0", "semver": "^7.5.4", @@ -16,17 +17,19 @@ }, "devDependencies": { "@aws-amplify/appsync-modelgen-plugin": "^2.3.3", + "@aws-amplify/auth": "^5.6.5", "@aws-amplify/datastore": "^4.0.0", "@aws-amplify/ui-react": "^4.6.0", "@aws-amplify/ui-react-storage": "^1.1.0", - "@testing-library/react-hooks": "^8.0.1", + "@testing-library/react-hooks": "^7.0.1", "@types/node": "^16.3.3", "@types/pluralize": "^0.0.29", "@types/react": "^17.0.4", "@types/semver": "^7.3.9", "aws-amplify": "^5.3.11", "pascalcase": "1.0.0", - "react": "^17.0.0" + "react": "^17.0.0", + "react-test-renderer": "^17.0.2" }, "optionalDependencies": { "prettier": "2.3.2" @@ -232,6 +235,15 @@ "tslib": "^1.8.0" } }, + "node_modules/@aws-amplify/codegen-ui": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.17.0.tgz", + "integrity": "sha512-7FCn1b5J26u+usGSBjf4Jzg6Fe2NqOsoi/AtYxI+rZD2VuUNGzhbatURxdcOe0CS0NTDDPzY1pN2RYWMCtnnig==", + "dependencies": { + "change-case": "^4.1.2", + "yup": "^0.32.11" + } + }, "node_modules/@aws-amplify/core": { "version": "5.8.5", "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-5.8.5.tgz", @@ -6349,9 +6361,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -6536,9 +6548,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", - "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", + "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", "dev": true, "peer": true, "dependencies": { @@ -7395,15 +7407,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz", - "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", + "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.9", + "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -8120,17 +8132,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz", - "integrity": "sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", + "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", "dev": true, "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.5", - "babel-plugin-polyfill-corejs3": "^0.8.3", - "babel-plugin-polyfill-regenerator": "^0.5.2", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", "semver": "^6.3.1" }, "engines": { @@ -8315,13 +8327,13 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz", - "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", + "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", "dev": true, "peer": true, "dependencies": { - "@babel/compat-data": "^7.22.20", + "@babel/compat-data": "^7.23.2", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", @@ -8347,15 +8359,15 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.15", + "@babel/plugin-transform-async-generator-functions": "^7.23.2", "@babel/plugin-transform-async-to-generator": "^7.22.5", "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.15", + "@babel/plugin-transform-block-scoping": "^7.23.0", "@babel/plugin-transform-class-properties": "^7.22.5", "@babel/plugin-transform-class-static-block": "^7.22.11", "@babel/plugin-transform-classes": "^7.22.15", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.15", + "@babel/plugin-transform-destructuring": "^7.23.0", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", "@babel/plugin-transform-dynamic-import": "^7.22.11", @@ -8367,9 +8379,9 @@ "@babel/plugin-transform-literals": "^7.22.5", "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.15", - "@babel/plugin-transform-modules-systemjs": "^7.22.11", + "@babel/plugin-transform-modules-amd": "^7.23.0", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-modules-systemjs": "^7.23.0", "@babel/plugin-transform-modules-umd": "^7.22.5", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.22.5", @@ -8378,7 +8390,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.22.15", "@babel/plugin-transform-object-super": "^7.22.5", "@babel/plugin-transform-optional-catch-binding": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.22.15", + "@babel/plugin-transform-optional-chaining": "^7.23.0", "@babel/plugin-transform-parameters": "^7.22.15", "@babel/plugin-transform-private-methods": "^7.22.5", "@babel/plugin-transform-private-property-in-object": "^7.22.11", @@ -8395,10 +8407,10 @@ "@babel/plugin-transform-unicode-regex": "^7.22.5", "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", "@babel/preset-modules": "0.1.6-no-external-plugins", - "@babel/types": "^7.22.19", - "babel-plugin-polyfill-corejs2": "^0.4.5", - "babel-plugin-polyfill-corejs3": "^0.8.3", - "babel-plugin-polyfill-regenerator": "^0.5.2", + "@babel/types": "^7.23.0", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -8453,9 +8465,9 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.0.tgz", - "integrity": "sha512-6P6VVa/NM/VlAYj5s2Aq/gdVg8FSENCg3wlZ6Qau9AcPaoF5LbN1nyGlR9DTRIw9PpxI94e+ReydsJHcjwAweg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", + "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", "dev": true, "peer": true, "dependencies": { @@ -8503,7 +8515,6 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -10143,27 +10154,26 @@ "dev": true }, "node_modules/@testing-library/react-hooks": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz", - "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz", + "integrity": "sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", + "@types/react": ">=16.9.0", + "@types/react-dom": ">=16.9.0", + "@types/react-test-renderer": ">=16.9.0", "react-error-boundary": "^3.1.0" }, "engines": { "node": ">=12" }, "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0", - "react-test-renderer": "^16.9.0 || ^17.0.0" + "react": ">=16.9.0", + "react-dom": ">=16.9.0", + "react-test-renderer": ">=16.9.0" }, "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, "react-dom": { "optional": true }, @@ -10414,6 +10424,11 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/lodash": { + "version": "4.14.199", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", + "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==" + }, "node_modules/@types/mapbox__point-geometry": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz", @@ -10485,6 +10500,24 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.13.tgz", + "integrity": "sha512-eJIUv7rPP+EC45uNYp/ThhSpE16k22VJUknt5OLoH9tbXoi8bMhwLf5xRuWMywamNbWzhrSmU7IBJfPup1+3fw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-test-renderer": { + "version": "18.0.3", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.3.tgz", + "integrity": "sha512-4wcNLnY6nIT+L6g94CpzL4CXX2P18JvKPU9CDlaHr3DnbP3GiaQLhDotJqjWlVqOcE4UhLRjp0MtxqwuNKONnA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -10855,14 +10888,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", - "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", + "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", "dev": true, "peer": true, "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.2", + "@babel/helper-define-polyfill-provider": "^0.4.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -10880,13 +10913,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.4.tgz", - "integrity": "sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.5.tgz", + "integrity": "sha512-Q6CdATeAvbScWPNLB8lzSO7fgUVBkQt6zLgNlfyeCr/EQaEQR+bWiBYYPYAFyE528BMjRhL+1QBMOI4jc/c5TA==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.2", + "@babel/helper-define-polyfill-provider": "^0.4.3", "core-js-compat": "^3.32.2" }, "peerDependencies": { @@ -10894,13 +10927,13 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", - "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", + "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.2" + "@babel/helper-define-polyfill-provider": "^0.4.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -11236,7 +11269,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -11246,8 +11278,7 @@ "node_modules/capital-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/chalk": { "version": "3.0.0", @@ -11266,7 +11297,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, "dependencies": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -11304,7 +11334,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -11313,8 +11342,7 @@ "node_modules/change-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/ci-info": { "version": "3.9.0", @@ -11574,7 +11602,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -11584,8 +11611,7 @@ "node_modules/constant-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/convert-source-map": { "version": "1.9.0", @@ -11831,7 +11857,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -11840,8 +11865,7 @@ "node_modules/dot-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/earcut": { "version": "2.2.4", @@ -12718,7 +12742,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, "dependencies": { "capital-case": "^1.0.4", "tslib": "^2.0.3" @@ -12727,8 +12750,7 @@ "node_modules/header-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/hermes-estree": { "version": "0.12.0", @@ -13437,13 +13459,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true, - "peer": true - }, "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", @@ -13580,13 +13595,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-validate/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true, - "peer": true - }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -13820,8 +13828,12 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -13906,7 +13918,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -13929,8 +13940,7 @@ "node_modules/lower-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/lru-cache": { "version": "5.1.1", @@ -15026,6 +15036,11 @@ "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", "dev": true }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -15047,7 +15062,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -15056,8 +15070,7 @@ "node_modules/no-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/nocache": { "version": "3.0.4", @@ -15379,7 +15392,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -15388,8 +15400,7 @@ "node_modules/param-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/parse-filepath": { "version": "1.0.2", @@ -15433,7 +15444,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -15442,8 +15452,7 @@ "node_modules/pascal-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/pascalcase": { "version": "1.0.0", @@ -15458,7 +15467,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -15467,8 +15475,7 @@ "node_modules/path-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/path-exists": { "version": "4.0.0", @@ -15736,6 +15743,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "peer": true + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -15785,6 +15799,11 @@ "dev": true, "peer": true }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", @@ -15957,11 +15976,10 @@ } }, "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "peer": true + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true }, "node_modules/react-map-gl": { "version": "7.0.23", @@ -15977,9 +15995,9 @@ } }, "node_modules/react-native": { - "version": "0.72.5", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.5.tgz", - "integrity": "sha512-oIewslu5DBwOmo7x5rdzZlZXCqDIna0R4dUwVpfmVteORYLr4yaZo5wQnMeR+H7x54GaMhmgeqp0ZpULtulJFg==", + "version": "0.72.6", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.6.tgz", + "integrity": "sha512-RafPY2gM7mcrFySS8TL8x+TIO3q7oAlHpzEmC7Im6pmXni6n1AuufGaVh0Narbr1daxstw7yW7T9BKW5dpVc2A==", "dev": true, "peer": true, "dependencies": { @@ -16247,7 +16265,6 @@ "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", "dev": true, - "peer": true, "dependencies": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" @@ -16285,6 +16302,27 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/react-test-renderer": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", + "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "react-is": "^17.0.2", + "react-shallow-renderer": "^16.13.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-test-renderer/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -16363,8 +16401,7 @@ "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -16461,9 +16498,9 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", - "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "peer": true, "dependencies": { @@ -16558,7 +16595,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -16683,7 +16719,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -16693,8 +16728,7 @@ "node_modules/sentence-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/serialize-error": { "version": "2.1.0", @@ -16880,7 +16914,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -16889,8 +16922,7 @@ "node_modules/snake-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/source-map": { "version": "0.7.4", @@ -17332,9 +17364,9 @@ } }, "node_modules/terser": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz", - "integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", + "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", "dev": true, "peer": true, "dependencies": { @@ -17470,6 +17502,11 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -17747,7 +17784,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -17756,7 +17792,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -17764,14 +17799,12 @@ "node_modules/upper-case-first/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/upper-case/node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/url": { "version": "0.11.0", @@ -18165,9 +18198,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", - "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", "dev": true, "peer": true, "engines": { @@ -18222,6 +18255,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", @@ -18438,6 +18488,15 @@ "tslib": "^1.8.0" } }, + "@aws-amplify/codegen-ui": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.17.0.tgz", + "integrity": "sha512-7FCn1b5J26u+usGSBjf4Jzg6Fe2NqOsoi/AtYxI+rZD2VuUNGzhbatURxdcOe0CS0NTDDPzY1pN2RYWMCtnnig==", + "requires": { + "change-case": "^4.1.2", + "yup": "^0.32.11" + } + }, "@aws-amplify/core": { "version": "5.8.5", "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-5.8.5.tgz", @@ -23678,9 +23737,9 @@ } }, "@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", "dev": true }, "@babel/core": { @@ -23826,9 +23885,9 @@ } }, "@babel/helper-define-polyfill-provider": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", - "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", + "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", "dev": true, "peer": true, "requires": { @@ -24425,15 +24484,15 @@ } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz", - "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", + "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", "dev": true, "peer": true, "requires": { - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.9", + "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" } }, @@ -24892,17 +24951,17 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz", - "integrity": "sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", + "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", "dev": true, "peer": true, "requires": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.5", - "babel-plugin-polyfill-corejs3": "^0.8.3", - "babel-plugin-polyfill-regenerator": "^0.5.2", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", "semver": "^6.3.1" }, "dependencies": { @@ -25020,13 +25079,13 @@ } }, "@babel/preset-env": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz", - "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", + "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", "dev": true, "peer": true, "requires": { - "@babel/compat-data": "^7.22.20", + "@babel/compat-data": "^7.23.2", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", @@ -25052,15 +25111,15 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.15", + "@babel/plugin-transform-async-generator-functions": "^7.23.2", "@babel/plugin-transform-async-to-generator": "^7.22.5", "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.15", + "@babel/plugin-transform-block-scoping": "^7.23.0", "@babel/plugin-transform-class-properties": "^7.22.5", "@babel/plugin-transform-class-static-block": "^7.22.11", "@babel/plugin-transform-classes": "^7.22.15", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.15", + "@babel/plugin-transform-destructuring": "^7.23.0", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", "@babel/plugin-transform-dynamic-import": "^7.22.11", @@ -25072,9 +25131,9 @@ "@babel/plugin-transform-literals": "^7.22.5", "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.15", - "@babel/plugin-transform-modules-systemjs": "^7.22.11", + "@babel/plugin-transform-modules-amd": "^7.23.0", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-modules-systemjs": "^7.23.0", "@babel/plugin-transform-modules-umd": "^7.22.5", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.22.5", @@ -25083,7 +25142,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.22.15", "@babel/plugin-transform-object-super": "^7.22.5", "@babel/plugin-transform-optional-catch-binding": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.22.15", + "@babel/plugin-transform-optional-chaining": "^7.23.0", "@babel/plugin-transform-parameters": "^7.22.15", "@babel/plugin-transform-private-methods": "^7.22.5", "@babel/plugin-transform-private-property-in-object": "^7.22.11", @@ -25100,10 +25159,10 @@ "@babel/plugin-transform-unicode-regex": "^7.22.5", "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", "@babel/preset-modules": "0.1.6-no-external-plugins", - "@babel/types": "^7.22.19", - "babel-plugin-polyfill-corejs2": "^0.4.5", - "babel-plugin-polyfill-corejs3": "^0.8.3", - "babel-plugin-polyfill-regenerator": "^0.5.2", + "@babel/types": "^7.23.0", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -25142,9 +25201,9 @@ } }, "@babel/preset-typescript": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.0.tgz", - "integrity": "sha512-6P6VVa/NM/VlAYj5s2Aq/gdVg8FSENCg3wlZ6Qau9AcPaoF5LbN1nyGlR9DTRIw9PpxI94e+ReydsJHcjwAweg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", + "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", "dev": true, "peer": true, "requires": { @@ -25180,7 +25239,6 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.11" } @@ -26559,12 +26617,15 @@ } }, "@testing-library/react-hooks": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz", - "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz", + "integrity": "sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", + "@types/react": ">=16.9.0", + "@types/react-dom": ">=16.9.0", + "@types/react-test-renderer": ">=16.9.0", "react-error-boundary": "^3.1.0" } }, @@ -26765,6 +26826,11 @@ "@types/istanbul-lib-report": "*" } }, + "@types/lodash": { + "version": "4.14.199", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", + "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==" + }, "@types/mapbox__point-geometry": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz", @@ -26836,6 +26902,24 @@ "csstype": "^3.0.2" } }, + "@types/react-dom": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.13.tgz", + "integrity": "sha512-eJIUv7rPP+EC45uNYp/ThhSpE16k22VJUknt5OLoH9tbXoi8bMhwLf5xRuWMywamNbWzhrSmU7IBJfPup1+3fw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-test-renderer": { + "version": "18.0.3", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.3.tgz", + "integrity": "sha512-4wcNLnY6nIT+L6g94CpzL4CXX2P18JvKPU9CDlaHr3DnbP3GiaQLhDotJqjWlVqOcE4UhLRjp0MtxqwuNKONnA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -27143,14 +27227,14 @@ "requires": {} }, "babel-plugin-polyfill-corejs2": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", - "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", + "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", "dev": true, "peer": true, "requires": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.2", + "@babel/helper-define-polyfill-provider": "^0.4.3", "semver": "^6.3.1" }, "dependencies": { @@ -27164,24 +27248,24 @@ } }, "babel-plugin-polyfill-corejs3": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.4.tgz", - "integrity": "sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.5.tgz", + "integrity": "sha512-Q6CdATeAvbScWPNLB8lzSO7fgUVBkQt6zLgNlfyeCr/EQaEQR+bWiBYYPYAFyE528BMjRhL+1QBMOI4jc/c5TA==", "dev": true, "peer": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.2", + "@babel/helper-define-polyfill-provider": "^0.4.3", "core-js-compat": "^3.32.2" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", - "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", + "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", "dev": true, "peer": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.2" + "@babel/helper-define-polyfill-provider": "^0.4.3" } }, "babel-plugin-syntax-trailing-function-commas": { @@ -27424,7 +27508,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, "requires": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -27434,8 +27517,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -27453,7 +27535,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, "requires": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -27473,7 +27554,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, "requires": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -27482,8 +27562,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -27721,7 +27800,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, "requires": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -27731,8 +27809,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -27931,7 +28008,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, "requires": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -27940,8 +28016,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -28611,7 +28686,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, "requires": { "capital-case": "^1.0.4", "tslib": "^2.0.3" @@ -28620,8 +28694,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -29147,13 +29220,6 @@ "peer": true } } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true, - "peer": true } } }, @@ -29257,13 +29323,6 @@ "peer": true } } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true, - "peer": true } } }, @@ -29456,8 +29515,12 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, "lodash.debounce": { "version": "4.0.8", @@ -29526,7 +29589,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, "requires": { "tslib": "^2.0.3" }, @@ -29534,8 +29596,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -30445,6 +30506,11 @@ "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", "dev": true }, + "nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -30463,7 +30529,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, "requires": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -30472,8 +30537,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -30709,7 +30773,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, "requires": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -30718,8 +30781,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -30756,7 +30818,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, "requires": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -30765,8 +30826,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -30780,7 +30840,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, "requires": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -30789,8 +30848,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -30992,6 +31050,13 @@ "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "peer": true } } }, @@ -31043,6 +31108,11 @@ } } }, + "property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, "protocol-buffers-schema": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", @@ -31169,11 +31239,10 @@ "requires": {} }, "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "peer": true + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true }, "react-map-gl": { "version": "7.0.23", @@ -31185,9 +31254,9 @@ } }, "react-native": { - "version": "0.72.5", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.5.tgz", - "integrity": "sha512-oIewslu5DBwOmo7x5rdzZlZXCqDIna0R4dUwVpfmVteORYLr4yaZo5wQnMeR+H7x54GaMhmgeqp0ZpULtulJFg==", + "version": "0.72.6", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.6.tgz", + "integrity": "sha512-RafPY2gM7mcrFySS8TL8x+TIO3q7oAlHpzEmC7Im6pmXni6n1AuufGaVh0Narbr1daxstw7yW7T9BKW5dpVc2A==", "dev": true, "peer": true, "requires": { @@ -31395,7 +31464,6 @@ "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", "dev": true, - "peer": true, "requires": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" @@ -31420,6 +31488,26 @@ } } }, + "react-test-renderer": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", + "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "react-is": "^17.0.2", + "react-shallow-renderer": "^16.13.1", + "scheduler": "^0.20.2" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + } + } + }, "readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -31488,8 +31576,7 @@ "regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { "version": "0.15.2", @@ -31570,9 +31657,9 @@ "dev": true }, "resolve": { - "version": "1.22.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", - "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "peer": true, "requires": { @@ -31646,7 +31733,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "peer": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -31753,7 +31839,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dev": true, "requires": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -31763,8 +31848,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -31924,7 +32008,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, "requires": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -31933,8 +32016,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -32294,9 +32376,9 @@ } }, "terser": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz", - "integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", + "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", "dev": true, "peer": true, "requires": { @@ -32420,6 +32502,11 @@ "dev": true, "peer": true }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -32607,7 +32694,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, "requires": { "tslib": "^2.0.3" }, @@ -32615,8 +32701,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -32624,7 +32709,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, "requires": { "tslib": "^2.0.3" }, @@ -32632,8 +32716,7 @@ "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -32943,9 +33026,9 @@ "dev": true }, "yaml": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", - "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", "dev": true, "peer": true }, @@ -32985,6 +33068,20 @@ "dev": true, "peer": true }, + "yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + } + }, "zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", diff --git a/packages/codegen-ui-react/package.json b/packages/codegen-ui-react/package.json index fd8bfe075..171bff251 100644 --- a/packages/codegen-ui-react/package.json +++ b/packages/codegen-ui-react/package.json @@ -21,17 +21,19 @@ }, "devDependencies": { "@aws-amplify/appsync-modelgen-plugin": "^2.3.3", + "@aws-amplify/auth": "^5.6.5", "@aws-amplify/datastore": "^4.0.0", "@aws-amplify/ui-react": "^4.6.0", "@aws-amplify/ui-react-storage": "^1.1.0", - "@testing-library/react-hooks": "^8.0.1", + "@testing-library/react-hooks": "^7.0.1", "@types/node": "^16.3.3", "@types/pluralize": "^0.0.29", "@types/react": "^17.0.4", "@types/semver": "^7.3.9", "aws-amplify": "^5.3.11", "pascalcase": "1.0.0", - "react": "^17.0.0" + "react": "^17.0.0", + "react-test-renderer": "^17.0.2" }, "dependencies": { "@aws-amplify/codegen-ui": "2.17.0", @@ -88,4 +90,4 @@ "hermes-engine": ">=0.10.0", "shell-quote": ">=1.7.3" } -} \ No newline at end of file +} diff --git a/packages/test-generator/integration-test-templates/src/ActionBindingTests.tsx b/packages/test-generator/integration-test-templates/src/ActionBindingTests.tsx index 504c093bc..10f802cc3 100644 --- a/packages/test-generator/integration-test-templates/src/ActionBindingTests.tsx +++ b/packages/test-generator/integration-test-templates/src/ActionBindingTests.tsx @@ -17,7 +17,7 @@ import { useState, useEffect } from 'react'; import '@aws-amplify/ui-react/styles.css'; import { AmplifyProvider } from '@aws-amplify/ui-react'; import { DataStore } from '@aws-amplify/datastore'; -import { useDataStoreBinding } from '@aws-amplify/ui-react/internal'; +import { useDataStoreBinding } from './ui-components/utils'; // eslint-disable-line import/extensions import { User, Listing } from './models'; // eslint-disable-next-line import/extensions import { MutationActionBindings, DataStoreActionBindings, InitialValueBindings } from './ui-components'; @@ -42,10 +42,12 @@ export default function ActionBindingTests() { // Pulling this binding out here, since currently there's a bug in observeQuery, that doesn't let // us define a component which gets observable updates if a criteria is defined // https://github.com/aws-amplify/amplify-js/issues/9573 - const listing = useDataStoreBinding({ - type: 'collection', - model: Listing, - }).items[0]; + const listing = ( + useDataStoreBinding({ + type: 'collection', + model: Listing, + }) as any + ).items[0]; if (!isInitialized) { return null; diff --git a/packages/test-generator/integration-test-templates/src/ViewTests.tsx b/packages/test-generator/integration-test-templates/src/ViewTests.tsx index 117ef414d..c7677978b 100644 --- a/packages/test-generator/integration-test-templates/src/ViewTests.tsx +++ b/packages/test-generator/integration-test-templates/src/ViewTests.tsx @@ -15,8 +15,8 @@ */ import '@aws-amplify/ui-react/styles.css'; import { AmplifyProvider, View, Heading } from '@aws-amplify/ui-react'; -import { createDataStorePredicate } from '@aws-amplify/ui-react/internal'; import { useState, useRef, useEffect } from 'react'; +import { createDataStorePredicate } from './ui-components/utils'; // eslint-disable-line import/extensions import { ListingExpanderWithComponentSlot, MyTheme } from './ui-components'; // eslint-disable-line import/extensions import { initializeListingTestData } from './mock-utils'; diff --git a/packages/test-generator/integration-test-templates/src/WorkflowTests.tsx b/packages/test-generator/integration-test-templates/src/WorkflowTests.tsx index d189b1396..8a5bc4274 100644 --- a/packages/test-generator/integration-test-templates/src/WorkflowTests.tsx +++ b/packages/test-generator/integration-test-templates/src/WorkflowTests.tsx @@ -18,7 +18,7 @@ import '@aws-amplify/ui-react/styles.css'; import { AmplifyProvider, View, Heading, Divider, Button } from '@aws-amplify/ui-react'; import { Hub } from 'aws-amplify'; import { DataStore } from '@aws-amplify/datastore'; -import { useDataStoreBinding } from '@aws-amplify/ui-react/internal'; +import { useDataStoreBinding } from './ui-components/utils'; // eslint-disable-line import/extensions import { ComplexModel, User } from './models'; import { AuthSignOutActions, @@ -115,7 +115,7 @@ export default function ComplexTests() { const complexModels = useDataStoreBinding({ type: 'collection', model: ComplexModel, - }); + }) as any; if (!isInitialized) { return null;