Skip to content

Commit

Permalink
feat: adds 'table' field type and scaffold for JSONTree component
Browse files Browse the repository at this point in the history
  • Loading branch information
jbottigliero committed Apr 23, 2024
1 parent c3d5d11 commit a539724
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 61 deletions.
9 changes: 8 additions & 1 deletion src/components/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import jsonata from "jsonata";

import RgbaField from "./Fields/RgbaField";
import ImageField from "./Fields/ImageField";
import TableField from "./Fields/TableField";
import FallbackField from "./Fields/FallbackField";

import type { GMetaResult } from "../globus/search";
Expand Down Expand Up @@ -69,7 +70,13 @@ export const FieldValue = ({
if (type === "image") {
return <ImageField value={value} />;
}
return <FallbackField value={value} />; // fallback
if (type === "table") {
return <TableField value={value} />;
}
/**
* If no `type` is provided, or the `type` is not recognized, use the fallback field.
*/
return <FallbackField value={value} />;
};

export const Field = ({
Expand Down
18 changes: 7 additions & 11 deletions src/components/Fields/FallbackField.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import React from "react";
import { Box, Code, Text } from "@chakra-ui/react";
import { Box, Text } from "@chakra-ui/react";
import { JSONTree } from "../JSONTree";

type Value = unknown;

// function isValidValue(value: unknown): value is Value {
// return true;
// }

/**
* A fallback field that will introspect the value and render it as best as it can.
*/
export default function FallbackField({ value }: { value: Value }) {
if (value === null || value === undefined) {
return <Text>&mdash;</Text>;
}
if (
typeof value === "string" ||
typeof value === "number" ||
typeof value === "boolean"
) {
return <Text as="p">{value}</Text>;
return <Text>{value}</Text>;
}
if (Array.isArray(value)) {
return value.map((v, i) => (
Expand All @@ -25,9 +25,5 @@ export default function FallbackField({ value }: { value: Value }) {
</Box>
));
}
return (
<Code as="pre" display="block" borderRadius={2} my={1}>
{JSON.stringify(value, null, 2)}
</Code>
);
return <JSONTree data={value} />;
}
50 changes: 50 additions & 0 deletions src/components/Fields/TableField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import {
Table,
Tbody,
Tr,
Thead,
Th,
Td,
TableContainer,
} from "@chakra-ui/react";
import { FieldValue } from "../Field";

type Value = Record<string, unknown>;

function isValidValue(value: unknown): value is Value {
return typeof value === "object" && value !== null;
}

/**
* Render a field as an RGBA color.
*/
export default function TableField({ value }: { value: unknown }) {
if (!isValidValue(value)) {
return;
}
return (
<TableContainer>
<Table size="sm">
<Thead>
<Tr>
<Th>Property</Th>
<Th>Value</Th>
</Tr>
</Thead>
<Tbody>
{Object.entries(value).map(([key, value]) => {
return (
<Tr key={key}>
<Td>{key}</Td>
<Td>
<FieldValue value={value} />
</Td>
</Tr>
);
})}
</Tbody>
</Table>
</TableContainer>
);
}
10 changes: 10 additions & 0 deletions src/components/JSONTree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from "react";

import { Code } from "@chakra-ui/react";

/**
* @todo
*/
export const JSONTree = ({ data }: { data: unknown }) => {
return <Code as="pre">{JSON.stringify(data, null, 2)}</Code>;
};
5 changes: 2 additions & 3 deletions src/components/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Heading,
Text,
Box,
Code,
Flex,
Button,
Drawer,
Expand All @@ -22,8 +21,8 @@ import {
import { getAttribute, getAttributeFrom } from "../../static";
import { Error } from "./Error";
import { isGError, type GError, type GMetaResult } from "@/globus/search";

import { Field, type FieldDefinition } from "./Field";
import { JSONTree } from "./JSONTree";

export type ResultComponentOptions = {
/**
Expand Down Expand Up @@ -104,7 +103,7 @@ export default function Result({ result }: { result?: GMetaResult | GError }) {
<Spacer />
<Box p="2">
<ResponseDrawer>
<Code as="pre">{JSON.stringify(result, null, 2)}</Code>
<JSONTree data={result} />
</ResponseDrawer>
</Box>
</Flex>
Expand Down
97 changes: 51 additions & 46 deletions src/components/ResultListing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Tr,
Td,
HStack,
Link,
} from "@chakra-ui/react";
import { getAttributeFrom, getAttribute } from "../../static";

Expand Down Expand Up @@ -125,60 +126,64 @@ export default function ResultListing({ gmeta }: { gmeta: GMetaResult }) {
src: string;
alt?: string;
}>();
getAttributeFrom<string>(gmeta, "components.ResultListing.heading").then(
(result) => {
setHeading(result);
},
);

getAttributeFrom<string>(gmeta, "components.ResultListing.summary").then(
(result) => {
setSummary(result);
},
);
useEffect(() => {
async function resolveAttributes() {
const heading = await getAttributeFrom<string>(
gmeta,
"components.ResultListing.heading",
);
const summary = await getAttributeFrom<string>(
gmeta,
"components.ResultListing.summary",
);
let image = await getAttributeFrom<
| string
| {
src: string;
alt?: string;
}
>(gmeta, "components.ResultListing.image");

getAttributeFrom<
| string
| {
src: string;
alt?: string;
setHeading(heading);
setSummary(summary);

if (typeof image === "string") {
image = { src: image };
}
>(gmeta, "components.ResultListing.image").then((result) => {
let image = result;
if (typeof image === "string") {
image = { src: image };
setImage(image);
}
setImage(image);
});
resolveAttributes();
}, [gmeta]);

const fields = getAttribute("components.ResultListing.fields");

return (
<LinkBox
as={NextLink}
href={`/results?subject=${encodeURIComponent(gmeta.subject)}`}
>
<Card size="sm" w="full">
<CardHeader>
<Heading size="md" color="brand">
<Card size="sm" w="full">
<CardHeader>
<Heading size="md" color="brand">
<Link
as={NextLink}
href={`/results?subject=${encodeURIComponent(gmeta.subject)}`}
>
{heading}
</Heading>
</CardHeader>
<CardBody>
<HStack>
{image && (
<ImageField
value={{
src: image.src,
alt: image?.alt,
}}
/>
)}
{summary && <Text noOfLines={[3, 5, 10]}>{summary}</Text>}
</HStack>
<ResultListingFields fields={fields} gmeta={gmeta} />
</CardBody>
</Card>
</LinkBox>
</Link>
</Heading>
</CardHeader>
<CardBody>
<HStack>
{image && (
<ImageField
value={{
src: image.src,
alt: image?.alt,
}}
/>
)}
{summary && <Text noOfLines={[3, 5, 10]}>{summary}</Text>}
</HStack>
<ResultListingFields fields={fields} gmeta={gmeta} />
</CardBody>
</Card>
);
}

0 comments on commit a539724

Please sign in to comment.