Skip to content

Commit

Permalink
feat: add support for non-standard JSON shapes via mapProp
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvxd committed Sep 19, 2019
1 parent f873219 commit 0b601db
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 12 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,48 @@ const Example = () => {
};
```

### Other JSON shapes

If your data doesn't follow the `type` | `props` shape, `react-from-json` makes it easy to map your data on the fly using the `mapProp` prop.

```jsx
import React from "react";
import ReactFromJSON from "react-from-json";
import mapping from "./mapping";

const entryWithDifferentShape = {
_type: "Burger",
chain: "Wahlburger",
children: {
_type: "Patty",
variant: "Impossible"
}
};

const mapProp = prop => {
if (prop._type) {
const { _type, ...props } = prop;

return {
type: _type,
props
};
}

return prop;
};

const Example = () => {
return (
<ReactFromJSON
entry={entryWithDifferentShape}
mapping={mapping}
mapProp={mapProp}
/>
);
};
```

### Flat trees

`react-from-json` also supports flat, non-recursive structures via the special `<ComponentLookup />` component. This is useful when working with typed systems like GraphQL, and you need to avoid unions.
Expand Down
28 changes: 28 additions & 0 deletions src/__helpers__/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,34 @@ export const recursiveEntry = {
}
};

export const entryWithDifferentShape = {
_type: "Burger",
chain: "Wahlburger",
bun: {
_type: "Bun",
variant: "sesame"
},
cheese: true,
children: [
{
_type: "Patty",
size: "large",
ingredient: {
_type: "PattyIngredient",
variant: "impossible"
}
},
{
_type: "Patty",
size: "large",
ingredient: {
_type: "PattyIngredient",
variant: "beef"
}
}
]
};

export const flatEntry = {
type: "Burger",
props: {
Expand Down
45 changes: 45 additions & 0 deletions src/__tests__/__snapshots__/index.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,48 @@ exports[`ReactFromJSON to render a recursive entry 1`] = `
</div>
</div>
`;

exports[`ReactFromJSON to render a recursive entry with a non-standard shape 1`] = `
<div>
<div />
<div>
<div>
<div />
sesame bun
</div>
</div>
<div>
cheese
</div>
<div>
<div
id="undefined"
>
<div>
0
</div>
large patty
<div>
impossible
</div>
</div>
<div
id="undefined"
>
<div>
Order: 1
</div>
large patty
<div>
beef
</div>
</div>
</div>
<div>
<div>
<div />
sesame bun
</div>
</div>
</div>
`;
26 changes: 26 additions & 0 deletions src/__tests__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Mapping, Components } from "../__helpers__/interfaces";
import {
mapping,
recursiveEntry,
entryWithDifferentShape,
flatEntry,
flatComponents
} from "../__helpers__/data";
Expand All @@ -25,6 +26,31 @@ describe("ReactFromJSON", () => {
expect(tree).toMatchSnapshot();
});

it("to render a recursive entry with a non-standard shape", () => {
const tree = renderer
.create(
<BurgerReactFromJSON
entry={entryWithDifferentShape}
mapping={mapping}
mapProp={prop => {
if (prop._type) {
const { _type, ...props } = prop;

return {
type: _type,
props
};
}

return prop;
}}
/>
)
.toJSON();

expect(tree).toMatchSnapshot();
});

it("to render a flat entry with a components attribute", () => {
const tree = renderer
.create(
Expand Down
28 changes: 16 additions & 12 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export interface ReactFromJSONProps<
ComponentsType = object
> {
components?: ComponentsType;
entry: Component;
entry: Component | any;
mapProp?: (obj: any) => any;
mapping: MappingType;
}

Expand Down Expand Up @@ -71,23 +72,26 @@ class ReactFromJSON<
};

resolveProp = (prop: any, index?: number): any => {
if (prop === null) {
return prop;
} else if (Array.isArray(prop)) {
return prop.map(this.resolveProp);
} else if (typeof prop === "object") {
const { mapProp = (p: any) => p } = this.props;
const mappedProp = mapProp(prop);

if (mappedProp === null) {
return mappedProp;
} else if (Array.isArray(mappedProp)) {
return mappedProp.map(this.resolveProp);
} else if (typeof mappedProp === "object") {
if (
// Typeguard
prop["type"] !== undefined &&
prop["props"] !== undefined
mappedProp["type"] !== undefined &&
mappedProp["props"] !== undefined
) {
const component: Component = prop;
const component: Component = mappedProp;

return this.renderComponent(component, index);
}
}

return prop;
return mappedProp;
};

getNextKey(type: string, propIndex?: number) {
Expand All @@ -97,7 +101,7 @@ class ReactFromJSON<
return `${type}_${this.counter[type]++}${propIndexKey}`;
}

renderComponent(component: Component, propIndex?: number) {
renderComponent(component: Component | any, propIndex?: number) {
const { mapping } = this.props;
const { type, props } = component;
const resolvedProps = {};
Expand Down Expand Up @@ -126,7 +130,7 @@ class ReactFromJSON<
render() {
const { entry } = this.props;

return <>{this.renderComponent(entry)}</>;
return <>{this.resolveProp(entry)}</>;
}
}

Expand Down

0 comments on commit 0b601db

Please sign in to comment.