Refactor apps UI (#3363)
* Bump macaw-ui * Add grouping util * Refactor App cards UI using CSS Grid * Rename AppListCard to AppListRow * Fix unit tests * Bump macaw-ui * Remove inline styling * Fix app installtion for second pair * Add keys to grid rows * Change grid template rows to repeat(4, auto) * Replace groupIntoPairs with chunk method from lodash * Fix borders on safari * Add paddings to section names
This commit is contained in:
parent
4eebf16b55
commit
51326e52c4
18 changed files with 254 additions and 154 deletions
14
package-lock.json
generated
14
package-lock.json
generated
|
@ -26,7 +26,7 @@
|
|||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@reach/auto-id": "^0.16.0",
|
||||
"@saleor/macaw-ui": "^0.8.0-pre.43",
|
||||
"@saleor/macaw-ui": "^0.8.0-pre.49",
|
||||
"@saleor/sdk": "^0.4.4",
|
||||
"@sentry/react": "^6.0.0",
|
||||
"@types/faker": "^5.1.6",
|
||||
|
@ -7271,9 +7271,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@saleor/macaw-ui": {
|
||||
"version": "0.8.0-pre.44",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.44.tgz",
|
||||
"integrity": "sha512-iSygNhSfMxJrmnbXKKmE/iKc7dMq/m996Hqfl5ZFcZ6KpurmI0940AjJcYDMnDXUl+Md6OJyRxv7PKKy8nU+Mg==",
|
||||
"version": "0.8.0-pre.49",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.49.tgz",
|
||||
"integrity": "sha512-SHUfBE1AaDsam/K3ZcotDtUH6/jcyRR9Gi/Zpvw/cTcwoosq09WcewMWZNdHgY5Fkbgve7HTKb+wCyah+sBO8w==",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom-interactions": "^0.5.0",
|
||||
"@radix-ui/react-radio-group": "^1.1.1",
|
||||
|
@ -41431,9 +41431,9 @@
|
|||
}
|
||||
},
|
||||
"@saleor/macaw-ui": {
|
||||
"version": "0.8.0-pre.44",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.44.tgz",
|
||||
"integrity": "sha512-iSygNhSfMxJrmnbXKKmE/iKc7dMq/m996Hqfl5ZFcZ6KpurmI0940AjJcYDMnDXUl+Md6OJyRxv7PKKy8nU+Mg==",
|
||||
"version": "0.8.0-pre.49",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.49.tgz",
|
||||
"integrity": "sha512-SHUfBE1AaDsam/K3ZcotDtUH6/jcyRR9Gi/Zpvw/cTcwoosq09WcewMWZNdHgY5Fkbgve7HTKb+wCyah+sBO8w==",
|
||||
"requires": {
|
||||
"@floating-ui/react-dom-interactions": "^0.5.0",
|
||||
"@radix-ui/react-radio-group": "^1.1.1",
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@reach/auto-id": "^0.16.0",
|
||||
"@saleor/macaw-ui": "^0.8.0-pre.43",
|
||||
"@saleor/macaw-ui": "^0.8.0-pre.49",
|
||||
"@saleor/sdk": "^0.4.4",
|
||||
"@sentry/react": "^6.0.0",
|
||||
"@types/faker": "^5.1.6",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { GetV2SaleorAppsResponse } from "@dashboard/apps/marketplace.types";
|
||||
import { resolveInstallationOfMarketplaceApp } from "@dashboard/apps/utils";
|
||||
import { AppInstallationFragment } from "@dashboard/graphql";
|
||||
import { Skeleton } from "@material-ui/lab";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import chunk from "lodash/chunk";
|
||||
import React from "react";
|
||||
|
||||
import AppListCard from "../AppListCard";
|
||||
import AppListRow from "../AppListRow";
|
||||
|
||||
interface AllAppListProps {
|
||||
appList?: GetV2SaleorAppsResponse.SaleorApp[];
|
||||
|
@ -20,28 +20,19 @@ const AllAppList: React.FC<AllAppListProps> = ({
|
|||
navigateToAppInstallPage,
|
||||
navigateToGithubForkPage,
|
||||
}) => {
|
||||
const appsPairs = React.useMemo(() => chunk(appList, 2), [appList]);
|
||||
|
||||
if (!appList) {
|
||||
return <Skeleton />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
display="grid"
|
||||
gridTemplateColumns={{
|
||||
mobile: 1,
|
||||
desktop: 2,
|
||||
}}
|
||||
gap={8}
|
||||
marginTop={8}
|
||||
>
|
||||
{appList.map(app => (
|
||||
<AppListCard
|
||||
key={app.name.en}
|
||||
app={app}
|
||||
appInstallation={resolveInstallationOfMarketplaceApp(
|
||||
app,
|
||||
appInstallationList,
|
||||
)}
|
||||
<Box display="flex" flexDirection="column" gap={8} marginTop={8}>
|
||||
{appsPairs.map(appPair => (
|
||||
<AppListRow
|
||||
key={appPair[0].name.en}
|
||||
appPair={appPair}
|
||||
appInstallationList={appInstallationList}
|
||||
navigateToAppInstallPage={navigateToAppInstallPage}
|
||||
navigateToGithubForkPage={navigateToGithubForkPage}
|
||||
/>
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
import { useAppListContext } from "@dashboard/apps/context";
|
||||
import { GetV2SaleorAppsResponse } from "@dashboard/apps/marketplace.types";
|
||||
import { getAppDetails } from "@dashboard/apps/utils";
|
||||
import { AppInstallationFragment } from "@dashboard/graphql";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import AppListCardActions from "./AppListCardActions";
|
||||
import AppListCardDescription from "./AppListCardDescription";
|
||||
import AppListCardIntegrations from "./AppListCardIntegrations";
|
||||
import AppListCardLinks from "./AppListCardLinks";
|
||||
|
||||
interface AppListCardProps {
|
||||
app: GetV2SaleorAppsResponse.SaleorApp;
|
||||
appInstallation?: AppInstallationFragment;
|
||||
navigateToAppInstallPage?: (manifestUrl: string) => void;
|
||||
navigateToGithubForkPage?: (githubForkUrl: string) => void;
|
||||
}
|
||||
|
||||
const AppListCard: React.FC<AppListCardProps> = ({
|
||||
app,
|
||||
appInstallation,
|
||||
navigateToAppInstallPage,
|
||||
navigateToGithubForkPage,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const { retryAppInstallation, removeAppInstallation } = useAppListContext();
|
||||
|
||||
const details = getAppDetails({
|
||||
intl,
|
||||
app,
|
||||
appInstallation,
|
||||
navigateToAppInstallPage,
|
||||
navigateToGithubForkPage,
|
||||
retryAppInstallation,
|
||||
removeAppInstallation,
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
justifyContent="space-between"
|
||||
borderStyle="solid"
|
||||
borderWidth={1}
|
||||
padding={8}
|
||||
borderRadius={3}
|
||||
borderColor="neutralPlain"
|
||||
>
|
||||
<Box>
|
||||
<AppListCardDescription app={app} />
|
||||
<AppListCardLinks links={details.links} />
|
||||
<AppListCardIntegrations app={app} />
|
||||
</Box>
|
||||
<Box>
|
||||
<AppListCardActions
|
||||
releaseDate={details.releaseDate}
|
||||
installationPending={details.installationPending}
|
||||
installHandler={details.installHandler}
|
||||
githubForkHandler={details.githubForkHandler}
|
||||
retryInstallHandler={details.retryInstallHandler}
|
||||
removeInstallHandler={details.removeInstallHandler}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
AppListCard.displayName = "AppListCard";
|
||||
export default AppListCard;
|
|
@ -1,3 +0,0 @@
|
|||
export * from "./AppListCard";
|
||||
export { default } from "./AppListCard";
|
||||
export * from "./AppLogo";
|
|
@ -3,7 +3,7 @@ import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
|||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { ListProps } from "@dashboard/types";
|
||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||
import { Box, sprinkles, Text } from "@saleor/macaw-ui/next";
|
||||
import React, { useCallback } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
@ -114,7 +114,12 @@ export const AppListPage: React.FC<AppListPageProps> = props => {
|
|||
<MarketplaceAlert error={marketplaceError} />
|
||||
{sectionsAvailability.all && !marketplaceError && (
|
||||
<Box marginTop={10}>
|
||||
<Text as="h3" variant="heading" color="textNeutralSubdued">
|
||||
<Text
|
||||
as="h3"
|
||||
variant="heading"
|
||||
color="textNeutralSubdued"
|
||||
className={sprinkles({ paddingX: 8 })}
|
||||
>
|
||||
<FormattedMessage {...messages.allApps} />
|
||||
</Text>
|
||||
<AllAppList
|
||||
|
@ -127,7 +132,12 @@ export const AppListPage: React.FC<AppListPageProps> = props => {
|
|||
)}
|
||||
{sectionsAvailability.comingSoon && !marketplaceError && (
|
||||
<Box marginTop={10}>
|
||||
<Text as="h3" variant="heading" color="textNeutralSubdued">
|
||||
<Text
|
||||
as="h3"
|
||||
variant="heading"
|
||||
color="textNeutralSubdued"
|
||||
className={sprinkles({ paddingX: 8 })}
|
||||
>
|
||||
{intl.formatMessage(messages.comingSoonApps)}
|
||||
</Text>
|
||||
<AllAppList
|
||||
|
|
|
@ -43,7 +43,18 @@ const AppListCardActions: React.FC<AppListCardActionsProps> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<Box display="flex" justifyContent="flex-end" gap={6}>
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="flex-end"
|
||||
gap={6}
|
||||
borderStyle="solid"
|
||||
borderWidth={1}
|
||||
borderBottomLeftRadius={3}
|
||||
borderBottomRightRadius={3}
|
||||
borderColor="neutralPlain"
|
||||
borderTopStyle="none"
|
||||
padding={8}
|
||||
>
|
||||
{githubForkHandler && (
|
||||
<Button
|
||||
variant="secondary"
|
|
@ -11,7 +11,15 @@ interface AppListCardDescriptionProps {
|
|||
const AppListCardDescription: React.FC<AppListCardDescriptionProps> = ({
|
||||
app,
|
||||
}) => (
|
||||
<Box marginBottom={6}>
|
||||
<Box
|
||||
borderStyle="solid"
|
||||
borderWidth={1}
|
||||
borderTopLeftRadius={3}
|
||||
borderTopRightRadius={3}
|
||||
borderColor="neutralPlain"
|
||||
borderBottomStyle="none"
|
||||
padding={8}
|
||||
>
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="row"
|
|
@ -4,21 +4,35 @@ import { Box, Text } from "@saleor/macaw-ui/next";
|
|||
import React from "react";
|
||||
|
||||
interface AppListCardIntegrationsProps {
|
||||
app: GetV2SaleorAppsResponse.SaleorApp;
|
||||
integrations: GetV2SaleorAppsResponse.SaleorApp["integrations"];
|
||||
}
|
||||
|
||||
const AppListCardIntegrations: React.FC<AppListCardIntegrationsProps> = ({
|
||||
app,
|
||||
integrations,
|
||||
}) => {
|
||||
const { themeType } = useTheme();
|
||||
|
||||
if (!app.integrations.length) {
|
||||
if (!integrations) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box as="ul" display="flex" flexDirection="row" flexWrap="wrap" gap={8}>
|
||||
{app.integrations.map(integration => (
|
||||
<Box
|
||||
as="ul"
|
||||
display="flex"
|
||||
flexDirection="row"
|
||||
flexWrap="wrap"
|
||||
gap={8}
|
||||
margin={0}
|
||||
borderColor="neutralPlain"
|
||||
borderLeftStyle="solid"
|
||||
borderRightStyle="solid"
|
||||
borderWidth={1}
|
||||
paddingY={5}
|
||||
paddingX={8}
|
||||
alignItems="start"
|
||||
>
|
||||
{integrations.map(integration => (
|
||||
<Box
|
||||
as="li"
|
||||
display="flex"
|
||||
|
@ -32,6 +46,7 @@ const AppListCardIntegrations: React.FC<AppListCardIntegrationsProps> = ({
|
|||
borderRadius={3}
|
||||
borderStyle="solid"
|
||||
borderColor="neutralPlain"
|
||||
borderWidth={1}
|
||||
padding={3}
|
||||
display="flex"
|
||||
placeItems="center"
|
|
@ -19,9 +19,14 @@ const AppListCardLinks: React.FC<AppListCardLinksProps> = ({ links }) => {
|
|||
flexDirection="row"
|
||||
flexWrap="wrap"
|
||||
gap={7}
|
||||
marginBottom={6}
|
||||
borderLeftStyle="solid"
|
||||
borderRightStyle="solid"
|
||||
borderWidth={1}
|
||||
borderColor="neutralPlain"
|
||||
paddingY={6}
|
||||
paddingX={8}
|
||||
>
|
||||
{links.map(link => (
|
||||
{links?.map(link => (
|
||||
<Box as="span" key={link.name}>
|
||||
<Text variant="body" size="small" color="textBrandDefault">
|
||||
<Link href={link.url} target="_blank">
|
||||
|
@ -33,5 +38,6 @@ const AppListCardLinks: React.FC<AppListCardLinksProps> = ({ links }) => {
|
|||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
AppListCardLinks.displayName = "AppListCardLinks";
|
||||
export default AppListCardLinks;
|
|
@ -12,7 +12,7 @@ import { render, screen, within } from "@testing-library/react";
|
|||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
|
||||
import AppListCard from "./AppListCard";
|
||||
import AppListRow from "./AppListRow";
|
||||
|
||||
jest.mock("@dashboard/apps/context", () => ({
|
||||
useAppListContext: jest.fn(() => ({
|
||||
|
@ -32,7 +32,10 @@ jest.mock("@dashboard/config", () => {
|
|||
};
|
||||
});
|
||||
|
||||
describe("Apps AppListCard", () => {
|
||||
const releasedAppPair = [releasedApp, releasedApp];
|
||||
const comingSoonAppPair = [comingSoonApp, comingSoonApp];
|
||||
|
||||
describe("Apps AppListRow", () => {
|
||||
it("displays released app details when released app data passed", () => {
|
||||
// Arrange
|
||||
const integrationImages = releasedApp.integrations.map(
|
||||
|
@ -40,17 +43,17 @@ describe("Apps AppListCard", () => {
|
|||
);
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard app={releasedApp} />
|
||||
<AppListRow appPair={releasedAppPair} />
|
||||
</Wrapper>,
|
||||
);
|
||||
const name = screen.queryByText(releasedApp.name.en);
|
||||
const description = screen.queryByText(releasedApp.description.en);
|
||||
const name = screen.queryAllByText(releasedApp.name.en);
|
||||
const description = screen.queryAllByText(releasedApp.description.en);
|
||||
const images = screen.getAllByRole("img");
|
||||
const links = screen.getAllByRole("link");
|
||||
|
||||
// Assert
|
||||
expect(name).toBeTruthy();
|
||||
expect(description).toBeTruthy();
|
||||
expect(name[0]).toBeTruthy();
|
||||
expect(description[0]).toBeTruthy();
|
||||
const expectedImages = [releasedApp.logo.source, ...integrationImages];
|
||||
images.forEach(image =>
|
||||
expect(expectedImages).toContain(image.getAttribute("src")),
|
||||
|
@ -71,22 +74,22 @@ describe("Apps AppListCard", () => {
|
|||
const navigateToVercelDeploymentPage = jest.fn();
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard
|
||||
app={releasedApp}
|
||||
<AppListRow
|
||||
appPair={releasedAppPair}
|
||||
navigateToAppInstallPage={navigateToAppInstallPage}
|
||||
navigateToGithubForkPage={navigateToVercelDeploymentPage}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
const installButton = screen.getByTestId("app-install-button");
|
||||
const deployToVercelButton = screen.getByTestId(
|
||||
const installButton = screen.getAllByTestId("app-install-button");
|
||||
const deployToVercelButton = screen.getAllByTestId(
|
||||
"app-fork-on-github-button",
|
||||
);
|
||||
|
||||
// Act
|
||||
await user.click(installButton);
|
||||
await user.click(deployToVercelButton);
|
||||
await user.click(installButton[0]);
|
||||
await user.click(deployToVercelButton[0]);
|
||||
|
||||
// Assert
|
||||
expect(navigateToAppInstallPage).toBeCalledTimes(1);
|
||||
|
@ -100,26 +103,26 @@ describe("Apps AppListCard", () => {
|
|||
);
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard app={comingSoonApp} />
|
||||
<AppListRow appPair={comingSoonAppPair} />
|
||||
</Wrapper>,
|
||||
);
|
||||
const name = screen.queryByText(comingSoonApp.name.en);
|
||||
const description = screen.queryByText(comingSoonApp.description.en);
|
||||
const name = screen.queryAllByText(comingSoonApp.name.en);
|
||||
const description = screen.queryAllByText(comingSoonApp.description.en);
|
||||
const images = screen.getAllByRole("img");
|
||||
const links = screen.queryAllByRole("link");
|
||||
const releaseDate = screen.queryByText(comingSoonApp.releaseDate, {
|
||||
const releaseDate = screen.queryAllByText(comingSoonApp.releaseDate, {
|
||||
exact: false,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(name).toBeTruthy();
|
||||
expect(description).toBeTruthy();
|
||||
expect(name[0]).toBeTruthy();
|
||||
expect(description[0]).toBeTruthy();
|
||||
const expectedImages = [comingSoonApp.logo.source, ...integrationImages];
|
||||
images.forEach(image =>
|
||||
expect(expectedImages).toContain(image.getAttribute("src")),
|
||||
);
|
||||
expect(links).toHaveLength(0);
|
||||
expect(releaseDate).toBeTruthy();
|
||||
expect(releaseDate[0]).toBeTruthy();
|
||||
});
|
||||
|
||||
it("displays placeholder initial when no released app logo passed", () => {
|
||||
|
@ -133,12 +136,14 @@ describe("Apps AppListCard", () => {
|
|||
};
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard app={app} />
|
||||
<AppListRow appPair={[app, app]} />
|
||||
</Wrapper>,
|
||||
);
|
||||
const logo = screen.getByTestId("app-logo");
|
||||
const logoPlaceholder = within(logo).queryByTestId("app-logo-placeholder");
|
||||
const logoImage = within(logo).queryByRole("img");
|
||||
const logo = screen.getAllByTestId("app-logo");
|
||||
const logoPlaceholder = within(logo[0]).queryByTestId(
|
||||
"app-logo-placeholder",
|
||||
);
|
||||
const logoImage = within(logo[0]).queryByRole("img");
|
||||
|
||||
// Assert
|
||||
expect(logoPlaceholder).toBeTruthy();
|
||||
|
@ -157,12 +162,14 @@ describe("Apps AppListCard", () => {
|
|||
};
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard app={app} />
|
||||
<AppListRow appPair={[app, app]} />
|
||||
</Wrapper>,
|
||||
);
|
||||
const logo = screen.getByTestId("app-logo");
|
||||
const logoPlaceholder = within(logo).queryByTestId("app-logo-placeholder");
|
||||
const logoImage = within(logo).queryByRole("img");
|
||||
const logo = screen.getAllByTestId("app-logo");
|
||||
const logoPlaceholder = within(logo[0]).queryByTestId(
|
||||
"app-logo-placeholder",
|
||||
);
|
||||
const logoImage = within(logo[0]).queryByRole("img");
|
||||
|
||||
// Assert
|
||||
expect(logoPlaceholder).toBeTruthy();
|
||||
|
@ -174,11 +181,14 @@ describe("Apps AppListCard", () => {
|
|||
// Arrange
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard app={releasedApp} appInstallation={failedAppInProgress} />
|
||||
<AppListRow
|
||||
appPair={releasedAppPair}
|
||||
appInstallationList={[failedAppInProgress]}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
const status = screen.getByTestId("app-installation-failed");
|
||||
const statusDetails = within(status).queryByText(
|
||||
const status = screen.getAllByTestId("app-installation-failed");
|
||||
const statusDetails = within(status[0]).queryByText(
|
||||
appInstallationStatusMessages.failed.defaultMessage,
|
||||
);
|
||||
|
||||
|
@ -190,11 +200,14 @@ describe("Apps AppListCard", () => {
|
|||
// Arrange
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard app={releasedApp} appInstallation={pendingAppInProgress} />
|
||||
<AppListRow
|
||||
appPair={releasedAppPair}
|
||||
appInstallationList={[pendingAppInProgress]}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
const status = screen.getByTestId("app-installation-pending");
|
||||
const statusText = within(status).queryByText(
|
||||
const status = screen.getAllByTestId("app-installation-pending");
|
||||
const statusText = within(status[0]).queryByText(
|
||||
appInstallationStatusMessages.pending.defaultMessage,
|
||||
);
|
||||
|
||||
|
@ -214,16 +227,19 @@ describe("Apps AppListCard", () => {
|
|||
}));
|
||||
render(
|
||||
<Wrapper>
|
||||
<AppListCard app={releasedApp} appInstallation={failedAppInProgress} />
|
||||
<AppListRow
|
||||
appPair={releasedAppPair}
|
||||
appInstallationList={[failedAppInProgress]}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
const retryButton = screen.getByTestId("app-retry-install-button");
|
||||
const removeButton = screen.getByTestId("app-remove-install-button");
|
||||
const retryButton = screen.getAllByTestId("app-retry-install-button");
|
||||
const removeButton = screen.getAllByTestId("app-remove-install-button");
|
||||
|
||||
// Act
|
||||
await user.click(retryButton);
|
||||
await user.click(removeButton);
|
||||
await user.click(retryButton[0]);
|
||||
await user.click(removeButton[0]);
|
||||
|
||||
// Assert
|
||||
expect(retryAppInstallation).toHaveBeenCalledWith(failedAppInProgress.id);
|
113
src/apps/components/AppListRow/AppListRow.tsx
Normal file
113
src/apps/components/AppListRow/AppListRow.tsx
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { useAppListContext } from "@dashboard/apps/context";
|
||||
import { GetV2SaleorAppsResponse } from "@dashboard/apps/marketplace.types";
|
||||
import {
|
||||
getAppDetails,
|
||||
resolveInstallationOfMarketplaceApp,
|
||||
} from "@dashboard/apps/utils";
|
||||
import { AppInstallationFragment } from "@dashboard/graphql";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import AppListCardActions from "./AppListCardActions";
|
||||
import AppListCardDescription from "./AppListCardDescription";
|
||||
import AppListCardIntegrations from "./AppListCardIntegrations";
|
||||
import AppListCardLinks from "./AppListCardLinks";
|
||||
|
||||
interface AppListRowProps {
|
||||
appPair: GetV2SaleorAppsResponse.SaleorApp[];
|
||||
appInstallationList?: AppInstallationFragment[];
|
||||
navigateToAppInstallPage?: (manifestUrl: string) => void;
|
||||
navigateToGithubForkPage?: (githubForkUrl: string) => void;
|
||||
}
|
||||
|
||||
const AppListRow: React.FC<AppListRowProps> = ({
|
||||
appPair,
|
||||
appInstallationList,
|
||||
navigateToAppInstallPage,
|
||||
navigateToGithubForkPage,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const { retryAppInstallation, removeAppInstallation } = useAppListContext();
|
||||
|
||||
const isSingleApp = appPair.length === 1;
|
||||
|
||||
const appDetails = React.useCallback(
|
||||
(app: GetV2SaleorAppsResponse.SaleorApp) =>
|
||||
getAppDetails({
|
||||
intl,
|
||||
app,
|
||||
appInstallation: resolveInstallationOfMarketplaceApp(
|
||||
app,
|
||||
appInstallationList,
|
||||
),
|
||||
navigateToAppInstallPage,
|
||||
navigateToGithubForkPage,
|
||||
retryAppInstallation,
|
||||
removeAppInstallation,
|
||||
}),
|
||||
[
|
||||
appInstallationList,
|
||||
intl,
|
||||
navigateToAppInstallPage,
|
||||
navigateToGithubForkPage,
|
||||
removeAppInstallation,
|
||||
retryAppInstallation,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
display="grid"
|
||||
gridTemplateColumns={2}
|
||||
__gridTemplateRows="repeat(4, auto)"
|
||||
gridAutoFlow={isSingleApp ? "column" : "row"}
|
||||
columnGap={8}
|
||||
padding={8}
|
||||
>
|
||||
{appPair.map(app => (
|
||||
<AppListCardDescription key={app.name.en + "description"} app={app} />
|
||||
))}
|
||||
{appPair.map(app => (
|
||||
<AppListCardLinks
|
||||
key={app.name.en + "links"}
|
||||
links={appDetails(app).links}
|
||||
/>
|
||||
))}
|
||||
{appPair.map(app => {
|
||||
if (appPair.every(app => !app.integrations?.length)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<AppListCardIntegrations
|
||||
key={app.name.en + "integrations"}
|
||||
integrations={app.integrations}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{appPair.map(app => {
|
||||
const {
|
||||
releaseDate,
|
||||
installationPending,
|
||||
installHandler,
|
||||
githubForkHandler,
|
||||
retryInstallHandler,
|
||||
removeInstallHandler,
|
||||
} = appDetails(app);
|
||||
return (
|
||||
<AppListCardActions
|
||||
key={app.name.en + "actions"}
|
||||
releaseDate={releaseDate}
|
||||
installationPending={installationPending}
|
||||
installHandler={installHandler}
|
||||
githubForkHandler={githubForkHandler}
|
||||
retryInstallHandler={retryInstallHandler}
|
||||
removeInstallHandler={removeInstallHandler}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
AppListRow.displayName = "AppListRow";
|
||||
export default AppListRow;
|
3
src/apps/components/AppListRow/index.ts
Normal file
3
src/apps/components/AppListRow/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export * from "./AppListRow";
|
||||
export { default } from "./AppListRow";
|
||||
export * from "./AppLogo";
|
|
@ -61,7 +61,7 @@ export const failedAppInProgress: AppInstallationFragment = {
|
|||
__typename: "AppInstallation",
|
||||
appName: "app",
|
||||
id: "QXBwSW5zdGFsbGF0aW9uOjk2",
|
||||
manifestUrl: "http://localhost:3000/manifest",
|
||||
manifestUrl: "https://www.released-example.com/manifest",
|
||||
message: "Failed to connect to app. Try later or contact with app support.",
|
||||
status: JobStatusEnum.FAILED,
|
||||
};
|
||||
|
@ -70,7 +70,7 @@ export const pendingAppInProgress: AppInstallationFragment = {
|
|||
__typename: "AppInstallation",
|
||||
appName: "app pending",
|
||||
id: "QXBwSW5zdGFsbGF0aW9uOjk2",
|
||||
manifestUrl: "http://localhost:3000/manifest",
|
||||
manifestUrl: "https://www.released-example.com/manifest",
|
||||
message: "Pending.",
|
||||
status: JobStatusEnum.PENDING,
|
||||
};
|
||||
|
@ -79,7 +79,7 @@ export const successAppInProgress: AppInstallationFragment = {
|
|||
__typename: "AppInstallation",
|
||||
appName: "app success",
|
||||
id: "QXBwSW5zdGFsbGF0aW9uOjk2",
|
||||
manifestUrl: "http://localhost:3000/manifest",
|
||||
manifestUrl: "https://www.released-example.com/manifest",
|
||||
message: "Success.",
|
||||
status: JobStatusEnum.SUCCESS,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue