void;
/** Show the fields without a way to change them */
readOnly?: boolean;
+ /** Drawn in one bar with the bulk-edit toggle, like a view-mode switch */
+ actions?: ReactNode;
};
export function FormUrlencodedEditor({
@@ -19,6 +21,7 @@ export function FormUrlencodedEditor({
forceUpdateKey,
onChange,
readOnly,
+ actions,
}: Props) {
const pairs = useMemo
(
() =>
@@ -52,6 +55,7 @@ export function FormUrlencodedEditor({
pairs={pairs}
onChange={handleChange}
readOnly={readOnly}
+ actions={actions}
forceUpdateKey={forceUpdateKey}
stateKey={`urlencoded.${requestId}`}
/>
diff --git a/packages/ui/src/components/pairs/PairOrBulkEditor.test.tsx b/packages/ui/src/components/pairs/PairOrBulkEditor.test.tsx
new file mode 100644
index 000000000..17d6134da
--- /dev/null
+++ b/packages/ui/src/components/pairs/PairOrBulkEditor.test.tsx
@@ -0,0 +1,40 @@
+import { renderToStaticMarkup } from "react-dom/server";
+import { describe, expect, test } from "vite-plus/test";
+import type { HostInputProps } from "../EditorHost";
+import { EditorHostProvider } from "../EditorHost";
+import { PairOrBulkEditor } from "./PairOrBulkEditor";
+
+function StubInput({ name, defaultValue }: HostInputProps) {
+ return {defaultValue}
;
+}
+
+function render(actions?: string) {
+ return renderToStaticMarkup(
+
+ {}}
+ actions={actions == null ? undefined : {actions}}
+ />
+ ,
+ );
+}
+
+describe("PairOrBulkEditor", () => {
+ test("with actions, its bulk toggle shares one actions bar with them", () => {
+ const markup = render("SWITCH");
+ expect(markup).toContain("group/actions");
+ // The toggle, then the given actions, inside the bar
+ expect(markup).toMatch(/group-hover\/actions[^>]*>.*Enable bulk edit.*SWITCH/);
+ });
+
+ test("without actions, its toggle keeps its own corner", () => {
+ const markup = render();
+ expect(markup).not.toContain("group/actions");
+ expect(markup).toContain("Enable bulk edit");
+ });
+});
diff --git a/packages/ui/src/components/pairs/PairOrBulkEditor.tsx b/packages/ui/src/components/pairs/PairOrBulkEditor.tsx
index 22f4facc4..7ac8fbea1 100644
--- a/packages/ui/src/components/pairs/PairOrBulkEditor.tsx
+++ b/packages/ui/src/components/pairs/PairOrBulkEditor.tsx
@@ -1,4 +1,6 @@
import classNames from "classnames";
+import type { ReactNode } from "react";
+import { ACTIONS_BAR_CONTAINER, ActionsBar } from "../ActionsBar";
import { useEditorHost } from "../EditorHost";
import { IconButton } from "../IconButton";
import { BulkPairEditor } from "./BulkPairEditor";
@@ -8,9 +10,14 @@ import { PairEditor } from "./PairEditor";
interface Props extends PairEditorProps {
preferenceName: string;
forcedEnvironmentId?: string;
+ /**
+ * Drawn after the bulk-edit toggle in one `ActionsBar`, like a view-mode switch. Giving it puts
+ * the toggle in that bar rather than its own corner; once every site has moved, it always is
+ */
+ actions?: ReactNode;
}
-export function PairOrBulkEditor({ preferenceName, ...props }: Props) {
+export function PairOrBulkEditor({ preferenceName, actions, ...props }: Props) {
const { useStoredValue } = useEditorHost();
const [useBulk, setUseBulk] = useStoredValue(
["bulk_edit", preferenceName],
@@ -18,20 +25,37 @@ export function PairOrBulkEditor({ preferenceName, ...props }: Props) {
"global",
);
+ const editor = useBulk ? : ;
+ const toggleProps = {
+ size: "sm",
+ variant: "border",
+ title: useBulk ? "Enable form edit" : "Enable bulk edit",
+ onClick: () => setUseBulk((b) => !b),
+ icon: useBulk ? "table" : "file_code",
+ } as const;
+
+ if (actions != null) {
+ return (
+
+ {editor}
+
+
+ {actions}
+
+
+ );
+ }
+
return (
- {useBulk ?
:
}
+ {editor}
setUseBulk((b) => !b)}
- icon={useBulk ? "table" : "file_code"}
/>