From 24f13ca532816b17d7dbe8c085a16529d360f681 Mon Sep 17 00:00:00 2001 From: Kusha Gharahi <3326002+kushagharahi@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:28:01 -0500 Subject: [PATCH 1/3] Add discord option to notifications --- .../NotificationFormatSelector.jsx | 6 ++- .../WebHookNotificationMethod.jsx | 47 +++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/components/notifications/NotificationFormatSelector.jsx b/src/components/notifications/NotificationFormatSelector.jsx index dc2647c1..609e2636 100644 --- a/src/components/notifications/NotificationFormatSelector.jsx +++ b/src/components/notifications/NotificationFormatSelector.jsx @@ -3,7 +3,7 @@ import Form from "react-bootstrap/Form"; import Col from "react-bootstrap/Col"; import { stateProperty } from "../../forms"; -export function NotificationFormatSelector(component, name) { +export function NotificationFormatSelector(component, name, { lockPlainText = false }) { return ( Notification Format @@ -12,7 +12,9 @@ export function NotificationFormatSelector(component, name) { size="sm" name={name} onChange={(e) => component.handleChange(e)} - value={stateProperty(component, name)} + value={lockPlainText ? "txt" : stateProperty(component, name)} + className={lockPlainText ? "opacity-50" : ""} + disabled={lockPlainText} > diff --git a/src/components/notifications/WebHookNotificationMethod.jsx b/src/components/notifications/WebHookNotificationMethod.jsx index 4d99745b..8419914a 100644 --- a/src/components/notifications/WebHookNotificationMethod.jsx +++ b/src/components/notifications/WebHookNotificationMethod.jsx @@ -14,6 +14,7 @@ export class WebHookNotificationMethod extends Component { this.state = { format: "txt", method: "POST", + discord: false, ...props.initial, }; this.handleChange = handleChange.bind(this); @@ -32,27 +33,43 @@ export class WebHookNotificationMethod extends Component { <> {RequiredField(this, "URL Endpoint", "endpoint", { autoFocus: true })} - - HTTP Method - this.handleChange(e)} - value={stateProperty(this, "method")} - > - - - - - {NotificationFormatSelector(this, "format")} + + { + this.setState({ discord: e.target.checked }); + }} + /> + + + + + + HTTP Method + this.handleChange(e)} + value={this.state.discord ? "POST" : stateProperty(this, "method")} + disabled={this.state.discord} + className={this.state.discord ? "opacity-50" : ""} + > + + + + + {NotificationFormatSelector(this, "format", { lockPlainText: this.state.discord })} + {OptionalField( this, "Additional Headers", "headers", - { as: "textarea", rows: 5 }, + { as: "textarea", rows: 5, disabled: this.state.discord, className: this.state.discord ? "opacity-50" : "" }, "Enter one header per line in the format 'Header: Value'.", )} From ca6124a686d94043bd7dbc4814c446f76ad8c3c3 Mon Sep 17 00:00:00 2001 From: Kusha Gharahi <3326002+kushagharahi@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:44:34 -0500 Subject: [PATCH 2/3] add label to checkbox instead --- src/components/notifications/WebHookNotificationMethod.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/notifications/WebHookNotificationMethod.jsx b/src/components/notifications/WebHookNotificationMethod.jsx index 8419914a..6289968e 100644 --- a/src/components/notifications/WebHookNotificationMethod.jsx +++ b/src/components/notifications/WebHookNotificationMethod.jsx @@ -33,10 +33,10 @@ export class WebHookNotificationMethod extends Component { <> {RequiredField(this, "URL Endpoint", "endpoint", { autoFocus: true })} - + + Discord { this.setState({ discord: e.target.checked }); From 5ac616aeddbe5418ba47ab13e00acfc8d36ea1e3 Mon Sep 17 00:00:00 2001 From: Kusha Gharahi <3326002+kushagharahi@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:01:28 -0500 Subject: [PATCH 3/3] add tests --- .../NotificationFormatSelector.jsx | 3 +- .../WebHookNotificationMethod.jsx | 2 + .../WebHookNotificationMethod.test.jsx | 42 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/components/notifications/NotificationFormatSelector.jsx b/src/components/notifications/NotificationFormatSelector.jsx index 609e2636..0a7a666b 100644 --- a/src/components/notifications/NotificationFormatSelector.jsx +++ b/src/components/notifications/NotificationFormatSelector.jsx @@ -3,7 +3,7 @@ import Form from "react-bootstrap/Form"; import Col from "react-bootstrap/Col"; import { stateProperty } from "../../forms"; -export function NotificationFormatSelector(component, name, { lockPlainText = false }) { +export function NotificationFormatSelector(component, name, { lockPlainText = false } = {}) { return ( Notification Format @@ -11,6 +11,7 @@ export function NotificationFormatSelector(component, name, { lockPlainText = fa as="select" size="sm" name={name} + data-testid="notification-format" onChange={(e) => component.handleChange(e)} value={lockPlainText ? "txt" : stateProperty(component, name)} className={lockPlainText ? "opacity-50" : ""} diff --git a/src/components/notifications/WebHookNotificationMethod.jsx b/src/components/notifications/WebHookNotificationMethod.jsx index 6289968e..69d12891 100644 --- a/src/components/notifications/WebHookNotificationMethod.jsx +++ b/src/components/notifications/WebHookNotificationMethod.jsx @@ -38,6 +38,7 @@ export class WebHookNotificationMethod extends Component { { this.setState({ discord: e.target.checked }); }} @@ -52,6 +53,7 @@ export class WebHookNotificationMethod extends Component { as="select" size="sm" name="method" + data-testid="http-method" onChange={(e) => this.handleChange(e)} value={this.state.discord ? "POST" : stateProperty(this, "method")} disabled={this.state.discord} diff --git a/tests/components/notifications/WebHookNotificationMethod.test.jsx b/tests/components/notifications/WebHookNotificationMethod.test.jsx index 4fb0f75e..a4efe117 100644 --- a/tests/components/notifications/WebHookNotificationMethod.test.jsx +++ b/tests/components/notifications/WebHookNotificationMethod.test.jsx @@ -20,5 +20,47 @@ it("can set fields", async () => { method: "POST", format: "txt", headers: "some:header\nanother:header", + discord: false }); }); + +describe("Discord functionality", () => { + it("toggles Discord correctly and updates related fields", async () => { + let ref = React.createRef(); + const { getByTestId } = render(); + + // Fill endpoint first so validation passes + fireEvent.change(getByTestId("control-endpoint"), { + target: { value: "http://some-endpoint:12345" }, + }); + expect(ref.current.validate()).toBe(true); + + const discordCheckbox = getByTestId("discord"); + const methodSelect = getByTestId("http-method"); + const headersTextarea = getByTestId("control-headers"); + const formatSelect = getByTestId("notification-format"); + + // Initially, Discord unchecked + expect(discordCheckbox.checked).toBe(false); + expect(methodSelect.value).toBe("POST"); + expect(methodSelect.disabled).toBe(false); + expect(headersTextarea.disabled).toBe(false); + expect(formatSelect.value).toBe("txt"); + + // Toggle Discord ON + fireEvent.click(discordCheckbox); + + expect(discordCheckbox.checked).toBe(true); + expect(methodSelect.value).toBe("POST"); // HTTP method forced to POST + expect(methodSelect.disabled).toBe(true); // method disabled + expect(headersTextarea.disabled).toBe(true); // headers disabled + expect(formatSelect.value).toBe("txt"); // format locked to plain text + + // Toggle Discord OFF + fireEvent.click(discordCheckbox); + + expect(discordCheckbox.checked).toBe(false); + expect(methodSelect.disabled).toBe(false); + expect(headersTextarea.disabled).toBe(false); + }); +}); \ No newline at end of file