Shopify / shopify-app-bridge

https://shopify.dev/docs/api/app-bridge
86 stars 9 forks source link

Polaris IndexTable inside App Bridge Modal produces runtime errors in the browser console #298

Open muchisx opened 7 months ago

muchisx commented 7 months ago

Describe the bug

Having and IndexTable from Polaris in a Modal (the newly released app-bridge-react v4) produces this runtime error in the console when the modal is opened.

chunk-2EXEP7CN.js?v=a98c7375:136 Warning: Failed prop type: Invalid prop `nodeRef.current` of type `HTMLDivElement` supplied to `Transition2`, expected instance of `Element`.
    at Transition2 (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:13786:34)
    at IndexFilters (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:24161:3)
    at div
    at https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:6501:7
    at div
    at ShadowBevel (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:6447:5)
    at Card (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:6613:17)
    at ConnectOccasionsModalContent (https://trial-frontpage-joan-carpet.trycloudflare.com/app/routes/app.addons_.new/route.tsx?t=1709592543645:813:7)
    at div
    at https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:6501:7
    at ModalContainer (https://trial-frontpage-joan-carpet.trycloudflare.com/app/components/ModalContainer.tsx:5:5)
    at div
    at ui-modal
    at https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/@shopify_app-bridge-react.js?v=a98c7375:605:56
    at NewAddon (https://trial-frontpage-joan-carpet.trycloudflare.com/app/routes/app.addons_.new/route.tsx?t=1709592543645:17:7)
    at RenderedRoute (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:321:5)
    at Outlet (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:598:26)
    at EphemeralPresenceManager (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:5494:13)
    at FocusManager (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:5446:13)
    at PortalsManager (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:5399:13)
    at MediaQueryProvider2 (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:5330:17)
    at AppProvider (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-VKVS4LV7.js?v=a98c7375:5576:9)
    at AppProvider (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/@shopify_shopify-app-remix_react.js?v=a98c7375:486:9)
    at App (https://trial-frontpage-joan-carpet.trycloudflare.com/app/routes/app.tsx:26:7)
    at RenderedRoute (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:321:5)
    at RenderErrorBoundary (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:809:9)
    at Outlet (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:598:26)
    at body
    at html
    at App
    at RenderedRoute (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:321:5)
    at RenderErrorBoundary (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:809:9)
    at DataRoutes (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:1307:5)
    at Router (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:605:15)
    at RouterProvider (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:1124:5)
    at RemixErrorBoundary (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:3034:9)
    at RemixBrowser (https://trial-frontpage-joan-carpet.trycloudflare.com/node_modules/.vite/deps/chunk-XST4C3PG.js?v=a98c7375:3778:46)
printWarning @ chunk-2EXEP7CN.js?v=a98c7375:136
error @ chunk-2EXEP7CN.js?v=a98c7375:120
checkPropTypes @ chunk-2EXEP7CN.js?v=a98c7375:1407
validatePropTypes @ chunk-2EXEP7CN.js?v=a98c7375:1531
createElementWithValidation @ chunk-2EXEP7CN.js?v=a98c7375:1601
IndexFilters @ chunk-VKVS4LV7.js?v=a98c7375:24345
renderWithHooks @ chunk-ZRJG7NCB.js?v=a98c7375:12171
updateFunctionComponent @ chunk-ZRJG7NCB.js?v=a98c7375:14577
beginWork @ chunk-ZRJG7NCB.js?v=a98c7375:15912
beginWork$1 @ chunk-ZRJG7NCB.js?v=a98c7375:19749
performUnitOfWork @ chunk-ZRJG7NCB.js?v=a98c7375:19194
workLoopSync @ chunk-ZRJG7NCB.js?v=a98c7375:19133
renderRootSync @ chunk-ZRJG7NCB.js?v=a98c7375:19112
performConcurrentWorkOnRoot @ chunk-ZRJG7NCB.js?v=a98c7375:18674
workLoop @ chunk-ZRJG7NCB.js?v=a98c7375:197
flushWork @ chunk-ZRJG7NCB.js?v=a98c7375:176
performWorkUntilDeadline @ chunk-ZRJG7NCB.js?v=a98c7375:384
Show 17 more frames
Show less

To Reproduce

Steps to reproduce the behaviour:

  1. Create a Modal
  2. Pass in a demo IndexTable (can be the one from the docs) to the Modal
  3. Open the Modal
  4. See the error

image

Expected behaviour

No error should appear or an explanation on how to work with IndexTables inside the modal should be given

Contextual information

Packages and versions

List the relevant packages you’re using, and their versions. For example:

Platform

Additional context

Building on the recent Remix template

jzazove commented 7 months ago

Thanks for sharing this. Are you wrapping the <IndexTable> in some other parent element in the modal?

muchisx commented 7 months ago

Hi @jzazove I'm wrapping it in a polaris <Card padding='0'>

here's my component that goes in the <Modal> as one of the children

function ConnectOccasionsModalContent() {
  const { mode, setMode } = useSetIndexFiltersMode();

  const [selected, setSelected] = useState(0);
  const [searchInputValue, setSearchInputValue] = useState('');

  const handleFiltersQueryChange = useCallback((value: string) => {
    setSearchInputValue(value);
  }, []);
  const handleQueryValueRemove = useCallback(() => setSearchInputValue(''), [setSearchInputValue]);
  const handleFiltersClearAll = useCallback(() => {
    handleQueryValueRemove();
  }, [handleQueryValueRemove]);

  const resourceName = {
    singular: 'occasion',
    plural: 'occasions',
  };

  const occasions = [
    {
      id: '1020',
      title: "Valentine's Day",
    },
    {
      id: '1019',
      title: 'Birthday',
    },
    {
      id: '1018',
      title: 'Wedding',
    },
    {
      id: '1561',
      title: 'Event',
    },
    {
      id: '5132',
      title: 'Anniversary',
    },
    {
      id: '8135',
      title: 'Graduation',
    },
  ];

  const { selectedResources, allResourcesSelected, handleSelectionChange } =
    useIndexResourceState(occasions);

  const rowMarkup = occasions.map(({ id, title }, index) => (
    <IndexTable.Row id={id} key={id} selected={selectedResources.includes(id)} position={index}>
      <IndexTable.Cell>
        <div className={styles.categoryContainer}>
          <Text variant="bodyMd" fontWeight="bold" as="span">
            {title}
          </Text>
        </div>
      </IndexTable.Cell>
    </IndexTable.Row>
  ));

  return (
    <Card padding="0">
      <IndexFilters
        queryValue={searchInputValue}
        queryPlaceholder="Search occasion"
        onQueryChange={handleFiltersQueryChange}
        canCreateNewView={false}
        onQueryClear={() => {
          setSearchInputValue('');
        }}
        cancelAction={{
          onAction: () => {},
          disabled: false,
          loading: false,
        }}
        tabs={[]}
        selected={selected}
        onSelect={setSelected}
        filters={[]}
        onClearAll={handleFiltersClearAll}
        mode={mode}
        setMode={setMode}
      />

      <IndexTable
        resourceName={resourceName}
        itemCount={false ? 0 : occasions.length}
        selectedItemsCount={allResourcesSelected ? 'All' : selectedResources.length}
        onSelectionChange={handleSelectionChange}
        headings={[{ title: 'Title' }]}
        emptyState={
          <div className={styles.categoriesEmptyStateContainer}>
            <BlockStack align="center" inlineAlign="center" gap="400">
              <BlockStack align="center" inlineAlign="center" gap="150">
                <Text as="p" fontWeight="bold" alignment="center">
                  No occasions connected
                </Text>
                <Text as="p" alignment="center">
                  Select an occasion to connect with this category
                </Text>
              </BlockStack>
              <Button variant="primary">Connect occasion</Button>
            </BlockStack>
          </div>
        }
      >
        {rowMarkup}
      </IndexTable>
    </Card>
  );
}

Here's my Modal (btw as a side-note, how can extend the button Interface to allow for the variant prop in ts/eslint? or will that come as an update later?

      <Modal open={openCreateCategoryModal} onHide={() => setOpenCreateCategoryModal(false)}>
        <TitleBar title="Create new category">
          {/* eslint-disable react/no-unknown-property */}
          <button type="button" onClick={() => setOpenCreateCategoryModal(false)}>
            Cancel
          </button>
          <button type="button" variant="primary" onClick={() => console.log('Category create')}>
            Create category
          </button>
          {/* eslint-enable */}
        </TitleBar>

        <ModalContainer>
          <CreateCategoryModalContent />
        </ModalContainer>
      </Modal>

And then to give the entire context here's my ModalContainer

export default function ModalContainer(props: { children: React.ReactNode }) {
  const { children } = props;
  return <Box padding="400">{children}</Box>;
}