From fca70af3a8fd238a3fce4826e560cc946f16e115 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Fri, 20 Aug 2021 18:51:26 +0900 Subject: [PATCH 1/5] Add application count badges to insight page --- .../application-count/index.stories.tsx | 23 ++++ .../application-count/index.tsx | 124 ++++++++++++++++++ .../application-counts/index.test.tsx | 49 +++++++ .../insight-page/application-counts/index.tsx | 69 ++++++++++ .../web/src/components/insight-page/index.tsx | 26 +++- 5 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 pkg/app/web/src/components/insight-page/application-counts/application-count/index.stories.tsx create mode 100644 pkg/app/web/src/components/insight-page/application-counts/application-count/index.tsx create mode 100644 pkg/app/web/src/components/insight-page/application-counts/index.test.tsx create mode 100644 pkg/app/web/src/components/insight-page/application-counts/index.tsx diff --git a/pkg/app/web/src/components/insight-page/application-counts/application-count/index.stories.tsx b/pkg/app/web/src/components/insight-page/application-counts/application-count/index.stories.tsx new file mode 100644 index 0000000000..5de64a433c --- /dev/null +++ b/pkg/app/web/src/components/insight-page/application-counts/application-count/index.stories.tsx @@ -0,0 +1,23 @@ +import { Story } from "@storybook/react"; +import { ApplicationCount, ApplicationCountProps } from "."; + +export default { + title: "application/ApplicationCount", + component: ApplicationCount, + argTypes: { + onClick: { + action: "onClick", + }, + }, +}; + +const Template: Story = (args) => ( + +); + +export const Overview = Template.bind({}); +Overview.args = { + enabledCount: 123, + disabledCount: 12, + kindName: "KUBERNETES", +}; diff --git a/pkg/app/web/src/components/insight-page/application-counts/application-count/index.tsx b/pkg/app/web/src/components/insight-page/application-counts/application-count/index.tsx new file mode 100644 index 0000000000..bcff508fbd --- /dev/null +++ b/pkg/app/web/src/components/insight-page/application-counts/application-count/index.tsx @@ -0,0 +1,124 @@ +import { + Box, + Card, + CardActionArea, + makeStyles, + Popover, + Typography, +} from "@material-ui/core"; +import clsx from "clsx"; +import { FC, memo, useState } from "react"; + +const useStyles = makeStyles((theme) => ({ + root: { + minWidth: 200, + display: "inline-block", + }, + actionArea: { + padding: theme.spacing(2), + }, + textSpace: { + marginLeft: theme.spacing(1), + }, + popover: { + pointerEvents: "none", + }, + popoverPaper: { + padding: theme.spacing(1), + }, +})); + +export interface ApplicationCountProps { + enabledCount: number; + disabledCount: number; + kindName: string; + onClick: () => void; + className?: string; +} + +export const ApplicationCount: FC = memo( + function ApplicationCount({ + enabledCount, + disabledCount, + kindName, + onClick, + className, + }) { + const classes = useStyles(); + + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const handlePopoverClose = (): void => { + setAnchorEl(null); + }; + + return ( + + { + setAnchorEl(event.currentTarget); + }} + onMouseLeave={handlePopoverClose} + > + + {kindName} + + + + {enabledCount} + + {disabledCount > 0 ? ( + + {`/${disabledCount}`} + + ) : null} + + apps + + + + + +
+ {enabledCount} + {" enabled applications"} +
+
+ {disabledCount} + {" disabled applications"} +
+
+
+ ); + } +); diff --git a/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx new file mode 100644 index 0000000000..5bec3c61d9 --- /dev/null +++ b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx @@ -0,0 +1,49 @@ +import { render, screen } from "~~/test-utils"; +import { ApplicationCounts } from "./index"; + +test("displaying application counts", () => { + render(, { + initialState: { + applicationCounts: { + counts: { + CLOUDRUN: { + DISABLED: 0, + ENABLED: 0, + }, + CROSSPLANE: { + DISABLED: 0, + ENABLED: 0, + }, + ECS: { + DISABLED: 0, + ENABLED: 0, + }, + KUBERNETES: { + DISABLED: 8, + ENABLED: 123, + }, + LAMBDA: { + DISABLED: 0, + ENABLED: 0, + }, + TERRAFORM: { + DISABLED: 2, + ENABLED: 75, + }, + }, + updatedAt: 0, + }, + }, + }); + + expect(screen.queryByText("KUBERNETES")).toBeInTheDocument(); + expect(screen.queryByText("123")).toBeInTheDocument(); + expect(screen.queryByText("/8")).toBeInTheDocument(); + expect(screen.queryByText("TERRAFORM")).toBeInTheDocument(); + expect(screen.queryByText("75")).toBeInTheDocument(); + expect(screen.queryByText("/2")).toBeInTheDocument(); + expect(screen.queryByText("CROSSPLANE")).not.toBeInTheDocument(); + expect(screen.queryByText("LAMBDA")).not.toBeInTheDocument(); + expect(screen.queryByText("CLOUDRUN")).not.toBeInTheDocument(); + expect(screen.queryByText("ECS")).not.toBeInTheDocument(); +}); diff --git a/pkg/app/web/src/components/insight-page/application-counts/index.tsx b/pkg/app/web/src/components/insight-page/application-counts/index.tsx new file mode 100644 index 0000000000..735c8de02d --- /dev/null +++ b/pkg/app/web/src/components/insight-page/application-counts/index.tsx @@ -0,0 +1,69 @@ +import { makeStyles } from "@material-ui/core"; +import { FC, memo } from "react"; +import { APPLICATION_ACTIVE_STATUS_NAME } from "~/constants/application-active-status"; +import { APPLICATION_KIND_BY_NAME } from "~/constants/application-kind"; +import { useAppSelector } from "~/hooks/redux"; +import { + ApplicationActiveStatus, + ApplicationKind, +} from "~/modules/applications"; +import { ApplicationCount } from "./application-count"; + +const useStyles = makeStyles((theme) => ({ + root: { + marginBottom: theme.spacing(2), + display: "flex", + justifyContent: "center", + }, + count: { + marginRight: theme.spacing(2), + display: "flex", + "&:last-child": { + marginRight: 0, + }, + }, +})); + +interface ApplicationCountsProps { + onClick: (kind: ApplicationKind) => void; +} + +export const ApplicationCounts: FC = memo( + function ApplicationCounts({ onClick }) { + const classes = useStyles(); + const counts = useAppSelector>>( + (state) => state.applicationCounts.counts + ); + + return ( +
+ {Object.keys(counts).map((kindName) => { + return ( + { + onClick(APPLICATION_KIND_BY_NAME[kindName]); + }} + className={classes.count} + /> + ); + })} +
+ ); + } +); diff --git a/pkg/app/web/src/components/insight-page/index.tsx b/pkg/app/web/src/components/insight-page/index.tsx index ae9d8f8425..297c72c04a 100644 --- a/pkg/app/web/src/components/insight-page/index.tsx +++ b/pkg/app/web/src/components/insight-page/index.tsx @@ -1,8 +1,11 @@ import { Box } from "@material-ui/core"; -import { FC, memo, useEffect } from "react"; +import { FC, memo, useEffect, useCallback } from "react"; import { useAppDispatch, useAppSelector } from "~/hooks/redux"; -import { fetchApplications, selectById } from "~/modules/applications"; +import { ApplicationKind, fetchApplications, selectById } from "~/modules/applications"; +import { useHistory } from "react-router-dom"; +import { PAGE_PATH_APPLICATIONS } from "~/constants/path"; import { InsightDataPoint } from "~/modules/insight"; +import { ApplicationCounts } from "./application-counts"; import { ChangeFailureRateChart } from "./change-failure-rate-chart"; import { DeploymentFrequencyChart } from "./deployment-frequency-chart"; import { InsightHeader } from "./insight-header"; @@ -11,6 +14,7 @@ import { MeanTimeToRestoreChart } from "./mean-time-to-restore-chart"; export const InsightIndexPage: FC = memo(function InsightIndexPage() { const dispatch = useAppDispatch(); + const history = useHistory(); const deploymentFrequency = useAppSelector( (state) => state.deploymentFrequency.data @@ -31,8 +35,26 @@ export const InsightIndexPage: FC = memo(function InsightIndexPage() { dispatch(fetchApplications()); }, [dispatch]); + const updateURL = useCallback((kind: ApplicationKind) => { + history.replace( + `${PAGE_PATH_APPLICATIONS}?kind=${kind}` + ); + }, + [history] + ); + + const handleApplicationCountClick = useCallback( + (kind: ApplicationKind) => { + updateURL(kind); + }, + [updateURL] + ); + return ( + + + Date: Fri, 20 Aug 2021 19:00:18 +0900 Subject: [PATCH 2/5] Move application count to insight page --- .../application-count/index.stories.tsx | 23 ---- .../application-count/index.tsx | 124 ------------------ .../application-counts/index.test.tsx | 49 ------- .../application-counts/index.tsx | 80 ----------- .../components/applications-page/index.tsx | 13 +- .../web/src/components/insight-page/index.tsx | 6 +- 6 files changed, 5 insertions(+), 290 deletions(-) delete mode 100644 pkg/app/web/src/components/applications-page/application-counts/application-count/index.stories.tsx delete mode 100644 pkg/app/web/src/components/applications-page/application-counts/application-count/index.tsx delete mode 100644 pkg/app/web/src/components/applications-page/application-counts/index.test.tsx delete mode 100644 pkg/app/web/src/components/applications-page/application-counts/index.tsx diff --git a/pkg/app/web/src/components/applications-page/application-counts/application-count/index.stories.tsx b/pkg/app/web/src/components/applications-page/application-counts/application-count/index.stories.tsx deleted file mode 100644 index 5de64a433c..0000000000 --- a/pkg/app/web/src/components/applications-page/application-counts/application-count/index.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Story } from "@storybook/react"; -import { ApplicationCount, ApplicationCountProps } from "."; - -export default { - title: "application/ApplicationCount", - component: ApplicationCount, - argTypes: { - onClick: { - action: "onClick", - }, - }, -}; - -const Template: Story = (args) => ( - -); - -export const Overview = Template.bind({}); -Overview.args = { - enabledCount: 123, - disabledCount: 12, - kindName: "KUBERNETES", -}; diff --git a/pkg/app/web/src/components/applications-page/application-counts/application-count/index.tsx b/pkg/app/web/src/components/applications-page/application-counts/application-count/index.tsx deleted file mode 100644 index bcff508fbd..0000000000 --- a/pkg/app/web/src/components/applications-page/application-counts/application-count/index.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { - Box, - Card, - CardActionArea, - makeStyles, - Popover, - Typography, -} from "@material-ui/core"; -import clsx from "clsx"; -import { FC, memo, useState } from "react"; - -const useStyles = makeStyles((theme) => ({ - root: { - minWidth: 200, - display: "inline-block", - }, - actionArea: { - padding: theme.spacing(2), - }, - textSpace: { - marginLeft: theme.spacing(1), - }, - popover: { - pointerEvents: "none", - }, - popoverPaper: { - padding: theme.spacing(1), - }, -})); - -export interface ApplicationCountProps { - enabledCount: number; - disabledCount: number; - kindName: string; - onClick: () => void; - className?: string; -} - -export const ApplicationCount: FC = memo( - function ApplicationCount({ - enabledCount, - disabledCount, - kindName, - onClick, - className, - }) { - const classes = useStyles(); - - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - - const handlePopoverClose = (): void => { - setAnchorEl(null); - }; - - return ( - - { - setAnchorEl(event.currentTarget); - }} - onMouseLeave={handlePopoverClose} - > - - {kindName} - - - - {enabledCount} - - {disabledCount > 0 ? ( - - {`/${disabledCount}`} - - ) : null} - - apps - - - - - -
- {enabledCount} - {" enabled applications"} -
-
- {disabledCount} - {" disabled applications"} -
-
-
- ); - } -); diff --git a/pkg/app/web/src/components/applications-page/application-counts/index.test.tsx b/pkg/app/web/src/components/applications-page/application-counts/index.test.tsx deleted file mode 100644 index 5bec3c61d9..0000000000 --- a/pkg/app/web/src/components/applications-page/application-counts/index.test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { render, screen } from "~~/test-utils"; -import { ApplicationCounts } from "./index"; - -test("displaying application counts", () => { - render(, { - initialState: { - applicationCounts: { - counts: { - CLOUDRUN: { - DISABLED: 0, - ENABLED: 0, - }, - CROSSPLANE: { - DISABLED: 0, - ENABLED: 0, - }, - ECS: { - DISABLED: 0, - ENABLED: 0, - }, - KUBERNETES: { - DISABLED: 8, - ENABLED: 123, - }, - LAMBDA: { - DISABLED: 0, - ENABLED: 0, - }, - TERRAFORM: { - DISABLED: 2, - ENABLED: 75, - }, - }, - updatedAt: 0, - }, - }, - }); - - expect(screen.queryByText("KUBERNETES")).toBeInTheDocument(); - expect(screen.queryByText("123")).toBeInTheDocument(); - expect(screen.queryByText("/8")).toBeInTheDocument(); - expect(screen.queryByText("TERRAFORM")).toBeInTheDocument(); - expect(screen.queryByText("75")).toBeInTheDocument(); - expect(screen.queryByText("/2")).toBeInTheDocument(); - expect(screen.queryByText("CROSSPLANE")).not.toBeInTheDocument(); - expect(screen.queryByText("LAMBDA")).not.toBeInTheDocument(); - expect(screen.queryByText("CLOUDRUN")).not.toBeInTheDocument(); - expect(screen.queryByText("ECS")).not.toBeInTheDocument(); -}); diff --git a/pkg/app/web/src/components/applications-page/application-counts/index.tsx b/pkg/app/web/src/components/applications-page/application-counts/index.tsx deleted file mode 100644 index d15446d244..0000000000 --- a/pkg/app/web/src/components/applications-page/application-counts/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { makeStyles } from "@material-ui/core"; -import { FC, memo } from "react"; -import { APPLICATION_ACTIVE_STATUS_NAME } from "~/constants/application-active-status"; -import { APPLICATION_KIND_BY_NAME } from "~/constants/application-kind"; -import { useAppSelector } from "~/hooks/redux"; -import { - ApplicationActiveStatus, - ApplicationKind, -} from "~/modules/applications"; -import { ApplicationCount } from "./application-count"; - -const useStyles = makeStyles((theme) => ({ - root: { - marginBottom: theme.spacing(2), - display: "flex", - justifyContent: "center", - }, - count: { - marginRight: theme.spacing(2), - display: "flex", - "&:last-child": { - marginRight: 0, - }, - }, -})); - -interface ApplicationCountsProps { - onClick: (kind: ApplicationKind) => void; -} - -export const ApplicationCounts: FC = memo( - function ApplicationCounts({ onClick }) { - const classes = useStyles(); - const counts = useAppSelector>>( - (state) => state.applicationCounts.counts - ); - - return ( -
- {Object.keys(counts).map((kindName) => { - if ( - counts[kindName][ - APPLICATION_ACTIVE_STATUS_NAME[ApplicationActiveStatus.ENABLED] - ] === 0 && - counts[kindName][ - APPLICATION_ACTIVE_STATUS_NAME[ApplicationActiveStatus.DISABLED] - ] === 0 - ) { - return null; - } - - return ( - { - onClick(APPLICATION_KIND_BY_NAME[kindName]); - }} - className={classes.count} - /> - ); - })} -
- ); - } -); diff --git a/pkg/app/web/src/components/applications-page/index.tsx b/pkg/app/web/src/components/applications-page/index.tsx index c73e21a40e..a676a466bd 100644 --- a/pkg/app/web/src/components/applications-page/index.tsx +++ b/pkg/app/web/src/components/applications-page/index.tsx @@ -21,12 +21,10 @@ import { UI_TEXT_REFRESH, } from "~/constants/ui-text"; import { useAppDispatch, useAppSelector } from "~/hooks/redux"; -import { fetchApplicationCount } from "~/modules/application-counts"; -import { ApplicationKind, fetchApplications } from "~/modules/applications"; +import { fetchApplications } from "~/modules/applications"; import { clearTemplateTarget } from "~/modules/deployment-configs"; import { stringifySearchParams, useSearchParams } from "~/utils/search-params"; import { AddApplicationDrawer } from "./add-application-drawer"; -import { ApplicationCounts } from "./application-counts"; import { ApplicationFilter } from "./application-filter"; import { ApplicationList } from "./application-list"; import { DeploymentConfigForm } from "./deployment-config-form"; @@ -97,20 +95,12 @@ export const ApplicationIndexPage: FC = () => { const fetchApplicationsWithOptions = useCallback(() => { dispatch(fetchApplications(filterOptions)); - dispatch(fetchApplicationCount()); }, [dispatch, filterOptions]); const handleCloseTemplateForm = (): void => { dispatch(clearTemplateTarget()); }; - const handleApplicationCountClick = useCallback( - (kind: ApplicationKind) => { - updateURL({ ...filterOptions, kind }); - }, - [updateURL, filterOptions] - ); - const handlePageChange = useCallback( (page: number) => { updateURL({ ...filterOptions, page }); @@ -157,7 +147,6 @@ export const ApplicationIndexPage: FC = () => {
- { dispatch(fetchApplications()); + dispatch(fetchApplicationCount()); }, [dispatch]); const updateURL = useCallback((kind: ApplicationKind) => { From 1d5a37dda8ba2be065d21fdf0c36c3d849513388 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Fri, 20 Aug 2021 19:14:00 +0900 Subject: [PATCH 3/5] Fix test --- .../components/insight-page/application-counts/index.test.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx index 5bec3c61d9..170b0c8ba1 100644 --- a/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx +++ b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx @@ -43,7 +43,11 @@ test("displaying application counts", () => { expect(screen.queryByText("75")).toBeInTheDocument(); expect(screen.queryByText("/2")).toBeInTheDocument(); expect(screen.queryByText("CROSSPLANE")).not.toBeInTheDocument(); + expect(screen.queryByText("0")).toBeInTheDocument(); expect(screen.queryByText("LAMBDA")).not.toBeInTheDocument(); + expect(screen.queryByText("0")).toBeInTheDocument(); expect(screen.queryByText("CLOUDRUN")).not.toBeInTheDocument(); + expect(screen.queryByText("0")).toBeInTheDocument(); expect(screen.queryByText("ECS")).not.toBeInTheDocument(); + expect(screen.queryByText("0")).toBeInTheDocument(); }); From 4a979f787597fd3ae5c0d71b76e9f909d650eed0 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Fri, 20 Aug 2021 19:15:48 +0900 Subject: [PATCH 4/5] Fix test --- .../insight-page/application-counts/index.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx index 170b0c8ba1..dd73b3c1a8 100644 --- a/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx +++ b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx @@ -42,12 +42,12 @@ test("displaying application counts", () => { expect(screen.queryByText("TERRAFORM")).toBeInTheDocument(); expect(screen.queryByText("75")).toBeInTheDocument(); expect(screen.queryByText("/2")).toBeInTheDocument(); - expect(screen.queryByText("CROSSPLANE")).not.toBeInTheDocument(); + expect(screen.queryByText("CROSSPLANE")).toBeInTheDocument(); expect(screen.queryByText("0")).toBeInTheDocument(); - expect(screen.queryByText("LAMBDA")).not.toBeInTheDocument(); + expect(screen.queryByText("LAMBDA")).toBeInTheDocument(); expect(screen.queryByText("0")).toBeInTheDocument(); - expect(screen.queryByText("CLOUDRUN")).not.toBeInTheDocument(); + expect(screen.queryByText("CLOUDRUN")).toBeInTheDocument(); expect(screen.queryByText("0")).toBeInTheDocument(); - expect(screen.queryByText("ECS")).not.toBeInTheDocument(); + expect(screen.queryByText("ECS")).toBeInTheDocument(); expect(screen.queryByText("0")).toBeInTheDocument(); }); From abb7cf9428fc039f5601fb2a6cb7b91be6ce2cde Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Fri, 20 Aug 2021 19:28:28 +0900 Subject: [PATCH 5/5] Ignore 0 text finding --- .../components/insight-page/application-counts/index.test.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx index dd73b3c1a8..e78c79d953 100644 --- a/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx +++ b/pkg/app/web/src/components/insight-page/application-counts/index.test.tsx @@ -43,11 +43,7 @@ test("displaying application counts", () => { expect(screen.queryByText("75")).toBeInTheDocument(); expect(screen.queryByText("/2")).toBeInTheDocument(); expect(screen.queryByText("CROSSPLANE")).toBeInTheDocument(); - expect(screen.queryByText("0")).toBeInTheDocument(); expect(screen.queryByText("LAMBDA")).toBeInTheDocument(); - expect(screen.queryByText("0")).toBeInTheDocument(); expect(screen.queryByText("CLOUDRUN")).toBeInTheDocument(); - expect(screen.queryByText("0")).toBeInTheDocument(); expect(screen.queryByText("ECS")).toBeInTheDocument(); - expect(screen.queryByText("0")).toBeInTheDocument(); });