diff --git a/src/apps/components/AppActivateDialog/AppActivateDialog.stories.tsx b/src/apps/components/AppActivateDialog/AppActivateDialog.stories.tsx index 27816d680..8e3991fca 100644 --- a/src/apps/components/AppActivateDialog/AppActivateDialog.stories.tsx +++ b/src/apps/components/AppActivateDialog/AppActivateDialog.stories.tsx @@ -15,4 +15,4 @@ const props: AppActivateDialogProps = { storiesOf("Views / Apps / Activate app", module) .addDecorator(Decorator) .add("default", () => ) - .add("unnamed app", () => ); + .add("unnamed app", () => ); diff --git a/src/apps/components/AppDeactivateDialog/AppDeactivateDialog.stories.tsx b/src/apps/components/AppDeactivateDialog/AppDeactivateDialog.stories.tsx index 456e1db3c..ab30b1bde 100644 --- a/src/apps/components/AppDeactivateDialog/AppDeactivateDialog.stories.tsx +++ b/src/apps/components/AppDeactivateDialog/AppDeactivateDialog.stories.tsx @@ -17,4 +17,4 @@ const props: AppDeactivateDialogProps = { storiesOf("Views / Apps / Deactivate app", module) .addDecorator(Decorator) .add("default", () => ) - .add("unnamed app", () => ); + .add("unnamed app", () => ); diff --git a/src/apps/components/AppDeleteDialog/AppDeleteDialog.stories.tsx b/src/apps/components/AppDeleteDialog/AppDeleteDialog.stories.tsx index cf9e00eba..3f62edb13 100644 --- a/src/apps/components/AppDeleteDialog/AppDeleteDialog.stories.tsx +++ b/src/apps/components/AppDeleteDialog/AppDeleteDialog.stories.tsx @@ -16,4 +16,4 @@ const props: AppDeleteDialogProps = { storiesOf("Views / Apps / Delete app", module) .addDecorator(Decorator) .add("default", () => ) - .add("unnamed app", () => ); + .add("unnamed app", () => ); diff --git a/src/apps/components/AppDetailsPage/AppDetailsPage.tsx b/src/apps/components/AppDetailsPage/AppDetailsPage.tsx index a433b10cb..0eeb4d0ff 100644 --- a/src/apps/components/AppDetailsPage/AppDetailsPage.tsx +++ b/src/apps/components/AppDetailsPage/AppDetailsPage.tsx @@ -63,7 +63,7 @@ export const AppDetailsPage: React.FC = ({
@@ -109,7 +109,11 @@ export const AppDetailsPage: React.FC = ({ })} /> - {!loading ? : } + {!loading ? ( + + ) : ( + + )} @@ -146,7 +150,7 @@ export const AppDetailsPage: React.FC = ({ - {(loading || data?.dataPrivacyUrl) && ( + {data?.dataPrivacyUrl && ( = ({ {!loading ? ( = ({ refetch, }) => { const shop = useShop(); - const frameRef = React.useRef(); + const frameRef = React.useRef(null); const { themeType } = useTheme(); const classes = useStyles(); const appOrigin = getOrigin(src); diff --git a/src/apps/components/AppFrame/useAppActions.ts b/src/apps/components/AppFrame/useAppActions.ts index 20c9c0b22..4a83c92ad 100644 --- a/src/apps/components/AppFrame/useAppActions.ts +++ b/src/apps/components/AppFrame/useAppActions.ts @@ -34,7 +34,7 @@ const isAppDeepUrlChange = (appId: string, from: string, to: string) => { }; export const useAppActions = ( - frameEl: React.MutableRefObject, + frameEl: React.MutableRefObject, appOrigin: string, appId: string, ) => { @@ -125,7 +125,7 @@ export const useAppActions = ( }; const postToExtension = (event: Events) => { - if (frameEl.current) { + if (frameEl?.current?.contentWindow) { frameEl.current.contentWindow.postMessage(event, appOrigin); } }; diff --git a/src/apps/components/AppFrame/useTokenRefresh.ts b/src/apps/components/AppFrame/useTokenRefresh.ts index e07e5801c..456330828 100644 --- a/src/apps/components/AppFrame/useTokenRefresh.ts +++ b/src/apps/components/AppFrame/useTokenRefresh.ts @@ -9,7 +9,10 @@ interface AppToken { const TIME_BEFORE_REFRESH = 30 * 1000; // 30 seconds const useTokenRefresh = (token?: string, refetch?: () => void) => { - let decoded: AppToken; + let decoded: AppToken = { + exp: 0, + iat: 0, + }; // For some reason jwt_decode causes seemingly unrelated error in tests // It seems like at some point undefined token is passed @@ -26,11 +29,13 @@ const useTokenRefresh = (token?: string, refetch?: () => void) => { const refreshTimeout = useRef>(null); - const tokenLife = (decoded?.exp - decoded?.iat) * 1000; // in ms + const tokenLife = ((decoded?.exp || 0) - (decoded?.iat || 0)) * 1000; // in ms const refreshTime = tokenLife - TIME_BEFORE_REFRESH; const setUpTimeout = () => { - refetch(); + if (refetch) { + refetch(); + } createTimeout(); }; @@ -50,7 +55,11 @@ const useTokenRefresh = (token?: string, refetch?: () => void) => { createTimeout(); } - return () => !!refetch && decodedSuccesfully && deleteTimeout(); + return () => { + if (!!refetch && decodedSuccesfully) { + deleteTimeout(); + } + }; }, [token]); }; diff --git a/src/apps/components/AppInProgressDeleteDialog/AppInProgressDeleteDialog.stories.tsx b/src/apps/components/AppInProgressDeleteDialog/AppInProgressDeleteDialog.stories.tsx index c72c53cd1..8a274cddd 100644 --- a/src/apps/components/AppInProgressDeleteDialog/AppInProgressDeleteDialog.stories.tsx +++ b/src/apps/components/AppInProgressDeleteDialog/AppInProgressDeleteDialog.stories.tsx @@ -17,6 +17,4 @@ const props: AppInProgressDeleteDialogProps = { storiesOf("Views / Apps / Delete app failed installation", module) .addDecorator(Decorator) .add("default", () => ) - .add("unnamed app", () => ( - - )); + .add("unnamed app", () => ); diff --git a/src/apps/components/AppInstallPage/AppInstallPage.stories.tsx b/src/apps/components/AppInstallPage/AppInstallPage.stories.tsx index 532c93f70..26d248955 100644 --- a/src/apps/components/AppInstallPage/AppInstallPage.stories.tsx +++ b/src/apps/components/AppInstallPage/AppInstallPage.stories.tsx @@ -9,7 +9,7 @@ const props: AppInstallPageProps = { data: installApp, loading: false, navigateToAppsList: () => undefined, - onSubmit: () => undefined, + onSubmit: () => Promise.resolve([]), }; storiesOf("Views / Apps / Install App", module) diff --git a/src/apps/components/AppInstallPage/AppInstallPage.tsx b/src/apps/components/AppInstallPage/AppInstallPage.tsx index 4f8946c61..8ad28b827 100644 --- a/src/apps/components/AppInstallPage/AppInstallPage.tsx +++ b/src/apps/components/AppInstallPage/AppInstallPage.tsx @@ -17,10 +17,12 @@ import { FormattedMessage, useIntl } from "react-intl"; import { useStyles } from "../../styles"; export interface AppInstallPageProps { - data: AppFetchMutation["appFetchManifest"]["manifest"]; + data: NonNullable["manifest"]; loading: boolean; navigateToAppsList: () => void; - onSubmit: () => SubmitPromise; + onSubmit: () => SubmitPromise< + NonNullable["errors"] + >; } export const AppInstallPage: React.FC = ({ diff --git a/src/apps/components/AppPage/AppPage.stories.tsx b/src/apps/components/AppPage/AppPage.stories.tsx index 4ef7c2cde..61a8f7995 100644 --- a/src/apps/components/AppPage/AppPage.stories.tsx +++ b/src/apps/components/AppPage/AppPage.stories.tsx @@ -7,7 +7,7 @@ import AppPage, { AppPageProps } from "./AppPage"; const props: AppPageProps = { data: appDetails, - url: appDetails.appUrl, + url: appDetails.appUrl!, aboutHref: "", onError: () => undefined, }; @@ -16,5 +16,5 @@ storiesOf("Views / Apps / App", module) .addDecorator(Decorator) .add("default", () => ) .add("settings", () => ( - + )); diff --git a/src/apps/components/AppPage/AppPage.tsx b/src/apps/components/AppPage/AppPage.tsx index 4559585b5..16164fd54 100644 --- a/src/apps/components/AppPage/AppPage.tsx +++ b/src/apps/components/AppPage/AppPage.tsx @@ -80,9 +80,9 @@ export const AppPage: React.FC = ({ {url && ( )} diff --git a/src/apps/components/AppsInProgress/AppsInProgress.tsx b/src/apps/components/AppsInProgress/AppsInProgress.tsx index 442478450..25feac9a3 100644 --- a/src/apps/components/AppsInProgress/AppsInProgress.tsx +++ b/src/apps/components/AppsInProgress/AppsInProgress.tsx @@ -51,65 +51,79 @@ const AppsInProgress: React.FC = ({ /> - {renderCollection(appsList, ({ status, appName, id, message }) => ( - - - {appName} - - {status === JobStatusEnum.PENDING && ( - - - - -
- -
+ {renderCollection( + appsList, + ({ + status, + appName, + id, + message, + }: AppsInstallationsQuery["appsInstallations"][number]) => ( + + + {appName} - )} - {status === JobStatusEnum.FAILED && ( - - - - - - - - - - - - - - onRemove(id)} - > - - - - - )} - - ))} + +
+ +
+
+ )} + {status === JobStatusEnum.FAILED && ( + + + + + + + + + + + + + + onRemove(id)} + > + + + + + )} +
+ ), + )}
diff --git a/src/apps/components/AppsListPage/AppListPage.stories.tsx b/src/apps/components/AppsListPage/AppListPage.stories.tsx index af5633bf1..160a3f5ad 100644 --- a/src/apps/components/AppsListPage/AppListPage.stories.tsx +++ b/src/apps/components/AppsListPage/AppListPage.stories.tsx @@ -47,7 +47,7 @@ storiesOf("Views / Apps / Apps list", module) {...props} appsInProgressList={undefined} disabled={true} - installedAppsList={undefined} + installedAppsList={[]} /> )) .add("no data", () => ( diff --git a/src/apps/components/AppsListPage/AppsListPage.tsx b/src/apps/components/AppsListPage/AppsListPage.tsx index d92fd0c8c..35808b392 100644 --- a/src/apps/components/AppsListPage/AppsListPage.tsx +++ b/src/apps/components/AppsListPage/AppsListPage.tsx @@ -78,15 +78,19 @@ const AppsListPage: React.FC = ({ [installedAppsList, fetchedSaleorApps], ); - const saleorApps = useMemo( + const saleorApps = useMemo( () => - fetchedSaleorApps - ?.map(app => - installedAppsList?.find(installedApp => - installedApp.manifestUrl?.includes(app.hostname), - ), - ) - .filter(Boolean), + (fetchedSaleorApps || []).reduce((acc, app) => { + const foundedApp = installedAppsList?.find(installedApp => + installedApp.manifestUrl?.includes(app.hostname), + ); + + if (foundedApp) { + acc.push(foundedApp); + } + + return acc; + }, []), [installedAppsList, fetchedSaleorApps], ); diff --git a/src/apps/components/ExternalAppContext/context.ts b/src/apps/components/ExternalAppContext/context.ts index c540b8933..c847dcb3c 100644 --- a/src/apps/components/ExternalAppContext/context.ts +++ b/src/apps/components/ExternalAppContext/context.ts @@ -16,4 +16,9 @@ export const ExternalAppContext = React.createContext<{ appData: AppData | undefined; setOpen: React.Dispatch>; setAppData: React.Dispatch>; -}>(undefined); +}>({ + open: false, + appData: undefined, + setOpen: () => null, + setAppData: () => null, +}); diff --git a/src/apps/components/HorizontalSpacer/HorizontalSpacer.tsx b/src/apps/components/HorizontalSpacer/HorizontalSpacer.tsx index 162c239d4..5cbdccb10 100644 --- a/src/apps/components/HorizontalSpacer/HorizontalSpacer.tsx +++ b/src/apps/components/HorizontalSpacer/HorizontalSpacer.tsx @@ -7,7 +7,7 @@ export interface HorizontalSpacerProps { const useStyles = makeStyles( theme => ({ - container: ({ spacing }: HorizontalSpacerProps) => ({ + container: ({ spacing }: Required) => ({ width: theme.spacing(spacing), }), }), diff --git a/src/apps/components/InstalledApps/InstalledApps.tsx b/src/apps/components/InstalledApps/InstalledApps.tsx index 6130a8e84..e22a3b436 100644 --- a/src/apps/components/InstalledApps/InstalledApps.tsx +++ b/src/apps/components/InstalledApps/InstalledApps.tsx @@ -104,11 +104,11 @@ const InstalledApps: React.FC = ({ )} - + ({ - container: ({ spacing }: VerticalSpacerProps) => ({ + container: ({ spacing }: Required) => ({ height: theme.spacing(spacing), }), }), diff --git a/src/apps/fixtures.ts b/src/apps/fixtures.ts index 19790a97c..02f40b359 100644 --- a/src/apps/fixtures.ts +++ b/src/apps/fixtures.ts @@ -99,7 +99,7 @@ export const appsInProgress: AppsInstallationsQuery["appsInstallations"] = [ }, ]; -export const appDetails: AppQuery["app"] = { +export const appDetails: NonNullable = { __typename: "App", aboutApp: "Lorem ipsum", accessToken: "token", @@ -134,7 +134,9 @@ export const appDetails: AppQuery["app"] = { webhooks: [], }; -export const installApp: AppFetchMutation["appFetchManifest"]["manifest"] = { +export const installApp: NonNullable< + AppFetchMutation["appFetchManifest"] +>["manifest"] = { __typename: "Manifest", about: "Lorem ipsum", appUrl: null, diff --git a/src/apps/useExtensions.ts b/src/apps/useExtensions.ts index 82f2ac762..b8f0b4a1f 100644 --- a/src/apps/useExtensions.ts +++ b/src/apps/useExtensions.ts @@ -14,7 +14,7 @@ import { AppDetailsUrlMountQueryParams } from "./urls"; export interface Extension { id: string; - app: RelayToFlat[0]["app"]; + app: RelayToFlat>[0]["app"]; accessToken: string; permissions: PermissionEnum[]; label: string; @@ -54,14 +54,14 @@ export const extensionMountPoints = { }; const filterAndMapToTarget = ( - extensions: RelayToFlat, + extensions: RelayToFlat>, openApp: (appData: AppData) => void, ): ExtensionWithParams[] => extensions.map( ({ id, accessToken, permissions, url, label, mount, target, app }) => ({ id, app, - accessToken, + accessToken: accessToken || "", permissions: permissions.map(({ code }) => code), url, label, @@ -69,7 +69,7 @@ const filterAndMapToTarget = ( open: (params: AppDetailsUrlMountQueryParams) => openApp({ id: app.id, - appToken: accessToken, + appToken: accessToken || "", src: url, label, target, @@ -153,7 +153,7 @@ export const useExtensions = ( }); const extensions = filterAndMapToTarget( - mapEdgesToItems(data?.appExtensions) || [], + mapEdgesToItems(data?.appExtensions ?? undefined) || [], openApp, ); diff --git a/src/apps/views/App/App.tsx b/src/apps/views/App/App.tsx index f7e59d66c..4f649d25f 100644 --- a/src/apps/views/App/App.tsx +++ b/src/apps/views/App/App.tsx @@ -37,14 +37,14 @@ export const App: React.FC = ({ id }) => { const appCompleteUrl = getAppCompleteUrlFromDashboardUrl( location.pathname, - data?.app.appUrl, + data?.app?.appUrl || "", id, ); return ( diff --git a/src/apps/views/AppDetails/AppDetails.tsx b/src/apps/views/AppDetails/AppDetails.tsx index ab8834c47..e51067b78 100644 --- a/src/apps/views/AppDetails/AppDetails.tsx +++ b/src/apps/views/AppDetails/AppDetails.tsx @@ -51,7 +51,7 @@ export const AppDetails: React.FC = ({ id, params }) => { refetch(); closeModal(); } else { - if (appExists) { + if (appExists && errors) { errors.forEach(error => notify({ status: "error", @@ -65,7 +65,7 @@ export const AppDetails: React.FC = ({ id, params }) => { const [deactivateApp, deactivateAppResult] = useAppDeactivateMutation({ onCompleted: data => { const errors = data?.appDeactivate?.errors; - if (errors.length === 0) { + if (errors?.length === 0) { notify({ status: "success", text: intl.formatMessage(appMessages.appDeactivated), @@ -73,7 +73,7 @@ export const AppDetails: React.FC = ({ id, params }) => { refetch(); closeModal(); } else { - if (appExists) { + if (appExists && errors) { errors.forEach(error => notify({ status: "error", @@ -105,20 +105,20 @@ export const AppDetails: React.FC = ({ id, params }) => { <> navigate(appUrl(id))} onAppActivateOpen={() => openModal("app-activate")} diff --git a/src/apps/views/AppInstall/AppInstall.tsx b/src/apps/views/AppInstall/AppInstall.tsx index d7ba39126..9845c4cfa 100644 --- a/src/apps/views/AppInstall/AppInstall.tsx +++ b/src/apps/views/AppInstall/AppInstall.tsx @@ -24,7 +24,9 @@ interface InstallAppCreateProps extends RouteComponentProps { export const InstallAppCreate: React.FC = ({ params, }) => { - const [, setActiveInstallations] = useLocalStorage("activeInstallations", []); + const [, setActiveInstallations] = useLocalStorage< + Array> + >("activeInstallations", []); const navigate = useNavigator(); const notify = useNotifier(); const intl = useIntl(); @@ -32,7 +34,7 @@ export const InstallAppCreate: React.FC = ({ const [fetchManifest, fetchManifestOpts] = useAppFetchMutation({ onCompleted: data => { - if (data.appFetchManifest.errors.length) { + if (data?.appFetchManifest?.errors.length) { data.appFetchManifest.errors.forEach(error => { notify({ status: "error", @@ -44,15 +46,20 @@ export const InstallAppCreate: React.FC = ({ }); const [installApp] = useAppInstallMutation({ onCompleted: data => { - const installationData = data.appInstall.appInstallation; - if (data.appInstall.errors.length === 0) { - setActiveInstallations(activeInstallations => [ - ...activeInstallations, - { id: installationData.id, name: installationData.appName }, - ]); + const installationData = data?.appInstall?.appInstallation; + if (data.appInstall?.errors.length === 0) { + if (installationData) { + setActiveInstallations(activeInstallations => [ + ...activeInstallations, + { + id: installationData.id, + name: installationData.appName, + }, + ]); + } navigateToAppsList(); } else { - data.appInstall.errors.forEach(error => { + (data?.appInstall?.errors ?? []).forEach(error => { notify({ status: "error", text: getAppErrorMessage(error, intl), @@ -72,7 +79,7 @@ export const InstallAppCreate: React.FC = ({ input: { appName: manifest?.name, manifestUrl, - permissions: manifest?.permissions.map( + permissions: manifest?.permissions?.map( permission => permission.code, ), }, @@ -97,7 +104,7 @@ export const InstallAppCreate: React.FC = ({ navigate("/")} /> ) : ( = ({ id }) => { return ( diff --git a/src/apps/views/AppsList/AppsList.tsx b/src/apps/views/AppsList/AppsList.tsx index 313b56621..0beab77b6 100644 --- a/src/apps/views/AppsList/AppsList.tsx +++ b/src/apps/views/AppsList/AppsList.tsx @@ -22,6 +22,7 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState, + PageInfo, PaginatorContext, } from "@saleor/hooks/usePaginator"; import { findById } from "@saleor/misc"; @@ -45,7 +46,7 @@ import { messages } from "./messages"; const getAppInProgressName = ( id: string, collection?: AppsInstallationsQuery["appsInstallations"], -) => collection?.find(app => app.id === id)?.appName; +) => collection?.find(app => app.id === id)?.appName || id; interface AppsListProps { params: AppListUrlQueryParams; } @@ -92,7 +93,7 @@ export const AppsList: React.FC = ({ params }) => { }); const paginationValues = usePaginator({ - pageInfo: data?.apps?.pageInfo, + pageInfo: data?.apps?.pageInfo as PageInfo, paginationState, queryString: params, }); @@ -113,11 +114,16 @@ export const AppsList: React.FC = ({ params }) => { const [retryInstallApp] = useAppRetryInstallMutation({ onCompleted: data => { if (!data?.appRetryInstall?.errors?.length) { - const appInstallation = data.appRetryInstall.appInstallation; - setActiveInstallations(installations => [ - ...installations, - { id: appInstallation.id, name: appInstallation.appName }, - ]); + const appInstallation = data.appRetryInstall?.appInstallation; + if (appInstallation) { + setActiveInstallations(installations => [ + ...installations, + { + id: appInstallation.id, + name: appInstallation.appName, + }, + ]); + } } }, }); @@ -226,14 +232,14 @@ export const AppsList: React.FC = ({ params }) => { const handleRemoveInProgressConfirm = () => deleteInProgressApp({ variables: { - id: params.id, + id: params?.id || "", }, }); const handleRemoveConfirm = () => deleteApp({ variables: { - id: params.id, + id: params?.id || "", }, }); @@ -245,10 +251,10 @@ export const AppsList: React.FC = ({ params }) => { }; const handleActivateAppConfirm = () => - activateApp({ variables: { id: params.id } }); + activateApp({ variables: { id: params?.id || "" } }); const handleDeactivateAppConfirm = () => - deactivateApp({ variables: { id: params.id } }); + deactivateApp({ variables: { id: params?.id || "" } }); const onAppInstallRetry = (id: string) => retryInstallApp({ variables: { id } }); @@ -261,8 +267,8 @@ export const AppsList: React.FC = ({ params }) => { [activateApp, deactivateApp], ); - const installedApps = mapEdgesToItems(data?.apps); - const currentAppName = findById(params.id, installedApps)?.name; + const installedApps = mapEdgesToItems(data?.apps || { edges: [] }) || []; + const currentAppName = findById(params?.id || "", installedApps)?.name || ""; return ( @@ -292,7 +298,7 @@ export const AppsList: React.FC = ({ params }) => { ( + const newPageInfo = useMemo( () => pageInfo ? { diff --git a/tsconfig.json b/tsconfig.json index edefe6f39..6bee63da1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,4 +25,4 @@ "resolveJsonModule": true }, "exclude": ["node_modules", "cypress"] -} \ No newline at end of file +}