From 9b70a2c81908ee5ce47275e99ffdcdafefc5a7ca Mon Sep 17 00:00:00 2001 From: RachelElysia <71795832+RachelElysia@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:23:08 -0500 Subject: [PATCH] Fleet UI: Surface download URL for Fleet-maintained app when adding (#25762) --- changes/23116-fma-dl-url | 1 + frontend/__mocks__/softwareMock.ts | 1 + frontend/components/DataSet/_styles.scss | 3 +- .../PlatformCell/PlatformCell.tests.tsx | 2 - frontend/interfaces/software.ts | 1 + .../AddFleetAppSoftwareModal.tsx | 5 +- .../FleetAppDetailsModal.tests.tsx | 51 ++++++++++++++++ .../FleetAppDetailsModal.tsx | 57 ++++++++++++++++++ .../FleetAppDetailsModal/_styles.scss | 12 ++++ .../FleetAppDetailsModal/index.ts | 1 + .../FleetMaintainedAppDetailsPage.tsx | 59 ++++++++++++++----- .../_styles.scss | 5 ++ 12 files changed, 179 insertions(+), 19 deletions(-) create mode 100644 changes/23116-fma-dl-url create mode 100644 frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tests.tsx create mode 100644 frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tsx create mode 100644 frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/_styles.scss create mode 100644 frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/index.ts diff --git a/changes/23116-fma-dl-url b/changes/23116-fma-dl-url new file mode 100644 index 0000000000..4314a262ac --- /dev/null +++ b/changes/23116-fma-dl-url @@ -0,0 +1 @@ +- Fleet UI: Surfaced download URL for Fleet-maintained app when adding the software to Fleet diff --git a/frontend/__mocks__/softwareMock.ts b/frontend/__mocks__/softwareMock.ts index 13a9a34b6f..6497bd7d45 100644 --- a/frontend/__mocks__/softwareMock.ts +++ b/frontend/__mocks__/softwareMock.ts @@ -290,6 +290,7 @@ const DEFAULT_FLEET_MAINTAINED_APP_DETAILS_MOCK: IFleetMaintainedAppDetails = { post_install_script: 'echo "Installed"', uninstall_script: "#!/bin/sh\n\n# Fleet extracts and saves package IDs\npkg_ids=$PACKAGE_ID", + url: "http://www.testurl1234abcd.com/testapp", }; export const createMockFleetMaintainedAppDetails = ( diff --git a/frontend/components/DataSet/_styles.scss b/frontend/components/DataSet/_styles.scss index 9f08ff2478..72f09fa23a 100644 --- a/frontend/components/DataSet/_styles.scss +++ b/frontend/components/DataSet/_styles.scss @@ -1,6 +1,7 @@ .data-set { font-size: $x-small; - min-width: max-content; + max-width: min-content; + overflow: hidden; // ff only @-moz-document url-prefix() { diff --git a/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx b/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx index 5278418ce0..28033a84f3 100644 --- a/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx +++ b/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx @@ -1,8 +1,6 @@ import React from "react"; import { render, screen } from "@testing-library/react"; -import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants"; - import { ScheduledQueryablePlatform } from "interfaces/platform"; import PlatformCell from "./PlatformCell"; diff --git a/frontend/interfaces/software.ts b/frontend/interfaces/software.ts index 7912fd0668..fef7a01e3a 100644 --- a/frontend/interfaces/software.ts +++ b/frontend/interfaces/software.ts @@ -463,4 +463,5 @@ export interface IFleetMaintainedAppDetails { install_script: string; post_install_script: string; // TODO: is this needed? uninstall_script: string; + url: string; } diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/AddFleetAppSoftwareModal/AddFleetAppSoftwareModal.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/AddFleetAppSoftwareModal/AddFleetAppSoftwareModal.tsx index 6608c05daa..9a4e3cd4be 100644 --- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/AddFleetAppSoftwareModal/AddFleetAppSoftwareModal.tsx +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/AddFleetAppSoftwareModal/AddFleetAppSoftwareModal.tsx @@ -1,7 +1,8 @@ +import React from "react"; +import { noop } from "lodash"; + import Modal from "components/Modal"; import Spinner from "components/Spinner"; -import { noop } from "lodash"; -import React from "react"; const baseClass = "add-fleet-app-software-modal"; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tests.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tests.tsx new file mode 100644 index 0000000000..3e9d93e0ee --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tests.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { screen } from "@testing-library/react"; +import { noop } from "lodash"; +import { createCustomRenderer } from "test/test-utils"; + +import FleetAppDetailsModal from "./FleetAppDetailsModal"; + +describe("FleetAppDetailsModal", () => { + const defaultProps = { + name: "Test App", + platform: "macOS", + version: "1.0.0", + url: "https://example.com/app", + onCancel: noop, + }; + + it("renders modal with correct title", () => { + const render = createCustomRenderer(); + + render(); + + const modalTitle = screen.getByText("Software details"); + expect(modalTitle).toBeInTheDocument(); + }); + + it("displays correct app details", () => { + const render = createCustomRenderer(); + + render(); + + expect(screen.getByText("Name")).toBeInTheDocument(); + expect(screen.getByText("Test App")).toBeInTheDocument(); + expect(screen.getByText("Platform")).toBeInTheDocument(); + expect(screen.getByText("macOS")).toBeInTheDocument(); + expect(screen.getByText("Version")).toBeInTheDocument(); + expect(screen.getByText("1.0.0")).toBeInTheDocument(); + expect(screen.getByText("URL")).toBeInTheDocument(); + expect( + screen.getAllByText("https://example.com/app").length + ).toBeGreaterThan(0); // Tooltip renders text twice causing use of toBeInTheDocument to fail + }); + + it("does not render URL field when url prop is not provided", () => { + const render = createCustomRenderer(); + const propsWithoutUrl = { ...defaultProps, url: undefined }; + + render(); + + expect(screen.queryByText("URL")).not.toBeInTheDocument(); + }); +}); diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tsx new file mode 100644 index 0000000000..5584dd295e --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/FleetAppDetailsModal.tsx @@ -0,0 +1,57 @@ +import React from "react"; + +import Modal from "components/Modal"; +import DataSet from "components/DataSet"; +import TooltipWrapper from "components/TooltipWrapper"; +import TooltipTruncatedText from "components/TooltipTruncatedText"; +import Button from "components/buttons/Button"; + +const baseClass = "fleet-app-details-modal"; + +interface IFleetAppDetailsModalProps { + name: string; + platform: string; + version: string; + url?: string; + onCancel: () => void; +} + +const TOOLTIP_MESSAGE = + "Fleet downloads the package from the URL and stores it. Hosts download it from Fleet before install."; + +const FleetAppDetailsModal = ({ + name, + platform, + version, + url, + onCancel, +}: IFleetAppDetailsModalProps) => { + return ( + + <> +
+ + + + {url && ( + + URL + + } + value={} + /> + )} +
+
+ +
+ +
+ ); +}; + +export default FleetAppDetailsModal; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/_styles.scss b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/_styles.scss new file mode 100644 index 0000000000..8d56e2bdef --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/_styles.scss @@ -0,0 +1,12 @@ +.fleet-app-details-modal { + &__modal-content { + display: flex; + column-gap: $pad-xxlarge; + row-gap: $pad-xlarge; + flex-wrap: wrap; + } + + .react-tooltip { + min-width: 120px; + } +} diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/index.ts b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/index.ts new file mode 100644 index 0000000000..6edaf09f4b --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsModal/index.ts @@ -0,0 +1 @@ +export { default } from "./FleetAppDetailsModal"; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx index 6c1be80dee..5e22a22718 100644 --- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx @@ -26,12 +26,14 @@ import SidePanelContent from "components/SidePanelContent"; import QuerySidePanel from "components/side_panels/QuerySidePanel"; import PremiumFeatureMessage from "components/PremiumFeatureMessage"; import Card from "components/Card"; - import SoftwareIcon from "pages/SoftwarePage/components/icons/SoftwareIcon"; - +import Button from "components/buttons/Button"; +import Icon from "components/Icon"; import FleetAppDetailsForm from "./FleetAppDetailsForm"; import { IFleetMaintainedAppFormData } from "./FleetAppDetailsForm/FleetAppDetailsForm"; + import AddFleetAppSoftwareModal from "./AddFleetAppSoftwareModal"; +import FleetAppDetailsModal from "./FleetAppDetailsModal"; import { getErrorMessage, @@ -48,35 +50,49 @@ const AUTOMATIC_POLICY_ERROR_MESSAGE = const baseClass = "fleet-maintained-app-details-page"; -interface ISoftwareSummaryProps { +interface IFleetAppSummaryProps { name: string; platform: string; version: string; + onClickShowAppDetails: (event: MouseEvent) => void; } const FleetAppSummary = ({ name, platform, version, -}: ISoftwareSummaryProps) => { + onClickShowAppDetails, +}: IFleetAppSummaryProps) => { return ( - -
-
{name}
-
-
- {PLATFORM_DISPLAY_NAMES[platform as Platform]} -
- • -
- {version} +
+ +
+
{name}
+
+
+ {PLATFORM_DISPLAY_NAMES[platform as Platform]} +
+ • +
+ {version} +
+
+ +
); }; @@ -123,6 +139,7 @@ const FleetMaintainedAppDetailsPage = ({ showAddFleetAppSoftwareModal, setShowAddFleetAppSoftwareModal, ] = useState(false); + const [showAppDetailsModal, setShowAppDetailsModal] = useState(false); const { data: fleetApp, @@ -159,6 +176,10 @@ const FleetMaintainedAppDetailsPage = ({ setSelectedOsqueryTable(tableName); }; + const onClickShowAppDetails = () => { + setShowAppDetailsModal(true); + }; + const backToAddSoftwareUrl = `${ PATHS.SOFTWARE_ADD_FLEET_MAINTAINED }?${buildQueryStringFromParams({ team_id: teamId })}`; @@ -288,6 +309,7 @@ const FleetMaintainedAppDetailsPage = ({ name={fleetApp.name} platform={fleetApp.platform} version={fleetApp.version} + onClickShowAppDetails={onClickShowAppDetails} /> )} {showAddFleetAppSoftwareModal && } + {showAppDetailsModal && fleetApp && ( + setShowAppDetailsModal(false)} + /> + )} ); }; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/_styles.scss b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/_styles.scss index dcd77d2b7c..606a83e11f 100644 --- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/_styles.scss +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/_styles.scss @@ -18,6 +18,11 @@ } &__fleet-app-summary { + display: flex; + justify-content: space-between; + } + + &__fleet-app-summary--left { display: flex; gap: $pad-medium; }