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