Skip to content

Commit

Permalink
feat(prop-view): override props
Browse files Browse the repository at this point in the history
  • Loading branch information
lynette-li committed Jul 30, 2020
1 parent 63fc4d0 commit 92bd665
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 31 deletions.
3 changes: 3 additions & 0 deletions src/hook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { HOOK_NAME } from "../shared/constants";
import { getBricks, getBrickByUid, getBrickInfo } from "./traverse";
import { inspectElement, dismissInspections } from "./inspector";
import { emit } from "./emit";
import { overrideProps } from "./overrideProps";

function injectHook(): void {
if (Object.prototype.hasOwnProperty.call(window, HOOK_NAME)) {
Expand All @@ -15,6 +16,8 @@ function injectHook(): void {
inspectBrick: (uid: number) => inspectElement(getBrickByUid(uid)),
dismissInspections,
emit,
overrideProps: (uid: number, propertyName: string, value: any) =>
overrideProps(getBrickByUid(uid), propertyName, value),
};

Object.defineProperty(hook, "pageHasBricks", {
Expand Down
17 changes: 17 additions & 0 deletions src/hook/overrideProps.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { overrideProps } from "./overrideProps";
import { BrickElement } from "../shared/interfaces";

describe("overrideProps", () => {
it("should work", () => {
overrideProps(undefined, "showCard", false);
const element =
{
tagName: "YOUR.awesome-brick",
} as BrickElement;
overrideProps(element, "showCard", false);
expect(element).toEqual({
tagName: "YOUR.awesome-brick",
showCard: false,
});
});
});
11 changes: 11 additions & 0 deletions src/hook/overrideProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { BrickElement } from "../shared/interfaces";

export function overrideProps(
element: BrickElement,
propertyName: string,
value: any
): void {
if (element) {
element[propertyName] = value;
}
}
21 changes: 17 additions & 4 deletions src/panel/components/PropList.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,17 @@ describe("PropItem", () => {
const wrapper = shallow(<PropItem propName="quality" propValue="good" />);
expect(wrapper.hasClass("expanded")).toBe(false);
expect(wrapper.find(Icon).prop("icon")).toBe("blank");
wrapper.find(".prop-item-label").invoke("onClick")(null);
// wrapper.find(".prop-item-label").invoke("onClick")(null);
// wrapper.find(`data-testid="prop-name-wrapper"`).invoke("onClick")(null);
wrapper.find('span[data-testid="prop-name-wrapper"]').invoke("onClick")(
null
);
expect(wrapper.hasClass("expanded")).toBe(true);
expect(wrapper.find(Icon).prop("icon")).toBe("blank");
wrapper.find(".prop-item-label").invoke("onClick")(null);
// wrapper.find(".prop-item-label").invoke("onClick")(null);
wrapper.find('span[data-testid="prop-name-wrapper"]').invoke("onClick")(
null
);
expect(wrapper.hasClass("expanded")).toBe(false);
expect(wrapper.find(Icon).prop("icon")).toBe("blank");
});
Expand All @@ -88,7 +95,10 @@ describe("PropItem", () => {
const wrapper = shallow(<PropItem propName="quality" propValue={[1]} />);
expect(wrapper.find(PropList).length).toBe(0);
expect(wrapper.find(Icon).prop("icon")).toBe("caret-right");
wrapper.find(".prop-item-label").invoke("onClick")(null);
// wrapper.find(".prop-item-label").invoke("onClick")(null);
wrapper.find('span[data-testid="prop-name-wrapper"]').invoke("onClick")(
null
);
expect(wrapper.find(PropList).prop("list")).toEqual([1]);
expect(wrapper.find(Icon).prop("icon")).toBe("caret-down");
});
Expand All @@ -102,7 +112,10 @@ describe("PropItem", () => {
it("should work for standalone with complex property value", () => {
const wrapper = shallow(<PropItem propValue={[1]} standalone />);
expect(wrapper.find(Icon).prop("icon")).toBe("caret-right");
wrapper.find(".prop-item-label").invoke("onClick")(null);
// wrapper.find(".prop-item-label").invoke("onClick")(null);
wrapper.find('span[data-testid="prop-name-wrapper"]').invoke("onClick")(
null
);
expect(wrapper.find(PropList).prop("list")).toEqual([1]);
expect(wrapper.find(Icon).prop("icon")).toBe("caret-down");
});
Expand Down
121 changes: 96 additions & 25 deletions src/panel/components/PropList.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
import React from "react";
import classNames from "classnames";
import { Icon } from "@blueprintjs/core";
import { Icon, TextArea } from "@blueprintjs/core";
import { PROP_DEHYDRATED } from "../../shared/constants";
import { VariableDisplay } from "./VariableDisplay";
import { isDehydrated, isObject } from "../libs/utils";

interface PropListProps {
list: any[] | Record<string, any>;
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;
return (
<ul className="prop-list">
{Array.isArray(value)
? value.map((item, index) => (
<PropItem key={index} propName={String(index)} propValue={item} />
<PropItem
key={index}
propName={String(index)}
propValue={item}
overrideProps={overrideProps}
editable={editable}
/>
))
: Object.entries(value).map((entry) => (
<PropItem key={entry[0]} propName={entry[0]} propValue={entry[1]} />
<PropItem
key={entry[0]}
propName={entry[0]}
propValue={entry[1]}
overrideProps={overrideProps}
editable={editable}
/>
))}
</ul>
);
Expand All @@ -30,50 +48,103 @@ 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 [resultValue, setResultValue] = React.useState(propValue);
const [error, setError] = React.useState(false);

React.useEffect(() => {
setResultValue(propValue);
}, [propValue]);

const handleClick = React.useCallback(() => {
setExpanded(!expanded);
}, [expanded]);

const hasChildren = isObject(propValue);
const handleDoubleClick = React.useCallback(() => {
if (editable) {
setEditing(true);
}
}, [editable]);

const handleBlur = () => {
try {
const result = JSON.parse(changeValue);
overrideProps?.(propName, changeValue);
setResultValue(result);
setEditing(false);
setError(false);
} catch (error) {
setError(true);
}
};

const handleChange = (v) => {
setChangeValue(v.target?.value);
};

const hasChildren = isObject(resultValue);

return React.createElement(
standalone ? "div" : "li",
{
className: classNames("prop-item", { expanded }),
},
<>
<div
className="bp3-text-overflow-ellipsis prop-item-label"
onClick={handleClick}
>
{(!standalone || hasChildren) && (
<Icon
icon={
hasChildren ? (expanded ? "caret-down" : "caret-right") : "blank"
}
iconSize={16}
<div className="bp3-text-overflow-ellipsis prop-item-label">
<span onClick={handleClick} data-testid="prop-name-wrapper">
{(!standalone || hasChildren) && (
<Icon
icon={
hasChildren && !editing
? expanded
? "caret-down"
: "caret-right"
: "blank"
}
iconSize={16}
/>
)}
{!standalone && (
<>
<span className="prop-name">{propName}</span>
<span className="prop-punctuation">:</span>{" "}
</>
)}
</span>
{editing && editable ? (
<TextArea
growVertically={true}
onChange={handleChange}
onBlur={handleBlur}
value={changeValue}
className={`prop-editable ${error ? "bp3-intent-danger" : ""}`}
/>
) : (
<span className="prop-value" onDoubleClick={handleDoubleClick}>
<VariableDisplay value={resultValue} expanded={expanded} />
</span>
)}
{!standalone && (
<>
<span className="prop-name">{propName}</span>
<span className="prop-punctuation">:</span>{" "}
</>
)}
<span className="prop-value">
<VariableDisplay value={propValue} expanded={expanded} />
</span>
</div>
{hasChildren && expanded && <PropList list={propValue} />}
{hasChildren && expanded && !editing && (
<div onDoubleClick={handleDoubleClick}>
<PropList list={resultValue} />
</div>
)}
</>
);
}
16 changes: 15 additions & 1 deletion src/panel/components/PropView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ export function PropView(): React.ReactElement {
}
}, [selectedBrick]);

const overrideProps = React.useCallback(
(propName: string, propValue: string) => {
if (selectedBrick) {
const evalParams = `window.${HOOK_NAME}.overrideProps(${selectedBrick.uid},"${propName}",${propValue})`;
chrome.devtools.inspectedWindow.eval(evalParams);
}
},
[selectedBrick]
);

if (!selectedBrick || !brickInfo) {
return null;
}
Expand Down Expand Up @@ -87,7 +97,11 @@ export function PropView(): React.ReactElement {
)}
</div>
<div className="expanded">
<PropList list={brickInfo[key]} />
<PropList
list={brickInfo[key]}
overrideProps={overrideProps}
editable
/>
</div>
</React.Fragment>
))}
Expand Down
9 changes: 8 additions & 1 deletion src/panel/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down

0 comments on commit 92bd665

Please sign in to comment.