diff --git a/changes/39325-dropdown-bugginess b/changes/39325-dropdown-bugginess new file mode 100644 index 0000000000..4978ee846c --- /dev/null +++ b/changes/39325-dropdown-bugginess @@ -0,0 +1 @@ +- Fleet UI: Fix dropdown rendering center of a row and from pushing down save button below open dropdown options diff --git a/changes/39465-transparency-url b/changes/39465-transparency-url new file mode 100644 index 0000000000..4fcb1487ce --- /dev/null +++ b/changes/39465-transparency-url @@ -0,0 +1 @@ +- Fleet Desktop: Surface custom transparency links to the device user diff --git a/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx b/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx index c137f559f7..50ba67b052 100644 --- a/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx +++ b/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx @@ -103,7 +103,7 @@ type DropdownWrapperVariant = "table-filter" | "button"; export interface IDropdownWrapper { options: CustomOptionType[]; - value?: PropsValue | string; + value?: PropsValue | string; // Future: Handle number types, cascade of type checking will be needed onChange: (newValue: SingleValue) => void; name: string; className?: string; diff --git a/frontend/pages/labels/components/PlatformField/PlatformField.tsx b/frontend/pages/labels/components/PlatformField/PlatformField.tsx index 7ceb0500d1..752733f522 100644 --- a/frontend/pages/labels/components/PlatformField/PlatformField.tsx +++ b/frontend/pages/labels/components/PlatformField/PlatformField.tsx @@ -1,8 +1,9 @@ import React from "react"; import { noop } from "lodash"; -// @ts-ignore -import Dropdown from "components/forms/fields/Dropdown"; +import DropdownWrapper, { + CustomOptionType, +} from "components/forms/fields/DropdownWrapper/DropdownWrapper"; import FormField from "components/forms/FormField"; const PLATFORM_STRINGS: { [key: string]: string } = { @@ -12,7 +13,7 @@ const PLATFORM_STRINGS: { [key: string]: string } = { centos: "CentOS Linux", }; -const platformOptions = [ +const platformOptions: CustomOptionType[] = [ { label: "All platforms", value: "" }, { label: "macOS", value: "darwin" }, { label: "Windows", value: "windows" }, @@ -33,25 +34,31 @@ const PlatformField = ({ isEditing = false, onChange = noop, }: IPlatformFieldProps) => { + const handleDropdownChange = (newValue: CustomOptionType | null) => { + // DropdownWrapper passes a SingleValue | null + onChange(newValue?.value ?? ""); + }; + return (
{!isEditing ? (
-
) : ( - <> -

{platform ? PLATFORM_STRINGS[platform] : "All platforms"}

- +

{platform ? PLATFORM_STRINGS[platform] : "All platforms"}

)}
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx index d444d7c07b..f1eb272127 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx @@ -16,8 +16,9 @@ import { getPathWithQueryParams } from "utilities/url"; import { getExtensionFromFileName } from "utilities/file/fileUtils"; import { getDisplayedSoftwareName } from "pages/SoftwarePage/helpers"; -// @ts-ignore -import Dropdown from "components/forms/fields/Dropdown"; +import DropdownWrapper, { + CustomOptionType, +} from "components/forms/fields/DropdownWrapper/DropdownWrapper"; import Modal from "components/Modal"; import DataError from "components/DataError"; import Spinner from "components/Spinner"; @@ -195,31 +196,34 @@ const InstallSoftwareModal = ({ // Filters and transforms software titles into dropdown options // to include only software compatible with the policy's platform(s) const availableSoftwareOptions = useCallback( - (policy: IFormPolicy) => { + (policy: IFormPolicy): CustomOptionType[] => { const policyPlatforms = policy.platform.split(","); - return titlesAvailableForInstall - ?.filter( - (title) => title.platform && policyPlatforms.includes(title.platform) - ) - .map((title) => { - return { - label: getDisplayedSoftwareName(title.name, title.display_name), - value: title.id, - helpText: generateSoftwareOptionHelpText(title), - }; - }); + return ( + titlesAvailableForInstall + ?.filter( + (title) => + title.platform && policyPlatforms.includes(title.platform) + ) + .map((title) => { + return { + label: getDisplayedSoftwareName(title.name, title.display_name), + value: String(title.id), // string for DropdownWrapper + helpText: generateSoftwareOptionHelpText(title), + }; + }) ?? [] + ); }, [titlesAvailableForInstall] ); // Cache availableSoftwareOptions for each unique platform const memoizedAvailableSoftwareOptions = useMemo(() => { - const cache = new Map(); - return (policy: IFormPolicy) => { - let options = availableSoftwareOptions(policy) || []; + const cache = new Map(); + return (policy: IFormPolicy): CustomOptionType[] => { + let options = availableSoftwareOptions(policy); const installOptionsByPlatformMismatchSelectedInstaller = policy.swIdToInstall && - !options.some((opt) => opt.value === policy.swIdToInstall); + !options.some((opt) => Number(opt.value) === policy.swIdToInstall); // More unique cache key if installOptionsByPlatformMismatchSelectedInstaller const key = `${policy.platform}${ @@ -241,7 +245,7 @@ const InstallSoftwareModal = ({ currentSoftware.name, currentSoftware.display_name ), - value: currentSoftware.id, + value: String(currentSoftware.id), helpText: generateSoftwareOptionHelpText(currentSoftware), }, ...options, @@ -251,7 +255,7 @@ const InstallSoftwareModal = ({ cache.set(key, options); } - return cache.get(key); + return cache.get(key) ?? []; }; }, [availableSoftwareOptions, titlesAvailableForInstall]); @@ -311,22 +315,27 @@ const InstallSoftwareModal = ({ onClick={(e) => { e.stopPropagation(); }} + className={`${baseClass}__dropdown-wrapper`} > - + onChange( onSelectPolicySoftware(item, { name: formPolicy.name, - value, + value: newValue?.value ? Number(newValue.value) : 0, }) ) } placeholder="Select software" className={`${baseClass}__software-dropdown`} name={formPolicy.name} - parseTarget + isSearchable /> ) : null; diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx index 627b3954f1..f392c90b76 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useRef } from "react"; import { useQuery } from "react-query"; +import { SingleValue } from "react-select-5"; import { omit } from "lodash"; import paths from "router/paths"; @@ -13,8 +14,9 @@ import scriptsAPI, { import { IScript } from "interfaces/script"; -// @ts-ignore -import Dropdown from "components/forms/fields/Dropdown"; +import DropdownWrapper, { + CustomOptionType, +} from "components/forms/fields/DropdownWrapper/DropdownWrapper"; import DataError from "components/DataError"; import Spinner from "components/Spinner"; import CustomLink from "components/CustomLink"; @@ -113,11 +115,6 @@ const PolicyRunScriptModal = ({ }; }; - const availableScriptOptions = availableScripts?.map((script) => ({ - label: script.name, - value: script.id, - })); - const renderContent = () => { if (isAvailableScriptsError) { return ; @@ -175,29 +172,49 @@ const PolicyRunScriptModal = ({ renderItemRow={(item, onChange) => { const formPolicy = { ...item, - runScriptEnabled: !!item.scriptIdToRun, + runScriptEnabled: !!item.runScriptEnabled, }; - return item.runScriptEnabled ? ( + + const availableScriptOptions = + availableScripts?.map((script) => ({ + label: script.name, + value: String(script.id), // DropdownWrapper expects string value + })) ?? []; + + const handleScriptChange = ( + newValue: SingleValue + ) => { + const numericId = newValue?.value + ? Number(newValue.value) + : null; + + onChange( + onSelectPolicyScript(item, { + name: formPolicy.name, + value: numericId ?? 0, + }) + ); + }; + + return formPolicy.runScriptEnabled ? ( ) => { e.stopPropagation(); }} + className={`${baseClass}__dropdown-wrapper`} > - - onChange( - onSelectPolicyScript(item, { - name: formPolicy.name, - value, - }) - ) + ) : null;