Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReactToSolid Error #265

Open
wakaztahir opened this issue Sep 26, 2023 · 3 comments
Open

ReactToSolid Error #265

wakaztahir opened this issue Sep 26, 2023 · 3 comments

Comments

@wakaztahir
Copy link

wakaztahir commented Sep 26, 2023

Many errors occurred during conversion from react to solid, I am trying to convert remix-ui, I think its the best ui library that if converted to solid would benefit a lot and also after shadcn-ui

Here's the error
throw new ManipulationError(sourceFile.getFilePath(), oldFileText, newFileText, message);
                  ^

ManipulationError: Manipulation error: A syntax error was inserted.

file.tsx:244:26 - error TS1003: Identifier expected.

244       <DummyDialog params.openLabel="Open Dialog" closeLabel="Close Dialog" />
                             ~
file.tsx:244:78 - error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?

244       <DummyDialog params.openLabel="Open Dialog" closeLabel="Close Dialog" />
                                                                                 ~

Error replacing tree: The children of the old and new trees were expected to have the same count (5:3).

-- Details --
Path: /file.tsx
Text: "... should return to the open button</li>\r\n    </ul>\r\n\r\n    <div style={{ display: 'flex', gap: 10 }}>\r\n      <DummyDialog params.openLabel=\"Open Dialog\" closeLabel=\"Close Dialog\" />\r\n      <input type=\"text\" defaultValue=\"some input\" />\r\n      <button type=\"button\" onClick={() => w..."
Stack: Error: Error replacing tree: The children of the old and new trees were expected to have the same count (5:3).
    at ParentFinderReplacementNodeHandler.handleChildren (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1438:19)
    at ParentFinderReplacementNodeHandler.handleNode (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1432:18)
    at ParentFinderReplacementNodeHandler.handleNode (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1572:19)
    at NodeHandlerHelper.handleForValues (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1375:21)
    at ParentFinderReplacementNodeHandler.handleChildren (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1442:25)
    at ParentFinderReplacementNodeHandler.handleNode (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1432:18)
    at ParentFinderReplacementNodeHandler.handleNode (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1572:19)
    at NodeHandlerHelper.handleForValues (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1375:21)
    at ParentFinderReplacementNodeHandler.handleChildren (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1442:25)
    at ParentFinderReplacementNodeHandler.handleNode (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:1432:18)
    at throwError (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:2300:19)
    at doManipulation (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:2294:13)
    at insertIntoParentTextRange (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:2319:5)
    at Identifier.replaceWithText (C:\Users\wakaz\AppData\Local\npm-cache\_npx\b38cb50d4b2a1350\node_modules\ts-morph\dist\ts-morph.js:3646:9)
    at renameIdentifiers (file:///C:/Users/wakaz/AppData/Local/npm-cache/_npx/b38cb50d4b2a1350/node_modules/@suid/codemod/utils/renameIdentifiers.js:14:17)
    at renameObjectBinding (file:///C:/Users/wakaz/AppData/Local/npm-cache/_npx/b38cb50d4b2a1350/node_modules/@suid/codemod/transforms/replaceObjectBinding.js:9:9)
    at replaceObjectBinding (file:///C:/Users/wakaz/AppData/Local/npm-cache/_npx/b38cb50d4b2a1350/node_modules/@suid/codemod/transforms/replaceObjectBinding.js:144:13)
    at file:///C:/Users/wakaz/AppData/Local/npm-cache/_npx/b38cb50d4b2a1350/node_modules/@suid/codemod/transforms/transformReactSource.js:77:57
    at Array.forEach (<anonymous>)
    at transformReactSource (file:///C:/Users/wakaz/AppData/Local/npm-cache/_npx/b38cb50d4b2a1350/node_modules/@suid/codemod/transforms/transformReactSource.js:77:39) {
  filePath: '/file.tsx',
  oldText: '/* eslint-disable jsx-a11y/accessible-emoji */\n' +
    "import ReactDOM from 'react-dom';\r\n" +
    "import { FocusScope } from '../focus-scope';\r\n" +
    "import * as Popper from '../popper';\r\n" +
    "import { Portal } from '../portal';\r\n" +
    "import { FocusGuards } from '../focus-guards';\r\n" +
    "import { RemoveScroll } from 'react-remove-scroll';\r\n" +
    "import { DismissableLayer } from '../dismissable-layer';\r\n" +
    "import { Slot } from '../slot';\r\n" +
    '\r\n' +
    'type DismissableLayerProps = React.ComponentProps<typeof DismissableLayer>;\r\n' +
    'type FocusScopeProps = React.ComponentProps<typeof FocusScope>;\r\n' +
    '\r\n' +
    "export default { title: 'Utilities/DismissableLayer' };\r\n" +
    '\r\n' +
    'const SYSTEM_FONT =\r\n' +
    `  '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';\r\n` +
    '\r\n' +
    'export const Basic = () => {\r\n' +
    '  const [open, setOpen] = React.useState(false);\r\n' +
    '  const openButtonRef = React.useRef(null);\r\n' +
    '\r\n' +
    '  const [dismissOnEscape, setDismissOnEscape] = React.useState(false);\r\n' +
    '  const [dismissOnPointerDownOutside, setDismissOnPointerDownOutside] = React.useState(false);\r\n' +
    '  const [dismissOnFocusOutside, setDismissOnFocusOutside] = React.useState(false);\r\n' +
    '  const [disabledOutsidePointerEvents, setDisableOutsidePointerEvents] = React.useState(false);\r\n' +
    '\r\n' +
    '  return (\r\n' +
    "    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>\r\n" +
    '      <h1>DismissableLayer</h1>\r\n' +
    '\r\n' +
    "      <div style={{ display: 'inline-block', textAlign: 'left', marginBottom: 20 }}>\r\n" +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={dismissOnEscape}\r\n' +
    '            onChange={(event) => setDismissOnEscape(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Dismiss on escape?\r\n' +
    '        </label>\r\n' +
    '\r\n' +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={dismissOnPointerDownOutside}\r\n' +
    '            onChange={(event) => setDismissOnPointerDownOutside(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Dismiss on pointer down outside?\r\n' +
    '        </label>\r\n' +
    '\r\n' +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={dismissOnFocusOutside}\r\n' +
    '            onChange={(event) => setDismissOnFocusOutside(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Dismiss on focus outside?\r\n' +
    '        </label>\r\n' +
    '\r\n' +
    '        <hr />\r\n' +
    '\r\n' +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={disabledOutsidePointerEvents}\r\n' +
    '            onChange={(event) => setDisableOutsidePointerEvents(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Disable outside pointer events?\r\n' +
    '        </label>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>\r\n' +
    "          {open ? 'Close' : 'Open'} layer\r\n" +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      {open ? (\r\n' +
    '        <DismissableLayer\r\n' +
    '          onEscapeKeyDown={(event) => {\r\n' +
    '            if (dismissOnEscape === false) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          onPointerDownOutside={(event) => {\r\n' +
    '            if (dismissOnPointerDownOutside === false || event.target === openButtonRef.current) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          onFocusOutside={(event) => {\r\n' +
    '            if (dismissOnFocusOutside === false) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          disableOutsidePointerEvents={disabledOutsidePointerEvents}\r\n' +
    '          onDismiss={() => setOpen(false)}\r\n' +
    '          style={{\r\n' +
    "            display: 'inline-flex',\r\n" +
    "            justifyContent: 'center',\r\n" +
    "            alignItems: 'center',\r\n" +
    "            verticalAlign: 'middle',\r\n" +
    '            width: 400,\r\n' +
    '            height: 300,\r\n' +
    "            backgroundColor: 'black',\r\n" +
    '            borderRadius: 10,\r\n' +
    '            marginBottom: 20,\r\n' +
    '          }}\r\n' +
    '        >\r\n' +
    '          <input type="text" />\r\n' +
    '        </DismissableLayer>\r\n' +
    '      ) : null}\r\n' +
    '\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <input type="text" defaultValue="hello" style={{ marginRight: 20 }} />\r\n' +
    `        <button type="button" onMouseDown={() => alert('hey!')}>\r\n` +
    '          hey!\r\n' +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '    </div>\r\n' +
    '  );\r\n' +
    '};\r\n' +
    '\r\n' +
    'export const Nested = () => {\r\n' +
    '  return (\r\n' +
    "    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>\r\n" +
    '      <h1>DismissableLayer (nested)</h1>\r\n' +
    '      <DismissableBox />\r\n' +
    '    </div>\r\n' +
    '  );\r\n' +
    '};\r\n' +
    '\r\n' +
    'export const WithFocusScope = () => {\r\n' +
    '  const [open, setOpen] = React.useState(false);\r\n' +
    '  const openButtonRef = React.useRef(null);\r\n' +
    '\r\n' +
    '  return (\r\n' +
    "    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>\r\n" +
    '      <h1>DismissableLayer + FocusScope</h1>\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>\r\n' +
    "          {open ? 'Close' : 'Open'} layer\r\n" +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      {open ? (\r\n' +
    '        <DismissableLayer\r\n' +
    '          asChild\r\n' +
    '          onPointerDownOutside={(event) => {\r\n' +
    '            if (event.target === openButtonRef.current) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          disableOutsidePointerEvents\r\n' +
    '          onDismiss={() => setOpen(false)}\r\n' +
    '        >\r\n' +
    '          <FocusScope\r\n' +
    '            trapped\r\n' +
    '            style={{\r\n' +
    "              display: 'inline-flex',\r\n" +
    "              justifyContent: 'center',\r\n" +
    "              alignItems: 'center',\r\n" +
    "              verticalAlign: 'middle',\r\n" +
    '              width: 400,\r\n' +
    '              height: 300,\r\n' +
    "              backgroundColor: 'black',\r\n" +
    '              borderRadius: 10,\r\n' +
    '              marginBottom: 20,\r\n' +
    '            }}\r\n' +
    '          >\r\n' +
    '            <input type="text" />\r\n' +
    '          </FocusScope>\r\n' +
    '        </DismissableLayer>\r\n' +
    '      ) : null}\r\n' +
    '\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <input type="text" defaultValue="hello" style={{ marginRight: 20 }} />\r\n' +
    `        <button type="button" onMouseDown={() => alert('hey!')}>\r\n` +
    '          hey!\r\n' +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '    </div>\r\n' +
    '  );\r\n' +
    '};\r\n' +
    '\r\n' +
    "type DismissableBoxProps = Omit<DismissableLayerProps, 'children'>;\r\n" +
    '\r\n' +
    'function DismissableBox(props: DismissableBoxProps) {\r\n' +
    '  const [open, setOpen] = React.useState(false);\r\n' +
    '  const openButtonRef = React.useRef(null);\r\n' +
    '\r\n' +
    '  return (\r\n' +
    '    <DismissableLayer\r\n' +
    '      {...props}\r\n' +
    '      style={{\r\n' +
    "        display: 'inline-block',\r\n" +
    "        verticalAlign: 'middle',\r\n" +
    '        padding: 20,\r\n' +
    "        backgroundColor: 'rgba(0, 0, 0, 0.2)',\r\n" +
    '        borderRadius: 10,\r\n' +
    '        marginTop: 20,\r\n' +
    '        ...props.style,\r\n' +
    '      }}\r\n' +
    '    >\r\n' +
    '      <div>\r\n' +
    '        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>\r\n' +
    "          {open ? 'Close' : 'Open'} new layer\r\n" +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      {open ? (\r\n' +
    '        <DismissableBox\r\n' +
    '          onPointerDownOutside={(event) => {\r\n' +
    '            if (event.target === openButtonRef.current) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          onFocusOutside={(event) => event.preventDefault()}\r\n' +
    '          onDismiss={() => setOpen(false)}\r\n' +
    '        />\r\n' +
    '      ) : null}\r\n' +
    '    </DismissableLayer>\r\n' +
    '  );\r\n' +
    '}\r\n' +
    '\r\n' +
    'export const DialogExample = () => (\r\n' +
    "  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>\r\n" +
    '    <h1>Dialog (fully modal example)</h1>\r\n' +
    "    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>\r\n" +
    '      <li>✅ focus should move inside `Dialog` when mounted</li>\r\n' +
    '      <li>✅ focus should be trapped inside `Dialog`</li>\r\n' +
    '      <li>✅ scrolling outside `Dialog` should be disabled</li>\r\n' +
    '      <li>✅ should be able to dismiss `Dialog` on pressing escape</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '      <li>\r\n' +
    '        ✅ interacting outside `Dialog` should be disabled (clicking the "alert me" button shouldn\'t\r\n' +
    '        do anything)\r\n' +
    '      </li>\r\n' +
    '      <li>➕</li>\r\n' +
    '      <li>✅ should be able to dismiss `Dialog` when interacting outside</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '    </ul>\r\n' +
    '\r\n' +
    "    <div style={{ display: 'flex', gap: 10 }}>\r\n" +
    '      <DummyDialog openLabel="Open Dialog" closeLabel="Close Dialog" />\r\n' +
    '      <input type="text" defaultValue="some input" />\r\n' +
    `      <button type="button" onClick={() => window.alert('clicked!')}>\r\n` +
    '        Alert me\r\n' +
    '      </button>\r\n' +
    '    </div>\r\n' +
    '  </div>\r\n' +
    ');\r\n' +
    '\r\n' +
    'export const PopoverFullyModal = () => (\r\n' +
    "  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>\r\n" +
    '    <h1>Popover (fully modal example)</h1>\r\n' +
    "    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>\r\n" +
    '      <li>✅ focus should move inside `Popover` when mounted</li>\r\n' +
    '      <li>✅ focus should be trapped inside `Popover`</li>\r\n' +
    '      <li>✅ scrolling outside `Popover` should be disabled</li>\r\n' +
    '      <li>✅ should be able to dismiss `Popover` on pressing escape</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '      <li>\r\n' +
    '        ✅ interacting outside `Popover` should be disabled (clicking the "alert me" button\r\n' +
    "        shouldn't do anything)\r\n" +
    '      </li>\r\n' +
    '      <li>➕</li>\r\n' +
    '      <li>✅ should be able to dismiss `Popover` when interacting outside</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '    </ul>\r\n' +
    '\r\n' +
    "    <div style={{ display: 'flex', gap: 10 }}>\r\n" +
    '      <DummyPopover\r\n' +
    '        openLabel="Open Popover"\r\n' +
    '        closeLabel="Close Popover"\r\n' +
    '        disableOutsidePointerEvents\r\n' +
    '        preventScroll\r\n' +
    '      />\r\n' +
    '      <input type="text" defaultValue="some input" />\r\n' +
    `      <button type="button" onClick={() => window.alert('clicked!')}>\r\n` +
    '        Alert me\r\n' +
    '      </button>\r\n' +
    '    </div>\r\n' +
    '  </div>\r\n' +
    ');\r\n' +
    '\r\n' +
    'export const PopoverSemiModal = () => {\r\n' +
    "  const [color, setColor] = React.useState('royalblue');\r\n" +
    '  const changeColorButtonRef = React.useRef(null);\r\n' +
    '  return (\r\n' +
    "    <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>\r\n" +
    '      <h1>Popover (semi-modal example)</h1>\r\n' +
    "      <ul style={{ listStyle: 'none', padding: 0, m"... 13484 more characters,
  newText: '/* eslint-disable jsx-a11y/accessible-emoji */\n' +
    "import ReactDOM from 'react-dom';\r\n" +
    "import { FocusScope } from '../focus-scope';\r\n" +
    "import * as Popper from '../popper';\r\n" +
    "import { Portal } from '../portal';\r\n" +
    "import { FocusGuards } from '../focus-guards';\r\n" +
    "import { RemoveScroll } from 'react-remove-scroll';\r\n" +
    "import { DismissableLayer } from '../dismissable-layer';\r\n" +
    "import { Slot } from '../slot';\r\n" +
    '\r\n' +
    'type DismissableLayerProps = React.ComponentProps<typeof DismissableLayer>;\r\n' +
    'type FocusScopeProps = React.ComponentProps<typeof FocusScope>;\r\n' +
    '\r\n' +
    "export default { title: 'Utilities/DismissableLayer' };\r\n" +
    '\r\n' +
    'const SYSTEM_FONT =\r\n' +
    `  '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';\r\n` +
    '\r\n' +
    'export const Basic = () => {\r\n' +
    '  const [open, setOpen] = React.useState(false);\r\n' +
    '  const openButtonRef = React.useRef(null);\r\n' +
    '\r\n' +
    '  const [dismissOnEscape, setDismissOnEscape] = React.useState(false);\r\n' +
    '  const [dismissOnPointerDownOutside, setDismissOnPointerDownOutside] = React.useState(false);\r\n' +
    '  const [dismissOnFocusOutside, setDismissOnFocusOutside] = React.useState(false);\r\n' +
    '  const [disabledOutsidePointerEvents, setDisableOutsidePointerEvents] = React.useState(false);\r\n' +
    '\r\n' +
    '  return (\r\n' +
    "    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>\r\n" +
    '      <h1>DismissableLayer</h1>\r\n' +
    '\r\n' +
    "      <div style={{ display: 'inline-block', textAlign: 'left', marginBottom: 20 }}>\r\n" +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={dismissOnEscape}\r\n' +
    '            onChange={(event) => setDismissOnEscape(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Dismiss on escape?\r\n' +
    '        </label>\r\n' +
    '\r\n' +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={dismissOnPointerDownOutside}\r\n' +
    '            onChange={(event) => setDismissOnPointerDownOutside(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Dismiss on pointer down outside?\r\n' +
    '        </label>\r\n' +
    '\r\n' +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={dismissOnFocusOutside}\r\n' +
    '            onChange={(event) => setDismissOnFocusOutside(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Dismiss on focus outside?\r\n' +
    '        </label>\r\n' +
    '\r\n' +
    '        <hr />\r\n' +
    '\r\n' +
    "        <label style={{ display: 'block' }}>\r\n" +
    '          <input\r\n' +
    '            type="checkbox"\r\n' +
    '            checked={disabledOutsidePointerEvents}\r\n' +
    '            onChange={(event) => setDisableOutsidePointerEvents(event.target.checked)}\r\n' +
    "          />{' '}\r\n" +
    '          Disable outside pointer events?\r\n' +
    '        </label>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>\r\n' +
    "          {open ? 'Close' : 'Open'} layer\r\n" +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      {open ? (\r\n' +
    '        <DismissableLayer\r\n' +
    '          onEscapeKeyDown={(event) => {\r\n' +
    '            if (dismissOnEscape === false) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          onPointerDownOutside={(event) => {\r\n' +
    '            if (dismissOnPointerDownOutside === false || event.target === openButtonRef.current) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          onFocusOutside={(event) => {\r\n' +
    '            if (dismissOnFocusOutside === false) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          disableOutsidePointerEvents={disabledOutsidePointerEvents}\r\n' +
    '          onDismiss={() => setOpen(false)}\r\n' +
    '          style={{\r\n' +
    "            display: 'inline-flex',\r\n" +
    "            justifyContent: 'center',\r\n" +
    "            alignItems: 'center',\r\n" +
    "            verticalAlign: 'middle',\r\n" +
    '            width: 400,\r\n' +
    '            height: 300,\r\n' +
    "            backgroundColor: 'black',\r\n" +
    '            borderRadius: 10,\r\n' +
    '            marginBottom: 20,\r\n' +
    '          }}\r\n' +
    '        >\r\n' +
    '          <input type="text" />\r\n' +
    '        </DismissableLayer>\r\n' +
    '      ) : null}\r\n' +
    '\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <input type="text" defaultValue="hello" style={{ marginRight: 20 }} />\r\n' +
    `        <button type="button" onMouseDown={() => alert('hey!')}>\r\n` +
    '          hey!\r\n' +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '    </div>\r\n' +
    '  );\r\n' +
    '};\r\n' +
    '\r\n' +
    'export const Nested = () => {\r\n' +
    '  return (\r\n' +
    "    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>\r\n" +
    '      <h1>DismissableLayer (nested)</h1>\r\n' +
    '      <DismissableBox />\r\n' +
    '    </div>\r\n' +
    '  );\r\n' +
    '};\r\n' +
    '\r\n' +
    'export const WithFocusScope = () => {\r\n' +
    '  const [open, setOpen] = React.useState(false);\r\n' +
    '  const openButtonRef = React.useRef(null);\r\n' +
    '\r\n' +
    '  return (\r\n' +
    "    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>\r\n" +
    '      <h1>DismissableLayer + FocusScope</h1>\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>\r\n' +
    "          {open ? 'Close' : 'Open'} layer\r\n" +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      {open ? (\r\n' +
    '        <DismissableLayer\r\n' +
    '          asChild\r\n' +
    '          onPointerDownOutside={(event) => {\r\n' +
    '            if (event.target === openButtonRef.current) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          disableOutsidePointerEvents\r\n' +
    '          onDismiss={() => setOpen(false)}\r\n' +
    '        >\r\n' +
    '          <FocusScope\r\n' +
    '            trapped\r\n' +
    '            style={{\r\n' +
    "              display: 'inline-flex',\r\n" +
    "              justifyContent: 'center',\r\n" +
    "              alignItems: 'center',\r\n" +
    "              verticalAlign: 'middle',\r\n" +
    '              width: 400,\r\n' +
    '              height: 300,\r\n' +
    "              backgroundColor: 'black',\r\n" +
    '              borderRadius: 10,\r\n' +
    '              marginBottom: 20,\r\n' +
    '            }}\r\n' +
    '          >\r\n' +
    '            <input type="text" />\r\n' +
    '          </FocusScope>\r\n' +
    '        </DismissableLayer>\r\n' +
    '      ) : null}\r\n' +
    '\r\n' +
    '      <div style={{ marginBottom: 20 }}>\r\n' +
    '        <input type="text" defaultValue="hello" style={{ marginRight: 20 }} />\r\n' +
    `        <button type="button" onMouseDown={() => alert('hey!')}>\r\n` +
    '          hey!\r\n' +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '    </div>\r\n' +
    '  );\r\n' +
    '};\r\n' +
    '\r\n' +
    "type DismissableBoxProps = Omit<DismissableLayerProps, 'children'>;\r\n" +
    '\r\n' +
    'function DismissableBox(props: DismissableBoxProps) {\r\n' +
    '  const [open, setOpen] = React.useState(false);\r\n' +
    '  const openButtonRef = React.useRef(null);\r\n' +
    '\r\n' +
    '  return (\r\n' +
    '    <DismissableLayer\r\n' +
    '      {...props}\r\n' +
    '      style={{\r\n' +
    "        display: 'inline-block',\r\n" +
    "        verticalAlign: 'middle',\r\n" +
    '        padding: 20,\r\n' +
    "        backgroundColor: 'rgba(0, 0, 0, 0.2)',\r\n" +
    '        borderRadius: 10,\r\n' +
    '        marginTop: 20,\r\n' +
    '        ...props.style,\r\n' +
    '      }}\r\n' +
    '    >\r\n' +
    '      <div>\r\n' +
    '        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>\r\n' +
    "          {open ? 'Close' : 'Open'} new layer\r\n" +
    '        </button>\r\n' +
    '      </div>\r\n' +
    '\r\n' +
    '      {open ? (\r\n' +
    '        <DismissableBox\r\n' +
    '          onPointerDownOutside={(event) => {\r\n' +
    '            if (event.target === openButtonRef.current) {\r\n' +
    '              event.preventDefault();\r\n' +
    '            }\r\n' +
    '          }}\r\n' +
    '          onFocusOutside={(event) => event.preventDefault()}\r\n' +
    '          onDismiss={() => setOpen(false)}\r\n' +
    '        />\r\n' +
    '      ) : null}\r\n' +
    '    </DismissableLayer>\r\n' +
    '  );\r\n' +
    '}\r\n' +
    '\r\n' +
    'export const DialogExample = () => (\r\n' +
    "  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>\r\n" +
    '    <h1>Dialog (fully modal example)</h1>\r\n' +
    "    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>\r\n" +
    '      <li>✅ focus should move inside `Dialog` when mounted</li>\r\n' +
    '      <li>✅ focus should be trapped inside `Dialog`</li>\r\n' +
    '      <li>✅ scrolling outside `Dialog` should be disabled</li>\r\n' +
    '      <li>✅ should be able to dismiss `Dialog` on pressing escape</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '      <li>\r\n' +
    '        ✅ interacting outside `Dialog` should be disabled (clicking the "alert me" button shouldn\'t\r\n' +
    '        do anything)\r\n' +
    '      </li>\r\n' +
    '      <li>➕</li>\r\n' +
    '      <li>✅ should be able to dismiss `Dialog` when interacting outside</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '    </ul>\r\n' +
    '\r\n' +
    "    <div style={{ display: 'flex', gap: 10 }}>\r\n" +
    '      <DummyDialog params.openLabel="Open Dialog" closeLabel="Close Dialog" />\r\n' +
    '      <input type="text" defaultValue="some input" />\r\n' +
    `      <button type="button" onClick={() => window.alert('clicked!')}>\r\n` +
    '        Alert me\r\n' +
    '      </button>\r\n' +
    '    </div>\r\n' +
    '  </div>\r\n' +
    ');\r\n' +
    '\r\n' +
    'export const PopoverFullyModal = () => (\r\n' +
    "  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>\r\n" +
    '    <h1>Popover (fully modal example)</h1>\r\n' +
    "    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>\r\n" +
    '      <li>✅ focus should move inside `Popover` when mounted</li>\r\n' +
    '      <li>✅ focus should be trapped inside `Popover`</li>\r\n' +
    '      <li>✅ scrolling outside `Popover` should be disabled</li>\r\n' +
    '      <li>✅ should be able to dismiss `Popover` on pressing escape</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '      <li>\r\n' +
    '        ✅ interacting outside `Popover` should be disabled (clicking the "alert me" button\r\n' +
    "        shouldn't do anything)\r\n" +
    '      </li>\r\n' +
    '      <li>➕</li>\r\n' +
    '      <li>✅ should be able to dismiss `Popover` when interacting outside</li>\r\n' +
    '      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>\r\n' +
    '    </ul>\r\n' +
    '\r\n' +
    "    <div style={{ display: 'flex', gap: 10 }}>\r\n" +
    '      <DummyPopover\r\n' +
    '        openLabel="Open Popover"\r\n' +
    '        closeLabel="Close Popover"\r\n' +
    '        disableOutsidePointerEvents\r\n' +
    '        preventScroll\r\n' +
    '      />\r\n' +
    '      <input type="text" defaultValue="some input" />\r\n' +
    `      <button type="button" onClick={() => window.alert('clicked!')}>\r\n` +
    '        Alert me\r\n' +
    '      </button>\r\n' +
    '    </div>\r\n' +
    '  </div>\r\n' +
    ');\r\n' +
    '\r\n' +
    'export const PopoverSemiModal = () => {\r\n' +
    "  const [color, setColor] = React.useState('royalblue');\r\n" +
    '  const changeColorButtonRef = React.useRef(null);\r\n' +
    '  return (\r\n' +
    "    <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>\r\n" +
    '      <h1>Popover (semi-modal example)</h1>\r\n' +
    "      <ul style={{ listStyle: 'none', paddin"... 13491 more characters
}

@wakaztahir
Copy link
Author

wakaztahir commented Sep 26, 2023

here's the whole file

Code
/* eslint-disable jsx-a11y/accessible-emoji */

import React from 'react';
import ReactDOM from 'react-dom';
import { FocusScope } from '../focus-scope';
import * as Popper from '../popper';
import { Portal } from '../portal';
import { FocusGuards } from '../focus-guards';
import { RemoveScroll } from 'react-remove-scroll';
import { DismissableLayer } from '../dismissable-layer';
import { Slot } from '../slot';

type DismissableLayerProps = React.ComponentProps<typeof DismissableLayer>;
type FocusScopeProps = React.ComponentProps<typeof FocusScope>;

export default { title: 'Utilities/DismissableLayer' };

const SYSTEM_FONT =
  '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';

export const Basic = () => {
  const [open, setOpen] = React.useState(false);
  const openButtonRef = React.useRef(null);

  const [dismissOnEscape, setDismissOnEscape] = React.useState(false);
  const [dismissOnPointerDownOutside, setDismissOnPointerDownOutside] = React.useState(false);
  const [dismissOnFocusOutside, setDismissOnFocusOutside] = React.useState(false);
  const [disabledOutsidePointerEvents, setDisableOutsidePointerEvents] = React.useState(false);

  return (
    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>
      <h1>DismissableLayer</h1>

      <div style={{ display: 'inline-block', textAlign: 'left', marginBottom: 20 }}>
        <label style={{ display: 'block' }}>
          <input
            type="checkbox"
            checked={dismissOnEscape}
            onChange={(event) => setDismissOnEscape(event.target.checked)}
          />{' '}
          Dismiss on escape?
        </label>

        <label style={{ display: 'block' }}>
          <input
            type="checkbox"
            checked={dismissOnPointerDownOutside}
            onChange={(event) => setDismissOnPointerDownOutside(event.target.checked)}
          />{' '}
          Dismiss on pointer down outside?
        </label>

        <label style={{ display: 'block' }}>
          <input
            type="checkbox"
            checked={dismissOnFocusOutside}
            onChange={(event) => setDismissOnFocusOutside(event.target.checked)}
          />{' '}
          Dismiss on focus outside?
        </label>

        <hr />

        <label style={{ display: 'block' }}>
          <input
            type="checkbox"
            checked={disabledOutsidePointerEvents}
            onChange={(event) => setDisableOutsidePointerEvents(event.target.checked)}
          />{' '}
          Disable outside pointer events?
        </label>
      </div>

      <div style={{ marginBottom: 20 }}>
        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>
          {open ? 'Close' : 'Open'} layer
        </button>
      </div>

      {open ? (
        <DismissableLayer
          onEscapeKeyDown={(event) => {
            if (dismissOnEscape === false) {
              event.preventDefault();
            }
          }}
          onPointerDownOutside={(event) => {
            if (dismissOnPointerDownOutside === false || event.target === openButtonRef.current) {
              event.preventDefault();
            }
          }}
          onFocusOutside={(event) => {
            if (dismissOnFocusOutside === false) {
              event.preventDefault();
            }
          }}
          disableOutsidePointerEvents={disabledOutsidePointerEvents}
          onDismiss={() => setOpen(false)}
          style={{
            display: 'inline-flex',
            justifyContent: 'center',
            alignItems: 'center',
            verticalAlign: 'middle',
            width: 400,
            height: 300,
            backgroundColor: 'black',
            borderRadius: 10,
            marginBottom: 20,
          }}
        >
          <input type="text" />
        </DismissableLayer>
      ) : null}

      <div style={{ marginBottom: 20 }}>
        <input type="text" defaultValue="hello" style={{ marginRight: 20 }} />
        <button type="button" onMouseDown={() => alert('hey!')}>
          hey!
        </button>
      </div>
    </div>
  );
};

export const Nested = () => {
  return (
    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>
      <h1>DismissableLayer (nested)</h1>
      <DismissableBox />
    </div>
  );
};

export const WithFocusScope = () => {
  const [open, setOpen] = React.useState(false);
  const openButtonRef = React.useRef(null);

  return (
    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>
      <h1>DismissableLayer + FocusScope</h1>
      <div style={{ marginBottom: 20 }}>
        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>
          {open ? 'Close' : 'Open'} layer
        </button>
      </div>

      {open ? (
        <DismissableLayer
          asChild
          onPointerDownOutside={(event) => {
            if (event.target === openButtonRef.current) {
              event.preventDefault();
            }
          }}
          disableOutsidePointerEvents
          onDismiss={() => setOpen(false)}
        >
          <FocusScope
            trapped
            style={{
              display: 'inline-flex',
              justifyContent: 'center',
              alignItems: 'center',
              verticalAlign: 'middle',
              width: 400,
              height: 300,
              backgroundColor: 'black',
              borderRadius: 10,
              marginBottom: 20,
            }}
          >
            <input type="text" />
          </FocusScope>
        </DismissableLayer>
      ) : null}

      <div style={{ marginBottom: 20 }}>
        <input type="text" defaultValue="hello" style={{ marginRight: 20 }} />
        <button type="button" onMouseDown={() => alert('hey!')}>
          hey!
        </button>
      </div>
    </div>
  );
};

type DismissableBoxProps = Omit<DismissableLayerProps, 'children'>;

function DismissableBox(props: DismissableBoxProps) {
  const [open, setOpen] = React.useState(false);
  const openButtonRef = React.useRef(null);

  return (
    <DismissableLayer
      {...props}
      style={{
        display: 'inline-block',
        verticalAlign: 'middle',
        padding: 20,
        backgroundColor: 'rgba(0, 0, 0, 0.2)',
        borderRadius: 10,
        marginTop: 20,
        ...props.style,
      }}
    >
      <div>
        <button ref={openButtonRef} type="button" onClick={() => setOpen((open) => !open)}>
          {open ? 'Close' : 'Open'} new layer
        </button>
      </div>

      {open ? (
        <DismissableBox
          onPointerDownOutside={(event) => {
            if (event.target === openButtonRef.current) {
              event.preventDefault();
            }
          }}
          onFocusOutside={(event) => event.preventDefault()}
          onDismiss={() => setOpen(false)}
        />
      ) : null}
    </DismissableLayer>
  );
}

export const DialogExample = () => (
  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>
    <h1>Dialog (fully modal example)</h1>
    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>
      <li>✅ focus should move inside `Dialog` when mounted</li>
      <li>✅ focus should be trapped inside `Dialog`</li>
      <li>✅ scrolling outside `Dialog` should be disabled</li>
      <li>✅ should be able to dismiss `Dialog` on pressing escape</li>
      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>
      <li>
        ✅ interacting outside `Dialog` should be disabled (clicking the "alert me" button shouldn't
        do anything)
      </li>
      <li>➕</li>
      <li>✅ should be able to dismiss `Dialog` when interacting outside</li>
      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>
    </ul>

    <div style={{ display: 'flex', gap: 10 }}>
      <DummyDialog openLabel="Open Dialog" closeLabel="Close Dialog" />
      <input type="text" defaultValue="some input" />
      <button type="button" onClick={() => window.alert('clicked!')}>
        Alert me
      </button>
    </div>
  </div>
);

export const PopoverFullyModal = () => (
  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>
    <h1>Popover (fully modal example)</h1>
    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>
      <li>✅ focus should move inside `Popover` when mounted</li>
      <li>✅ focus should be trapped inside `Popover`</li>
      <li>✅ scrolling outside `Popover` should be disabled</li>
      <li>✅ should be able to dismiss `Popover` on pressing escape</li>
      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>
      <li>
        ✅ interacting outside `Popover` should be disabled (clicking the "alert me" button
        shouldn't do anything)
      </li>
      <li>➕</li>
      <li>✅ should be able to dismiss `Popover` when interacting outside</li>
      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>
    </ul>

    <div style={{ display: 'flex', gap: 10 }}>
      <DummyPopover
        openLabel="Open Popover"
        closeLabel="Close Popover"
        disableOutsidePointerEvents
        preventScroll
      />
      <input type="text" defaultValue="some input" />
      <button type="button" onClick={() => window.alert('clicked!')}>
        Alert me
      </button>
    </div>
  </div>
);

export const PopoverSemiModal = () => {
  const [color, setColor] = React.useState('royalblue');
  const changeColorButtonRef = React.useRef(null);
  return (
    <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>
      <h1>Popover (semi-modal example)</h1>
      <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>
        <li>✅ focus should move inside `Popover` when mounted</li>
        <li>✅ focus should be trapped inside `Popover`</li>
        <li>✅ scrolling outside `Popover` should be allowed</li>
        <li>✅ should be able to dismiss `Popover` on pressing escape</li>
        <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>
        <li>
          ✅ interacting outside `Popover` should be allowed (clicking the "alert me" button should
          trigger)
        </li>
        <li>➕</li>
        <li>
          ✅ should be able to dismiss `Popover` when interacting outside{' '}
          <span style={{ fontWeight: 600 }}>unless specified (ie. change color button)</span>
        </li>
        <li style={{ marginLeft: 30 }}>
          ✅ focus should <span style={{ fontWeight: 600 }}>NOT</span> return to the open button
          when unmounted, natural focus should occur
        </li>
      </ul>

      <div style={{ display: 'flex', gap: 10 }}>
        <DummyPopover
          color={color}
          openLabel="Open Popover"
          closeLabel="Close Popover"
          onPointerDownOutside={(event) => {
            if (event.target === changeColorButtonRef.current) {
              event.preventDefault();
            }
          }}
        />
        <input type="text" defaultValue="some input" />
        <button type="button" onClick={() => window.alert('clicked!')}>
          Alert me
        </button>
        <button
          ref={changeColorButtonRef}
          type="button"
          onClick={() =>
            setColor((prevColor) => (prevColor === 'royalblue' ? 'tomato' : 'royalblue'))
          }
        >
          Change color
        </button>
      </div>
    </div>
  );
};

export const PopoverNonModal = () => (
  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>
    <h1>Popover (non modal example)</h1>
    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>
      <li>✅ focus should move inside `Popover` when mounted</li>
      <li>
        ✅ focus should <span style={{ fontWeight: 600 }}>NOT</span> be trapped inside `Popover`
      </li>
      <li>✅ scrolling outside `Popover` should be allowed</li>
      <li>✅ should be able to dismiss `Popover` on pressing escape</li>
      <li style={{ marginLeft: 30 }}>✅ focus should return to the open button</li>
      <li>
        ✅ interacting outside `Popover` should be allowed (clicking the "alert me" button should
        trigger)
      </li>
      <li>➕</li>
      <li>✅ should be able to dismiss `Popover` when clicking outside</li>
      <li style={{ marginLeft: 30 }}>
        ✅ focus should <span style={{ fontWeight: 600 }}>NOT</span> return to the open button when
        unmounted, natural focus should occur
      </li>
      <li>✅ should be able to dismiss `Popover` when focus leaves it</li>
      <li style={{ marginLeft: 30 }}>
        ❓ focus should move to next tabbable element after open button
        <div style={{ fontWeight: 600 }}>
          <span style={{ marginLeft: 20 }}>notes:</span>
          <ul>
            <li>
              I have left this one out for now as I am still unsure in which case it should do this
            </li>
            <li>
              for the moment, focus will be returned to the open button when `FocusScope` unmounts
            </li>
            <li>Need to do some more thinking, in the meantime, I think this behavior is ok</li>
          </ul>
        </div>
      </li>
    </ul>

    <div style={{ display: 'flex', gap: 10 }}>
      <DummyPopover openLabel="Open Popover" closeLabel="Close Popover" trapped={false} />
      <input type="text" defaultValue="some input" />
      <button type="button" onClick={() => window.alert('clicked!')}>
        Alert me
      </button>
    </div>
  </div>
);

export const PopoverInDialog = () => (
  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>
    <h1>Popover (semi-modal) in Dialog (fully modal)</h1>
    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>
      <li>
        ✅ dismissing `Popover` by pressing escape should{' '}
        <span style={{ fontWeight: 600 }}>NOT</span> dismiss `Dialog`
      </li>
      <li>✅ dismissing `Popover` by clicking outside should also dismiss `Dialog`</li>
    </ul>

    <div style={{ display: 'flex', gap: 10 }}>
      <DummyDialog openLabel="Open Dialog" closeLabel="Close Dialog">
        <DummyPopover openLabel="Open Popover" closeLabel="Close Popover" />
      </DummyDialog>
      <input type="text" defaultValue="some input" />
      <button type="button" onClick={() => window.alert('clicked!')}>
        Alert me
      </button>
    </div>
  </div>
);

export const PopoverNested = () => (
  <div style={{ height: '300vh', fontFamily: SYSTEM_FONT }}>
    <h1>Popover (nested example)</h1>
    <ul style={{ listStyle: 'none', padding: 0, marginBottom: 30 }}>
      <li>
        ✅ dismissing a `Popover` by pressing escape should only dismiss that given `Popover`, not
        its parents
      </li>
      <li>
        ✅ interacting outside the blue `Popover` should only dismiss itself and not its parents
      </li>
      <li>✅ interacting outside the red `Popover` should dismiss itself and the black one</li>
      <li>✅ unless the click wasn't outside the black one</li>
      <li>
        ✅ when the blue `Popover` is open, there should be{' '}
        <span style={{ fontWeight: 600 }}>NO</span> text cursor above the red or black inputs
      </li>
      <li>
        ✅ when the red `Popover` is open, there should be a text cursor above the black input but
        not the one on the page behind
      </li>
    </ul>

    <div style={{ display: 'flex', gap: 10 }}>
      <DummyPopover
        disableOutsidePointerEvents
        onInteractOutside={() => {
          console.log('interact outside black');
        }}
      >
        <DummyPopover
          color="tomato"
          openLabel="Open red"
          closeLabel="Close red"
          onInteractOutside={() => {
            console.log('interact outside red');
          }}
        >
          <DummyPopover
            color="royalblue"
            openLabel="Open blue"
            closeLabel="Close blue"
            disableOutsidePointerEvents
            onInteractOutside={() => {
              console.log('interact outside blue');
            }}
          ></DummyPopover>
        </DummyPopover>
      </DummyPopover>
      <input type="text" defaultValue="some input" />
      <button type="button" onClick={() => window.alert('clicked!')}>
        Alert me
      </button>
    </div>
  </div>
);

/* -------------------------------------------------------------------------------------------------
 * Dummy components
 * -----------------------------------------------------------------------------------------------*/

type DummyDialogProps = {
  children?: React.ReactNode;
  openLabel?: string;
  closeLabel?: string;
};

function DummyDialog({ children, openLabel = 'Open', closeLabel = 'Close' }: DummyDialogProps) {
  const [open, setOpen] = React.useState(false);
  return (
    <>
      <button type="button" onClick={() => setOpen((prevOpen) => !prevOpen)}>
        {openLabel}
      </button>
      {open ? (
        <FocusGuards>
          <Portal asChild>
            <div
              style={{
                position: 'fixed',
                top: 0,
                right: 0,
                bottom: 0,
                left: 0,
                pointerEvents: 'none',
                backgroundColor: 'black',
                opacity: 0.2,
              }}
            />
          </Portal>
          <Portal asChild>
            <RemoveScroll as={Slot}>
              <DismissableLayer
                asChild
                disableOutsidePointerEvents
                onDismiss={() => setOpen(false)}
              >
                <FocusScope
                  trapped
                  style={{
                    boxSizing: 'border-box',
                    display: 'flex',
                    alignItems: 'start',
                    gap: 10,
                    position: 'fixed',
                    top: '50%',
                    left: '50%',
                    transform: 'translate(-50%, -50%)',
                    background: 'white',
                    minWidth: 300,
                    minHeight: 200,
                    padding: 40,
                    borderRadius: 10,
                    backgroundColor: 'white',
                    boxShadow: '0 2px 10px rgba(0, 0, 0, 0.12)',
                  }}
                >
                  {children}
                  <button type="button" onClick={() => setOpen(false)}>
                    {closeLabel}
                  </button>
                  <input type="text" defaultValue="hello world" />
                </FocusScope>
              </DismissableLayer>
            </RemoveScroll>
          </Portal>
        </FocusGuards>
      ) : null}
    </>
  );
}

type DummyPopoverOwnProps = {
  children?: React.ReactNode;
  openLabel?: string;
  closeLabel?: string;
  color?: string;
  preventScroll?: boolean;
};
type DummyPopoverProps = DummyPopoverOwnProps &
  Omit<FocusScopeProps, 'children'> &
  Omit<DismissableLayerProps, 'children'>;

function DummyPopover({
  children,
  openLabel = 'Open',
  closeLabel = 'Close',
  color = '#333',
  trapped = true,
  onEscapeKeyDown,
  onPointerDownOutside,
  onFocusOutside,
  onInteractOutside,
  disableOutsidePointerEvents = false,
  preventScroll = false,
}: DummyPopoverProps) {
  const [skipUnmountAutoFocus, setSkipUnmountAutoFocus] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  const openButtonRef = React.useRef(null);
  const ScrollContainer = preventScroll ? RemoveScroll : React.Fragment;
  const scrollLockWrapperProps = preventScroll ? { as: Slot } : undefined;

  return (
    <Popper.Root>
      <Popper.Anchor asChild>
        <button type="button" ref={openButtonRef} onClick={() => setOpen((prevOpen) => !prevOpen)}>
          {openLabel}
        </button>
      </Popper.Anchor>
      {open ? (
        <FocusGuards>
          <ScrollContainer {...scrollLockWrapperProps}>
            <Portal asChild>
              <DismissableLayer
                asChild
                disableOutsidePointerEvents={disableOutsidePointerEvents}
                onEscapeKeyDown={onEscapeKeyDown}
                onPointerDownOutside={(event) => {
                  setSkipUnmountAutoFocus(!disableOutsidePointerEvents);
                  if (event.target === openButtonRef.current) {
                    event.preventDefault();
                  } else {
                    onPointerDownOutside?.(event);
                  }
                }}
                onFocusOutside={onFocusOutside}
                onInteractOutside={onInteractOutside}
                onDismiss={() => setOpen(false)}
              >
                <FocusScope
                  asChild
                  trapped={trapped}
                  onUnmountAutoFocus={(event) => {
                    if (skipUnmountAutoFocus) {
                      event.preventDefault();
                    }
                    setSkipUnmountAutoFocus(false);
                  }}
                >
                  <Popper.Content
                    style={{
                      filter: 'drop-shadow(0 2px 10px rgba(0, 0, 0, 0.12))',
                      display: 'flex',
                      alignItems: 'flex-start',
                      gap: 10,
                      background: 'white',
                      minWidth: 200,
                      minHeight: 150,
                      padding: 20,
                      borderRadius: 4,
                      backgroundColor: color,
                    }}
                    side="bottom"
                    sideOffset={10}
                  >
                    {children}
                    <button type="button" onClick={() => setOpen(false)}>
                      {closeLabel}
                    </button>
                    <input type="text" defaultValue="hello world" />
                    <Popper.Arrow width={10} height={4} style={{ fill: color }} offset={20} />
                  </Popper.Content>
                </FocusScope>
              </DismissableLayer>
            </Portal>
          </ScrollContainer>
        </FocusGuards>
      ) : null}
    </Popper.Root>
  );
}

export const InPopupWindow = () => {
  const handlePopupClick = React.useCallback(() => {
    const popupWindow = window.open(undefined, undefined, 'width=300,height=300,top=100,left=100');
    if (!popupWindow) {
      console.error('Failed to open popup window, check your popup blocker settings');
      return;
    }

    const containerNode = popupWindow.document.createElement('div');
    popupWindow.document.body.append(containerNode);

    ReactDOM.render(<DismissableBox />, containerNode);
  }, []);
  return (
    <div style={{ fontFamily: 'sans-serif', textAlign: 'center' }}>
      <button onClick={handlePopupClick}>Open Popup</button>
    </div>
  );
};

@juanrgm
Copy link
Member

juanrgm commented Sep 26, 2023

There are some problems:

  1. Convert import React from 'react'; to import * as React from 'react';.
  2. Remove the emoji icons.
  3. Don't destruct the component props at DummyDialog/DummyPopover.

I will keep it in mind for future versions.

Remember that ReactToSolid is only a conversion approximation, if the code is complex, manual adaptations will need to be carried out.

@wakaztahir
Copy link
Author

wakaztahir commented Oct 26, 2023

I came across useAssets hook , which is undocumented hook being used here

Being used here https://github.com/swordev/suid/blob/main/packages/styled-engine/src/createStyle.tsx#L81

I used the useAssets hook and it works in SolidStart, however not in Astro

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants