diff --git a/frontend/components/ActionsDropdown/_styles.scss b/frontend/components/ActionsDropdown/_styles.scss index f5e6ed540b..f657eb123c 100644 --- a/frontend/components/ActionsDropdown/_styles.scss +++ b/frontend/components/ActionsDropdown/_styles.scss @@ -31,3 +31,9 @@ .actions-dropdown__option:focus-visible { outline: none; } + +// Drop the stale highlight for as long as a disabled option is hovered. +.actions-dropdown-select__menu-list:has(.actions-dropdown-select__option--is-disabled:hover) + .actions-dropdown-select__option--is-focused { + background-color: transparent; +} diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tests.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tests.tsx index 6b39371ec2..291ee4f4d2 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tests.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tests.tsx @@ -158,17 +158,27 @@ describe("UsersForm", () => { }); describe("platform tabs", () => { - it("hides the Windows tab when Windows MDM is not enabled and configured", () => { - renderWithMdmEnabled(); + it("keeps the Windows tab but disables the checkbox when Windows MDM is not configured", async () => { + const { user } = renderWithMdmEnabled(); expect(screen.getByRole("tab", { name: "macOS" })).toBeInTheDocument(); + + await user.click(screen.getByRole("tab", { name: "Windows" })); + + // The Checkbox renders a div with role="checkbox", so aria-disabled is the disabled signal. expect( - screen.queryByRole("tab", { name: "Windows" }) - ).not.toBeInTheDocument(); + screen.getByRole("checkbox", { name: "Create hidden admin" }) + ).toHaveAttribute("aria-disabled", "true"); }); - it("shows the Windows tab when Windows MDM is enabled and configured", () => { - renderWithWindowsMdmEnabled(); - expect(screen.getByRole("tab", { name: "Windows" })).toBeInTheDocument(); + it("enables the checkbox when Windows MDM is enabled and configured", async () => { + const { user } = renderWithWindowsMdmEnabled( + + ); + await user.click(screen.getByRole("tab", { name: "Windows" })); + + expect( + screen.getByRole("checkbox", { name: "Create hidden admin" }) + ).toHaveAttribute("aria-disabled", "false"); }); // Which section each tab renders. The sections' own contents are their components' concern, so this only pins the wiring. diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tsx index 90c9145232..51b07d75b5 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tsx @@ -196,11 +196,9 @@ const UsersForm = ({ macOS - {isWindowsMdmEnabledAndConfigured && ( - - Windows - - )} + + Windows + - {isWindowsMdmEnabledAndConfigured && ( - - - - )} + + + {endUserAuthEnabled && (
- - To enable, first turn on{" "} - - . - - ) : undefined - } - disableTooltip={!!isMacMdmEnabledAndConfigured} - underline={false} - position="left" - showArrow + Lock end user info - +
)} diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx index e29b25da84..5580884efe 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx @@ -1,14 +1,11 @@ import React from "react"; -import PATHS from "router/paths"; - -import CustomLink from "components/CustomLink"; -import TooltipWrapper from "components/TooltipWrapper"; import Radio from "components/forms/fields/Radio"; import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { EndUserLocalAccountType } from "interfaces/mdm"; import ManagedAccountCheckbox from "../ManagedAccountCheckbox"; +import TurnOnMdmTooltipWrapper from "../TurnOnMdmTooltipWrapper"; import { IUsersFormData } from "../../UsersForm"; const baseClass = "local-account-section"; @@ -38,24 +35,9 @@ const LocalAccountSection = ({ localAccountType !== EndUserLocalAccountType.ADMIN; return (
- - To enable, first turn on{" "} - - . - - ) : undefined - } - disableTooltip={isMacMdmEnabledAndConfigured} - underline={false} - position="left" - showArrow + - +
); }; diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/TurnOnMdmTooltipWrapper.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/TurnOnMdmTooltipWrapper.tsx new file mode 100644 index 0000000000..dc3baaaa5c --- /dev/null +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/TurnOnMdmTooltipWrapper.tsx @@ -0,0 +1,47 @@ +import React from "react"; + +import PATHS from "router/paths"; + +import CustomLink from "components/CustomLink"; +import TooltipWrapper from "components/TooltipWrapper"; + +const MDM_BY_PLATFORM = { + apple: { url: PATHS.ADMIN_INTEGRATIONS_MDM_APPLE, text: "Apple MDM" }, + windows: { url: PATHS.ADMIN_INTEGRATIONS_MDM_WINDOWS, text: "Windows MDM" }, +} as const; + +interface ITurnOnMdmTooltipWrapperProps { + platform: keyof typeof MDM_BY_PLATFORM; + isMdmEnabledAndConfigured: boolean; + children: React.ReactNode; +} + +/** Explains that a control is disabled because the platform's MDM isn't turned on yet, and links to the page that turns it on. */ +const TurnOnMdmTooltipWrapper = ({ + platform, + isMdmEnabledAndConfigured, + children, +}: ITurnOnMdmTooltipWrapperProps) => { + const { url, text } = MDM_BY_PLATFORM[platform]; + + return ( + + To enable, first turn on{" "} + . + + ) : undefined + } + disableTooltip={isMdmEnabledAndConfigured} + underline={false} + position="left" + showArrow + > + {children} + + ); +}; + +export default TurnOnMdmTooltipWrapper; diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/index.ts b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/index.ts new file mode 100644 index 0000000000..8f18c8b88a --- /dev/null +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/index.ts @@ -0,0 +1 @@ +export { default } from "./TurnOnMdmTooltipWrapper"; diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx index 6cde72508c..41c5ece1c9 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx @@ -5,18 +5,21 @@ import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants"; import ManagedAccountCheckbox from "../ManagedAccountCheckbox"; +import TurnOnMdmTooltipWrapper from "../TurnOnMdmTooltipWrapper"; const baseClass = "windows-account-section"; interface IWindowsAccountSectionProps { enableManagedLocalAccount: boolean; onEnableManagedLocalAccountChange: (value: boolean) => void; + isWindowsMdmEnabledAndConfigured: boolean; } -/** Windows tab of the Users card. The tab is only rendered when Windows MDM is configured, so unlike the macOS section there is no "turn on MDM first" state to handle here. */ +/** Windows tab of the Users card. The managed account checkbox mirrors the macOS tab, including the disabled state that points admins at Windows MDM when it isn't turned on yet. */ const WindowsAccountSection = ({ enableManagedLocalAccount, onEnableManagedLocalAccountChange, + isWindowsMdmEnabledAndConfigured, }: IWindowsAccountSectionProps) => { return (
@@ -32,18 +35,23 @@ const WindowsAccountSection = ({

Managed account

- ( - - )} - /> + + ( + + )} + /> + ); diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss index 1659d8f801..7a4e851b68 100644 --- a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss +++ b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss @@ -13,3 +13,9 @@ min-width: 200px; } } + +// Left-align error tooltip. +.host-actions-dropdown__managed-account-error { + display: block; + text-align: left; +} diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx index 5a15dcb728..fe1575bf19 100644 --- a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx +++ b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx @@ -857,7 +857,11 @@ const modifyOptions = ( ); } else if (managedAccountStatus === "failed") { // The reason the host reported is the actionable part, so prefer it over generic copy. - managedAccountOption.tooltipContent = managedAccountDetail || ( + managedAccountOption.tooltipContent = managedAccountDetail ? ( + + {managedAccountDetail} + + ) : ( <> The managed account failed to be
diff --git a/orbit/pkg/managedaccount/managedaccount_windows.go b/orbit/pkg/managedaccount/managedaccount_windows.go index b1e5439a9b..345bf3683d 100644 --- a/orbit/pkg/managedaccount/managedaccount_windows.go +++ b/orbit/pkg/managedaccount/managedaccount_windows.go @@ -128,7 +128,7 @@ func ensureUser(username, password string) error { 0, // parm_err ) if ret != 0 { - return accountError(fmt.Sprintf("resetting password for %s", username), ret, len(password)) + return accountError(fmt.Sprintf("Resetting password for %s", username), ret, len(password)) } // Resetting the password is not enough to make the account usable again. If it was disabled, locked out, or had // its never-expire flag removed after we created it, Fleet would escrow a password that cannot actually log in. @@ -154,7 +154,7 @@ func ensureUser(username, password string) error { 0, // parm_err ) if ret != 0 { - return accountError(fmt.Sprintf("creating %s", username), ret, len(password)) + return accountError(fmt.Sprintf("Creating %s", username), ret, len(password)) } return nil } @@ -166,8 +166,8 @@ func ensureUser(username, password string) error { func accountError(op string, ret uintptr, passwordLen int) error { if ret == nerrPasswordTooShort || ret == errorPasswordRestriction { return fmt.Errorf( - "%s: this device's password policy rejected the generated %d-character password; "+ - "check the minimum password length and any custom password filter on the host", + "%s. This device's password policy rejected the generated %d-character password. "+ + "Check any custom password filter on the host.", op, passwordLen) } return fmt.Errorf("%s: %w", op, windows.Errno(ret))