Using Shopify Polaris components with Gadget
This page contains some code samples that can be used to integrate Shopify Polaris components with Gadget.
Polaris Data Table
Use the following template to take data from a Gadget app and place it in a Polaris Data Table. Copy-pasting it into theweb/ShopPage.jsx
file that is generated when a new Gadget app is connected to Shopify will render a table containing the data from the shopifyShop
model.
web/routes/index.jsx
React
1import { useFindMany } from "@gadgetinc/react";2import { api } from "../api";3import { Card, DataTable, Page, Pagination, Text, BlockStack } from "@shopify/polaris";4import { useState, useCallback, useEffect } from "react";56/**7 * CHANGE NUM_RECORDS TO MODIFY THE NUMBER OF RECORDS FETCHED AT A SINGLE TIME8 */9const NUM_RECORDS = 10;1011// helper functions used to capitalize headings12const toCapitalizedWords = (name: string) => {13 var words = name.match(/[A-Za-z][a-z]*/g) || [];14 return words.map(capitalize).join(" ");15};1617const capitalize = (word: string) => {18 return word.charAt(0).toUpperCase() + word.substring(1);19};2021type CursorType = {22 first?: number;23 last?: number;24 before?: string;25 after?: string;26};27const initialCursor: CursorType = {28 first: NUM_RECORDS,29 last: undefined,30 before: undefined,31 after: undefined,32};3334const ShopPage = () => {35 // React state to manage table data and settings, as well as pagination cursor36 const [cursor, setCursor] = useState<CursorType>(initialCursor);37 const [rows, setRows] = useState<(string | number | boolean | null)[][]>([]);38 const [headings, setHeadings] = useState<string[]>([]);39 const [columnContentTypes, setColumnContentTypes] = useState<("text" | "numeric")[]>([]);4041 /**42 * CHANGE shopifyShop TO YOUR GADGET MODEL'S API IDENTIFIER TO FETCH DIFFERENT DATA43 */44 const [{ data, fetching, error }] = useFindMany(api.shopifyShop, {45 ...cursor,46 });4748 // set all data to state values49 useEffect(() => {50 if (data) {51 // get JSON format for returned data52 const jsonData = data.toJSON();5354 // loop through returned data to stringify objects55 // this may not be necessary, depending on the shape of your displayed data model (see commented out line below)56 // this can also be optimized if you know what columns need to be transformed (use column indexes instead of looping with Object.values(d))57 setRows(58 jsonData?.map((d) =>59 Object.values(d).map((value) => {60 // make sure objects are able to be rendered in table61 if (value instanceof Object) {62 /**63 * YOU MAY NEED TO CHANGE THIS IF YOU NEED SPECIFIC TRANSFORMS FOR OBJECTS64 */65 return JSON.stringify(value);66 }67 return value;68 })69 )70 );7172 // an alternative way to set rows if data transforms are not required73 // setRows(jsonData.map(d => Object.values(d)))7475 // create readable headings and get column types76 if (headings.length === 0 || headings.length !== Object.keys(jsonData[0]).length) {77 const row = jsonData[0];78 let headings = Object.keys(row);79 setHeadings(headings.map((key) => toCapitalizedWords(key)));8081 let values = Object.values(row);82 setColumnContentTypes(values.map((value) => (typeof value !== "number" || !isNaN(value) ? "text" : "numeric")));83 }84 }85 }, [data]);8687 // handle pagination88 const pageForward = useCallback(() => {89 setCursor({ after: data?.endCursor, first: NUM_RECORDS });90 }, [data]);91 const pageBackward = useCallback(() => {92 setCursor({ before: data?.startCursor, last: NUM_RECORDS });93 }, [data]);9495 // display any request errors96 if (error) {97 return (98 <Page title="Error">99 <Text variant="bodyMd" as="p">100 Error: {error.toString()}101 </Text>102 </Page>103 );104 }105106 // draw the page and data table107 return (108 <Page title="Gadget + Polaris data table" fullWidth>109 <Card>110 <BlockStack>111 <Text as="h4">112 For DataTable component details, see the{" "}113 <a href="https://polaris.shopify.com/components/tables/data-table" target="_blank">114 Polaris docs115 </a>116 </Text>117 <Pagination118 label="Use to paginate"119 hasPrevious={data?.hasPreviousPage}120 onPrevious={pageBackward}121 hasNext={data?.hasNextPage}122 onNext={pageForward}123 />124 {data && (125 <DataTable columnContentTypes={columnContentTypes} headings={headings} rows={rows} hasZebraStripingOnData stickyHeader />126 )}127 </BlockStack>128 </Card>129 </Page>130 );131};132133export default ShopPage;
1import { useFindMany } from "@gadgetinc/react";2import { api } from "../api";3import { Card, DataTable, Page, Pagination, Text, BlockStack } from "@shopify/polaris";4import { useState, useCallback, useEffect } from "react";56/**7 * CHANGE NUM_RECORDS TO MODIFY THE NUMBER OF RECORDS FETCHED AT A SINGLE TIME8 */9const NUM_RECORDS = 10;1011// helper functions used to capitalize headings12const toCapitalizedWords = (name: string) => {13 var words = name.match(/[A-Za-z][a-z]*/g) || [];14 return words.map(capitalize).join(" ");15};1617const capitalize = (word: string) => {18 return word.charAt(0).toUpperCase() + word.substring(1);19};2021type CursorType = {22 first?: number;23 last?: number;24 before?: string;25 after?: string;26};27const initialCursor: CursorType = {28 first: NUM_RECORDS,29 last: undefined,30 before: undefined,31 after: undefined,32};3334const ShopPage = () => {35 // React state to manage table data and settings, as well as pagination cursor36 const [cursor, setCursor] = useState<CursorType>(initialCursor);37 const [rows, setRows] = useState<(string | number | boolean | null)[][]>([]);38 const [headings, setHeadings] = useState<string[]>([]);39 const [columnContentTypes, setColumnContentTypes] = useState<("text" | "numeric")[]>([]);4041 /**42 * CHANGE shopifyShop TO YOUR GADGET MODEL'S API IDENTIFIER TO FETCH DIFFERENT DATA43 */44 const [{ data, fetching, error }] = useFindMany(api.shopifyShop, {45 ...cursor,46 });4748 // set all data to state values49 useEffect(() => {50 if (data) {51 // get JSON format for returned data52 const jsonData = data.toJSON();5354 // loop through returned data to stringify objects55 // this may not be necessary, depending on the shape of your displayed data model (see commented out line below)56 // this can also be optimized if you know what columns need to be transformed (use column indexes instead of looping with Object.values(d))57 setRows(58 jsonData?.map((d) =>59 Object.values(d).map((value) => {60 // make sure objects are able to be rendered in table61 if (value instanceof Object) {62 /**63 * YOU MAY NEED TO CHANGE THIS IF YOU NEED SPECIFIC TRANSFORMS FOR OBJECTS64 */65 return JSON.stringify(value);66 }67 return value;68 })69 )70 );7172 // an alternative way to set rows if data transforms are not required73 // setRows(jsonData.map(d => Object.values(d)))7475 // create readable headings and get column types76 if (headings.length === 0 || headings.length !== Object.keys(jsonData[0]).length) {77 const row = jsonData[0];78 let headings = Object.keys(row);79 setHeadings(headings.map((key) => toCapitalizedWords(key)));8081 let values = Object.values(row);82 setColumnContentTypes(values.map((value) => (typeof value !== "number" || !isNaN(value) ? "text" : "numeric")));83 }84 }85 }, [data]);8687 // handle pagination88 const pageForward = useCallback(() => {89 setCursor({ after: data?.endCursor, first: NUM_RECORDS });90 }, [data]);91 const pageBackward = useCallback(() => {92 setCursor({ before: data?.startCursor, last: NUM_RECORDS });93 }, [data]);9495 // display any request errors96 if (error) {97 return (98 <Page title="Error">99 <Text variant="bodyMd" as="p">100 Error: {error.toString()}101 </Text>102 </Page>103 );104 }105106 // draw the page and data table107 return (108 <Page title="Gadget + Polaris data table" fullWidth>109 <Card>110 <BlockStack>111 <Text as="h4">112 For DataTable component details, see the{" "}113 <a href="https://polaris.shopify.com/components/tables/data-table" target="_blank">114 Polaris docs115 </a>116 </Text>117 <Pagination118 label="Use to paginate"119 hasPrevious={data?.hasPreviousPage}120 onPrevious={pageBackward}121 hasNext={data?.hasNextPage}122 onNext={pageForward}123 />124 {data && (125 <DataTable columnContentTypes={columnContentTypes} headings={headings} rows={rows} hasZebraStripingOnData stickyHeader />126 )}127 </BlockStack>128 </Card>129 </Page>130 );131};132133export default ShopPage;
There are a couple of things that can be changed to customize the table:
- Displayed data model - change
api.shopifyShop
inside theuseFindMany
hook to the API identifier of the model displayed - Included columns - by default, all columns are displayed. This can be changed by adding a
select
condition to theuseFindMany
hook
web/ShopPage.jsx
React
1import { useFindMany } from "@gadgetinc/react";2import { api } from "./api";34// for example: only include the shop id and name in the table5const [{ data, fetching, error }] = useFindMany(api.shopifyShop, {6 select: {7 id: true,8 name: true,9 },10});
1import { useFindMany } from "@gadgetinc/react";2import { api } from "./api";34// for example: only include the shop id and name in the table5const [{ data, fetching, error }] = useFindMany(api.shopifyShop, {6 select: {7 id: true,8 name: true,9 },10});
NUM_RECORDS
- the number of records to fetch and display at a time. Pagination is built-in, and this is the way to set the page size