;
+ editable?: boolean;
+ overrideProps?: (propName: string, propValue: string) => void;
}
-export function PropList({ list }: PropListProps): React.ReactElement {
+export function PropList({
+ list,
+ editable,
+ overrideProps,
+}: PropListProps): React.ReactElement {
const value = isDehydrated(list)
? list[PROP_DEHYDRATED].children || {}
: list;
@@ -17,10 +23,22 @@ export function PropList({ list }: PropListProps): React.ReactElement {
{Array.isArray(value)
? value.map((item, index) => (
-
+
))
: Object.entries(value).map((entry) => (
-
+
))}
);
@@ -30,19 +48,54 @@ interface PropItemProps {
propValue: any;
propName?: string;
standalone?: boolean;
+ editable?: boolean;
+ overrideProps?: (propName: string, propValue: string) => void;
}
export function PropItem({
propValue,
propName,
standalone,
+ editable,
+ overrideProps,
}: PropItemProps): React.ReactElement {
const [expanded, setExpanded] = React.useState(false);
+ const [editing, setEditing] = React.useState(false);
+ const [changeValue, setChangeValue] = React.useState(
+ JSON.stringify(propValue, null, 2)
+ );
+ const [error, setError] = React.useState(false);
const handleClick = React.useCallback(() => {
setExpanded(!expanded);
}, [expanded]);
+ const handleDoubleClick = React.useCallback(() => {
+ if (editable) {
+ setEditing(true);
+ }
+ }, [editable]);
+
+ const handleBlur = () => {
+ try {
+ let result;
+ if (changeValue === "" || changeValue === undefined) {
+ result = undefined;
+ } else {
+ result = JSON.parse(changeValue);
+ }
+ overrideProps?.(propName, changeValue);
+ setEditing(false);
+ setError(false);
+ } catch (error) {
+ setError(true);
+ }
+ };
+
+ const handleChange = (v) => {
+ setChangeValue(v.target?.value);
+ };
+
const hasChildren = isObject(propValue);
return React.createElement(
@@ -51,29 +104,47 @@ export function PropItem({
className: classNames("prop-item", { expanded }),
},
<>
-
- {(!standalone || hasChildren) && (
-
+
+ {(!standalone || hasChildren) && (
+
+ )}
+ {!standalone && (
+ <>
+ {propName}
+ :{" "}
+ >
+ )}
+
+ {editing && editable ? (
+
+ ) : (
+
+
+
)}
- {!standalone && (
- <>
- {propName}
- :{" "}
- >
- )}
-
-
-
- {hasChildren && expanded && }
+ {hasChildren && expanded && !editing && (
+
+ )}
>
);
}
diff --git a/src/panel/components/PropView.spec.tsx b/src/panel/components/PropView.spec.tsx
index b62c15a..9dfb009 100644
--- a/src/panel/components/PropView.spec.tsx
+++ b/src/panel/components/PropView.spec.tsx
@@ -56,10 +56,11 @@ describe("PropView", () => {
expect(wrapper.find(PropList).at(0).prop("list")).toEqual({
quality: "good",
});
+ expect(wrapper.find(PropList).at(0).prop("editable")).toBe(true);
expect(wrapper.find(PropList).at(1).prop("list")).toEqual([
["click", { action: "console.log" }],
]);
-
+ expect(wrapper.find(PropList).at(1).prop("editable")).toBe(false);
(useSelectedBrickContext as jest.Mock).mockReset();
});
@@ -75,4 +76,26 @@ describe("PropView", () => {
expect(document.execCommand).toBeCalledWith("copy");
expect(document.removeEventListener).toHaveBeenCalled();
});
+
+ it("should work when overrideProps", () => {
+ const selectedBrick = {
+ uid: 1,
+ };
+ (useSelectedBrickContext as jest.Mock).mockReturnValue({ selectedBrick });
+ const wrapper = mount();
+ expect(mockEval.mock.calls[0][0]).toBe(
+ "window.__BRICK_NEXT_DEVTOOLS_HOOK__.getBrickInfo(1)"
+ );
+ const mockEvalOverrideProps = jest.fn();
+ (window as any).chrome = {
+ devtools: {
+ inspectedWindow: {
+ eval: mockEvalOverrideProps,
+ },
+ },
+ };
+ wrapper.find(PropList).at(0).invoke("overrideProps")("quality", "bad");
+ expect(mockEvalOverrideProps).toHaveBeenCalled();
+ (useSelectedBrickContext as jest.Mock).mockReset();
+ });
});
diff --git a/src/panel/components/PropView.tsx b/src/panel/components/PropView.tsx
index e2f21ee..f5a9440 100644
--- a/src/panel/components/PropView.tsx
+++ b/src/panel/components/PropView.tsx
@@ -36,21 +36,36 @@ export function PropView(): React.ReactElement {
const { selectedBrick } = useSelectedBrickContext();
const [brickInfo, setBrickInfo] = React.useState({});
+ const handleBrickInfoChange = React.useCallback(() => {
+ chrome.devtools.inspectedWindow.eval(
+ `window.${HOOK_NAME}.getBrickInfo(${selectedBrick.uid})`,
+ function (result: DehydratedBrickInfo, error) {
+ // istanbul ignore if
+ if (error) {
+ console.error("getBrickInfo()", error, result);
+ }
+ setBrickInfo(hydrate(result.info, result.repo));
+ }
+ );
+ }, [selectedBrick]);
+
React.useEffect(() => {
if (selectedBrick) {
- chrome.devtools.inspectedWindow.eval(
- `window.${HOOK_NAME}.getBrickInfo(${selectedBrick.uid})`,
- function (result: DehydratedBrickInfo, error) {
- // istanbul ignore if
- if (error) {
- console.error("getBrickInfo()", error, result);
- }
-
- setBrickInfo(hydrate(result.info, result.repo));
- }
- );
+ handleBrickInfoChange();
}
- }, [selectedBrick]);
+ }, [selectedBrick, handleBrickInfoChange]);
+
+ const overrideProps = React.useCallback(
+ (propName: string, propValue: string) => {
+ // istanbul ignore else
+ if (selectedBrick) {
+ const evalParams = `window.${HOOK_NAME}.overrideProps(${selectedBrick.uid},"${propName}",${propValue})`;
+ chrome.devtools.inspectedWindow.eval(evalParams);
+ handleBrickInfoChange();
+ }
+ },
+ [selectedBrick, handleBrickInfoChange]
+ );
if (!selectedBrick || !brickInfo) {
return null;
@@ -87,7 +102,11 @@ export function PropView(): React.ReactElement {
)}
))}
diff --git a/src/panel/style.css b/src/panel/style.css
index 1c05d07..bd99859 100644
--- a/src/panel/style.css
+++ b/src/panel/style.css
@@ -156,7 +156,7 @@ body {
cursor: default;
}
-.prop-item-label > .bp3-icon {
+.prop-item-label > span > .bp3-icon {
/* margin-top: -1px; */
vertical-align: middle;
color: var(--caret-color);
@@ -166,6 +166,13 @@ body {
color: var(--source-code-name);
}
+.prop-editable {
+ font-size: 12px;
+ margin: 10px 0 10px 18px;
+ display: block;
+ width: 90%;
+}
+
.variable-string {
color: var(--source-code-string);
}