diff --git a/plugins/services/src/js/columns/ServicesTableCPUColumn.tsx b/plugins/services/src/js/columns/ServicesTableCPUColumn.tsx index 0e5d052a5d..7d690ab823 100644 --- a/plugins/services/src/js/columns/ServicesTableCPUColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableCPUColumn.tsx @@ -8,16 +8,16 @@ import Units from "#SRC/js/utils/Units"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; import ServiceTableUtil from "../utils/ServiceTableUtil"; +export const ServiceCPU = React.memo(({ resource }: { resource: string }) => ( + + {Units.formatResource("cpus", resource)} + +)); + export function cpuRenderer( service: Service | Pod | ServiceTree ): React.ReactNode { - const resource = service.getResources()[`cpus`]; - - return ( - - {Units.formatResource("cpus", resource)} - - ); + return ; } export function cpuSorter( diff --git a/plugins/services/src/js/columns/ServicesTableDiskColumn.tsx b/plugins/services/src/js/columns/ServicesTableDiskColumn.tsx index 253b709c78..dfaa027241 100644 --- a/plugins/services/src/js/columns/ServicesTableDiskColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableDiskColumn.tsx @@ -8,16 +8,16 @@ import Units from "#SRC/js/utils/Units"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; import ServiceTableUtil from "../utils/ServiceTableUtil"; +export const ServiceDisk = React.memo(({ resource }: { resource: string }) => ( + + {Units.formatResource("disk", resource)} + +)); + export function diskRenderer( service: Service | Pod | ServiceTree ): React.ReactNode { - const resource = service.getResources()[`disk`]; - - return ( - - {Units.formatResource("disk", resource)} - - ); + return ; } export function diskSorter( diff --git a/plugins/services/src/js/columns/ServicesTableGPUColumn.tsx b/plugins/services/src/js/columns/ServicesTableGPUColumn.tsx index 62cf000cae..bd895fa619 100644 --- a/plugins/services/src/js/columns/ServicesTableGPUColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableGPUColumn.tsx @@ -8,16 +8,16 @@ import Units from "#SRC/js/utils/Units"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; import ServiceTableUtil from "../utils/ServiceTableUtil"; +export const ServiceGPU = React.memo(({ resource }: { resource: string }) => ( + + {Units.formatResource("gpus", resource)} + +)); + export function gpuRenderer( service: Service | Pod | ServiceTree ): React.ReactNode { - const resource = service.getResources()[`gpus`]; - - return ( - - {Units.formatResource("gpus", resource)} - - ); + return ; } export function gpuSorter( diff --git a/plugins/services/src/js/columns/ServicesTableInstancesColumn.tsx b/plugins/services/src/js/columns/ServicesTableInstancesColumn.tsx index 055788d741..fe56f7ec1e 100644 --- a/plugins/services/src/js/columns/ServicesTableInstancesColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableInstancesColumn.tsx @@ -11,34 +11,49 @@ import StringUtil from "#SRC/js/utils/StringUtil"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; import ServiceTableUtil from "../utils/ServiceTableUtil"; -export function instancesRenderer( - service: Service | Pod | ServiceTree -): React.ReactNode { - const instancesCount = service.getInstancesCount(); - const runningInstances = service.getRunningInstancesCount(); - const overview = - runningInstances === instancesCount - ? ` ${runningInstances}` - : ` ${runningInstances}/${instancesCount}`; +const ServiceInstances = React.memo( + ({ + instancesCount, + runningInstances + }: { + instancesCount: number; + runningInstances: number; + }) => { + const overview = + runningInstances === instancesCount + ? ` ${runningInstances}` + : ` ${runningInstances}/${instancesCount}`; - const content = !Number.isInteger(instancesCount) - ? EmptyStates.CONFIG_VALUE - : overview; + const content = !Number.isInteger(instancesCount) + ? EmptyStates.CONFIG_VALUE + : overview; - // L10NTODO: Pluralize - const tooltipContent = ( - - {runningInstances} {StringUtil.pluralize("instance", runningInstances)}{" "} - running out of {instancesCount} - - ); + // L10NTODO: Pluralize + const tooltipContent = ( + + {runningInstances} {StringUtil.pluralize("instance", runningInstances)}{" "} + running out of {instancesCount} + + ); + return ( + + + {content} + + + ); + } +); + +export function instancesRenderer( + service: Service | Pod | ServiceTree +): React.ReactNode { return ( - - - {content} - - + ); } diff --git a/plugins/services/src/js/columns/ServicesTableMemColumn.tsx b/plugins/services/src/js/columns/ServicesTableMemColumn.tsx index 7934e1f451..fa205e8a68 100644 --- a/plugins/services/src/js/columns/ServicesTableMemColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableMemColumn.tsx @@ -8,15 +8,16 @@ import Units from "#SRC/js/utils/Units"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; import ServiceTableUtil from "../utils/ServiceTableUtil"; +export const ServiceMem = React.memo(({ resource }: { resource: string }) => ( + + {Units.formatResource("mem", resource)} + +)); + export function memRenderer( service: Service | Pod | ServiceTree ): React.ReactNode { - const resource = service.getResources()[`mem`]; - return ( - - {Units.formatResource("mem", resource)} - - ); + return ; } export function memSorter( diff --git a/plugins/services/src/js/columns/ServicesTableNameColumn.tsx b/plugins/services/src/js/columns/ServicesTableNameColumn.tsx index 2aeed8dae0..34bb3be721 100644 --- a/plugins/services/src/js/columns/ServicesTableNameColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableNameColumn.tsx @@ -14,33 +14,70 @@ import Pod from "../structs/Pod"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; import ServiceTableUtil from "../utils/ServiceTableUtil"; +const ServiceName = React.memo( + ({ + isFiltered, + id, + isGroup, + name, + image, + webUrl + }: { + isFiltered: boolean; + id: string; + isGroup: boolean; + name: string; + image: string | null; + webUrl: string | null; + }) => { + const serviceLink = isGroup + ? `/services/overview/${id}` + : `/services/detail/${id}`; + + return ( + +
+ + {getImage(image, isGroup)} + + + {getServiceLink(id, name, isGroup, isFiltered)} + {getOpenInNewWindowLink(webUrl)} + +
+
+ ); + } +); + export function nameRenderer( isFiltered: boolean, service: Service | Pod | ServiceTree ): React.ReactNode { - const id: string = encodeURIComponent(service.getId().toString()); - const isGroup = service instanceof ServiceTree; - const serviceLink = isGroup - ? `/services/overview/${id}` - : `/services/detail/${id}`; + // These do not work with instanceof ServiceTree due to TS + const image = + service instanceof Pod || service instanceof Service + ? service.getImages()["icon-small"] + : null; + const webUrl = + service instanceof Pod || (service instanceof Service && hasWebUI(service)) + ? service.getWebURL() + : null; return ( - -
- - {getImage(service)} - - - {getServiceLink(service, isFiltered)} - {getOpenInNewWindowLink(service)} - -
-
+ ); } -function getImage(service: any): any { - if (service instanceof ServiceTree) { +function getImage(image: string | null, isGroup: boolean): React.ReactNode { + if (isGroup) { // Get serviceTree image/icon return ( @@ -52,14 +89,17 @@ function getImage(service: any): any { // Get service image/icon return ( - + ); } -function getServiceLink(service: any, isFiltered: boolean): any { - const id = encodeURIComponent(service.getId()); - const isGroup = service instanceof ServiceTree; +function getServiceLink( + id: string, + name: string, + isGroup: boolean, + isFiltered: boolean +): React.ReactNode { const serviceLink = isGroup ? `/services/overview/${id}` : `/services/detail/${id}`; @@ -77,22 +117,22 @@ function getServiceLink(service: any, isFiltered: boolean): any { return ( - {service.getName()} + {name} ); } -function getOpenInNewWindowLink(service: any): any { +function getOpenInNewWindowLink(webUrl: string | null): React.ReactNode { // This might be a serviceTree and therefore we need this check // And getWebURL might therefore not be available - if (!hasWebUI(service)) { + if (!webUrl) { return null; } return ( diff --git a/plugins/services/src/js/columns/ServicesTableRegionColumn.tsx b/plugins/services/src/js/columns/ServicesTableRegionColumn.tsx index 170c724329..cc50ae5d15 100644 --- a/plugins/services/src/js/columns/ServicesTableRegionColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableRegionColumn.tsx @@ -8,6 +8,14 @@ import ServiceTree from "../structs/ServiceTree"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; import ServiceTableUtil from "../utils/ServiceTableUtil"; +const ServiceRegion = React.memo(({ regions }: { regions: string }) => ( + + + {regions} + + +)); + export function regionRendererFactory(localRegion: string | undefined) { return (service: Service | Pod | ServiceTree) => { let regions: string[] = service.getRegions(); @@ -20,13 +28,7 @@ export function regionRendererFactory(localRegion: string | undefined) { regions.push("N/A"); } - return ( - - - {regions.join(", ")} - - - ); + return ; }; } export function regionSorter( diff --git a/plugins/services/src/js/columns/ServicesTableVersionColumn.tsx b/plugins/services/src/js/columns/ServicesTableVersionColumn.tsx index 3d97f598cb..4228cfcfd2 100644 --- a/plugins/services/src/js/columns/ServicesTableVersionColumn.tsx +++ b/plugins/services/src/js/columns/ServicesTableVersionColumn.tsx @@ -8,6 +8,22 @@ import Service from "../structs/Service"; import ServiceTree from "../structs/ServiceTree"; import { SortDirection } from "plugins/services/src/js/types/SortDirection"; +const ServiceVersion = React.memo( + ({ + rawVersion, + displayVersion + }: { + rawVersion: string; + displayVersion: string; + }) => ( + + + {displayVersion} + + + ) +); + export function versionRenderer( service: Service | Pod | ServiceTree ): React.ReactNode { @@ -17,11 +33,10 @@ export function versionRenderer( } return ( - - - {version.displayVersion} - - + ); } diff --git a/plugins/services/src/js/components/ServiceStatusProgressBar.tsx b/plugins/services/src/js/components/ServiceStatusProgressBar.tsx index 911c1a940a..590f639596 100644 --- a/plugins/services/src/js/components/ServiceStatusProgressBar.tsx +++ b/plugins/services/src/js/components/ServiceStatusProgressBar.tsx @@ -14,6 +14,36 @@ interface ServiceStatusProgressBarProps { service: Service | ServiceTree | Pod; } +export const ServiceProgressBar = React.memo( + ({ + instancesCount, + runningInstances + }: { + instancesCount: number; + runningInstances: number; + }) => ( + + + + } + > + + + ) +); + class ServiceStatusProgressBar extends React.Component< ServiceStatusProgressBarProps > { @@ -25,23 +55,6 @@ class ServiceStatusProgressBar extends React.Component< ]).isRequired }; - getTooltipContent() { - const { service } = this.props; - const runningInstances = service.getRunningInstancesCount(); - const instancesTotal = service.getInstancesCount(); - - return ( -
- -
- ); - } - render() { const { service } = this.props; const instancesCount = service.getInstancesCount(); @@ -52,18 +65,10 @@ class ServiceStatusProgressBar extends React.Component< } return ( - - - + ); } } diff --git a/plugins/services/src/js/components/__tests__/ServiceStatusProgressBar-test.js b/plugins/services/src/js/components/__tests__/ServiceStatusProgressBar-test.js index 1191e10969..2569735427 100644 --- a/plugins/services/src/js/components/__tests__/ServiceStatusProgressBar-test.js +++ b/plugins/services/src/js/components/__tests__/ServiceStatusProgressBar-test.js @@ -1,95 +1,26 @@ import React from "react"; -import { shallow } from "enzyme"; - -import ServiceStatusProgressBar from "../ServiceStatusProgressBar"; - -const Tooltip = require("reactjs-components").Tooltip; - -const ProgressBar = require("#SRC/js/components/ProgressBar"); -const Application = require("../../structs/Application"); -const Pod = require("../../structs/Pod"); - -const service = new Application({ - instances: 1, - tasksRunning: 1, - tasksHealthy: 0, - tasksUnhealthy: 0, - tasksUnknown: 0, - tasksStaged: 0 -}); - -let thisInstance; - -describe("ServiceStatusProgressBar", function() { - beforeEach(function() { - thisInstance = shallow(); - }); - - describe("ProgressBar", function() { - it("display ProgressBar", function() { - thisInstance = shallow( - - ); - - const progressBar = thisInstance.find(ProgressBar); - expect(progressBar).toBeTruthy(); - expect(progressBar.prop("total")).toBe(10); - expect(progressBar.prop("data")[0].value).toBe(4); - }); - }); - - describe("Tooltip", function() { - it("display Tooltip", function() { - const app = new Application({ - queue: { - overdue: true - } - }); - - thisInstance = shallow(); - - expect(expect(thisInstance.find(Tooltip).exists())).toBeTruthy(); - }); - - it("get #getTooltipContent", function() { - const childrenContent = shallow( - thisInstance.instance().getTooltipContent() - ) - .find("WithI18n") - .props().values; - - expect(childrenContent).toMatchObject({ - instancesTotal: 1, - runningInstances: 0 - }); +import renderer from "react-test-renderer"; + +import { ServiceProgressBar } from "../ServiceStatusProgressBar"; + +describe("ServiceProgressBar", () => { + for (const [runningInstances, instancesCount] of [ + [0, 1], + [0, 10], + [3, 4], + [10, 10] + ]) { + it(`displays a (${runningInstances} / ${instancesCount}) ProgressBar`, () => { + expect( + renderer + .create( + + ) + .toJSON() + ).toMatchSnapshot(); }); - }); + } }); diff --git a/plugins/services/src/js/components/__tests__/__snapshots__/ServiceStatusProgressBar-test.js.snap b/plugins/services/src/js/components/__tests__/__snapshots__/ServiceStatusProgressBar-test.js.snap new file mode 100644 index 0000000000..0a9b16bffc --- /dev/null +++ b/plugins/services/src/js/components/__tests__/__snapshots__/ServiceStatusProgressBar-test.js.snap @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ServiceProgressBar displays a (0 / 1) ProgressBar 1`] = ` +
+
+
+`; + +exports[`ServiceProgressBar displays a (0 / 10) ProgressBar 1`] = ` +
+
+
+`; + +exports[`ServiceProgressBar displays a (3 / 4) ProgressBar 1`] = ` +
+
+ +
+
+`; + +exports[`ServiceProgressBar displays a (10 / 10) ProgressBar 1`] = ` +
+
+ +
+
+`; diff --git a/plugins/services/src/js/containers/services/__tests__/ServicesTableRenderers-test.js b/plugins/services/src/js/containers/services/__tests__/ServicesTableRenderers-test.js deleted file mode 100644 index f9ca116e2c..0000000000 --- a/plugins/services/src/js/containers/services/__tests__/ServicesTableRenderers-test.js +++ /dev/null @@ -1,190 +0,0 @@ -import { NumberCell } from "@dcos/ui-kit"; - -import { regionRendererFactory } from "../../../columns/ServicesTableRegionColumn"; -import { cpuRenderer } from "../../../columns/ServicesTableCPUColumn"; -import { memRenderer } from "../../../columns/ServicesTableMemColumn"; -import { diskRenderer } from "../../../columns/ServicesTableDiskColumn"; -import { gpuRenderer } from "../../../columns/ServicesTableGPUColumn"; - -// Explicitly mock for react-intl -jest.mock("../../../components/modals/ServiceActionDisabledModal"); - -/* eslint-disable no-unused-vars */ -const React = require("react"); -/* eslint-enable no-unused-vars */ -const renderer = require("react-test-renderer"); -const Application = require("../../../structs/Application"); - -function renderNumberCell(value) { - return ( - - {value} - - ); -} - -describe("ServicesTable", function() { - const healthyService = new Application({ - healthChecks: [{ path: "", protocol: "HTTP" }], - cpus: 1, - gpus: 1, - instances: 1, - mem: 2048, - disk: 0, - tasksStaged: 0, - tasksRunning: 2, - tasksHealthy: 2, - tasksUnhealthy: 0 - }); - - describe("#renderStats", function() { - it("renders resources/stats cpus property", function() { - var cpusCell = cpuRenderer(healthyService); - - expect(cpusCell).toEqual(renderNumberCell(1)); - }); - - it("renders resources/stats gpus property", function() { - var gpusCell = gpuRenderer(healthyService); - - expect(gpusCell).toEqual(renderNumberCell(1)); - }); - - it("renders resources/stats mem property", function() { - var memCell = memRenderer(healthyService); - - expect(memCell).toEqual(renderNumberCell("2 GiB")); - }); - - it("renders resources/stats disk property", function() { - var disksCell = diskRenderer(healthyService); - - expect(disksCell).toEqual(renderNumberCell("0 B")); - }); - - it("renders sum of resources/stats cpus property", function() { - const application = new Application({ - healthChecks: [{ path: "", protocol: "HTTP" }], - cpus: 1, - instances: 2, - mem: 2048, - disk: 0, - tasksStaged: 0, - tasksRunning: 2, - tasksHealthy: 2, - tasksUnhealthy: 0, - getResources: jest.fn() - }); - - application.getResources.mockReturnValue({ cpus: application.cpus }); - var cpusCell = cpuRenderer(application); - - expect(cpusCell).toEqual(renderNumberCell(1)); - }); - - it("renders sum of resources/stats gpus property", function() { - const application = new Application({ - healthChecks: [{ path: "", protocol: "HTTP" }], - cpus: 1, - gpus: 1, - instances: 2, - mem: 2048, - disk: 0, - tasksStaged: 0, - tasksRunning: 2, - tasksHealthy: 2, - tasksUnhealthy: 0, - getResources: jest.fn() - }); - - application.getResources.mockReturnValue({ gpus: application.gpus }); - var gpusCell = gpuRenderer(application); - - expect(gpusCell).toEqual(renderNumberCell(application.gpus)); - }); - - it("renders sum of resources/stats mem property", function() { - const application = new Application({ - healthChecks: [{ path: "", protocol: "HTTP" }], - cpus: 1, - gpus: 1, - instances: 2, - mem: 2048, - disk: 0, - tasksStaged: 0, - tasksRunning: 2, - tasksHealthy: 2, - tasksUnhealthy: 0, - getResources: jest.fn() - }); - - application.getResources.mockReturnValue({ mem: application.mem }); - var memCell = memRenderer(application); - - expect(memCell).toEqual(renderNumberCell("2 GiB")); - }); - - it("renders sum of resources/stats disk property", function() { - const application = new Application({ - healthChecks: [{ path: "", protocol: "HTTP" }], - cpus: 1, - gpus: 1, - instances: 2, - mem: 2048, - disk: 0, - tasksStaged: 0, - tasksRunning: 2, - tasksHealthy: 2, - tasksUnhealthy: 0, - getResources: jest.fn() - }); - - application.getResources.mockReturnValue({ disk: application.disk }); - var diskCell = diskRenderer(application); - - expect(diskCell).toEqual(renderNumberCell("0 B")); - }); - }); - - describe("#renderRegions", function() { - const renderRegions = regionRendererFactory("aws/eu-central-2"); - let mockService; - beforeEach(function() { - mockService = { - getRegions: jest.fn() - }; - }); - - it("renders with no regions", function() { - mockService.getRegions.mockReturnValue([]); - - expect( - renderer.create(renderRegions(mockService)).toJSON() - ).toMatchSnapshot(); - }); - - it("renders with one region", function() { - mockService.getRegions.mockReturnValue(["aws/eu-central-1"]); - - expect( - renderer.create(renderRegions(mockService)).toJSON() - ).toMatchSnapshot(); - }); - - it("renders with one local region", function() { - mockService.getRegions.mockReturnValue(["aws/eu-central-2"]); - - expect( - renderer.create(renderRegions(mockService)).toJSON() - ).toMatchSnapshot(); - }); - - it("renders with multiple regions", function() { - mockService.getRegions.mockReturnValue(["aws/eu-central-1", "dc-east"]); - - expect( - renderer.create(renderRegions(mockService)).toJSON() - ).toMatchSnapshot(); - }); - }); -}); diff --git a/plugins/services/src/js/containers/services/__tests__/__snapshots__/ServicesTableRenderers-test.js.snap b/plugins/services/src/js/containers/services/__tests__/__snapshots__/ServicesTableRenderers-test.js.snap deleted file mode 100644 index d78259dcf4..0000000000 --- a/plugins/services/src/js/containers/services/__tests__/__snapshots__/ServicesTableRenderers-test.js.snap +++ /dev/null @@ -1,57 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ServicesTable #renderRegions renders with multiple regions 1`] = ` -
- - aws/eu-central-1, dc-east - -
-`; - -exports[`ServicesTable #renderRegions renders with no regions 1`] = ` -
- - N/A - -
-`; - -exports[`ServicesTable #renderRegions renders with one local region 1`] = ` -
- - aws/eu-central-2 (Local) - -
-`; - -exports[`ServicesTable #renderRegions renders with one region 1`] = ` -
- - aws/eu-central-1 - -
-`;