Skip to content

Commit

Permalink
fix: fix keys with the same name in tree-view (#22)
Browse files Browse the repository at this point in the history
Keys with the same name in Tree-view will expand and collapse together, because they are not unique.
Adding a fix to generate unique key for every item in a Tree-view

fix #11
  • Loading branch information
volodymyr-ladnik-ck authored Dec 13, 2018
1 parent e85e6a0 commit 4764ba4
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 30 deletions.
27 changes: 14 additions & 13 deletions src/renderer/containers/graphql/TreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface IProps {
interface ILevelProps extends IProps {
name: string;
required: boolean;
prefix: string;
onDelete: () => void;
}

Expand All @@ -36,7 +37,7 @@ const LevelMenu: React.SFC<IMenuProps> = (props) => {
const used = new Set(Object.keys(keys));
const avail = fields.filter((f) => !used.has(f.name));
if (avail.length > 0) {
items.push(<SubMenu title="Add child">
items.push(<SubMenu title="Add child" key="add">
{avail.sort((a, b) => a.name.localeCompare(b.name)).map((field) =>
<Menu.Item key={field.name}>
<a onClick={() => onChange({
Expand All @@ -50,7 +51,7 @@ const LevelMenu: React.SFC<IMenuProps> = (props) => {
if (schema.kind === "INTERFACE" || schema.kind === "UNION") {
const possibleTypes = types[schema.kind][schema.name].possibleTypes.filter((t) => t.name !== __type);
if (possibleTypes.length > 0) {
items.push(<SubMenu title="Replace">
items.push(<SubMenu title="Replace" key="replace">
{possibleTypes.map((type) =>
<Menu.Item key={type.name}>
<a onClick={() => onChange(generateGraphqlResponse(types, type, false))}>
Expand All @@ -63,7 +64,7 @@ const LevelMenu: React.SFC<IMenuProps> = (props) => {
}
break;
case "LIST":
items.push(<Menu.Item key="add_child">
items.push(<Menu.Item key="add">
<a onClick={() => onChange([...data,
generateGraphqlResponse(types, schema.ofType, false),
])}>Add Child</a>
Expand All @@ -75,7 +76,7 @@ const LevelMenu: React.SFC<IMenuProps> = (props) => {
const menu = (
<Menu>
{items}
<Menu.Item disabled={required}>{required ? "Remove" : <a onClick={onDelete}>Remove</a>}</Menu.Item>
<Menu.Item key="remove" disabled={required}>{required ? "Remove" : <a onClick={onDelete}>Remove</a>}</Menu.Item>
</Menu>);
return (
<Dropdown overlay={menu} trigger={["click"]}>
Expand Down Expand Up @@ -146,24 +147,24 @@ const Title: React.SFC<ILevelProps> = (props) => {
};

const Level: React.SFC<ILevelProps> = (props) => {
const { types, name, data, onChange, schema } = props;
const { types, data, onChange, schema, prefix } = props;
switch (schema.kind) {
case "OBJECT":
case "INTERFACE":
case "UNION":
const {__type, ...keys} = data;
const fields = types.OBJECT[__type].fields;
return (
<TreeNode title={<Title {...props} />} key={name} selectable={false}>
{Object.keys(keys).sort().map((key) => {
<TreeNode title={<Title {...props} />} key={prefix} selectable={false}>
{Object.keys(keys).sort().map((key, index) => {
let { type } = fields.find((f) => f.name === key)!;
let required = false;
if (type.kind === "NON_NULL") {
required = true;
type = type.ofType;
}
return Level({
name: key, required, types, data: data[key], schema: type,
name: key, required, types, data: data[key], schema: type, prefix: prefix + index,
onChange: (d: any) => onChange({ ...data, [key]: d }),
onDelete: () => {
const { [key]: deleted, ...rest } = data;
Expand All @@ -174,26 +175,26 @@ const Level: React.SFC<ILevelProps> = (props) => {
</TreeNode>);
case "LIST":
return (
<TreeNode title={<Title {...props} />} key={name} selectable={false}>
<TreeNode title={<Title {...props} />} key={prefix} selectable={false}>
{data.map((el: any, index: number) => {
return Level({
name: index.toString(), required: false, types, data: el, schema: schema.ofType,
name: index.toString(), required: false, types, data: el, schema: schema.ofType, prefix: prefix + index,
onChange: (d: any) => onChange(Object.assign([...data], { [index]: d })),
onDelete: () => onChange(data.filter((e: any, i: number) => i !== index)),
});
})}
</TreeNode>);
case "ENUM":
case "SCALAR":
return <TreeNode title={<Title {...props} />} key={name} selectable={false} isLeaf />;
return <TreeNode title={<Title {...props} />} key={prefix} selectable={false} isLeaf />;
case "NON_NULL":
return Level({ ...props, schema: schema.ofType});
}
};

export const TreeView: React.SFC<IProps> = (props) =>
<Tree showLine defaultExpandedKeys={["root"]} className="tree-view">
{Level({ ...props, name: "root", required: true, onDelete: () => null })}
<Tree showLine defaultExpandedKeys={["."]} className="tree-view">
{Level({ ...props, name: "root", prefix: ".", required: true, onDelete: () => null })}
</Tree>;

export default TreeView;
5 changes: 4 additions & 1 deletion src/renderer/containers/thrift/Documentation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,10 @@ export class Documentation extends React.PureComponent<IProps, IState> {
public func = (service: string, f: ThriftFile.IFunction, i: number) =>
<div className="definition" key={i}>
<h4>Function: {service}.{f.name}</h4>
<pre><code>{this.formatType(f.returnTypeId, f.returnType, f.returnExtra)}</code> {f.name}
<pre>
<code>
{f.oneway ? "oneway " : ""}{this.formatType(f.returnTypeId, f.returnType, f.returnExtra)} {f.name}
</code>
({this.args(f.arguments)}) {f.exceptions.length > 0 ? "throws" : ""} {this.args(f.exceptions)}
</pre>
{f.doc ? <pre>{f.doc}</pre> : null}
Expand Down
33 changes: 17 additions & 16 deletions src/renderer/containers/thrift/TreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const typeClass = (type?: ThriftFile.Type): string | null => {
interface ILevelProps extends IProps {
name: string;
required: boolean;
prefix: string;
onDelete: () => void;
}

Expand All @@ -55,7 +56,7 @@ const LevelMenu: React.SFC<IMenuProps> = (props) => {
const fields = struct.fields.filter((f) => !used.has(f.name));
if (fields.length === 0) { break; }
if (type.typeId === "union") {
items = <SubMenu title="Replace child">
items = <SubMenu title="Replace child" key="replace">
{fields.map((field) =>
<Menu.Item key={field.key}>
<a onClick={() => onChange({
Expand All @@ -65,7 +66,7 @@ const LevelMenu: React.SFC<IMenuProps> = (props) => {
)}
</SubMenu>;
} else {
items = <SubMenu title="Add child">
items = <SubMenu title="Add child" key="add">
{fields.map((field) =>
<Menu.Item key={field.key}>
<a onClick={() => onChange({
Expand All @@ -79,14 +80,14 @@ const LevelMenu: React.SFC<IMenuProps> = (props) => {
break;
case "set":
case "list":
items = <Menu.Item key="add_child">
items = <Menu.Item key="add">
<a onClick={() => onChange([...data,
generateThriftResponse(type.elemTypeId, thrift, type.elemType, type.extra),
])}>Add Child</a>
</Menu.Item>;
break;
case "map":
items = <Menu.Item key="add_child">
items = <Menu.Item key="add">
<a onClick={() => askFieldName((name) => {
onChange({
...data,
Expand Down Expand Up @@ -194,20 +195,20 @@ const Title: React.SFC<ILevelProps> = (props) => {
};

const Level: React.SFC<ILevelProps> = (props) => {
const { thrift, name, data, onChange, type: { type, typeId } } = props;
const { thrift, data, onChange, type: { type, typeId }, prefix } = props;
if (type) {
switch (type.typeId) {
case "exception":
case "struct":
case "union":
const struct = thrift.structs.filter((s) => s.name === type.class)[0];
return (
<TreeNode title={<Title {...props} />} key={name} selectable={false}>
{Object.keys(data).sort().map((key) => {
<TreeNode title={<Title {...props} />} key={prefix} selectable={false}>
{Object.keys(data).sort().map((key, index) => {
const field = struct.fields.filter((f) => f.name === key)[0];
const required = type.typeId === "union" || field.required !== "optional";
return Level({
name: key, required, thrift, data: data[key], type: field,
name: key, required, thrift, data: data[key], type: field, prefix: prefix + index,
onChange: (d: any) => onChange({ ...data, [key]: d }),
onDelete: () => {
const { [key]: deleted, ...rest } = data;
Expand All @@ -220,10 +221,10 @@ const Level: React.SFC<ILevelProps> = (props) => {
case "list":
const { elemTypeId, elemType, extra: elemExtra } = type;
return (
<TreeNode title={<Title {...props} />} key={name} selectable={false}>
<TreeNode title={<Title {...props} />} key={prefix} selectable={false}>
{data.map((el: any, index: number) => {
return Level({
name: index.toString(), required: false, thrift, data: el,
name: index.toString(), required: false, thrift, data: el, prefix: prefix + index,
type: { typeId: elemTypeId, type: elemType, extra: elemExtra },
onChange: (d: any) => onChange(Object.assign([...data], { [index]: d })),
onDelete: () => onChange(data.filter((e: any, i: number) => i !== index)),
Expand All @@ -233,10 +234,10 @@ const Level: React.SFC<ILevelProps> = (props) => {
case "map":
const { valueTypeId, valueType, valueExtra } = type;
return (
<TreeNode title={<Title {...props} />} key={name} selectable={false}>
{Object.keys(data).sort().map((key) =>
<TreeNode title={<Title {...props} />} key={prefix} selectable={false}>
{Object.keys(data).sort().map((key, index) =>
Level({
name: key, required: false, thrift, data: data[key],
name: key, required: false, thrift, data: data[key], prefix: prefix + index,
type: { typeId: valueTypeId, type: valueType, extra: valueExtra },
onChange: (d: any) => onChange({ ...data, [key]: d }),
onDelete: () => {
Expand All @@ -250,12 +251,12 @@ const Level: React.SFC<ILevelProps> = (props) => {
throw new Error(`Can't handle "${typeId}" type yet`);
}
}
return <TreeNode title={<Title {...props} />} key={name} selectable={false} />;
return <TreeNode title={<Title {...props} />} key={prefix} selectable={false} />;
};

export const TreeView: React.SFC<IProps> = (props) =>
<Tree showLine defaultExpandedKeys={["root"]} className="tree-view">
{Level({ ...props, name: "root", required: true, onDelete: () => null })}
<Tree showLine defaultExpandedKeys={["."]} className="tree-view">
{Level({ ...props, name: "root", prefix: ".", required: true, onDelete: () => null })}
</Tree>;

export default TreeView;

0 comments on commit 4764ba4

Please sign in to comment.