diff --git a/changes/issue-4864-enter-submits-form b/changes/issue-4864-enter-submits-form new file mode 100644 index 0000000000..3628a889dc --- /dev/null +++ b/changes/issue-4864-enter-submits-form @@ -0,0 +1 @@ +* Pressing enter submits forms app-wide \ No newline at end of file diff --git a/cypress/integration/all/app/labelflow.spec.ts b/cypress/integration/all/app/labelflow.spec.ts index 6158d0643b..05d0b56c05 100644 --- a/cypress/integration/all/app/labelflow.spec.ts +++ b/cypress/integration/all/app/labelflow.spec.ts @@ -49,7 +49,7 @@ describe("Labels flow", () => { cy.findByText(/show all mac usernames/i).click(); }); cy.getAttached(".manage-hosts__label-block button").last().click(); - cy.getAttached(".manage-hosts__modal-buttons > .button--alert") + cy.getAttached(".delete-label-modal") .contains("button", /delete/i) .click(); cy.getAttached(".host-side-panel").within(() => { diff --git a/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx b/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx index 8cc8a5c29d..ab89dc6a18 100644 --- a/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx +++ b/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import Modal from "components/Modal"; import Button from "components/buttons/Button"; // @ts-ignore @@ -31,6 +31,20 @@ const EnrollSecretModal = ({ setSelectedSecret, globalSecrets, }: IEnrollSecretModal): JSX.Element => { + useEffect(() => { + const listener = (event: KeyboardEvent) => { + if (event.code === "Enter" || event.code === "NumpadEnter") { + event.preventDefault(); + onReturnToApp(); + } + }; + + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, []); + const renderTeam = () => { if (typeof selectedTeam === "string") { selectedTeam = parseInt(selectedTeam, 10); diff --git a/frontend/components/forms/LabelForm/LabelForm.tsx b/frontend/components/forms/LabelForm/LabelForm.tsx index 4efa94b3fb..7e1d70a423 100644 --- a/frontend/components/forms/LabelForm/LabelForm.tsx +++ b/frontend/components/forms/LabelForm/LabelForm.tsx @@ -91,6 +91,25 @@ const LabelForm = ({ debounceSQL(query); }, [query]); + useEffect(() => { + const listener = (event: KeyboardEvent) => { + if (event.code === "Enter" || event.code === "NumpadEnter") { + event.preventDefault(); + handleSubmit({ + name, + query, + description, + platform, + }); + } + }; + + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, [name, query, description, platform]); + const onLoad = (editor: IAceEditor) => { editor.setOptions({ enableLinking: true, diff --git a/frontend/components/forms/packs/PackForm/PackForm.tsx b/frontend/components/forms/packs/PackForm/PackForm.tsx index 84f10e2815..c8060fac9b 100644 --- a/frontend/components/forms/packs/PackForm/PackForm.tsx +++ b/frontend/components/forms/packs/PackForm/PackForm.tsx @@ -37,6 +37,7 @@ const EditPackForm = ({ const onChangePackName = (value: string) => { setPackName(value); + setErrors({}); }; const onChangePackDescription = (value: string) => { diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx index 7e427ff295..e9dc1344d8 100644 --- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx +++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useState, useEffect } from "react"; import { INewMembersBody, ITeam } from "interfaces/team"; import endpoints from "fleet/endpoints"; @@ -41,7 +41,7 @@ const AddMemberModal = ({ }, [selectedMembers, onSubmit]); return ( - +

Add team members

{ + useEffect(() => { + const listener = (event: KeyboardEvent) => { + if (event.code === "Enter" || event.code === "NumpadEnter") { + event.preventDefault(); + onSubmit(); + } + }; + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, []); + return ( - +

You are about to remove{" "} diff --git a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx index 3088089f79..6826069c99 100644 --- a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx +++ b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx @@ -48,7 +48,6 @@ import Button from "components/buttons/Button"; // @ts-ignore import Dropdown from "components/forms/fields/Dropdown"; import HostSidePanel from "components/side_panels/HostSidePanel"; // @ts-ignore import LabelForm from "components/forms/LabelForm"; -import Modal from "components/Modal"; import QuerySidePanel from "components/side_panels/QuerySidePanel"; import TableContainer from "components/TableContainer"; import TableDataError from "components/TableDataError"; @@ -84,6 +83,7 @@ import PoliciesFilter from "./components/PoliciesFilter"; // @ts-ignore import EditColumnsModal from "./components/EditColumnsModal/EditColumnsModal"; import TransferHostModal from "./components/TransferHostModal"; import DeleteHostModal from "./components/DeleteHostModal"; +import DeleteLabelModal from "./components/DeleteLabelModal"; import SoftwareVulnerabilities from "./components/SoftwareVulnerabilities"; import EditColumnsIcon from "../../../../assets/images/icon-edit-columns-16x16@2x.png"; import PencilIcon from "../../../../assets/images/icon-pencil-14x14@2x.png"; @@ -378,7 +378,6 @@ const ManageHostsPage = ({ setShowEnrollSecretModal(!showEnrollSecretModal); }; - // this is called when we click add or edit const toggleSecretEditorModal = () => { // open and closes add/edit modal setShowSecretEditorModal(!showSecretEditorModal); @@ -402,6 +401,10 @@ const ManageHostsPage = ({ setShowAddHostsModal(!showAddHostsModal); }; + const toggleEditColumnsModal = () => { + setShowEditColumnsModal(!showEditColumnsModal); + }; + const toggleAllMatchingHosts = (shouldSelect: boolean) => { if (typeof shouldSelect !== "undefined") { setIsAllMatchingHostsSelected(shouldSelect); @@ -674,25 +677,13 @@ const ManageHostsPage = ({ ); }; - const onEditColumnsClick = () => { - setShowEditColumnsModal(true); - }; - - const onCancelColumns = () => { - setShowEditColumnsModal(false); - }; - const onSaveColumns = (newHiddenColumns: string[]) => { localStorage.setItem("hostHiddenColumns", JSON.stringify(newHiddenColumns)); setHiddenColumns(newHiddenColumns); setShowEditColumnsModal(false); }; - const onCancelAddLabel = () => { - router.goBack(); - }; - - const onCancelEditLabel = () => { + const onCancelLabel = () => { router.goBack(); }; @@ -1086,28 +1077,26 @@ const ManageHostsPage = ({ /> ); - const renderPoliciesFilterBlock = () => { - return ( -

- -
- Policy - {policy?.name} - -
+ const renderPoliciesFilterBlock = () => ( +
+ +
+ Policy + {policy?.name} +
- ); - }; +
+ ); const renderSoftwareFilterBlock = () => { if (softwareDetails) { @@ -1155,120 +1144,68 @@ const ManageHostsPage = ({ }; const renderEditColumnsModal = () => { - if (!showEditColumnsModal || !config || !currentUser) { + if (!config || !currentUser) { return null; } return ( - setShowEditColumnsModal(false)} - className={`${baseClass}__invite-modal`} - > - - - ); - }; - - const renderSecretEditorModal = () => { - if (!canEnrollHosts || !showSecretEditorModal) { - return null; - } - - return ( - ); }; - const renderDeleteSecretModal = () => { - if (!canEnrollHosts || !showDeleteSecretModal) { - return null; - } + const renderSecretEditorModal = () => ( + + ); - return ( - - ); - }; + const renderDeleteSecretModal = () => ( + + ); - const renderEnrollSecretModal = () => { - if (!canEnrollHosts || !showEnrollSecretModal) { - return null; - } + const renderEnrollSecretModal = () => ( + setShowEnrollSecretModal(false)} + toggleSecretEditorModal={toggleSecretEditorModal} + toggleDeleteSecretModal={toggleDeleteSecretModal} + setSelectedSecret={setSelectedSecret} + globalSecrets={globalSecrets} + /> + ); - return ( - setShowEnrollSecretModal(false)} - toggleSecretEditorModal={toggleSecretEditorModal} - toggleDeleteSecretModal={toggleDeleteSecretModal} - setSelectedSecret={setSelectedSecret} - globalSecrets={globalSecrets} - /> - ); - }; + const renderDeleteLabelModal = () => ( + + ); - const renderDeleteLabelModal = () => { - if (!showDeleteLabelModal) { - return false; - } - - return ( - - <> -

Are you sure you wish to delete this label?

-
- - -
- -
- ); - }; - - const renderAddHostsModal = () => { - if (!showAddHostsModal) { - return null; - } - - return ( - - ); - }; + const renderAddHostsModal = () => ( + + ); const renderTransferHostModal = () => { - if (!showTransferHostModal || !teams) { + if (!teams) { return null; } @@ -1282,20 +1219,14 @@ const ManageHostsPage = ({ ); }; - const renderDeleteHostModal = () => { - if (!showDeleteHostModal) { - return null; - } - - return ( - - ); - }; + const renderDeleteHostModal = () => ( + + ); const renderHeaderLabelBlock = () => { if (selectedLabel) { @@ -1332,25 +1263,23 @@ const ManageHostsPage = ({ return null; }; - const renderHeader = () => { - return ( -
-
-
- {isFreeTier &&

Hosts

} - {isPremiumTier && - availableTeams && - (availableTeams.length > 1 || isOnGlobalTeam) && - renderTeamsFilterDropdown()} - {isPremiumTier && - !isOnGlobalTeam && - availableTeams && - availableTeams.length === 1 &&

{availableTeams[0].name}

} -
+ const renderHeader = () => ( +
+
+
+ {isFreeTier &&

Hosts

} + {isPremiumTier && + availableTeams && + (availableTeams.length > 1 || isOnGlobalTeam) && + renderTeamsFilterDropdown()} + {isPremiumTier && + !isOnGlobalTeam && + availableTeams && + availableTeams.length === 1 &&

{availableTeams[0].name}

}
- ); - }; +
+ ); const onExportHostsResults = async ( evt: React.MouseEvent @@ -1455,7 +1384,7 @@ const ManageHostsPage = ({ return (
{ - return ( - - ); - }; + const renderStatusDropdown = () => ( + + ); const renderTable = () => { if ( @@ -1606,7 +1533,7 @@ const ManageHostsPage = ({ } emptyComponent={EmptyHosts} customControl={renderStatusDropdown} - onActionButtonClick={onEditColumnsClick} + onActionButtonClick={toggleEditColumnsModal} onPrimarySelectActionClick={onDeleteHostsClick} onQueryChange={onTableQueryChange} toggleAllPagesSelected={toggleAllMatchingHosts} @@ -1703,14 +1630,14 @@ const ManageHostsPage = ({
)} {renderSidePanel()} - {renderDeleteSecretModal()} - {renderSecretEditorModal()} - {renderEnrollSecretModal()} - {renderEditColumnsModal()} - {renderDeleteLabelModal()} - {renderAddHostsModal()} - {renderTransferHostModal()} - {renderDeleteHostModal()} + {canEnrollHosts && showDeleteSecretModal && renderDeleteSecretModal()} + {canEnrollHosts && showSecretEditorModal && renderSecretEditorModal()} + {canEnrollHosts && showEnrollSecretModal && renderEnrollSecretModal()} + {showEditColumnsModal && renderEditColumnsModal()} + {showDeleteLabelModal && renderDeleteLabelModal()} + {showAddHostsModal && renderAddHostsModal()} + {showTransferHostModal && renderTransferHostModal()} + {showDeleteHostModal && renderDeleteHostModal()}
); }; diff --git a/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/DeleteLabelModal.tsx b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/DeleteLabelModal.tsx new file mode 100644 index 0000000000..43506fdfba --- /dev/null +++ b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/DeleteLabelModal.tsx @@ -0,0 +1,47 @@ +import React, { useEffect } from "react"; + +import Modal from "components/Modal"; +import Button from "components/buttons/Button"; + +const baseClass = "delete-label-modal"; + +interface IDeleteLabelModalProps { + onSubmit: () => void; + onCancel: () => void; +} + +const DeleteLabelModal = ({ + onSubmit, + onCancel, +}: IDeleteLabelModalProps): JSX.Element => { + useEffect(() => { + const listener = (event: KeyboardEvent) => { + if (event.code === "Enter" || event.code === "NumpadEnter") { + event.preventDefault(); + onSubmit(); + } + }; + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, []); + + return ( + + <> +

Are you sure you wish to delete this label?

+
+ + +
+ +
+ ); +}; + +export default DeleteLabelModal; diff --git a/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/_styles.scss b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/_styles.scss new file mode 100644 index 0000000000..6ab1d046e8 --- /dev/null +++ b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/_styles.scss @@ -0,0 +1,11 @@ +.delete-label-modal { + &__btn-wrap { + display: flex; + flex-direction: row-reverse; + margin-top: $pad-xxlarge; + } + + &__btn { + margin-left: 12px; + } +} diff --git a/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/index.ts b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/index.ts new file mode 100644 index 0000000000..baa937e25c --- /dev/null +++ b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/index.ts @@ -0,0 +1 @@ +export { default } from "./DeleteLabelModal"; diff --git a/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx b/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx index fd46e196a3..4e92813294 100644 --- a/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx +++ b/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import PropTypes from "prop-types"; +import Modal from "components/Modal"; import Checkbox from "../../../../../components/forms/fields/Checkbox"; import Button from "../../../../../components/buttons/Button"; @@ -57,37 +58,43 @@ const EditColumnsModal = ({ ); return ( -
-

Choose which columns you see:

-
- {columnItems.map((column) => { - if (column.disableHidden) return null; - return ( -
- updateColumnItems(column.accessor)} - > - {column.name} - -
- ); - })} -
-
- - -
-
+ + <> +

Choose which columns you see:

+
+ {columnItems.map((column) => { + if (column.disableHidden) return null; + return ( +
+ updateColumnItems(column.accessor)} + > + {column.name} + +
+ ); + })} +
+
+ + +
+ +
); }; diff --git a/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx index 1e60cea215..18fd1609c8 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { IPolicy } from "interfaces/policy"; import { IWebhookFailingPolicies } from "interfaces/webhook"; @@ -120,7 +120,9 @@ const ManageAutomationsModal = ({ setDestinationUrl(value); }; - const handleSaveAutomation = (evt: React.MouseEvent) => { + const handleSaveAutomation = ( + evt: React.MouseEvent | KeyboardEvent + ) => { evt.preventDefault(); const { valid, errors: newErrors } = validateWebhookURL(destination_url); @@ -147,6 +149,19 @@ const ManageAutomationsModal = ({ } }; + useEffect(() => { + const listener = (event: KeyboardEvent) => { + if (event.code === "Enter" || event.code === "NumpadEnter") { + event.preventDefault(); + handleSaveAutomation(event); + } + }; + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, [handleSaveAutomation]); + if (showPreviewPayloadModal) { return ; } diff --git a/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx b/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx index 8f77433e91..5abca7098a 100644 --- a/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx +++ b/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx @@ -1,6 +1,6 @@ /* This component is used for creating and editing both global and team scheduled queries */ -import React, { useState, useCallback, useContext } from "react"; +import React, { useState, useCallback, useContext, useEffect } from "react"; import { pull } from "lodash"; import { AppContext } from "context/app"; @@ -218,6 +218,19 @@ const ScheduleEditorModal = ({ ); }; + useEffect(() => { + const listener = (event: KeyboardEvent) => { + if (event.code === "Enter" || event.code === "NumpadEnter") { + event.preventDefault(); + onFormSubmit(); + } + }; + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, [onFormSubmit]); + if (showPreviewDataModal) { return ; }