Search code examples
reactjsreact-nativeshopifyshopify-apppolaris

Shopify Polaris - Custom layout issue and styles


Currently I am trying to create a very basic abandoned cart app just to teach myself more about GraphQL and Shopify's app creation with Polaris. Polaris has a great group of components but I am having trouble editing them to how I require. So far I haven't found any way to alter the styles of components or layouts and so am having some trouble. I am not new to programming but I am somewhat new to react.

I saw that Polaris does not support styles because the developers want it to remain consistent across all apps. Attached I have some images - I am just wondering if I can move from the current to the goal?

The goal is to have three cards in a column with a table of the same width underneath them, with a space such as this. In this image the table didn't go all the way across but that was due to how I made the image.

At the moment, it looks like this (multiple line state) - no space between components, and despite copying the code from the component library, three equal columns go over multiple lines, rather than in the second current image on the same line, but it requires that the middle card must be different (I think as it won't do it if they are the same contents). There also is no gap beneath the card and table - single line current state.

I have tried to find out online in other places and do some workarounds but I am out of ideas. Is it possible to edit the current state of the cards to make them similar to the goal state due to Polaris' restrictions?

[Edit] Uploaded the same image for the last two images, fixed

[Edit 2] Added code + image

import { Checkbox , FormLayout , TextField , DataTable, ResourceList, DisplayText, EmptyState, Layout, Page , Card, TextStyle, Heading} from '@shopify/polaris';
import { ResourcePicker } from '@shopify/app-bridge-react';
import store from 'store-js';
import ResourceListWithProducts from '../components/ResourceList';


const img = 'https://cdn.shopify.com/s/files/1/0757/9955/files/empty-state.svg';

function SortableDataTableExample() {
  const [sortedRows, setSortedRows] = useState(null);

  const initiallySortedRows = [
    ['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00'],
    ['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00'],
    [
      'Navy Merinaaso Wool Blazer with khaki chinos and yellow belt',
      '$445.00',
      124518,
      32,
      '$14,240.00',
    ],
  ];
  

  return (
    <Page title="Sales by product">
      <Card>
        <DataTable
          columnContentTypes={[
            'text',
            'numeric',
            'numeric',
            'numeric',
            'numeric',
          ]}
          headings={[
            'Product',
            'Price',
            'SKU Number',
            'Net quantity',
            'Net sales',
          ]}
          rows={rows}
          totals={['', '', '', 255, '$155,830.00']}
          sortable={[false, true, false, false, true]}
          defaultSortDirection="descending"
          initialSortColumnIndex={4}
          onSort={handleSort}
        />
      </Card>
    </Page>
  );

  
  function sortCurrency(rows, index, direction) {
    return [...rows].sort((rowA, rowB) => {
      const amountA = parseFloat(rowA[index].substring(1));
      const amountB = parseFloat(rowB[index].substring(1));

      return direction === 'descending' ? amountB - amountA : amountA - amountB;
    });
  }
}

class Index extends React.Component {
  state = { open: false };
  render() {

    

    const rows = [
      ['Abandon Cart (1 hr)', '74', 19, 1432, , 'true',],
      ['Abandon Cart (24 hrs)', '52', 23, 2132, , 'true'],,
      
    ];

    const emptyState = !store.get('ids');
    return (
      <Page
        primaryAction={{
          content: 'Select products',
          onAction: () => this.setState({ open: true }),
        }}
      >

        <ResourcePicker
          resourceType="Product"
          showVariants={false}
          open={this.state.open}
          onSelection={(resources) => this.handleSelection(resources)}
          onCancel={() => this.setState({ open: false })}
        />
        {emptyState ? (
          <Layout>

<Layout>
        <Layout.Section oneThird>
          <Card title="Emails Sent" actions={[{ content: "Manage" }]}>
            <Card.Section>
              <DisplayText size="large">172</DisplayText>
            </Card.Section>
          </Card>
        </Layout.Section>
        <Layout.Section oneThird>
          <Card title="Sales" actions={[{ content: "Manage" }]}>
            <Card.Section>
            <Layout>
        
        
        <Layout.Section oneThird>
          
        </Layout.Section>
      </Layout>
            </Card.Section>
          </Card>
        </Layout.Section>
        <Layout.Section oneThird>
          <Card title="ROI" actions={[{ content: "Manage" }]}>
            <Card.Section>
              <DisplayText size="large">+745%</DisplayText>
            </Card.Section>
          </Card>
        </Layout.Section>
      </Layout>

      <Card>
        <DataTable
          columnContentTypes={[
            'text',
            'numeric',
            'numeric',
            'numeric',
            'boolean',
          ]}
          headings={[
            'Email',
            'Emails Sent',
            'Orders',
            'Sales ($)',
            'Active',
            
          ]}
          rows={rows}
          totals={['', '', '', 3564, '']}
        />
      </Card>
     

            <EmptyState
              heading="Discount your products temporarily"
              action={{
                content: 'Select products',
                onAction: () => this.setState({ open: true }),
              }}
              image={img}
            >
              <p>Select products to change their price temporarily.</p>
            </EmptyState>
          </Layout>
        ) : (
            <ResourceListWithProducts />
          )}

          <FormLayout>
            <TextField type="text" label="Call Script" onChange={() => {}} />
            <TextField type="text" label="Voicemail Script" onChange={() => {}} />
          </FormLayout>


      </Page>
    );
  }

  handleSelection = (resources) => {
    const idsFromResources = resources.selection.map((product) => product.id);
    this.setState({ open: false });
    store.set('ids', idsFromResources);
  };
}

export default Index;`

Solution

  • You had nested <Layout> with <Layout> causing the columns to stack on top of each other. Replaced it with a wrapping <div> tag instead. For the spacing on the bottom, I wrapped the three cards in a <div> tag and added style of paddingBottom: '15px'. Let me know if this is what you were looking for.

    import { Checkbox , FormLayout , TextField , DataTable, ResourceList, DisplayText, EmptyState, Layout, Page , Card, TextStyle, Heading} from '@shopify/polaris';
    import { ResourcePicker } from '@shopify/app-bridge-react';
    import store from 'store-js';
    import ResourceListWithProducts from '../components/ResourceList';
    
    
    const img = 'https://cdn.shopify.com/s/files/1/0757/9955/files/empty-state.svg';
    
    function SortableDataTableExample() {
      const [sortedRows, setSortedRows] = useState(null);
    
      const initiallySortedRows = [
        ['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00'],
        ['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00'],
        [
          'Navy Merinaaso Wool Blazer with khaki chinos and yellow belt',
          '$445.00',
          124518,
          32,
          '$14,240.00',
        ],
      ];
    
    
      return (
        <Page title="Sales by product">
          <Card>
            <DataTable
              columnContentTypes={[
                'text',
                'numeric',
                'numeric',
                'numeric',
                'numeric',
              ]}
              headings={[
                'Product',
                'Price',
                'SKU Number',
                'Net quantity',
                'Net sales',
              ]}
              rows={rows}
              totals={['', '', '', 255, '$155,830.00']}
              sortable={[false, true, false, false, true]}
              defaultSortDirection="descending"
              initialSortColumnIndex={4}
              onSort={handleSort}
            />
          </Card>
        </Page>
      );
    
    
      function sortCurrency(rows, index, direction) {
        return [...rows].sort((rowA, rowB) => {
          const amountA = parseFloat(rowA[index].substring(1));
          const amountB = parseFloat(rowB[index].substring(1));
    
          return direction === 'descending' ? amountB - amountA : amountA - amountB;
        });
      }
    }
    
    class Index extends React.Component {
      state = { open: false };
      render() {
    
    
    
        const rows = [
          ['Abandon Cart (1 hr)', '74', 19, 1432, , 'true',],
          ['Abandon Cart (24 hrs)', '52', 23, 2132, , 'true'],,
    
        ];
    
        const emptyState = !store.get('ids');
        return (
          <Page
            primaryAction={{
              content: 'Select products',
              onAction: () => this.setState({ open: true }),
            }}
          >
    
            <ResourcePicker
              resourceType="Product"
              showVariants={false}
              open={this.state.open}
              onSelection={(resources) => this.handleSelection(resources)}
              onCancel={() => this.setState({ open: false })}
            />
            {emptyState ? (
              <div>
    
                <div style={{ paddingBottom : '15px' }}>
                  <Layout>
                    <Layout.Section oneThird>
                      <Card title="Emails Sent" actions={[{ content: "Manage" }]}>
                        <Card.Section>
                          <DisplayText size="large">172</DisplayText>
                        </Card.Section>
                      </Card>
                    </Layout.Section>
                    <Layout.Section oneThird>
                      <Card title="Sales" actions={[{ content: "Manage" }]}>
                        <Card.Section>
                          <DisplayText size="large">+$300</DisplayText>
                        </Card.Section>
                      </Card>
                    </Layout.Section>
                    <Layout.Section oneThird>
                      <Card title="ROI" actions={[{ content: "Manage" }]}>
                        <Card.Section>
                          <DisplayText size="large">+745%</DisplayText>
                        </Card.Section>
                      </Card>
                    </Layout.Section>
                  </Layout>
                </div>
    
                <Card>
                  <DataTable
                    columnContentTypes={[
                      'text',
                      'numeric',
                      'numeric',
                      'numeric',
                      'boolean',
                    ]}
                    headings={[
                      'Email',
                      'Emails Sent',
                      'Orders',
                      'Sales ($)',
                      'Active',
    
                    ]}
                    rows={rows}
                    totals={['', '', '', 3564, '']}
                  />
                </Card>
    
    
                <EmptyState
                  heading="Discount your products temporarily"
                  action={{
                    content: 'Select products',
                    onAction: () => this.setState({ open: true }),
                  }}
                  image={img}
                >
                  <p>Select products to change their price temporarily.</p>
                </EmptyState>
              </div>
            ) : (
              <ResourceListWithProducts />
            )}
    
            <FormLayout>
              <TextField type="text" label="Call Script" onChange={() => {}} />
              <TextField type="text" label="Voicemail Script" onChange={() => {}} />
            </FormLayout>
    
    
          </Page>
        );
      }
    
      handleSelection = (resources) => {
        const idsFromResources = resources.selection.map((product) => product.id);
        this.setState({ open: false });
        store.set('ids', idsFromResources);
      };
    }
    
    export default Index;