From ef542e158b0b8c6c780546cc600cdadf50a9d630 Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 10:47:15 +0200 Subject: [PATCH 01/71] Integrate lost-pixel --- .github/workflows/vis-reg-tests.yml | 25 ++ .gitignore | 1 + package.json | 1 + packages/design-system/lostpixel.config.ts | 8 + packages/design-system/package.json | 1 + turbo.json | 3 + yarn.lock | 381 ++++++++++++++++++++- 7 files changed, 415 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/vis-reg-tests.yml create mode 100644 packages/design-system/lostpixel.config.ts diff --git a/.github/workflows/vis-reg-tests.yml b/.github/workflows/vis-reg-tests.yml new file mode 100644 index 0000000000..bfe5dab3ac --- /dev/null +++ b/.github/workflows/vis-reg-tests.yml @@ -0,0 +1,25 @@ +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: 16.x + cache: 'npm' + + - name: Install dependencies + run: yarn install --frozen-lockfile --ignore-scripts + + - name: Build Storybook for Design System + run: turbo run storybook:build --filter=design-system + + - name: Lost Pixel + uses: lost-pixel/lost-pixel@v2.20.0 diff --git a/.gitignore b/.gitignore index 8fec2a6098..04b912530e 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ yarn-error.log* # builds packages/**/lib generated +storybook-static diff --git a/package.json b/package.json index cedbfb78f5..8458172137 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"", "prepare": "husky install", "storybook": "turbo run storybook:run", + "storybook:build": "turbo run storybook:build", "migrations": "cd apps/designer && migrations --dev" }, "devDependencies": { diff --git a/packages/design-system/lostpixel.config.ts b/packages/design-system/lostpixel.config.ts new file mode 100644 index 0000000000..d545155100 --- /dev/null +++ b/packages/design-system/lostpixel.config.ts @@ -0,0 +1,8 @@ +import type { CustomProjectConfig } from "lost-pixel"; + +export const config: CustomProjectConfig = { + storybookShots: { + storybookUrl: "storybook-static", + }, + generateOnly: true, +}; diff --git a/packages/design-system/package.json b/packages/design-system/package.json index af8d3695ff..822e8d4c02 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -24,6 +24,7 @@ "@storybook/react": "^6.5.6", "@storybook/testing-library": "^0.0.11", "@webstudio-is/jest-config": "*", + "lost-pixel": "^2.20.0", "tsup": "^6.1.3", "typescript": "4.7.4" }, diff --git a/turbo.json b/turbo.json index 7c9bd87ce1..9abfb616e9 100644 --- a/turbo.json +++ b/turbo.json @@ -12,6 +12,9 @@ }, "storybook:run": { "cache": false + }, + "storybook:build": { + "cache": false } } } diff --git a/yarn.lock b/yarn.lock index eb1079d87e..1145295fef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2050,6 +2050,13 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@design-systems/utils@2.12.0": version "2.12.0" resolved "https://registry.yarnpkg.com/@design-systems/utils/-/utils-2.12.0.tgz#955c108be07cb8f01532207cbfea8f848fa760c9" @@ -2514,6 +2521,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": version "0.3.14" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" @@ -4740,6 +4755,26 @@ mkdirp "^1.0.4" path-browserify "^1.0.1" +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + "@types/acorn@^4.0.0": version "4.0.6" resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" @@ -5773,6 +5808,11 @@ acorn-walk@^7.1.1, acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" @@ -6192,6 +6232,11 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== +async@3.2.4, async@^3.1.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -6225,6 +6270,14 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +axios@0.27.2, axios@^0.27.0: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -6498,6 +6551,13 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +block-stream2@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/block-stream2/-/block-stream2-2.1.0.tgz#ac0c5ef4298b3857796e05be8ebed72196fa054b" + integrity sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg== + dependencies: + readable-stream "^3.4.0" + bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -6633,6 +6693,11 @@ browser-assert@^1.2.1: resolved "https://registry.yarnpkg.com/browser-assert/-/browser-assert-1.2.1.tgz#9aaa5a2a8c74685c2ae05bfe46efd606f068c200" integrity sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ== +browser-or-node@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-1.3.0.tgz#f2a4e8568f60263050a6714b2cc236bb976647a7" + integrity sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg== + browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -6728,6 +6793,11 @@ bson-objectid@^2.0.2, bson-objectid@^2.0.3: resolved "https://registry.yarnpkg.com/bson-objectid/-/bson-objectid-2.0.3.tgz#d840185172846b2f10c42ce2bcdb4a50956a9db5" integrity sha512-WYwVtY9yqk179EPMNuF3vcxufdrGLEo2XwqdRVbfLVe9X6jLt7WKZQgP+ObOcprakBGbHxzl76tgTaieqsH29g== +buffer-crc32@^0.2.13: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -7473,6 +7543,11 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -7642,6 +7717,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -7662,7 +7742,7 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-browserify@^3.11.0: +crypto-browserify@^3.11.0, crypto-browserify@^3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== @@ -8397,6 +8477,11 @@ es5-shim@^4.5.13: resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955" integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ== +es6-error@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + es6-shim@^0.35.5: version "0.35.6" resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0" @@ -9476,11 +9561,25 @@ fast-shallow-equal@^1.0.0: resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + fast-xml-parser@3.19.0: version "3.19.0" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== +fast-xml-parser@^3.17.5: + version "3.21.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736" + integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg== + dependencies: + strnum "^1.0.4" + fastest-stable-stringify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" @@ -9584,6 +9683,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== + finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -9692,6 +9796,11 @@ focus-lock@^0.8.0: dependencies: tslib "^1.9.3" +follow-redirects@^1.14.9: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + fontkit@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/fontkit/-/fontkit-2.0.2.tgz#ac5384f3ecab8327c6d2ea2e4d384afc544b48fd" @@ -9768,6 +9877,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + format@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" @@ -9837,6 +9955,11 @@ fs-jetpack@^4.3.1: minimatch "^3.0.2" rimraf "^2.6.3" +fs-memo@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fs-memo/-/fs-memo-1.2.0.tgz#a2ec3be606b902077adbb37ec529c5ec5fb2e037" + integrity sha512-YEexkCpL4j03jn5SxaMHqcO6IuWuqm8JFUYhyCep7Ao89JIYmB8xoKhK7zXXJ9cCaNXpyNH5L3QtAmoxjoHW2w== + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -9974,6 +10097,13 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-port-please@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/get-port-please/-/get-port-please-2.6.1.tgz#80143de24fcaab39b01df977f66ad967e06b17d1" + integrity sha512-4PDSrL6+cuMM1xs6w36ZIkaKzzE0xzfVBCfebHIJ3FE8iB9oic/ECwPw3iNiD4h1AoJ5XLLBhEviFAVrZsDC5A== + dependencies: + fs-memo "^1.2.0" + get-port@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" @@ -10828,6 +10958,11 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + is-absolute-url@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" @@ -12001,6 +12136,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-stream/-/json-stream-1.0.0.tgz#1a3854e28d2bbeeab31cc7ddf683d2ddc5652708" + integrity sha512-H/ZGY0nIAg3QcOwE1QN/rK/Fa7gJn7Ii5obwp6zyPO4xiPNwpIMjqy2gwjBEGqzkF/vSWEIBQCBuN19hYiL6Qg== + json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -12262,6 +12402,16 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -12327,6 +12477,27 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lost-pixel@^2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/lost-pixel/-/lost-pixel-2.20.0.tgz#c8a1d40298694e34df7e99d95674a24e0d9b6910" + integrity sha512-gYmyA/Cqu7Xn21VvKuW942jV8hFk1OmjT+YWeCH1HBS4BptltzvbQRHDY4hg8t3DuouaNPDaQpMJyJjMOCGoow== + dependencies: + async "3.2.4" + axios "0.27.2" + fs-extra "^10.1.0" + get-port-please "2.6.1" + lodash.get "^4.4.2" + lodash.kebabcase "^4.1.1" + minio "7.0.32" + pixelmatch "5.3.0" + playwright "1.25.2" + pngjs "^6.0.0" + posthog-node "^2.1.0" + serve-handler "^6.1.3" + ts-node "10.9.1" + uuid "^9.0.0" + yargs "^17.5.1" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -13057,7 +13228,19 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.30, mime-types@~2.1.24, mime-types@~2.1.34: +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@^2.1.12, mime-types@^2.1.14, mime-types@^2.1.27, mime-types@^2.1.30, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -13121,6 +13304,13 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -13140,6 +13330,29 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1. resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minio@7.0.32: + version "7.0.32" + resolved "https://registry.yarnpkg.com/minio/-/minio-7.0.32.tgz#fed6a4679c5954d3efc6df47f73f7e7124446e2c" + integrity sha512-txa7Vr0N24MKzeAybP/wY1jxbLnfGHXwZYyfFXuMW55HX2+HOcKEIgH4hU6Qj/kiMgyXs/ozHjAuLIDrR8nwLg== + dependencies: + async "^3.1.0" + block-stream2 "^2.0.0" + browser-or-node "^1.3.0" + buffer-crc32 "^0.2.13" + crypto-browserify "^3.12.0" + es6-error "^4.1.1" + fast-xml-parser "^3.17.5" + ipaddr.js "^2.0.1" + json-stream "^1.0.0" + lodash "^4.17.21" + mime-types "^2.1.14" + mkdirp "^0.5.1" + query-string "^7.1.1" + through2 "^3.0.1" + web-encoding "^1.1.5" + xml "^1.0.0" + xml2js "^0.4.15" + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -14038,6 +14251,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -14063,6 +14281,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -14164,6 +14387,13 @@ pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.5: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== +pixelmatch@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.3.0.tgz#5e5321a7abedfb7962d60dbf345deda87cb9560a" + integrity sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q== + dependencies: + pngjs "^6.0.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -14185,11 +14415,28 @@ pkg-dir@^5.0.0: dependencies: find-up "^5.0.0" +playwright-core@1.25.2: + version "1.25.2" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.25.2.tgz#ea4baa398a4d45fcdfe48799482b599e3d0f033f" + integrity sha512-0yTbUE9lIddkEpLHL3u8PoCL+pWiZtj5A/j3U7YoNjcmKKDGBnCrgHJMzwd2J5vy6l28q4ki3JIuz7McLHhl1A== + +playwright@1.25.2: + version "1.25.2" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.25.2.tgz#0fc67e4385a52a51371ff9114bf68e3ad50a7f41" + integrity sha512-RwMB5SFRV/8wSfK+tK8ycpqdzORvoqUNz9DUeRfSgZFrZej5uuBl9wFjWcc+OkXFEtaPmx1acAVGG7hA4IJ1kg== + dependencies: + playwright-core "1.25.2" + pluralize@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +pngjs@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" + integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== + pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -14331,6 +14578,13 @@ postcss@^8.2.15: picocolors "^1.0.0" source-map-js "^1.0.2" +posthog-node@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-2.1.0.tgz#d99acda28b208cbeefed2e6cd0ddfa8cc2d79910" + integrity sha512-xr56mZRQo7rnL2YdwbipcxTZeyi5dcI6IM4++wIN7JLYwinrJYcQv01nan4gU4kMy33Qz5qT6boWMQRwpKZJVQ== + dependencies: + axios "^0.27.0" + prebuild-install@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" @@ -14573,7 +14827,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@^1.2.4: +punycode@^1.2.4, punycode@^1.3.2: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== @@ -14602,6 +14856,16 @@ qs@^6.10.0: dependencies: side-channel "^1.0.4" +query-string@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" + integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -14647,6 +14911,11 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -14980,7 +15249,7 @@ read-pkg@^5.1.1, read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -15584,6 +15853,11 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" +sax@>=0.6.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -15739,6 +16013,20 @@ serve-favicon@^2.5.0: parseurl "~1.3.2" safe-buffer "5.1.1" +serve-handler@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8" + integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.0.4" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + serve-static@1.14.2: version "1.14.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" @@ -16077,6 +16365,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -16217,6 +16510,11 @@ stream-slice@^0.1.2: resolved "https://registry.yarnpkg.com/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" integrity sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA== +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== + string-argv@^0.3.1, string-argv@~0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -16406,6 +16704,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== +strnum@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + style-loader@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e" @@ -16678,6 +16981,14 @@ through2@^2.0.0, through2@^2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" +through2@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" + integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== + dependencies: + inherits "^2.0.4" + readable-stream "2 || 3" + through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -16857,6 +17168,25 @@ ts-morph@12.0.0: "@ts-morph/common" "~0.11.0" code-block-writer "^10.1.1" +ts-node@10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + ts-node@8.9.1: version "8.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.9.1.tgz#2f857f46c47e91dcd28a14e052482eb14cfd65a5" @@ -17551,6 +17881,11 @@ uvu@^0.5.0: kleur "^4.0.3" sade "^1.7.3" +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-to-istanbul@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" @@ -17690,7 +18025,7 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-encoding@1.1.5: +web-encoding@1.1.5, web-encoding@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864" integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA== @@ -18084,6 +18419,24 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml2js@^0.4.15: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xml@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" @@ -18137,6 +18490,11 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.9: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.0.0: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@^15.0.2: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -18167,6 +18525,19 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.5.1: + version "17.5.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" + integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.0.0" + yarn-or-npm@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/yarn-or-npm/-/yarn-or-npm-3.0.1.tgz#6336eea4dff7e23e226acc98c1a8ada17a1b8666" From 46a41caa0e0b65fbd536c0d022d123ee32d8bc42 Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 10:57:16 +0200 Subject: [PATCH 02/71] Add env variables --- .github/workflows/vis-reg-tests.yml | 3 +++ packages/design-system/lostpixel.config.js | 16 ++++++++++++++++ packages/design-system/lostpixel.config.ts | 9 ++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 packages/design-system/lostpixel.config.js diff --git a/.github/workflows/vis-reg-tests.yml b/.github/workflows/vis-reg-tests.yml index bfe5dab3ac..908bb8f1ca 100644 --- a/.github/workflows/vis-reg-tests.yml +++ b/.github/workflows/vis-reg-tests.yml @@ -23,3 +23,6 @@ jobs: - name: Lost Pixel uses: lost-pixel/lost-pixel@v2.20.0 + env: + S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} + S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} diff --git a/packages/design-system/lostpixel.config.js b/packages/design-system/lostpixel.config.js new file mode 100644 index 0000000000..952e026e5e --- /dev/null +++ b/packages/design-system/lostpixel.config.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.config = void 0; +var process = require("process"); +exports.config = { + storybookShots: { + storybookUrl: "storybook-static", + }, + lostPixelProjectId: "cl8a8xmx714904901m9oce4vhqc", + s3: { + endPoint: "ams3.digitaloceanspaces.com", + accessKey: process.env.S3_ACCESS_KEY, + secretKey: process.env.S3_SECRET_KEY, + bucketName: "beta-seven-turtle-time-blue-narrow", + }, +}; diff --git a/packages/design-system/lostpixel.config.ts b/packages/design-system/lostpixel.config.ts index d545155100..9138398e69 100644 --- a/packages/design-system/lostpixel.config.ts +++ b/packages/design-system/lostpixel.config.ts @@ -1,8 +1,15 @@ import type { CustomProjectConfig } from "lost-pixel"; +import * as process from "process"; export const config: CustomProjectConfig = { storybookShots: { storybookUrl: "storybook-static", }, - generateOnly: true, + lostPixelProjectId: "cl8a8xmx714904901m9oce4vhqc", + s3: { + endPoint: "ams3.digitaloceanspaces.com", + accessKey: process.env.S3_ACCESS_KEY, + secretKey: process.env.S3_SECRET_KEY, + bucketName: "beta-seven-turtle-time-blue-narrow", + }, }; From d4dbef762c8df887243dafe4688a2919eeb9d1fb Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 10:58:16 +0200 Subject: [PATCH 03/71] Add LOST_PIXEL_CONFIG_DIR --- .github/workflows/vis-reg-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/vis-reg-tests.yml b/.github/workflows/vis-reg-tests.yml index 908bb8f1ca..53936fada3 100644 --- a/.github/workflows/vis-reg-tests.yml +++ b/.github/workflows/vis-reg-tests.yml @@ -26,3 +26,4 @@ jobs: env: S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} + LOST_PIXEL_CONFIG_DIR: packages/design-system From 2391418d2c55c58afedb0e6cea4bfdde9c6f4281 Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 11:04:39 +0200 Subject: [PATCH 04/71] Use yarn to run turbo --- .github/workflows/vis-reg-tests.yml | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vis-reg-tests.yml b/.github/workflows/vis-reg-tests.yml index 53936fada3..922f2c2e77 100644 --- a/.github/workflows/vis-reg-tests.yml +++ b/.github/workflows/vis-reg-tests.yml @@ -19,7 +19,7 @@ jobs: run: yarn install --frozen-lockfile --ignore-scripts - name: Build Storybook for Design System - run: turbo run storybook:build --filter=design-system + run: yarn storybook:ds:build - name: Lost Pixel uses: lost-pixel/lost-pixel@v2.20.0 diff --git a/package.json b/package.json index 8458172137..541558b3fb 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "prepare": "husky install", "storybook": "turbo run storybook:run", "storybook:build": "turbo run storybook:build", + "storybook:ds:build": "turbo run storybook:build --filter=design-system", "migrations": "cd apps/designer && migrations --dev" }, "devDependencies": { From ce7bda51f9b392541cdbe01f099fb38d482f98fd Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 11:14:01 +0200 Subject: [PATCH 05/71] Run build before building storybook --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 541558b3fb..da9d121455 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"", "prepare": "husky install", "storybook": "turbo run storybook:run", - "storybook:build": "turbo run storybook:build", - "storybook:ds:build": "turbo run storybook:build --filter=design-system", + "storybook:build": "npm run build && turbo run storybook:build", + "storybook:ds:build": "npm run build && turbo run storybook:build --filter=design-system", "migrations": "cd apps/designer && migrations --dev" }, "devDependencies": { From df9f99ecb5c5b22717c036536ae5b2d428eaa713 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 23 Sep 2022 11:45:01 +0200 Subject: [PATCH 06/71] fix: build command for storybook --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da9d121455..18281f6052 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "prepare": "husky install", "storybook": "turbo run storybook:run", "storybook:build": "npm run build && turbo run storybook:build", - "storybook:ds:build": "npm run build && turbo run storybook:build --filter=design-system", + "storybook:ds:build": "turbo run build --filter=@webstudio-is/icons && turbo run storybook:build --filter=@webstudio-is/design-system ", "migrations": "cd apps/designer && migrations --dev" }, "devDependencies": { From 8d0e1d850273206d3207dc4a0e68e01fed2e7d1e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 23 Sep 2022 11:49:42 +0200 Subject: [PATCH 07/71] fix: let turborepo handle dependent package --- package.json | 2 +- turbo.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 18281f6052..8f2f00e5da 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "prepare": "husky install", "storybook": "turbo run storybook:run", "storybook:build": "npm run build && turbo run storybook:build", - "storybook:ds:build": "turbo run build --filter=@webstudio-is/icons && turbo run storybook:build --filter=@webstudio-is/design-system ", + "storybook:ds:build": "turbo run storybook:build --filter=@webstudio-is/design-system", "migrations": "cd apps/designer && migrations --dev" }, "devDependencies": { diff --git a/turbo.json b/turbo.json index 9abfb616e9..b4ef16d9e1 100644 --- a/turbo.json +++ b/turbo.json @@ -14,6 +14,7 @@ "cache": false }, "storybook:build": { + "dependsOn": ["@webstudio-is/icons#build"], "cache": false } } From acf9bf3f1561451f04b2384a08b19e63dfc4bbd7 Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 12:35:11 +0200 Subject: [PATCH 08/71] Remove duplicate JS config file --- packages/design-system/lostpixel.config.js | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 packages/design-system/lostpixel.config.js diff --git a/packages/design-system/lostpixel.config.js b/packages/design-system/lostpixel.config.js deleted file mode 100644 index 952e026e5e..0000000000 --- a/packages/design-system/lostpixel.config.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.config = void 0; -var process = require("process"); -exports.config = { - storybookShots: { - storybookUrl: "storybook-static", - }, - lostPixelProjectId: "cl8a8xmx714904901m9oce4vhqc", - s3: { - endPoint: "ams3.digitaloceanspaces.com", - accessKey: process.env.S3_ACCESS_KEY, - secretKey: process.env.S3_SECRET_KEY, - bucketName: "beta-seven-turtle-time-blue-narrow", - }, -}; From 7e455fa97e562b35caded3588963305408e93a3d Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 12:41:20 +0200 Subject: [PATCH 09/71] Add tmate --- .github/workflows/vis-reg-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/vis-reg-tests.yml b/.github/workflows/vis-reg-tests.yml index 922f2c2e77..ca79e771d8 100644 --- a/.github/workflows/vis-reg-tests.yml +++ b/.github/workflows/vis-reg-tests.yml @@ -21,6 +21,9 @@ jobs: - name: Build Storybook for Design System run: yarn storybook:ds:build + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + - name: Lost Pixel uses: lost-pixel/lost-pixel@v2.20.0 env: From 8e98a7cbf6d3725645aa6c8f22a18b5bc7ad80bf Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 12:50:30 +0200 Subject: [PATCH 10/71] Update path to storybook-static --- packages/design-system/lostpixel.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/design-system/lostpixel.config.ts b/packages/design-system/lostpixel.config.ts index 9138398e69..9214fcc2ae 100644 --- a/packages/design-system/lostpixel.config.ts +++ b/packages/design-system/lostpixel.config.ts @@ -3,7 +3,7 @@ import * as process from "process"; export const config: CustomProjectConfig = { storybookShots: { - storybookUrl: "storybook-static", + storybookUrl: "packages/design-system/storybook-static", }, lostPixelProjectId: "cl8a8xmx714904901m9oce4vhqc", s3: { From 43c1158eb2acd378e17ecd9d1248f8368e3ece1f Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 12:53:01 +0200 Subject: [PATCH 11/71] Remove tmate --- .github/workflows/vis-reg-tests.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/vis-reg-tests.yml b/.github/workflows/vis-reg-tests.yml index ca79e771d8..922f2c2e77 100644 --- a/.github/workflows/vis-reg-tests.yml +++ b/.github/workflows/vis-reg-tests.yml @@ -21,9 +21,6 @@ jobs: - name: Build Storybook for Design System run: yarn storybook:ds:build - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - - name: Lost Pixel uses: lost-pixel/lost-pixel@v2.20.0 env: From c05dfe277321be37306bd5f66e4443cb2f271f3d Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Fri, 23 Sep 2022 13:01:00 +0200 Subject: [PATCH 12/71] update lost pixel baseline --- .../components-combobox--custom-input.png | Bin 0 -> 6430 bytes .../baseline/components-combobox--simple.png | Bin 0 -> 6430 bytes ...-create-content-controller--detect-input.png | Bin 0 -> 18657 bytes ...components-primitives-dnd-canvas--canvas.png | Bin 0 -> 5568 bytes ...mitives-dnd-sortable-list--sortable-list.png | Bin 0 -> 13236 bytes .../baseline/components-select--boundaries.png | Bin 0 -> 6497 bytes .../baseline/components-select--full-width.png | Bin 0 -> 7504 bytes .../baseline/components-select--ghost.png | Bin 0 -> 7147 bytes .../baseline/components-select--simple.png | Bin 0 -> 6444 bytes .../components-select--with-complex-items.png | Bin 0 -> 7039 bytes .lostpixel/baseline/components-text--label.png | Bin 0 -> 6529 bytes .lostpixel/baseline/components-text--mono.png | Bin 0 -> 6229 bytes .../baseline/components-text--regular.png | Bin 0 -> 6885 bytes .lostpixel/baseline/components-text--tiny.png | Bin 0 -> 5986 bytes .lostpixel/baseline/components-text--title.png | Bin 0 -> 6313 bytes .lostpixel/baseline/components-text--unit.png | Bin 0 -> 5944 bytes .../baseline/components-tree--stress-test.png | Bin 0 -> 19106 bytes .../numeric-scrub-control--numeric-input.png | Bin 0 -> 5663 bytes 18 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .lostpixel/baseline/components-combobox--custom-input.png create mode 100644 .lostpixel/baseline/components-combobox--simple.png create mode 100644 .lostpixel/baseline/components-primitives-create-content-controller--detect-input.png create mode 100644 .lostpixel/baseline/components-primitives-dnd-canvas--canvas.png create mode 100644 .lostpixel/baseline/components-primitives-dnd-sortable-list--sortable-list.png create mode 100644 .lostpixel/baseline/components-select--boundaries.png create mode 100644 .lostpixel/baseline/components-select--full-width.png create mode 100644 .lostpixel/baseline/components-select--ghost.png create mode 100644 .lostpixel/baseline/components-select--simple.png create mode 100644 .lostpixel/baseline/components-select--with-complex-items.png create mode 100644 .lostpixel/baseline/components-text--label.png create mode 100644 .lostpixel/baseline/components-text--mono.png create mode 100644 .lostpixel/baseline/components-text--regular.png create mode 100644 .lostpixel/baseline/components-text--tiny.png create mode 100644 .lostpixel/baseline/components-text--title.png create mode 100644 .lostpixel/baseline/components-text--unit.png create mode 100644 .lostpixel/baseline/components-tree--stress-test.png create mode 100644 .lostpixel/baseline/numeric-scrub-control--numeric-input.png diff --git a/.lostpixel/baseline/components-combobox--custom-input.png b/.lostpixel/baseline/components-combobox--custom-input.png new file mode 100644 index 0000000000000000000000000000000000000000..572fc3220a45efea3016e4992e92fdaa555b7876 GIT binary patch literal 6430 zcmeHL`%@EF6yBgUr~}$sQRERiYC9b$SOSTWN7U-1v>kBN5fmh%5*`|Y6JrP=glY$> z3^-clApyoZ)(#XXKEf*`lnM-zWD$^H5=d0Y7J(!fAmrU$^iSw--Jia@_nf_F?|1HZ z?>&3cFd*Cntd;~Y$2Hn@d0P@CLH7KaCLPsyM8hbWtQrBO7q?~`-TkZIJbxaV{w#)h+4)hy7# zoc)>t%Bv~TCAz9?;#1@;(V-||8wu;_E3oTOh*6)p7t(Df4?J5 zanlEx1wa!rR0Iucr13a4v5Je!NuLH<7a{;6fk1<93}~*oBTkX}pWz6-y}d)sGS7m< z#A4VvF?Ty+>Utw=XaYw^4>Jr^-c$^d(}J)MeASpS+06;kBBwos2G&z;Nem?OfcVWH zVL<+5ouo?Pc|$# zA-Rp5-Cq~H!m?)-e!^b%l5LX3<(${Zh@dwkf8aZL$4!`NA`!YX(^#u=&5+Y*G_!u( zMC5Z{h)imBo{DjutZ(H^1tv#Rt=Cnx7aiEx-LD3%^cG&eIM+PR>`yHR?`f8M^T7GO zrq`dW%;8U~Hnm({nO2S8ax&*H(cfsN)y588P`+E5wreyKPUd3yU~L<5!Ph65s$OlSP!ZR>~WSk56+f&u7#?S@bw)A zCu#mEF7Lz)*IW}y9ACKla%?Q2XB1)SRI1n;14^{r9Ec+c=U;p|6>KMdi{5(T)&2-ejQKhmw~1a00eN&(*yqx29k6<4kU9 z#KG&q?x2tfupTP-?Mq5aKVDuj9Or5#nP#aKrhFS)cQ2mGYYkaR)G^&iCs&8BRTE*A zHQI{mNbfD-VX3Kt}Qd#G94$ozlE>OXjma<{c}GT4-zcY8K{}P z&%|!_qe4PU&GS7-T8vVM1lv_oQqra`oPn6smFh{E3i*A=uMBv<36Q^XxbH%SG}?CpZZPhxcyCE@3qZPmT3dj? z#?MhBN&>nPP&vSB03`t>0X-c606ijM9iSwjB%n7&^xBNp4rq-8?}R7`C<*8<1GGj8 gfMxJMC=^}mqgyLV_HEmdfy_7NVC;d${iiPf3!HACIRF3v literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-combobox--simple.png b/.lostpixel/baseline/components-combobox--simple.png new file mode 100644 index 0000000000000000000000000000000000000000..572fc3220a45efea3016e4992e92fdaa555b7876 GIT binary patch literal 6430 zcmeHL`%@EF6yBgUr~}$sQRERiYC9b$SOSTWN7U-1v>kBN5fmh%5*`|Y6JrP=glY$> z3^-clApyoZ)(#XXKEf*`lnM-zWD$^H5=d0Y7J(!fAmrU$^iSw--Jia@_nf_F?|1HZ z?>&3cFd*Cntd;~Y$2Hn@d0P@CLH7KaCLPsyM8hbWtQrBO7q?~`-TkZIJbxaV{w#)h+4)hy7# zoc)>t%Bv~TCAz9?;#1@;(V-||8wu;_E3oTOh*6)p7t(Df4?J5 zanlEx1wa!rR0Iucr13a4v5Je!NuLH<7a{;6fk1<93}~*oBTkX}pWz6-y}d)sGS7m< z#A4VvF?Ty+>Utw=XaYw^4>Jr^-c$^d(}J)MeASpS+06;kBBwos2G&z;Nem?OfcVWH zVL<+5ouo?Pc|$# zA-Rp5-Cq~H!m?)-e!^b%l5LX3<(${Zh@dwkf8aZL$4!`NA`!YX(^#u=&5+Y*G_!u( zMC5Z{h)imBo{DjutZ(H^1tv#Rt=Cnx7aiEx-LD3%^cG&eIM+PR>`yHR?`f8M^T7GO zrq`dW%;8U~Hnm({nO2S8ax&*H(cfsN)y588P`+E5wreyKPUd3yU~L<5!Ph65s$OlSP!ZR>~WSk56+f&u7#?S@bw)A zCu#mEF7Lz)*IW}y9ACKla%?Q2XB1)SRI1n;14^{r9Ec+c=U;p|6>KMdi{5(T)&2-ejQKhmw~1a00eN&(*yqx29k6<4kU9 z#KG&q?x2tfupTP-?Mq5aKVDuj9Or5#nP#aKrhFS)cQ2mGYYkaR)G^&iCs&8BRTE*A zHQI{mNbfD-VX3Kt}Qd#G94$ozlE>OXjma<{c}GT4-zcY8K{}P z&%|!_qe4PU&GS7-T8vVM1lv_oQqra`oPn6smFh{E3i*A=uMBv<36Q^XxbH%SG}?CpZZPhxcyCE@3qZPmT3dj? z#?MhBN&>nPP&vSB03`t>0X-c606ijM9iSwjB%n7&^xBNp4rq-8?}R7`C<*8<1GGj8 gfMxJMC=^}mqgyLV_HEmdfy_7NVC;d${iiPf3!HACIRF3v literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-primitives-create-content-controller--detect-input.png b/.lostpixel/baseline/components-primitives-create-content-controller--detect-input.png new file mode 100644 index 0000000000000000000000000000000000000000..baec796150599ef71a269133a3774e2dd69a39de GIT binary patch literal 18657 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 aUZt}u?xD$=-%rR0k~w+qM9lGvxBdrjxG+or literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-primitives-dnd-canvas--canvas.png b/.lostpixel/baseline/components-primitives-dnd-canvas--canvas.png new file mode 100644 index 0000000000000000000000000000000000000000..dbdec2b55bd43279d25c441b4e227ecacb0b08a2 GIT binary patch literal 5568 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 bKUnHKgacTZ1^xLUR5}S?CB`+x9y#+DFGJC^ literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-select--full-width.png b/.lostpixel/baseline/components-select--full-width.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f4dae307aa83bf4126c583d6c686811798bbac GIT binary patch literal 7504 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 literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-select--ghost.png b/.lostpixel/baseline/components-select--ghost.png new file mode 100644 index 0000000000000000000000000000000000000000..ede0a4853efdf90ce1b0d387ef508509d9ce5e2b GIT binary patch literal 7147 zcmeHM`&Uxw8pf_p9kouUnu8(ZX{t46O*$>b@`j$QtV5!vXlY_imWJUaL`wo}TIIA# z9P3ox&`~QD;&N0(K_O4m*vy2e0lPrN#z_rOLIey14)iz7?FW9?>-paO?aRBL{XWn4 z?QdUCK*oBl0j~jpKwgIq?mGekt#me*4STL~f|#gGae@c#NbFt^b7;$35Xkqzp?&{K zzED4_B(PNm>Y(0ysJbbp{2Sb@0L(u{SC5bVqbmUW*`_;`8no=&V8tj3^>=Bo@A^C2 zZhf<_WU^y}|H0aW%^_`^WcjtRPrn}il;?T!>aFT+tE-kwK2HvVAAMY9k`7J^4K|{% zQEaU@sBi+6irYSt6YNS6^tI#+!pGGBor5p?#Dyz1fIOV=Nj1pR6%MS3c7f-1&@v}P zf8nvt6@K+mnG4n(2CZ;H*@@ryRJO1Ow5U*tX?gu7`sqqXA)?Zi4Tx-nSAj0rDmG~9 z8<)CBBt6?QXC^01?igDXO#2l-8{kZOhhMr+JR`2LCdMwkd|ngIqS0iG^wf+}ZBqQs zOu^M1t_w}%ze=eiT$xT~KFYk=-!Y{!^D!6@=Cl0%LOhIz1zC63Rw?*P?pD^<2{{naaSht1Nl}Qh_sG5B-&7%X6~m! z>*|(?S7juG&q|nRqLG6U8ju`Oh(gkFx)K;Z`GoFxl`U=UR+^NsYu>)-bSww8^BAuS zIiuiGqUNf@H1#-5)BYQ313{bU1Mh1?!u1NNL)#hh(t0Nq-QMWscck&{%m3aDB=abd zQ!^F|K%tXb9Z8Y4=Rq8xL8*Qny9tPDFj2#r@>O7h5ytuQDGo2{(Mt(JqGln8oXkC3 zlT3|LwdYX_2{s^7*giF7avszP`}tOr@w?mcQZCYhgcL5`)6{Gf%M9YE%%F#=+FB-2d zw@QSW1m!A#iSFkepzxXl_zSndk@KVA(9*-4ZUI{OF#*Nt3I--FFUKrC-7NJZTw+oz z>h;pGF~KUYJ^5D_Q&IT&>nf(lw2Acs8QQxYNwRtmv{Xhkhn1+40W|KkvPjueTkzt3 z#Tlp}KQ#pipym#j88FY^Y*n`|7qg`FR3I@kvYCgUO>2quW@U~SWKhYkhtv2bQP{%7 z6}s7Om6@&76suNAmva<)rRvnggg*@P9UB}`5RI2l;_y#j(lL$w1QY^+vn`fq%Wj@c zqNM{&n1L%)-52Wo%b~|Q=NfGdjUpbo7;=8Gs;$6+0?+95_f`B?s_3OX)g)PmGxHe8a3vHaIpwmK0% z+HM>7s?$~Px%h8t5k?3OKO@=BSnYhE=({4j=eyionZ#n$(0hqi%WEGbE6}g#1a(wL z!2hs+b4trdn_v)g$9HJUdXMkZ-n)FVVz?JnDtMgEbhjYiubG>U4SDS1VZ4~nWu zIOB`}80`4qh-7Mr6;c)-q0h2ehWO*Q-oy$y(X6DaXD{Hz{Xr_Te25w*b-D;^qZrN| z(8E_RFvE2`!j)54c~|>x)g@dDEQOJVQsi@$3Ih3A&LYKTR&eirPOwshFb0Ej{>@rW zHW1)jZxFHsX=KX_gp`ArHdC9@fq)O4#j|o6tu#yD!f}wSd8400f1!HrdAH<`pQ^M1 z?5cY=$gYA&Mxhk`G1va`s@}KHDC6-upY0XHpTamP#Pra5Wn_whYVnGC7Gq zt9}e$Ey<4Jr)N0rsWfM#x%B+|d2sMVUz&X(2ygS+oq|9hW_g+bnBC&5=D>2*ZCkxr zA=nUxs5^K!aJIERxkMG-nFo$y7Ar)Ga+j00VKC>^*h0yF+F#_F#(JFsevn@JHy7)R z(T&d#(SK#6b4iFw*A9>)(4$}jBZJ>PR}S>; pdz0In{@7n8ZXf>jeE6lqW8db~yyxR5Yh8i^S_=92&b literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-select--simple.png b/.lostpixel/baseline/components-select--simple.png new file mode 100644 index 0000000000000000000000000000000000000000..8c0544c377cdba34516130316efa1dce0fc1dd11 GIT binary patch literal 6444 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 U&Xt6efFwaNQTrlmb|3liAE$l3%m4rY literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-text--mono.png b/.lostpixel/baseline/components-text--mono.png new file mode 100644 index 0000000000000000000000000000000000000000..deadaefed4ae974e84df37ec8ef2b7f0f30fb17c GIT binary patch literal 6229 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 X>#-lKPRvzc2YrdVQ{r29=AHf@15HxF literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-text--tiny.png b/.lostpixel/baseline/components-text--tiny.png new file mode 100644 index 0000000000000000000000000000000000000000..ea4edee218d153c13d48f3c7d3dbba6ed05e2955 GIT binary patch literal 5986 zcmeAS@N?(olHy`uVBq!ia0y~yUVN>*X6J#_lUl8)j&91hY69`KZ|&N9&}7mzAbEr`^2$dKKS&%jdTKwV&sG)_ns6 zJV2WnCVZLx|0{^WaA2}B0~3&Tn8CmS;wLgGfT$ME1`s8nMrFyrUkk-8il3div$Hro zEiJC%VQb~{x#iDxKA$(+{C>@6{`DL8?Xz3I@7JsPJ$c#L+1s{ld$x3X+^_Y|LBZEx zKl%7Q#)>yLHbzE8&04y2sb$%l8#?;>=N~>TI7Cwx?b$8qeqYa{P+9)^Iu{_yef z@#p{k{{H;u^ZEa;zVEI6e(w4FdOM)2KVNj0Kf8Q>ofR-j&&KSpt2Hw>e}1Ty`}0Zl z`DYrL*`KXmzi-y+@b&MW-2eskhx+WqbRaO7+p=?~B`Bo-{{DXc;oMaN?X7$^GyUIND`{!z&tES4|9@5qGT?#yycgOB{sW^kS4>>|zv{oUXLNLQ zY`W@=KkqKgKR?gbKgABDPUF^A*#;2fz`NU5^TA1*VYfE}3s9uNfKdP>bBM(OL~#f+ zFajwBpHa!tKp0I4quF7!Oc*T~M{9@CDsr@)04xbd17S1}Mq9?C-Gk9S(r8B!SQ3r~ p!e}6jj++3}z-S=QAP_d}XPgjorS*6^*B?+e^K|udS?83{1OTxaUWxz! literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-text--title.png b/.lostpixel/baseline/components-text--title.png new file mode 100644 index 0000000000000000000000000000000000000000..e7f5e95c06d4aa5af7ad5eeb9083ee3ec926ee0f GIT binary patch literal 6313 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 C-Qgqv literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/numeric-scrub-control--numeric-input.png b/.lostpixel/baseline/numeric-scrub-control--numeric-input.png new file mode 100644 index 0000000000000000000000000000000000000000..22f080c980b0bdc2ab62097c3c2a737028f713d9 GIT binary patch literal 5663 zcmeAS@N?(olHy`uVBq!ia0y~yU$W?%wR4l@`y zK-@$o1rXK3*#M#h)Tkxtzf8ht_St0;%=LF_?CRfWgG^s@C7=Dk%^bBwAI^XJ3vxv1 zJO+^KaJXgLB!(Y9KR+)lC@_fIS7W(t`}X4}PoC7e1~Q7l+eeP!{rmUFFJ8Q8VPhk+ zef##uj~*q>nK!R*^XAPf!x>>}^!4?R_siQK`}px=M0~t_baeFNx3{-Hetf(?cs6%@+qb>z|3x_wL=o%F3Nx7eR&}_;2u2z5&d-@65mgWHcBs3V`^B zSR6nUM|8A~SoeKdQ2gxR{!{q*=bw%1K@uB+CPP#_c>jdc03<^7WI?0UHfk*Ga`I@{ zj)pBwtBTPCHk!aj6Bv$05Y-xFqq%G}myPDK(Od>>)sF_kXdsN%E7TnA7_D4KE7#G= gb+lf=U9WtTXPcMJbUJW1J5UXSr>mdKI;Vst06*2zjsO4v literal 0 HcmV?d00001 From 521876d33d049b3ea374a21f872d4406b9b0123b Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Fri, 23 Sep 2022 17:41:36 +0300 Subject: [PATCH 13/71] don't build things that don't need to be built every time, those are commited anyways --- packages/react-sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 397567ba0d..97423e4f31 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -6,7 +6,7 @@ "homepage": "https://webstudio.is", "scripts": { "dev": "tsup src/index.ts --format esm,cjs --watch --out-dir=lib", - "build": "yarn build:mdn-data && yarn build:args && yarn build:lib", + "build": "yarn build:lib", "build:mdn-data": "tsx ./bin/mdn-data.ts ./src/css", "build:args": "tsx ./bin/generate-arg-types.ts './src/components/*.tsx !./src/**/*.stories.tsx !./src/**/*.ws.tsx'", "typecheck": "tsc --noEmit", From cfcf9c5b62efd33109725642895e24bbe3ed8e83 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 26 Sep 2022 19:12:21 +0300 Subject: [PATCH 14/71] fix critical css for global styles, reset styles for ssr --- apps/designer/app/critical-css.ts | 40 ++++++++++++++++++-------- packages/react-sdk/src/stitches/css.ts | 13 +++++++-- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/apps/designer/app/critical-css.ts b/apps/designer/app/critical-css.ts index a79e95cd53..adef6a6865 100644 --- a/apps/designer/app/critical-css.ts +++ b/apps/designer/app/critical-css.ts @@ -1,24 +1,40 @@ import { insertCriticalCss as insert, getCssText as getCanvasCssText, + resetCss as resetCanvasCss, } from "@webstudio-is/react-sdk"; -import { getCssText as getDesignerCssText } from "@webstudio-is/design-system"; +import { + getCssText as getDesignerCssText, + reset as resetDesignerCss, +} from "@webstudio-is/design-system"; import config from "./config"; -const getCssTextFunctions = { - [config.previewPath]: getCanvasCssText, - [config.canvasPath]: getCanvasCssText, - [config.designerPath]: getDesignerCssText, - [config.dashboardPath]: getDesignerCssText, - [config.loginPath]: getDesignerCssText, +const flushCanvas = () => { + const css = getCanvasCssText(); + resetCanvasCss(); + return css; }; -const getCssTextFunction = (url: string) => { +const flushDesigner = () => { + const css = getDesignerCssText(); + resetDesignerCss(); + return css; +}; + +const flushFunctions = { + [config.previewPath]: flushCanvas, + [config.canvasPath]: flushCanvas, + [config.designerPath]: flushDesigner, + [config.dashboardPath]: flushDesigner, + [config.loginPath]: flushDesigner, +}; + +const getFlushFunction = (url: string) => { const { pathname } = new URL(url); - let path: keyof typeof getCssTextFunctions; - for (path in getCssTextFunctions) { + let path: keyof typeof flushFunctions; + for (path in flushFunctions) { if (pathname.indexOf(path) === 0) { - return getCssTextFunctions[path]; + return flushFunctions[path]; } } return getCanvasCssText; @@ -30,5 +46,5 @@ const getCssTextFunction = (url: string) => { * @todo find a better way */ export const insertCriticalCss = (markup: string, url: string): string => { - return insert(markup, getCssTextFunction(url)); + return insert(markup, getFlushFunction(url)); }; diff --git a/packages/react-sdk/src/stitches/css.ts b/packages/react-sdk/src/stitches/css.ts index 38f559bda1..6baf04d7c1 100644 --- a/packages/react-sdk/src/stitches/css.ts +++ b/packages/react-sdk/src/stitches/css.ts @@ -1,6 +1,6 @@ import { createStitches, - globalCss, + globalCss as globalCssImport, type CSS, css as createCss, } from "@stitches/core"; @@ -12,6 +12,8 @@ let media = {}; // eslint-disable-next-line @typescript-eslint/no-explicit-any let stitches: any; +export { type CSS }; + export const getCachedConfig = () => { if (stitches === undefined) { stitches = createStitches({ media }); @@ -23,13 +25,18 @@ export const css: typeof createCss = (...args) => { return getCachedConfig().css(...args); }; -export { globalCss }; -export { type CSS }; +export const globalCss: typeof globalCssImport = (...args) => { + return getCachedConfig().globalCss(...args); +}; export const getCssText = (): string => { return getCachedConfig().getCssText(); }; +export const resetCss = () => { + getCachedConfig().reset(); +}; + export const setBreakpoints = (breakpoints: Array) => { const nextMedia: Record = {}; for (const breakpoint of breakpoints) { From 5ca48aef581d88353582c17f9a5e87432d576fb3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 27 Sep 2022 14:27:09 +0200 Subject: [PATCH 15/71] fix: use isomorphic fetch for worker, node & browser --- packages/http-client/package.json | 5 +- packages/http-client/src/fetch-polyfill.ts | 6 --- packages/http-client/src/index.ts | 2 +- yarn.lock | 54 ++++++++-------------- 4 files changed, 23 insertions(+), 44 deletions(-) delete mode 100644 packages/http-client/src/fetch-polyfill.ts diff --git a/packages/http-client/package.json b/packages/http-client/package.json index 13ec25dc5e..4ed1de7ea0 100644 --- a/packages/http-client/package.json +++ b/packages/http-client/package.json @@ -13,15 +13,16 @@ "checks": "yarn typecheck && yarn lint" }, "devDependencies": { + "@types/isomorphic-fetch": "^0.0.36", "@webstudio-is/jest-config": "*", - "@webstudio-is/react-sdk": "*", "@webstudio-is/prisma-client": "*", + "@webstudio-is/react-sdk": "*", "tsup": "^6.1.3", "typescript": "4.7.4" }, "peerDependencies": {}, "dependencies": { - "node-fetch": "^3.2.10" + "isomorphic-fetch": "^3.0.0" }, "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/http-client/src/fetch-polyfill.ts b/packages/http-client/src/fetch-polyfill.ts deleted file mode 100644 index a2a7307ea8..0000000000 --- a/packages/http-client/src/fetch-polyfill.ts +++ /dev/null @@ -1,6 +0,0 @@ -import fetch from "node-fetch"; - -if (globalThis.fetch === undefined) { - // @ts-expect-error node-fetch doesn't implement a complete spec - globalThis.fetch = fetch; -} diff --git a/packages/http-client/src/index.ts b/packages/http-client/src/index.ts index 45ffdbad67..e3a88f3872 100644 --- a/packages/http-client/src/index.ts +++ b/packages/http-client/src/index.ts @@ -1,4 +1,4 @@ -import "./fetch-polyfill"; +import fetch from "isomorphic-fetch"; import type { Includes, Project } from "./index.d"; const protocol = process.env.NODE_ENV === "production" ? "https" : "http"; diff --git a/yarn.lock b/yarn.lock index 1145295fef..b0fe4f9694 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4959,6 +4959,11 @@ resolved "https://registry.yarnpkg.com/@types/is-function/-/is-function-1.0.1.tgz#2d024eace950c836d9e3335a66b97960ae41d022" integrity sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q== +"@types/isomorphic-fetch@^0.0.36": + version "0.0.36" + resolved "https://registry.yarnpkg.com/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.36.tgz#3a6d8f63974baf26b10fd1314db910633108a769" + integrity sha512-ulw4d+vW1HKn4oErSmNN2HYEcHGq0N1C5exlrMM0CRqX1UUpFhGb5lwiom5j9KN3LBJJDLRmYIZz1ghm7FIzZw== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -7878,11 +7883,6 @@ data-uri-to-buffer@^3.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -data-uri-to-buffer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b" - integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA== - data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -9613,14 +9613,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - fetch-retry@^5.0.2: version "5.0.3" resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.3.tgz#edfa3641892995f9afee94f25b168827aa97fe3d" @@ -9891,13 +9883,6 @@ format@^0.2.0: resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -11467,6 +11452,14 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== +isomorphic-fetch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== + dependencies: + node-fetch "^2.6.1" + whatwg-fetch "^3.4.1" + isomorphic-unfetch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" @@ -13610,11 +13603,6 @@ node-dir@^0.1.10, node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - node-fetch@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -13627,15 +13615,6 @@ node-fetch@^2.6.1, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^3.2.10: - version "3.2.10" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.10.tgz#e8347f94b54ae18b57c9c049ef641cef398a85c8" - integrity sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -18039,7 +18018,7 @@ web-namespaces@^1.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== -web-streams-polyfill@^3.0.3, web-streams-polyfill@^3.1.1: +web-streams-polyfill@^3.1.1: version "3.2.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== @@ -18201,6 +18180,11 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" +whatwg-fetch@^3.4.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== + whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" From 14a829c69ecb3afa3f34cbcb9f0fa797fd49a932 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 27 Sep 2022 14:27:57 +0200 Subject: [PATCH 16/71] chore: version++ --- packages/http-client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/http-client/package.json b/packages/http-client/package.json index 4ed1de7ea0..2d2f75cf01 100644 --- a/packages/http-client/package.json +++ b/packages/http-client/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/http-client", - "version": "0.0.1", + "version": "0.1.0", "description": "Webstudio HTTP Client", "author": "Webstudio ", "homepage": "https://webstudio.is", From 82884ef248a14d571e1cf30a628039235ab81806 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Tue, 27 Sep 2022 19:11:32 +0300 Subject: [PATCH 17/71] move flush css function to the corresponding packages --- apps/designer/app/critical-css.ts | 32 +++++-------------- packages/design-system/src/index.ts | 14 +------- packages/design-system/src/stitches.config.ts | 10 +++++- .../react-sdk/src/stitches/critical-css.tsx | 6 ++-- packages/react-sdk/src/stitches/css.ts | 11 +++---- 5 files changed, 26 insertions(+), 47 deletions(-) diff --git a/apps/designer/app/critical-css.ts b/apps/designer/app/critical-css.ts index adef6a6865..28145afd71 100644 --- a/apps/designer/app/critical-css.ts +++ b/apps/designer/app/critical-css.ts @@ -1,32 +1,16 @@ import { insertCriticalCss as insert, - getCssText as getCanvasCssText, - resetCss as resetCanvasCss, + flushCss as flushCanvasCss, } from "@webstudio-is/react-sdk"; -import { - getCssText as getDesignerCssText, - reset as resetDesignerCss, -} from "@webstudio-is/design-system"; +import { flushCss as flushDesignerCss } from "@webstudio-is/design-system"; import config from "./config"; -const flushCanvas = () => { - const css = getCanvasCssText(); - resetCanvasCss(); - return css; -}; - -const flushDesigner = () => { - const css = getDesignerCssText(); - resetDesignerCss(); - return css; -}; - const flushFunctions = { - [config.previewPath]: flushCanvas, - [config.canvasPath]: flushCanvas, - [config.designerPath]: flushDesigner, - [config.dashboardPath]: flushDesigner, - [config.loginPath]: flushDesigner, + [config.previewPath]: flushCanvasCss, + [config.canvasPath]: flushCanvasCss, + [config.designerPath]: flushDesignerCss, + [config.dashboardPath]: flushDesignerCss, + [config.loginPath]: flushDesignerCss, }; const getFlushFunction = (url: string) => { @@ -37,7 +21,7 @@ const getFlushFunction = (url: string) => { return flushFunctions[path]; } } - return getCanvasCssText; + return flushCanvasCss; }; /** diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts index ec369e4873..54bb4137dc 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -1,16 +1,4 @@ -export { - config, - createTheme, - css, - darkTheme, - getCssText, - globalCss, - keyframes, - reset, - styled, - theme, - type CSS, -} from "./stitches.config"; +export * from "./stitches.config"; export * from "./components/toast"; export * as Collapsible from "@radix-ui/react-collapsible"; diff --git a/packages/design-system/src/stitches.config.ts b/packages/design-system/src/stitches.config.ts index e159c2e7d7..b1906c6d94 100644 --- a/packages/design-system/src/stitches.config.ts +++ b/packages/design-system/src/stitches.config.ts @@ -118,7 +118,7 @@ import { import type * as Stitches from "@stitches/react"; export type { VariantProps } from "@stitches/react"; -export const { +const { styled, css, theme, @@ -414,6 +414,14 @@ export const { export type CSS = Stitches.CSS; +export { styled, css, theme, globalCss, keyframes, config }; + +export const flushCss = () => { + const css = getCssText(); + reset(); + return css; +}; + export const darkTheme = createTheme({ colors: { ...grayDark, diff --git a/packages/react-sdk/src/stitches/critical-css.tsx b/packages/react-sdk/src/stitches/critical-css.tsx index efd45dda7e..77f6268bae 100644 --- a/packages/react-sdk/src/stitches/critical-css.tsx +++ b/packages/react-sdk/src/stitches/critical-css.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { getCssText as getCssTextDefault } from "./css"; +import { flushCss as flushCssDefault } from "./css"; const criticalCssMarker = "__critical-css__"; @@ -8,7 +8,7 @@ export const CriticalCss = (): JSX.Element | null => export const insertCriticalCss = ( markup: string, - getCssText: typeof getCssTextDefault = getCssTextDefault + flushCss: typeof flushCssDefault = flushCssDefault ): string => { - return markup.replace(criticalCssMarker, ``); + return markup.replace(criticalCssMarker, ``); }; diff --git a/packages/react-sdk/src/stitches/css.ts b/packages/react-sdk/src/stitches/css.ts index 6baf04d7c1..af71864036 100644 --- a/packages/react-sdk/src/stitches/css.ts +++ b/packages/react-sdk/src/stitches/css.ts @@ -29,12 +29,11 @@ export const globalCss: typeof globalCssImport = (...args) => { return getCachedConfig().globalCss(...args); }; -export const getCssText = (): string => { - return getCachedConfig().getCssText(); -}; - -export const resetCss = () => { - getCachedConfig().reset(); +export const flushCss = () => { + const config = getCachedConfig(); + const css = config.getCssText(); + config.reset(); + return css; }; export const setBreakpoints = (breakpoints: Array) => { From cb137e6a49eef0d6873a2a3e0731e88051931d95 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 28 Sep 2022 12:07:33 +0200 Subject: [PATCH 18/71] fix: process is not defined in worker, so url is defined in a different way --- packages/http-client/package.json | 2 +- packages/http-client/src/index.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/http-client/package.json b/packages/http-client/package.json index 2d2f75cf01..1336e98bfb 100644 --- a/packages/http-client/package.json +++ b/packages/http-client/package.json @@ -1,6 +1,6 @@ { "name": "@webstudio-is/http-client", - "version": "0.1.0", + "version": "0.1.1", "description": "Webstudio HTTP Client", "author": "Webstudio ", "homepage": "https://webstudio.is", diff --git a/packages/http-client/src/index.ts b/packages/http-client/src/index.ts index e3a88f3872..51ca682cb6 100644 --- a/packages/http-client/src/index.ts +++ b/packages/http-client/src/index.ts @@ -1,18 +1,19 @@ import fetch from "isomorphic-fetch"; import type { Includes, Project } from "./index.d"; -const protocol = process.env.NODE_ENV === "production" ? "https" : "http"; - export const loadProject = async ({ + webstudioAPIUrl = null, projectId, - host = process.env.DESIGNER_HOST || "localhost:3000", include = { tree: true, props: true, breakpoints: true }, }: { + webstudioAPIUrl: string | null; projectId: string; - host?: string; include?: Includes; }): Promise => { - const baseUrl = new URL("/", `${protocol}://${host}`); + if (!webstudioAPIUrl) { + throw new Error("Webstudio API URL is required."); + } + const baseUrl = new URL("/", `${webstudioAPIUrl}`); const treeUrl = new URL(`/rest/tree/${projectId}`, baseUrl); const propsUrl = new URL(`/rest/props/${projectId}`, baseUrl); const breakpointsUrl = new URL(`/rest/breakpoints/${projectId}`, baseUrl); From 01d1d9451e2902cba86aba6c51dfbca132c669de Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 28 Sep 2022 12:10:18 +0200 Subject: [PATCH 19/71] fix: tests --- packages/http-client/src/index.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/http-client/src/index.test.ts b/packages/http-client/src/index.test.ts index 0381dd59ea..9428e2e13c 100644 --- a/packages/http-client/src/index.test.ts +++ b/packages/http-client/src/index.test.ts @@ -2,21 +2,27 @@ import { loadProject } from "./index"; const existingProject = "ab3619d3-831f-46f8-abb6-609558cffd99"; const notPublishedProject = "7ec397c6-b3d0-4967-9073-9d83623fcf8e"; +const webstudioAPIUrl = "http://localhost:3000"; const nonExistingProject = "1"; describe("getProjectDetails", () => { test("loads existing project", async () => { - const response = await loadProject({ projectId: existingProject }); + const response = await loadProject({ + webstudioAPIUrl, + projectId: existingProject, + }); expect(response.tree.id).toBeTruthy(); }); test("loads non-existing project", async () => { const response = await loadProject({ + webstudioAPIUrl, projectId: nonExistingProject, }); expect(response.tree.errors).toBe("Project required"); }); test("loads not published project", async () => { const response = await loadProject({ + webstudioAPIUrl, projectId: notPublishedProject, }); expect(response.tree.errors).toBe( From c681299fdd122a86175d45ab7e5ac05aa0002966 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Wed, 28 Sep 2022 13:33:28 +0300 Subject: [PATCH 20/71] separate critical css and remix stuff on canvas and in designer We need to reuse canvas stuff on the published site as well --- apps/designer/app/critical-css.ts | 34 ------------------- apps/designer/app/entry.server.tsx | 26 +++++++------- apps/designer/app/routes/dashboard.tsx | 4 +-- apps/designer/app/routes/designer.tsx | 4 +-- apps/designer/app/routes/login.tsx | 4 +-- apps/designer/app/shared/remix/constants.ts | 1 + .../shared/remix/handle-request.server.tsx | 23 +++++++++++++ apps/designer/app/shared/remix/index.ts | 2 ++ .../app/shared/remix/insert-critical-css.ts | 6 ++++ .../designer.tsx => remix/root.tsx} | 6 ++-- packages/react-sdk/src/canvas/index.ts | 1 - packages/react-sdk/src/index.ts | 2 +- packages/react-sdk/src/remix/constants.ts | 1 + .../src/remix/handle-request.server.tsx | 22 ++++++++++++ packages/react-sdk/src/remix/index.ts | 2 ++ .../src/remix/insert-critical-css.ts | 6 ++++ .../src/{canvas/canvas.tsx => remix/root.tsx} | 4 +-- .../react-sdk/src/stitches/critical-css.tsx | 14 -------- packages/react-sdk/src/stitches/index.ts | 3 +- .../src/stitches/{css.ts => stitches.ts} | 2 +- packages/react-sdk/src/stitches/to-css.ts | 2 +- 21 files changed, 90 insertions(+), 79 deletions(-) delete mode 100644 apps/designer/app/critical-css.ts create mode 100644 apps/designer/app/shared/remix/constants.ts create mode 100644 apps/designer/app/shared/remix/handle-request.server.tsx create mode 100644 apps/designer/app/shared/remix/index.ts create mode 100644 apps/designer/app/shared/remix/insert-critical-css.ts rename apps/designer/app/shared/{documents/designer.tsx => remix/root.tsx} (79%) delete mode 100644 packages/react-sdk/src/canvas/index.ts create mode 100644 packages/react-sdk/src/remix/constants.ts create mode 100644 packages/react-sdk/src/remix/handle-request.server.tsx create mode 100644 packages/react-sdk/src/remix/index.ts create mode 100644 packages/react-sdk/src/remix/insert-critical-css.ts rename packages/react-sdk/src/{canvas/canvas.tsx => remix/root.tsx} (84%) delete mode 100644 packages/react-sdk/src/stitches/critical-css.tsx rename packages/react-sdk/src/stitches/{css.ts => stitches.ts} (96%) diff --git a/apps/designer/app/critical-css.ts b/apps/designer/app/critical-css.ts deleted file mode 100644 index 28145afd71..0000000000 --- a/apps/designer/app/critical-css.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - insertCriticalCss as insert, - flushCss as flushCanvasCss, -} from "@webstudio-is/react-sdk"; -import { flushCss as flushDesignerCss } from "@webstudio-is/design-system"; -import config from "./config"; - -const flushFunctions = { - [config.previewPath]: flushCanvasCss, - [config.canvasPath]: flushCanvasCss, - [config.designerPath]: flushDesignerCss, - [config.dashboardPath]: flushDesignerCss, - [config.loginPath]: flushDesignerCss, -}; - -const getFlushFunction = (url: string) => { - const { pathname } = new URL(url); - let path: keyof typeof flushFunctions; - for (path in flushFunctions) { - if (pathname.indexOf(path) === 0) { - return flushFunctions[path]; - } - } - return flushCanvasCss; -}; - -/** - * This works around the problem with getCssTextfunction being a different one - * depending on createStitches() used in each outlet. - * @todo find a better way - */ -export const insertCriticalCss = (markup: string, url: string): string => { - return insert(markup, getFlushFunction(url)); -}; diff --git a/apps/designer/app/entry.server.tsx b/apps/designer/app/entry.server.tsx index 0d2bdf0cc2..2d217876da 100644 --- a/apps/designer/app/entry.server.tsx +++ b/apps/designer/app/entry.server.tsx @@ -1,10 +1,10 @@ -import { renderToString } from "react-dom/server"; -import { RemixServer } from "@remix-run/react"; -import { insertCriticalCss } from "./critical-css"; import type { EntryContext } from "@remix-run/node"; import * as Sentry from "@sentry/remix"; import { initSentry } from "./shared/sentry"; import { prisma } from "@webstudio-is/prisma-client"; +import { handleRequest as handleRequestDesigner } from "./shared/remix"; +import { handleRequest as handleRequestCanvas } from "@webstudio-is/react-sdk"; +import config from "./config"; initSentry({ integrations: [new Sentry.Integrations.Prisma({ client: prisma })], @@ -16,17 +16,15 @@ export default function handleRequest( responseHeaders: Headers, remixContext: EntryContext ) { - let markup = renderToString( - - ); + const { pathname } = new URL(request.url); + let handle = handleRequestDesigner; - markup = insertCriticalCss(markup, request.url); + if ( + pathname.indexOf(config.previewPath) === 0 || + pathname.indexOf(config.canvasPath) === 0 + ) { + handle = handleRequestCanvas; + } - responseHeaders.set("Content-Type", "text/html"); - responseHeaders.set("Accept-CH", "Sec-CH-Prefers-Color-Scheme"); - - return new Response("" + markup, { - status: responseStatusCode, - headers: responseHeaders, - }); + return handle(request, responseStatusCode, responseHeaders, remixContext); } diff --git a/apps/designer/app/routes/dashboard.tsx b/apps/designer/app/routes/dashboard.tsx index 20bd4e9ce0..4a0cb028ab 100644 --- a/apps/designer/app/routes/dashboard.tsx +++ b/apps/designer/app/routes/dashboard.tsx @@ -1,5 +1,5 @@ import type { LoaderFunction, MetaFunction } from "@remix-run/node"; -import { Designer } from "~/shared/documents/designer"; +import { Root } from "~/shared/remix"; import env from "~/env.server"; import { getThemeData } from "~/shared/theme"; @@ -14,4 +14,4 @@ export const meta: MetaFunction = () => { return { title: "Webstudio Dashboard" }; }; -export default Designer; +export default Root; diff --git a/apps/designer/app/routes/designer.tsx b/apps/designer/app/routes/designer.tsx index 7097e1b5cf..44e8680655 100644 --- a/apps/designer/app/routes/designer.tsx +++ b/apps/designer/app/routes/designer.tsx @@ -1,5 +1,5 @@ import type { LoaderFunction, MetaFunction } from "@remix-run/node"; -import { Designer } from "~/shared/documents/designer"; +import { Root } from "~/shared/remix"; import env from "~/env.server"; import { getThemeData } from "~/shared/theme"; @@ -14,4 +14,4 @@ export const meta: MetaFunction = () => { return { title: "Webstudio" }; }; -export default Designer; +export default Root; diff --git a/apps/designer/app/routes/login.tsx b/apps/designer/app/routes/login.tsx index 99d9febb01..a76cc559f8 100644 --- a/apps/designer/app/routes/login.tsx +++ b/apps/designer/app/routes/login.tsx @@ -1,5 +1,5 @@ import { LoaderFunction, MetaFunction } from "@remix-run/node"; -import { Designer } from "~/shared/documents/designer"; +import { Root } from "~/shared/remix"; import env from "~/env.server"; import { getThemeData } from "~/shared/theme"; @@ -14,4 +14,4 @@ export const meta: MetaFunction = () => { return { title: "Webstudio Login" }; }; -export default Designer; +export default Root; diff --git a/apps/designer/app/shared/remix/constants.ts b/apps/designer/app/shared/remix/constants.ts new file mode 100644 index 0000000000..b5ddbd71f5 --- /dev/null +++ b/apps/designer/app/shared/remix/constants.ts @@ -0,0 +1 @@ +export const CRITICAL_CSS_MARKER = "__CRITICAL_CSS__"; diff --git a/apps/designer/app/shared/remix/handle-request.server.tsx b/apps/designer/app/shared/remix/handle-request.server.tsx new file mode 100644 index 0000000000..9b72cb7cc7 --- /dev/null +++ b/apps/designer/app/shared/remix/handle-request.server.tsx @@ -0,0 +1,23 @@ +import { renderToString } from "react-dom/server"; +import { RemixServer } from "@remix-run/react"; +import type { EntryContext } from "@remix-run/node"; +import { insertCriticalCss } from "./insert-critical-css"; + +export const handleRequest = ( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext +) => { + const markup = renderToString( + + ); + + responseHeaders.set("Content-Type", "text/html"); + responseHeaders.set("Accept-CH", "Sec-CH-Prefers-Color-Scheme"); + + return new Response("" + insertCriticalCss(markup), { + status: responseStatusCode, + headers: responseHeaders, + }); +}; diff --git a/apps/designer/app/shared/remix/index.ts b/apps/designer/app/shared/remix/index.ts new file mode 100644 index 0000000000..444dfb6c40 --- /dev/null +++ b/apps/designer/app/shared/remix/index.ts @@ -0,0 +1,2 @@ +export * from "./handle-request.server"; +export * from "./root"; diff --git a/apps/designer/app/shared/remix/insert-critical-css.ts b/apps/designer/app/shared/remix/insert-critical-css.ts new file mode 100644 index 0000000000..fda5504048 --- /dev/null +++ b/apps/designer/app/shared/remix/insert-critical-css.ts @@ -0,0 +1,6 @@ +import { flushCss } from "@webstudio-is/design-system"; +import { CRITICAL_CSS_MARKER } from "./constants"; + +export const insertCriticalCss = (markup: string): string => { + return markup.replace(CRITICAL_CSS_MARKER, ``); +}; diff --git a/apps/designer/app/shared/documents/designer.tsx b/apps/designer/app/shared/remix/root.tsx similarity index 79% rename from apps/designer/app/shared/documents/designer.tsx rename to apps/designer/app/shared/remix/root.tsx index 4638c4919a..38a1e662c6 100644 --- a/apps/designer/app/shared/documents/designer.tsx +++ b/apps/designer/app/shared/remix/root.tsx @@ -1,9 +1,9 @@ import { Links, LiveReload, Meta, Outlet, Scripts } from "@remix-run/react"; -import { CriticalCss } from "@webstudio-is/react-sdk"; import { Env } from "~/shared/env"; import { useThemeProps } from "~/shared/theme"; +import { CRITICAL_CSS_MARKER } from "./constants"; -export const Designer = () => { +export const Root = () => { const themeProps = useThemeProps(); return ( @@ -12,7 +12,7 @@ export const Designer = () => { - + {typeof document === "undefined" ? CRITICAL_CSS_MARKER : null} diff --git a/packages/react-sdk/src/canvas/index.ts b/packages/react-sdk/src/canvas/index.ts deleted file mode 100644 index df456a3704..0000000000 --- a/packages/react-sdk/src/canvas/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./canvas"; diff --git a/packages/react-sdk/src/index.ts b/packages/react-sdk/src/index.ts index 58ede255ac..7458f83c4d 100644 --- a/packages/react-sdk/src/index.ts +++ b/packages/react-sdk/src/index.ts @@ -6,4 +6,4 @@ export * as componentsMeta from "./components/meta"; export * from "./user-props"; export * from "./pubsub"; export * from "./db"; -export * from "./canvas"; +export * from "./remix"; diff --git a/packages/react-sdk/src/remix/constants.ts b/packages/react-sdk/src/remix/constants.ts new file mode 100644 index 0000000000..b5ddbd71f5 --- /dev/null +++ b/packages/react-sdk/src/remix/constants.ts @@ -0,0 +1 @@ +export const CRITICAL_CSS_MARKER = "__CRITICAL_CSS__"; diff --git a/packages/react-sdk/src/remix/handle-request.server.tsx b/packages/react-sdk/src/remix/handle-request.server.tsx new file mode 100644 index 0000000000..8efe2c9943 --- /dev/null +++ b/packages/react-sdk/src/remix/handle-request.server.tsx @@ -0,0 +1,22 @@ +import { renderToString } from "react-dom/server"; +import { RemixServer } from "@remix-run/react"; +import type { EntryContext } from "@remix-run/node"; +import { insertCriticalCss } from "./insert-critical-css"; + +export const handleRequest = ( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext +) => { + const markup = renderToString( + + ); + + responseHeaders.set("Content-Type", "text/html"); + + return new Response("" + insertCriticalCss(markup), { + status: responseStatusCode, + headers: responseHeaders, + }); +}; diff --git a/packages/react-sdk/src/remix/index.ts b/packages/react-sdk/src/remix/index.ts new file mode 100644 index 0000000000..1287530143 --- /dev/null +++ b/packages/react-sdk/src/remix/index.ts @@ -0,0 +1,2 @@ +export * from "./root"; +export * from "./handle-request.server"; diff --git a/packages/react-sdk/src/remix/insert-critical-css.ts b/packages/react-sdk/src/remix/insert-critical-css.ts new file mode 100644 index 0000000000..9f0bdd20ca --- /dev/null +++ b/packages/react-sdk/src/remix/insert-critical-css.ts @@ -0,0 +1,6 @@ +import { flushCss } from "../stitches"; +import { CRITICAL_CSS_MARKER } from "./constants"; + +export const insertCriticalCss = (markup: string): string => { + return markup.replace(CRITICAL_CSS_MARKER, ``); +}; diff --git a/packages/react-sdk/src/canvas/canvas.tsx b/packages/react-sdk/src/remix/root.tsx similarity index 84% rename from packages/react-sdk/src/canvas/canvas.tsx rename to packages/react-sdk/src/remix/root.tsx index b20e8fb98e..eced0b5951 100644 --- a/packages/react-sdk/src/canvas/canvas.tsx +++ b/packages/react-sdk/src/remix/root.tsx @@ -1,5 +1,5 @@ import { Links, Meta, Outlet as DefaultOutlet } from "@remix-run/react"; -import { CriticalCss } from ".."; +import { CRITICAL_CSS_MARKER } from "./constants"; /** * We are using Outlet prop from index layout when user renders site from a subdomain. @@ -18,7 +18,7 @@ export const Root = ({ - + {typeof document === "undefined" ? CRITICAL_CSS_MARKER : null} diff --git a/packages/react-sdk/src/stitches/critical-css.tsx b/packages/react-sdk/src/stitches/critical-css.tsx deleted file mode 100644 index 77f6268bae..0000000000 --- a/packages/react-sdk/src/stitches/critical-css.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; -import { flushCss as flushCssDefault } from "./css"; - -const criticalCssMarker = "__critical-css__"; - -export const CriticalCss = (): JSX.Element | null => - typeof document === "undefined" ? <>{criticalCssMarker} : null; - -export const insertCriticalCss = ( - markup: string, - flushCss: typeof flushCssDefault = flushCssDefault -): string => { - return markup.replace(criticalCssMarker, ``); -}; diff --git a/packages/react-sdk/src/stitches/index.ts b/packages/react-sdk/src/stitches/index.ts index f5c9580245..cba98204fc 100644 --- a/packages/react-sdk/src/stitches/index.ts +++ b/packages/react-sdk/src/stitches/index.ts @@ -1,3 +1,2 @@ export * from "./to-css"; -export * from "./css"; -export * from "./critical-css"; +export * from "./stitches"; diff --git a/packages/react-sdk/src/stitches/css.ts b/packages/react-sdk/src/stitches/stitches.ts similarity index 96% rename from packages/react-sdk/src/stitches/css.ts rename to packages/react-sdk/src/stitches/stitches.ts index af71864036..67662b3eb3 100644 --- a/packages/react-sdk/src/stitches/css.ts +++ b/packages/react-sdk/src/stitches/stitches.ts @@ -14,7 +14,7 @@ let stitches: any; export { type CSS }; -export const getCachedConfig = () => { +const getCachedConfig = () => { if (stitches === undefined) { stitches = createStitches({ media }); } diff --git a/packages/react-sdk/src/stitches/to-css.ts b/packages/react-sdk/src/stitches/to-css.ts index 5c4420caa5..4cee92ba01 100644 --- a/packages/react-sdk/src/stitches/to-css.ts +++ b/packages/react-sdk/src/stitches/to-css.ts @@ -1,4 +1,4 @@ -import type { CSS } from "./css"; +import type { CSS } from "./stitches"; import type { StyleProperty, StyleValue, CssRule, Breakpoint } from "../css"; export const toValue = (value: StyleValue): string => { From fb30ed034e21926765228f37c600e75cc55dce82 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 28 Sep 2022 13:09:07 +0200 Subject: [PATCH 21/71] fixes --- packages/http-client/src/index.test.ts | 24 ++++++++---------------- packages/http-client/src/index.ts | 8 ++++---- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/http-client/src/index.test.ts b/packages/http-client/src/index.test.ts index 9428e2e13c..4e1065e9f9 100644 --- a/packages/http-client/src/index.test.ts +++ b/packages/http-client/src/index.test.ts @@ -1,32 +1,24 @@ import { loadProject } from "./index"; -const existingProject = "ab3619d3-831f-46f8-abb6-609558cffd99"; -const notPublishedProject = "7ec397c6-b3d0-4967-9073-9d83623fcf8e"; -const webstudioAPIUrl = "http://localhost:3000"; -const nonExistingProject = "1"; +const existingProjectId = "ab3619d3-831f-46f8-abb6-609558cffd99"; +const notPublishedProjectId = "7ec397c6-b3d0-4967-9073-9d83623fcf8e"; +const apiUrl = "http://localhost:3000"; describe("getProjectDetails", () => { test("loads existing project", async () => { const response = await loadProject({ - webstudioAPIUrl, - projectId: existingProject, + apiUrl, + projectId: existingProjectId, }); expect(response.tree.id).toBeTruthy(); }); - test("loads non-existing project", async () => { - const response = await loadProject({ - webstudioAPIUrl, - projectId: nonExistingProject, - }); - expect(response.tree.errors).toBe("Project required"); - }); test("loads not published project", async () => { const response = await loadProject({ - webstudioAPIUrl, - projectId: notPublishedProject, + apiUrl, + projectId: notPublishedProjectId, }); expect(response.tree.errors).toBe( - "Site needs to be published, production tree ID is null." + "Project 7ec397c6-b3d0-4967-9073-9d83623fcf8e needs to be published first" ); }); }); diff --git a/packages/http-client/src/index.ts b/packages/http-client/src/index.ts index 51ca682cb6..6dec39ec30 100644 --- a/packages/http-client/src/index.ts +++ b/packages/http-client/src/index.ts @@ -2,18 +2,18 @@ import fetch from "isomorphic-fetch"; import type { Includes, Project } from "./index.d"; export const loadProject = async ({ - webstudioAPIUrl = null, + apiUrl, projectId, include = { tree: true, props: true, breakpoints: true }, }: { - webstudioAPIUrl: string | null; + apiUrl: string; projectId: string; include?: Includes; }): Promise => { - if (!webstudioAPIUrl) { + if (apiUrl === undefined) { throw new Error("Webstudio API URL is required."); } - const baseUrl = new URL("/", `${webstudioAPIUrl}`); + const baseUrl = new URL(`${apiUrl}`); const treeUrl = new URL(`/rest/tree/${projectId}`, baseUrl); const propsUrl = new URL(`/rest/props/${projectId}`, baseUrl); const breakpointsUrl = new URL(`/rest/breakpoints/${projectId}`, baseUrl); From fec1fd1cac504551d9a3bc692eaf40c14f56abef Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Wed, 28 Sep 2022 18:44:11 +0300 Subject: [PATCH 22/71] fix github/google login, don"t use _json key, github may not provide it simplify the functions a bit --- apps/designer/app/services/auth.server.ts | 69 ++++++++++++++-------- apps/designer/app/shared/db/user.server.ts | 25 ++------ 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/apps/designer/app/services/auth.server.ts b/apps/designer/app/services/auth.server.ts index d1e9bb4f72..b14c66e110 100644 --- a/apps/designer/app/services/auth.server.ts +++ b/apps/designer/app/services/auth.server.ts @@ -1,8 +1,8 @@ import { User } from "@webstudio-is/prisma-client"; import { Authenticator } from "remix-auth"; import { FormStrategy } from "remix-auth-form"; -import { GitHubStrategy } from "remix-auth-github"; -import { GoogleStrategy } from "remix-auth-google"; +import { GitHubStrategy, type GitHubProfile } from "remix-auth-github"; +import { GoogleStrategy, type GoogleProfile } from "remix-auth-google"; import config from "~/config"; import * as db from "~/shared/db"; import { sessionStorage } from "~/services/session.server"; @@ -15,41 +15,57 @@ const url = `${ : `http://localhost:${process.env.PORT || 3000}` }${config.authPath}`; -const github = new GitHubStrategy( - { - clientID: process.env.GH_CLIENT_ID as string, - clientSecret: process.env.GH_CLIENT_SECRET as string, - callbackURL: `${url}${config.githubCallbackPath}`, - }, - async ({ profile }) => { - const user = await db.user.createOrLoginWithGithub(profile); - +const strategyCallback = async ({ + profile, +}: { + profile: GitHubProfile | GoogleProfile; +}) => { + try { + const user = await db.user.createOrLoginWithOAuth(profile); return user; + } catch (error: unknown) { + if (error instanceof Error) { + sentryException({ + error, + extras: { + loginMethod: AUTH_PROVIDERS.LOGIN_DEV, + }, + }); + } + throw error; } -); - -const google = new GoogleStrategy( - { - clientID: process.env.GOOGLE_CLIENT_ID as string, - clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, - callbackURL: `${url}${config.googleCallbackPath}`, - }, - async ({ profile }) => { - const user = await db.user.createOrLoginWithGoogle(profile); - - return user; - } -); +}; // Create an instance of the authenticator, pass a generic with what // strategies will return and will store in the session -export const authenticator = new Authenticator(sessionStorage); +export const authenticator = new Authenticator(sessionStorage, { + throwOnError: true, +}); + if (process.env.GH_CLIENT_ID && process.env.GH_CLIENT_SECRET) { + const github = new GitHubStrategy( + { + clientID: process.env.GH_CLIENT_ID as string, + clientSecret: process.env.GH_CLIENT_SECRET as string, + callbackURL: `${url}${config.githubCallbackPath}`, + }, + strategyCallback + ); authenticator.use(github, "github"); } + if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) { + const google = new GoogleStrategy( + { + clientID: process.env.GOOGLE_CLIENT_ID as string, + clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, + callbackURL: `${url}${config.googleCallbackPath}`, + }, + strategyCallback + ); authenticator.use(google, "google"); } + if (process.env.DEV_LOGIN === "true") { authenticator.use( new FormStrategy(async ({ form }) => { @@ -68,6 +84,7 @@ if (process.env.DEV_LOGIN === "true") { }, }); } + throw error; } } diff --git a/apps/designer/app/shared/db/user.server.ts b/apps/designer/app/shared/db/user.server.ts index e12dbb1e5e..ca944f2d17 100644 --- a/apps/designer/app/shared/db/user.server.ts +++ b/apps/designer/app/shared/db/user.server.ts @@ -1,5 +1,5 @@ -import { GitHubProfile } from "remix-auth-github"; -import { GoogleProfile } from "remix-auth-google"; +import type { GitHubProfile } from "remix-auth-github"; +import type { GoogleProfile } from "remix-auth-google"; import { prisma, User } from "@webstudio-is/prisma-client"; export const createDemoUser = async (userId: string) => { @@ -73,26 +73,13 @@ const genericCreateAccount = async (userData: { return newTeam.users[0]; }; -export const createOrLoginWithGithub = async ( - profile: GitHubProfile +export const createOrLoginWithOAuth = async ( + profile: GoogleProfile | GitHubProfile ): Promise => { const userData = { - email: profile._json.email, + email: (profile.emails ?? [])[0]?.value, username: profile.displayName, - image: profile._json.avatar_url, - provider: profile.provider, - }; - const newUser = await genericCreateAccount(userData); - return newUser; -}; - -export const createOrLoginWithGoogle = async ( - profile: GoogleProfile -): Promise => { - const userData = { - email: profile._json.email, - username: profile.displayName, - image: profile._json.picture, + image: (profile.photos ?? [])[0]?.value, provider: profile.provider, }; const newUser = await genericCreateAccount(userData); From 3098257ad5596589a877e3820172a2d22eec198e Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Wed, 28 Sep 2022 21:13:06 +0300 Subject: [PATCH 23/71] remove as string assertions --- apps/designer/app/services/auth.server.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/designer/app/services/auth.server.ts b/apps/designer/app/services/auth.server.ts index b14c66e110..23690cdc09 100644 --- a/apps/designer/app/services/auth.server.ts +++ b/apps/designer/app/services/auth.server.ts @@ -45,8 +45,8 @@ export const authenticator = new Authenticator(sessionStorage, { if (process.env.GH_CLIENT_ID && process.env.GH_CLIENT_SECRET) { const github = new GitHubStrategy( { - clientID: process.env.GH_CLIENT_ID as string, - clientSecret: process.env.GH_CLIENT_SECRET as string, + clientID: process.env.GH_CLIENT_ID, + clientSecret: process.env.GH_CLIENT_SECRET, callbackURL: `${url}${config.githubCallbackPath}`, }, strategyCallback @@ -57,8 +57,8 @@ if (process.env.GH_CLIENT_ID && process.env.GH_CLIENT_SECRET) { if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) { const google = new GoogleStrategy( { - clientID: process.env.GOOGLE_CLIENT_ID as string, - clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, + clientID: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: `${url}${config.googleCallbackPath}`, }, strategyCallback From e4cea813d7b63e867b43511f6aef214697583a0a Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 29 Sep 2022 22:29:54 +0200 Subject: [PATCH 24/71] When we quit text editing mode, we need to safe again otherwise it can happen before debounce calls. --- .../text-editor/plugins/plugin-on-change.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-on-change.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-on-change.tsx index a4426d566d..196693fa9b 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-on-change.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-on-change.tsx @@ -1,5 +1,5 @@ import { type ChildrenUpdates } from "@webstudio-is/react-sdk"; -import { useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useDebounce } from "react-use"; import { OnChangePlugin as LexicalOnChangePlugin, @@ -14,17 +14,18 @@ export const OnChangePlugin = ({ }) => { const [editorState, setEditorState] = useState(); - useDebounce( - () => { - if (editorState === undefined) return; - editorState.read(() => { - const updates = toUpdates(editorState.toJSON().root); - onChange(updates); - }); - }, - 500, - [editorState] - ); + const handleChange = useCallback(() => { + if (editorState === undefined) return; + editorState.read(() => { + const updates = toUpdates(editorState.toJSON().root); + onChange(updates); + }); + }, [editorState, onChange]); + + // When we quit editing mode, we need to safe again otherwise it can happen before debounce calls. + useEffect(() => handleChange); + + useDebounce(handleChange, 1000, [handleChange]); return ; }; From 5e45419bfa445636601e49542d1b68a21c9143bf Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 3 Oct 2022 22:09:49 +0200 Subject: [PATCH 25/71] create image component --- .../react-sdk/src/components/image.props.json | 2525 +++++++++++++++++ .../src/components/image.stories.tsx | 18 + packages/react-sdk/src/components/image.tsx | 14 + .../react-sdk/src/components/image.ws.tsx | 15 + packages/react-sdk/src/components/index.ts | 1 + packages/react-sdk/src/components/meta.ts | 1 + 6 files changed, 2574 insertions(+) create mode 100644 packages/react-sdk/src/components/image.props.json create mode 100644 packages/react-sdk/src/components/image.stories.tsx create mode 100644 packages/react-sdk/src/components/image.tsx create mode 100644 packages/react-sdk/src/components/image.ws.tsx diff --git a/packages/react-sdk/src/components/image.props.json b/packages/react-sdk/src/components/image.props.json new file mode 100644 index 0000000000..f9ad6eb46a --- /dev/null +++ b/packages/react-sdk/src/components/image.props.json @@ -0,0 +1,2525 @@ +{ + "slot": { + "defaultValue": null, + "description": "", + "name": "slot", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "style": { + "defaultValue": null, + "description": "", + "name": "style", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "CSSProperties" + }, + "control": { + "type": "text" + } + }, + "title": { + "defaultValue": null, + "description": "", + "name": "title", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "alt": { + "defaultValue": null, + "description": "", + "name": "alt", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "crossOrigin": { + "defaultValue": null, + "description": "", + "name": "crossOrigin", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"\" | \"anonymous\" | \"use-credentials\"", + "value": [ + { + "value": "\"\"" + }, + { + "value": "\"anonymous\"" + }, + { + "value": "\"use-credentials\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["\"\"", "anonymous", "use-credentials"] + }, + "decoding": { + "defaultValue": null, + "description": "", + "name": "decoding", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"async\" | \"auto\" | \"sync\"", + "value": [ + { + "value": "\"async\"" + }, + { + "value": "\"auto\"" + }, + { + "value": "\"sync\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["async", "auto", "sync"] + }, + "height": { + "defaultValue": null, + "description": "", + "name": "height", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string | number" + }, + "control": { + "type": "text" + } + }, + "loading": { + "defaultValue": null, + "description": "", + "name": "loading", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"eager\" | \"lazy\"", + "value": [ + { + "value": "\"eager\"" + }, + { + "value": "\"lazy\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["eager", "lazy"] + }, + "referrerPolicy": { + "defaultValue": null, + "description": "", + "name": "referrerPolicy", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "HTMLAttributeReferrerPolicy", + "value": [ + { + "value": "\"\"" + }, + { + "value": "\"no-referrer\"" + }, + { + "value": "\"no-referrer-when-downgrade\"" + }, + { + "value": "\"origin\"" + }, + { + "value": "\"origin-when-cross-origin\"" + }, + { + "value": "\"same-origin\"" + }, + { + "value": "\"strict-origin\"" + }, + { + "value": "\"strict-origin-when-cross-origin\"" + }, + { + "value": "\"unsafe-url\"" + } + ] + }, + "control": { + "type": "select" + }, + "options": [ + "\"\"", + "no-referrer", + "no-referrer-when-downgrade", + "origin", + "origin-when-cross-origin", + "same-origin", + "strict-origin", + "strict-origin-when-cross-origin", + "unsafe-url" + ] + }, + "sizes": { + "defaultValue": null, + "description": "", + "name": "sizes", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "src": { + "defaultValue": { + "value": "placeholder.webp" + }, + "description": "", + "name": "src", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "srcSet": { + "defaultValue": null, + "description": "", + "name": "srcSet", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "useMap": { + "defaultValue": null, + "description": "", + "name": "useMap", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "width": { + "defaultValue": null, + "description": "", + "name": "width", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "ImgHTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string | number" + }, + "control": { + "type": "text" + } + }, + "defaultChecked": { + "defaultValue": null, + "description": "", + "name": "defaultChecked", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "boolean" + }, + "control": { + "type": "boolean" + } + }, + "defaultValue": { + "defaultValue": null, + "description": "", + "name": "defaultValue", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string | number | readonly string[]" + }, + "control": { + "type": "text" + } + }, + "suppressContentEditableWarning": { + "defaultValue": null, + "description": "", + "name": "suppressContentEditableWarning", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "boolean" + }, + "control": { + "type": "boolean" + } + }, + "suppressHydrationWarning": { + "defaultValue": null, + "description": "", + "name": "suppressHydrationWarning", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "boolean" + }, + "control": { + "type": "boolean" + } + }, + "accessKey": { + "defaultValue": null, + "description": "", + "name": "accessKey", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "className": { + "defaultValue": null, + "description": "", + "name": "className", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "contentEditable": { + "defaultValue": null, + "description": "", + "name": "contentEditable", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish | \"inherit\"" + }, + "control": { + "type": "text" + } + }, + "contextMenu": { + "defaultValue": null, + "description": "", + "name": "contextMenu", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "dir": { + "defaultValue": null, + "description": "", + "name": "dir", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "draggable": { + "defaultValue": null, + "description": "", + "name": "draggable", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "hidden": { + "defaultValue": null, + "description": "", + "name": "hidden", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "boolean" + }, + "control": { + "type": "boolean" + } + }, + "id": { + "defaultValue": null, + "description": "", + "name": "id", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "lang": { + "defaultValue": null, + "description": "", + "name": "lang", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "placeholder": { + "defaultValue": null, + "description": "", + "name": "placeholder", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "spellCheck": { + "defaultValue": null, + "description": "", + "name": "spellCheck", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "tabIndex": { + "defaultValue": null, + "description": "", + "name": "tabIndex", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "translate": { + "defaultValue": null, + "description": "", + "name": "translate", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"yes\" | \"no\"", + "value": [ + { + "value": "\"yes\"" + }, + { + "value": "\"no\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["yes", "no"] + }, + "radioGroup": { + "defaultValue": null, + "description": "", + "name": "radioGroup", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "role": { + "defaultValue": null, + "description": "", + "name": "role", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "AriaRole" + }, + "control": { + "type": "text" + } + }, + "about": { + "defaultValue": null, + "description": "", + "name": "about", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "datatype": { + "defaultValue": null, + "description": "", + "name": "datatype", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "inlist": { + "defaultValue": null, + "description": "", + "name": "inlist", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "any" + }, + "control": { + "type": "text" + } + }, + "prefix": { + "defaultValue": null, + "description": "", + "name": "prefix", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "property": { + "defaultValue": null, + "description": "", + "name": "property", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "resource": { + "defaultValue": null, + "description": "", + "name": "resource", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "typeof": { + "defaultValue": null, + "description": "", + "name": "typeof", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "vocab": { + "defaultValue": null, + "description": "", + "name": "vocab", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "autoCapitalize": { + "defaultValue": null, + "description": "", + "name": "autoCapitalize", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "autoCorrect": { + "defaultValue": null, + "description": "", + "name": "autoCorrect", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "autoSave": { + "defaultValue": null, + "description": "", + "name": "autoSave", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "color": { + "description": "", + "name": "color", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "color" + } + }, + "itemProp": { + "defaultValue": null, + "description": "", + "name": "itemProp", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "itemScope": { + "defaultValue": null, + "description": "", + "name": "itemScope", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "boolean" + }, + "control": { + "type": "boolean" + } + }, + "itemType": { + "defaultValue": null, + "description": "", + "name": "itemType", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "itemID": { + "defaultValue": null, + "description": "", + "name": "itemID", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "itemRef": { + "defaultValue": null, + "description": "", + "name": "itemRef", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "results": { + "defaultValue": null, + "description": "", + "name": "results", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "security": { + "defaultValue": null, + "description": "", + "name": "security", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "unselectable": { + "defaultValue": null, + "description": "", + "name": "unselectable", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"on\" | \"off\"", + "value": [ + { + "value": "\"on\"" + }, + { + "value": "\"off\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["on", "off"] + }, + "inputMode": { + "defaultValue": null, + "description": "Hints at the type of data that might be entered by the user while editing the element or its contents\n@see https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute", + "name": "inputMode", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"text\" | \"none\" | \"search\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"", + "value": [ + { + "value": "\"text\"" + }, + { + "value": "\"none\"" + }, + { + "value": "\"search\"" + }, + { + "value": "\"tel\"" + }, + { + "value": "\"url\"" + }, + { + "value": "\"email\"" + }, + { + "value": "\"numeric\"" + }, + { + "value": "\"decimal\"" + } + ] + }, + "control": { + "type": "select" + }, + "options": [ + "text", + "none", + "search", + "tel", + "url", + "email", + "numeric", + "decimal" + ] + }, + "is": { + "defaultValue": null, + "description": "Specify that a standard HTML element should behave like a defined custom built-in element\n@see https://html.spec.whatwg.org/multipage/custom-elements.html#attr-is", + "name": "is", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "HTMLAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-activedescendant": { + "defaultValue": null, + "description": "Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.", + "name": "aria-activedescendant", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-atomic": { + "defaultValue": null, + "description": "Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute.", + "name": "aria-atomic", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-autocomplete": { + "defaultValue": null, + "description": "Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be\npresented if they are made.", + "name": "aria-autocomplete", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"list\" | \"none\" | \"inline\" | \"both\"", + "value": [ + { + "value": "\"list\"" + }, + { + "value": "\"none\"" + }, + { + "value": "\"inline\"" + }, + { + "value": "\"both\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["list", "none", "inline", "both"] + }, + "aria-busy": { + "defaultValue": null, + "description": "Indicates an element is being modified and that assistive technologies MAY want to wait until the modifications are complete before exposing them to the user.", + "name": "aria-busy", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-checked": { + "defaultValue": null, + "description": "Indicates the current \"checked\" state of checkboxes, radio buttons, and other widgets.\n@see aria-pressed\n@see aria-selected.", + "name": "aria-checked", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "boolean | \"true\" | \"false\" | \"mixed\"" + }, + "control": { + "type": "text" + } + }, + "aria-colcount": { + "defaultValue": null, + "description": "Defines the total number of columns in a table, grid, or treegrid.\n@see aria-colindex.", + "name": "aria-colcount", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-colindex": { + "defaultValue": null, + "description": "Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.\n@see aria-colcount\n@see aria-colspan.", + "name": "aria-colindex", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-colspan": { + "defaultValue": null, + "description": "Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.\n@see aria-colindex\n@see aria-rowspan.", + "name": "aria-colspan", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-controls": { + "defaultValue": null, + "description": "Identifies the element (or elements) whose contents or presence are controlled by the current element.\n@see aria-owns.", + "name": "aria-controls", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-current": { + "defaultValue": null, + "description": "Indicates the element that represents the current item within a container or set of related elements.", + "name": "aria-current", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "boolean | \"time\" | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\"" + }, + "control": { + "type": "text" + } + }, + "aria-describedby": { + "defaultValue": null, + "description": "Identifies the element (or elements) that describes the object.\n@see aria-labelledby", + "name": "aria-describedby", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-details": { + "defaultValue": null, + "description": "Identifies the element that provides a detailed, extended description for the object.\n@see aria-describedby.", + "name": "aria-details", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-disabled": { + "defaultValue": null, + "description": "Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.\n@see aria-hidden\n@see aria-readonly.", + "name": "aria-disabled", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-dropeffect": { + "defaultValue": null, + "description": "Indicates what functions can be performed when a dragged object is released on the drop target.\n@deprecated in ARIA 1.1", + "name": "aria-dropeffect", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"link\" | \"none\" | \"copy\" | \"execute\" | \"move\" | \"popup\"", + "value": [ + { + "value": "\"link\"" + }, + { + "value": "\"none\"" + }, + { + "value": "\"copy\"" + }, + { + "value": "\"execute\"" + }, + { + "value": "\"move\"" + }, + { + "value": "\"popup\"" + } + ] + }, + "control": { + "type": "select" + }, + "options": ["link", "none", "copy", "execute", "move", "popup"] + }, + "aria-errormessage": { + "defaultValue": null, + "description": "Identifies the element that provides an error message for the object.\n@see aria-invalid\n@see aria-describedby.", + "name": "aria-errormessage", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-expanded": { + "defaultValue": null, + "description": "Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.", + "name": "aria-expanded", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-flowto": { + "defaultValue": null, + "description": "Identifies the next element (or elements) in an alternate reading order of content which, at the user's discretion,\nallows assistive technology to override the general default of reading in document source order.", + "name": "aria-flowto", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-grabbed": { + "defaultValue": null, + "description": "Indicates an element's \"grabbed\" state in a drag-and-drop operation.\n@deprecated in ARIA 1.1", + "name": "aria-grabbed", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-haspopup": { + "defaultValue": null, + "description": "Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.", + "name": "aria-haspopup", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "boolean | \"dialog\" | \"menu\" | \"true\" | \"false\" | \"grid\" | \"listbox\" | \"tree\"" + }, + "control": { + "type": "text" + } + }, + "aria-hidden": { + "defaultValue": null, + "description": "Indicates whether the element is exposed to an accessibility API.\n@see aria-disabled.", + "name": "aria-hidden", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-invalid": { + "defaultValue": null, + "description": "Indicates the entered value does not conform to the format expected by the application.\n@see aria-errormessage.", + "name": "aria-invalid", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"" + }, + "control": { + "type": "text" + } + }, + "aria-keyshortcuts": { + "defaultValue": null, + "description": "Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element.", + "name": "aria-keyshortcuts", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-label": { + "defaultValue": null, + "description": "Defines a string value that labels the current element.\n@see aria-labelledby.", + "name": "aria-label", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-labelledby": { + "defaultValue": null, + "description": "Identifies the element (or elements) that labels the current element.\n@see aria-describedby.", + "name": "aria-labelledby", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-level": { + "defaultValue": null, + "description": "Defines the hierarchical level of an element within a structure.", + "name": "aria-level", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-live": { + "defaultValue": null, + "description": "Indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region.", + "name": "aria-live", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"off\" | \"assertive\" | \"polite\"", + "value": [ + { + "value": "\"off\"" + }, + { + "value": "\"assertive\"" + }, + { + "value": "\"polite\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["off", "assertive", "polite"] + }, + "aria-modal": { + "defaultValue": null, + "description": "Indicates whether an element is modal when displayed.", + "name": "aria-modal", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-multiline": { + "defaultValue": null, + "description": "Indicates whether a text box accepts multiple lines of input or only a single line.", + "name": "aria-multiline", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-multiselectable": { + "defaultValue": null, + "description": "Indicates that the user may select more than one item from the current selectable descendants.", + "name": "aria-multiselectable", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-orientation": { + "defaultValue": null, + "description": "Indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous.", + "name": "aria-orientation", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"horizontal\" | \"vertical\"", + "value": [ + { + "value": "\"horizontal\"" + }, + { + "value": "\"vertical\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["horizontal", "vertical"] + }, + "aria-owns": { + "defaultValue": null, + "description": "Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship\nbetween DOM elements where the DOM hierarchy cannot be used to represent the relationship.\n@see aria-controls.", + "name": "aria-owns", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-placeholder": { + "defaultValue": null, + "description": "Defines a short hint (a word or short phrase) intended to aid the user with data entry when the control has no value.\nA hint could be a sample value or a brief description of the expected format.", + "name": "aria-placeholder", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-posinset": { + "defaultValue": null, + "description": "Defines an element's number or position in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.\n@see aria-setsize.", + "name": "aria-posinset", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-pressed": { + "defaultValue": null, + "description": "Indicates the current \"pressed\" state of toggle buttons.\n@see aria-checked\n@see aria-selected.", + "name": "aria-pressed", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "boolean | \"true\" | \"false\" | \"mixed\"" + }, + "control": { + "type": "text" + } + }, + "aria-readonly": { + "defaultValue": null, + "description": "Indicates that the element is not editable, but is otherwise operable.\n@see aria-disabled.", + "name": "aria-readonly", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-relevant": { + "defaultValue": null, + "description": "Indicates what notifications the user agent will trigger when the accessibility tree within a live region is modified.\n@see aria-atomic.", + "name": "aria-relevant", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"", + "value": [ + { + "value": "\"text\"" + }, + { + "value": "\"additions\"" + }, + { + "value": "\"additions removals\"" + }, + { + "value": "\"additions text\"" + }, + { + "value": "\"all\"" + }, + { + "value": "\"removals\"" + }, + { + "value": "\"removals additions\"" + }, + { + "value": "\"removals text\"" + }, + { + "value": "\"text additions\"" + }, + { + "value": "\"text removals\"" + } + ] + }, + "control": { + "type": "select" + }, + "options": [ + "text", + "additions", + "additions removals", + "additions text", + "all", + "removals", + "removals additions", + "removals text", + "text additions", + "text removals" + ] + }, + "aria-required": { + "defaultValue": null, + "description": "Indicates that user input is required on the element before a form may be submitted.", + "name": "aria-required", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-roledescription": { + "defaultValue": null, + "description": "Defines a human-readable, author-localized description for the role of an element.", + "name": "aria-roledescription", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + }, + "aria-rowcount": { + "defaultValue": null, + "description": "Defines the total number of rows in a table, grid, or treegrid.\n@see aria-rowindex.", + "name": "aria-rowcount", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-rowindex": { + "defaultValue": null, + "description": "Defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.\n@see aria-rowcount\n@see aria-rowspan.", + "name": "aria-rowindex", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-rowspan": { + "defaultValue": null, + "description": "Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.\n@see aria-rowindex\n@see aria-colspan.", + "name": "aria-rowspan", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-selected": { + "defaultValue": null, + "description": "Indicates the current \"selected\" state of various widgets.\n@see aria-checked\n@see aria-pressed.", + "name": "aria-selected", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "Booleanish" + }, + "control": { + "type": "boolean" + } + }, + "aria-setsize": { + "defaultValue": null, + "description": "Defines the number of items in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.\n@see aria-posinset.", + "name": "aria-setsize", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-sort": { + "defaultValue": null, + "description": "Indicates if items in a table or grid are sorted in ascending or descending order.", + "name": "aria-sort", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "enum", + "raw": "\"none\" | \"ascending\" | \"descending\" | \"other\"", + "value": [ + { + "value": "\"none\"" + }, + { + "value": "\"ascending\"" + }, + { + "value": "\"descending\"" + }, + { + "value": "\"other\"" + } + ] + }, + "control": { + "type": "radio" + }, + "options": ["none", "ascending", "descending", "other"] + }, + "aria-valuemax": { + "defaultValue": null, + "description": "Defines the maximum allowed value for a range widget.", + "name": "aria-valuemax", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-valuemin": { + "defaultValue": null, + "description": "Defines the minimum allowed value for a range widget.", + "name": "aria-valuemin", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-valuenow": { + "defaultValue": null, + "description": "Defines the current value for a range widget.\n@see aria-valuetext.", + "name": "aria-valuenow", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "number" + }, + "control": { + "type": "number" + } + }, + "aria-valuetext": { + "defaultValue": null, + "description": "Defines the human readable text alternative of aria-valuenow for a range widget.", + "name": "aria-valuetext", + "parent": { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + }, + "declarations": [ + { + "fileName": "webstudio-designer/node_modules/@types/react/index.d.ts", + "name": "AriaAttributes" + } + ], + "required": false, + "type": { + "name": "string" + }, + "control": { + "type": "text" + } + } +} diff --git a/packages/react-sdk/src/components/image.stories.tsx b/packages/react-sdk/src/components/image.stories.tsx new file mode 100644 index 0000000000..d52ca3ad1e --- /dev/null +++ b/packages/react-sdk/src/components/image.stories.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import type { ComponentMeta, ComponentStory } from "@storybook/react"; +import { Image as ImagePrimitive } from "./image"; +import argTypes from "./image.props.json"; + +export default { + title: "Components/Image", + component: ImagePrimitive, + argTypes, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + +); + +export const Image = Template.bind({}); + +Image.args = {}; diff --git a/packages/react-sdk/src/components/image.tsx b/packages/react-sdk/src/components/image.tsx new file mode 100644 index 0000000000..dfe590ba20 --- /dev/null +++ b/packages/react-sdk/src/components/image.tsx @@ -0,0 +1,14 @@ +import React, { forwardRef, type ElementRef, type ComponentProps } from "react"; + +const defaultTag = "img"; + +type ButtonProps = ComponentProps; + +export const Image = forwardRef, ButtonProps>( + (props, ref) => +); + +Image.defaultProps = { + src: "placeholder.webp", // Match the platform default +}; +Image.displayName = "Image"; diff --git a/packages/react-sdk/src/components/image.ws.tsx b/packages/react-sdk/src/components/image.ws.tsx new file mode 100644 index 0000000000..384d00f272 --- /dev/null +++ b/packages/react-sdk/src/components/image.ws.tsx @@ -0,0 +1,15 @@ +import { ImageIcon } from "@webstudio-is/icons"; +import type { WsComponentMeta } from "./component-type"; +import { Image } from "./image"; + +const meta: WsComponentMeta = { + Icon: ImageIcon, + Component: Image, + canAcceptChildren: false, + isContentEditable: false, + isInlineOnly: false, + isListed: true, + label: "Image", +}; + +export default meta; diff --git a/packages/react-sdk/src/components/index.ts b/packages/react-sdk/src/components/index.ts index 68e139f668..186ff8a309 100644 --- a/packages/react-sdk/src/components/index.ts +++ b/packages/react-sdk/src/components/index.ts @@ -9,3 +9,4 @@ export { default as Italic } from "./italic.ws"; export { default as Button } from "./button.ws"; export { default as Input } from "./input.ws"; export { default as Form } from "./form.ws"; +export { default as Image } from "./image.ws"; diff --git a/packages/react-sdk/src/components/meta.ts b/packages/react-sdk/src/components/meta.ts index 06bda80f11..7a29f4a12c 100644 --- a/packages/react-sdk/src/components/meta.ts +++ b/packages/react-sdk/src/components/meta.ts @@ -9,3 +9,4 @@ export { default as Italic } from "./italic.stories"; export { default as Link } from "./link.stories"; export { default as Paragraph } from "./paragraph.stories"; export { default as TextBlock } from "./text-block.stories"; +export { default as Image } from "./image.stories"; From 835120cbd80fa8bbcf69719468d1df1ee0da44ac Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Tue, 4 Oct 2022 13:16:19 +0200 Subject: [PATCH 26/71] Add focus and blur handling + test --- packages/design-system/package.json | 6 +- .../src/components/text-field.stories.tsx | 30 ++ .../src/components/text-field.tsx | 16 + yarn.lock | 435 +++++++++++++++++- 4 files changed, 480 insertions(+), 7 deletions(-) diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 822e8d4c02..cac926dd46 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -17,12 +17,13 @@ "devDependencies": { "@storybook/addon-actions": "^6.5.6", "@storybook/addon-essentials": "^6.5.6", - "@storybook/addon-interactions": "^6.5.6", + "@storybook/addon-interactions": "^6.5.12", "@storybook/addon-links": "^6.5.6", "@storybook/builder-webpack4": "^6.5.6", + "@storybook/jest": "^0.0.10", "@storybook/manager-webpack4": "^6.5.6", "@storybook/react": "^6.5.6", - "@storybook/testing-library": "^0.0.11", + "@storybook/testing-library": "^0.0.13", "@webstudio-is/jest-config": "*", "lost-pixel": "^2.20.0", "tsup": "^6.1.3", @@ -61,6 +62,7 @@ "@radix-ui/react-toggle-group": "^1.0.0", "@radix-ui/react-tooltip": "^1.0.0", "@radix-ui/react-use-layout-effect": "^1.0.0", + "@react-aria/interactions": "^3.11.0", "@stitches/react": "^1.2.8", "@webstudio-is/icons": "*", "react-hot-toast": "^2.3.0" diff --git a/packages/design-system/src/components/text-field.stories.tsx b/packages/design-system/src/components/text-field.stories.tsx index 752d5acb2e..055ce3e114 100644 --- a/packages/design-system/src/components/text-field.stories.tsx +++ b/packages/design-system/src/components/text-field.stories.tsx @@ -1,3 +1,5 @@ +import { expect } from "@storybook/jest"; +import { userEvent, waitFor, within } from "@storybook/testing-library"; import React from "react"; import { ComponentStory } from "@storybook/react"; import { BrushIcon, ChevronDownIcon } from "@webstudio-is/icons"; @@ -11,6 +13,10 @@ import { Text } from "./text"; export default { component: TextField, + argTypes: { + onFocus: { action: true }, + onBlur: { action: true }, + }, }; export const Default: ComponentStory = () => { @@ -184,3 +190,27 @@ export const Interactive: ComponentStory = () => { ); }; + +export const FocusEvents: ComponentStory = (args) => { + return ( + + ); +}; +FocusEvents.play = async ({ args, canvasElement }) => { + const canvas = within(canvasElement); + + const el = canvas.getByLabelText("Focus and blur:"); + + await userEvent.tab(); + await waitFor(() => expect(el).toHaveFocus()); + await userEvent.tab(); + await waitFor(() => expect(args.onBlur).not.toHaveBeenCalled()); + await userEvent.tab(); + await waitFor(() => expect(args.onBlur).toHaveBeenCalled()); + + await waitFor(() => expect(args.onFocus).toHaveBeenCalledTimes(1)); + await waitFor(() => expect(args.onBlur).toHaveBeenCalledTimes(1)); +}; diff --git a/packages/design-system/src/components/text-field.tsx b/packages/design-system/src/components/text-field.tsx index c44ed101c1..68a1078cc6 100644 --- a/packages/design-system/src/components/text-field.tsx +++ b/packages/design-system/src/components/text-field.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useFocusWithin } from "@react-aria/interactions"; import { styled } from "../stitches.config"; import { Flex } from "./flex"; @@ -165,9 +166,23 @@ export const TextField = React.forwardRef( size, state, variant, + onFocus, + onBlur, ...textFieldProps } = props; + const { focusWithinProps } = useFocusWithin({ + isDisabled: disabled, + onFocusWithin: (event) => { + // @ts-expect-error Type mismatch from react-aria + onFocus?.(event); + }, + onBlurWithin: (event) => { + // @ts-expect-error Type mismatch from react-aria + onBlur?.(event); + }, + }); + return ( ( state={state} variant={variant} css={css} + {...focusWithinProps} > {/* We want input to be the first element in DOM so it receives the focus first */} diff --git a/yarn.lock b/yarn.lock index b0fe4f9694..100c3d98b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adobe/css-tools@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd" + integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g== + "@ampproject/remapping@^2.1.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" @@ -1979,7 +1984,7 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== @@ -2336,6 +2341,13 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/expect-utils@^29.1.2": + version "29.1.2" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.1.2.tgz#66dbb514d38f7d21456bc774419c9ae5cca3f88d" + integrity sha512-4a48bhKfGj/KAH39u0ppzNTABXQ8QPccWAFUFobWBaEMSMp+sB31Z2fK/l47c4a/Mu1po2ffmfAIPxXbVTXdtg== + dependencies: + jest-get-type "^29.0.0" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -2388,6 +2400,13 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" +"@jest/schemas@^29.0.0": + version "29.0.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" + integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== + dependencies: + "@sinclair/typebox" "^0.24.1" + "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" @@ -2481,6 +2500,18 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^29.1.2": + version "29.1.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.1.2.tgz#7442d32b16bcd7592d9614173078b8c334ec730a" + integrity sha512-DcXGtoTykQB5jiwCmVr8H4vdg2OJhQex3qPkG+ISyDO7xQXbt/4R6dowcRyPemRnkH7JoHvZuxPBdlq+9JxFCg== + dependencies: + "@jest/schemas" "^29.0.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -3401,6 +3432,45 @@ dependencies: "@babel/runtime" "^7.13.10" +"@react-aria/interactions@^3.11.0": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.11.0.tgz#aa6118af58ff443670152393edab97e403d6d359" + integrity sha512-ZjK4m8u6FlV7Q9/1h9P2Ii6j/NwKR3BmTeGeBQssS2i4dV2pJeOPePnGzVQQGG8FzGQ+TcNRvZPXKaU4AlnBjw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/utils" "^3.13.3" + "@react-types/shared" "^3.14.1" + +"@react-aria/ssr@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.3.0.tgz#25e81daf0c7a270a4a891159d8d984578e4512d8" + integrity sha512-yNqUDuOVZIUGP81R87BJVi/ZUZp/nYOBXbPsRe7oltJOfErQZD+UezMpw4vM2KRz18cURffvmC8tJ6JTeyDtaQ== + dependencies: + "@babel/runtime" "^7.6.2" + +"@react-aria/utils@^3.13.3": + version "3.13.3" + resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.13.3.tgz#1b27912e4630f0db6a7b39eb1013f6c4f710075c" + integrity sha512-wqjGNFX4TrXriUU1gvCaoqRhuckdoYogUWN0iyQRkTmzvb7H/NNzQzHou5ggWAdts/NzJUInwKarBWM9hCZZbg== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/ssr" "^3.3.0" + "@react-stately/utils" "^3.5.1" + "@react-types/shared" "^3.14.1" + clsx "^1.1.1" + +"@react-stately/utils@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.5.1.tgz#502de762e5d33e892347c5f58053674e06d3bc92" + integrity sha512-INeQ5Er2Jm+db8Py4upKBtgfzp3UYgwXYmbU/XJn49Xw27ktuimH9e37qP3bgHaReb5L3g8IrGs38tJUpnGPHA== + dependencies: + "@babel/runtime" "^7.6.2" + +"@react-types/shared@^3.14.1": + version "3.14.1" + resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.14.1.tgz#8fe25f729426e8043054e442eb5392364200e028" + integrity sha512-yPPgVRWWanXqbdxFTgJmVwx0JlcnEK3dqkKDIbVk6mxAHvEESI9+oDnHvO8IMHqF+GbrTCzVtAs0zwhYI/uHJA== + "@remix-run/dev@^1.6.4": version "1.6.4" resolved "https://registry.yarnpkg.com/@remix-run/dev/-/dev-1.6.4.tgz#7a2a7fc24e66434c5cf64d6e79a62d35d09b73de" @@ -3701,6 +3771,11 @@ dependencies: "@sentry/cli" "^1.74.4" +"@sinclair/typebox@^0.24.1": + version "0.24.44" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.44.tgz#0a0aa3bf4a155a678418527342a3ee84bd8caa5c" + integrity sha512-ka0W0KN5i6LfrSocduwliMMpqVgohtPFidKdMEOUjoOFCHcOOYkKsPRxfs5f15oPNHTm6ERAm0GV/+/LTKeiWg== + "@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" @@ -3847,6 +3922,27 @@ regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" +"@storybook/addon-interactions@^6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-6.5.12.tgz#b9b7ae50de4c41ea2c9023dcaacf3f318a072ea4" + integrity sha512-wAkKqUX57qZMzgykMIltjDrFfA0II+pKz2SchK4hXHK3/2rtOe+Ah0Sj5nTQo++Ee5oC1PVZWIq/Y1kufCTBJQ== + dependencies: + "@devtools-ds/object-inspector" "^1.1.2" + "@storybook/addons" "6.5.12" + "@storybook/api" "6.5.12" + "@storybook/client-logger" "6.5.12" + "@storybook/components" "6.5.12" + "@storybook/core-common" "6.5.12" + "@storybook/core-events" "6.5.12" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/instrumenter" "6.5.12" + "@storybook/theming" "6.5.12" + core-js "^3.8.2" + global "^4.4.0" + jest-mock "^27.0.6" + polished "^4.2.2" + ts-dedent "^2.2.0" + "@storybook/addon-interactions@^6.5.6", "@storybook/addon-interactions@^6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-6.5.9.tgz#4356e96beae8f44000955d8870c3acc6c8d1fb3a" @@ -3946,6 +4042,23 @@ prop-types "^15.7.2" regenerator-runtime "^0.13.7" +"@storybook/addons@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.12.tgz#891767b5f88ea99b956cf19e9e2893594068adc7" + integrity sha512-y3cgxZq41YGnuIlBJEuJjSFdMsm8wnvlNOGUP9Q+Er2dgfx8rJz4Q22o4hPjpvpaj4XdBtxCJXI2NeFpN59+Cw== + dependencies: + "@storybook/api" "6.5.12" + "@storybook/channels" "6.5.12" + "@storybook/client-logger" "6.5.12" + "@storybook/core-events" "6.5.12" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.12" + "@storybook/theming" "6.5.12" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + "@storybook/addons@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.9.tgz#5a9d7395c579a9cbc44dfc122362fb3c95dfb9d5" @@ -3963,6 +4076,29 @@ global "^4.4.0" regenerator-runtime "^0.13.7" +"@storybook/api@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.12.tgz#7cc82087fc9298be03f15bf4ab9c4aab294b3bac" + integrity sha512-DuUZmMlQxkFNU9Vgkp9aNfCkAongU76VVmygvCuSpMVDI9HQ2lG0ydL+ppL4XKoSMCCoXTY6+rg4hJANnH+1AQ== + dependencies: + "@storybook/channels" "6.5.12" + "@storybook/client-logger" "6.5.12" + "@storybook/core-events" "6.5.12" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.12" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.5.12" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/api@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.9.tgz#303733214c9de0422d162f7c54ae05d088b89bf9" @@ -4107,6 +4243,15 @@ global "^4.4.0" telejson "^6.0.8" +"@storybook/channels@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.12.tgz#98baf01691d263e2ac341853361ec69c1a6621bc" + integrity sha512-X5XaKbe4b7LXJ4sUakBo00x6pXnW78JkOonHoaKoWsccHLlEzwfBZpVVekhVZnqtCoLT23dB8wjKgA71RYWoiw== + dependencies: + core-js "^3.8.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/channels@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.9.tgz#abfab89a6587a2688e9926d4aafeb11c9d8b2e79" @@ -4142,6 +4287,14 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/client-logger@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.12.tgz#d9809e13dc7939eb61452a5e94b1ccb61c4a022c" + integrity sha512-IrkMr5KZcudX935/C2balFbxLHhkvQnJ78rbVThHDVckQ7l3oIXTh66IMzldeOabVFDZEMiW8AWuGEYof+JtLw== + dependencies: + core-js "^3.8.2" + global "^4.4.0" + "@storybook/client-logger@6.5.9", "@storybook/client-logger@^6.4.0", "@storybook/client-logger@^6.4.0 || >=6.5.0-0": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.9.tgz#dc1669abe8c45af1cc38f74c6f4b15ff33e63014" @@ -4150,6 +4303,20 @@ core-js "^3.8.2" global "^4.4.0" +"@storybook/components@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.12.tgz#e137f0683ea92e22de116bfa62cfd65ce4efe01d" + integrity sha512-NAAGl5PDXaHdVLd6hA+ttmLwH3zAVGXeUmEubzKZ9bJzb+duhFKxDa9blM4YEkI+palumvgAMm0UgS7ou680Ig== + dependencies: + "@storybook/client-logger" "6.5.12" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.12" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + util-deprecate "^1.0.2" + "@storybook/components@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.9.tgz#97e07ffe11ab76c01ccee380888991bd161f75b2" @@ -4192,6 +4359,62 @@ unfetch "^4.2.0" util-deprecate "^1.0.2" +"@storybook/core-common@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.12.tgz#9f8d5cb3812382c49c84dcfb4279a39e228a1b83" + integrity sha512-gG20+eYdIhwQNu6Xs805FLrOCWtkoc8Rt8gJiRt8yXzZh9EZkU4xgCRoCxrrJ03ys/gTiCFbBOfRi749uM3z4w== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-decorators" "^7.12.12" + "@babel/plugin-proposal-export-default-from" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.7" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-private-property-in-object" "^7.12.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.12" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/preset-env" "^7.12.11" + "@babel/preset-react" "^7.12.10" + "@babel/preset-typescript" "^7.12.7" + "@babel/register" "^7.12.1" + "@storybook/node-logger" "6.5.12" + "@storybook/semver" "^7.3.2" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/pretty-hrtime" "^1.0.0" + babel-loader "^8.0.0" + babel-plugin-macros "^3.0.1" + babel-plugin-polyfill-corejs3 "^0.1.0" + chalk "^4.1.0" + core-js "^3.8.2" + express "^4.17.1" + file-system-cache "^1.0.5" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.0.4" + fs-extra "^9.0.1" + glob "^7.1.6" + handlebars "^4.7.7" + interpret "^2.2.0" + json5 "^2.1.3" + lazy-universal-dotenv "^3.0.1" + picomatch "^2.3.0" + pkg-dir "^5.0.0" + pretty-hrtime "^1.0.3" + resolve-from "^5.0.0" + slash "^3.0.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack "4" + "@storybook/core-common@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.9.tgz#7ca8258ea2634b1d64695c1e4262f71cc7457989" @@ -4248,6 +4471,13 @@ util-deprecate "^1.0.2" webpack "4" +"@storybook/core-events@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.12.tgz#28bd727cc4216012409bfac412fcb708346c56bc" + integrity sha512-0AMyMM19R/lHsYRfWqM8zZTXthasTAK2ExkSRzYi2GkIaVMxRKtM33YRwxKIpJ6KmIKIs8Ru3QCXu1mfCmGzNg== + dependencies: + core-js "^3.8.2" + "@storybook/core-events@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.9.tgz#5b0783c7d22a586c0f5e927a61fe1b1223e19637" @@ -4361,6 +4591,24 @@ lodash "^4.17.21" regenerator-runtime "^0.13.7" +"@storybook/expect@storybook-jest": + version "27.5.2-0" + resolved "https://registry.yarnpkg.com/@storybook/expect/-/expect-27.5.2-0.tgz#131fc4336106805ccd100809a3c2852d31f1da68" + integrity sha512-cP99mhWN/JeCp7VSIiymvj5tmuMY050iFohvp8Zq+kewKsBSZ6/qpTJAGCCZk6pneTcp4S0Fm5BSqyxzbyJ3gw== + dependencies: + "@types/jest" ">=26.0.0" + +"@storybook/instrumenter@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-6.5.12.tgz#acd721c50b65183a53fb5913fe7cdb35b06e2433" + integrity sha512-VMl+Yg6ab79A1VqlhC69sBFI6ySw4Lganqdf9JJf6Y7uNRcZwsjfGpGfYiL4WGH1q72tGiq/25XKGIJV0jgi1Q== + dependencies: + "@storybook/addons" "6.5.12" + "@storybook/client-logger" "6.5.12" + "@storybook/core-events" "6.5.12" + core-js "^3.8.2" + global "^4.4.0" + "@storybook/instrumenter@6.5.9", "@storybook/instrumenter@^6.4.0", "@storybook/instrumenter@^6.4.0 || >=6.5.0-0": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-6.5.9.tgz#885d9dec31b7b7fa6ea29b446105480450e527b8" @@ -4372,6 +4620,16 @@ core-js "^3.8.2" global "^4.4.0" +"@storybook/jest@^0.0.10": + version "0.0.10" + resolved "https://registry.yarnpkg.com/@storybook/jest/-/jest-0.0.10.tgz#4fbc43ebc3ae6cce8232d6bd9b56ef710f481e52" + integrity sha512-qeYLIplpcOUQXboJde5pRCjTvkGmF80jEszRUoNYCNcEPfC2sMK68Wq6Ct8EQj3CoEdJqsK54O2YYh+7D9S+ag== + dependencies: + "@storybook/expect" storybook-jest + "@storybook/instrumenter" "^6.4.0" + "@testing-library/jest-dom" "^5.16.2" + jest-mock "^27.3.0" + "@storybook/manager-webpack4@6.5.9", "@storybook/manager-webpack4@^6.5.6", "@storybook/manager-webpack4@^6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.9.tgz#c75d2cced4550c8a786f00b0e57b203d613e706c" @@ -4468,6 +4726,17 @@ prettier ">=2.2.1 <=2.3.0" ts-dedent "^2.0.0" +"@storybook/node-logger@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.12.tgz#0f9efcd1a37c7aae493b22fe33cacca87c135b9b" + integrity sha512-jdLtT3mX5GQKa+0LuX0q0sprKxtCGf6HdXlKZGD5FEuz4MgJUGaaiN0Hgi+U7Z4tVNOtSoIbYBYXHqfUgJrVZw== + dependencies: + "@types/npmlog" "^4.1.2" + chalk "^4.1.0" + core-js "^3.8.2" + npmlog "^5.0.1" + pretty-hrtime "^1.0.3" + "@storybook/node-logger@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.9.tgz#129cfe0d0f79cab4f6a2ba194d39516680b1626f" @@ -4562,6 +4831,17 @@ util-deprecate "^1.0.2" webpack ">=4.43.0 <6.0.0" +"@storybook/router@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.12.tgz#58efbc1f2f301c8584802af1c710b2f6f03f948c" + integrity sha512-xHubde9YnBbpkDY5+zGO4Pr6VPxP8H9J2v4OTF3H82uaxCIKR0PKG0utS9pFKIsEiP3aM62Hb9qB8nU+v1nj3w== + dependencies: + "@storybook/client-logger" "6.5.12" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + "@storybook/router@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.9.tgz#4740248f8517425b2056273fb366ace8a17c65e8" @@ -4658,6 +4938,16 @@ "@testing-library/user-event" "^13.2.1" ts-dedent "^2.2.0" +"@storybook/theming@6.5.12": + version "6.5.12" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.12.tgz#7df1b52913d49c5e84fc1f2e837c02d9fa8cc639" + integrity sha512-uWOo84qMQ2R6c1C0faZ4Q0nY01uNaX7nXoJKieoiJ6ZqY9PSYxJl1kZLi3uPYnrxLZjzjVyXX8MgdxzbppYItA== + dependencies: + "@storybook/client-logger" "6.5.12" + core-js "^3.8.2" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + "@storybook/theming@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.9.tgz#13f60a3a3cd73ceb5caf9f188e1627e79f1891aa" @@ -4716,6 +5006,21 @@ lz-string "^1.4.4" pretty-format "^27.0.2" +"@testing-library/jest-dom@^5.16.2": + version "5.16.5" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" + integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== + dependencies: + "@adobe/css-tools" "^4.0.1" + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + "@testing-library/react-hooks@^8.0.1": version "8.0.1" resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12" @@ -4983,6 +5288,14 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest@*", "@types/jest@>=26.0.0": + version "29.1.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.1.1.tgz#cf21a0835a1ba9a30ea1966019f1261c6a114c92" + integrity sha512-U9Ey07dGWl6fUFaIaUQUKWG5NoKi/zizeVQCGV8s4nSU0jPgqphVZvS64+8BtWYvrc3ZGw6wo943NSYPxkrp/g== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/jest@^27.4.0", "@types/jest@^27.4.1": version "27.5.2" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" @@ -5236,6 +5549,13 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.5" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz#d113709c90b3c75fdb127ec338dad7d5f86c974f" + integrity sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ== + dependencies: + "@types/jest" "*" + "@types/uglify-js@*": version "3.16.0" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.16.0.tgz#2cf74a0e6ebb6cd54c0d48e509d5bd91160a9602" @@ -5303,6 +5623,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^17.0.8": + version "17.0.13" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76" + integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@^5.36.2": version "5.36.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.36.2.tgz#6df092a20e0f9ec748b27f293a12cb39d0c1fe4d" @@ -7051,6 +7378,14 @@ chalk@^2.0.0, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -7330,7 +7665,7 @@ clsx@1.1.0: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.0.tgz#62937c6adfea771247c34b54d320fb99624f5702" integrity sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA== -clsx@^1.0.4: +clsx@^1.0.4, clsx@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -7839,6 +8174,11 @@ css-what@^6.0.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -8121,6 +8461,11 @@ diff-sequences@^27.5.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.0.0: + version "29.0.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.0.0.tgz#bae49972ef3933556bcb0800b72e8579d19d9e4f" + integrity sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -8168,7 +8513,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.9: +dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: version "0.5.14" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== @@ -9397,6 +9742,17 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" +expect@^29.0.0: + version "29.1.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.1.2.tgz#82f8f28d7d408c7c68da3a386a490ee683e1eced" + integrity sha512-AuAGn1uxva5YBbBlXb+2JPxJRuemZsmlGcapPXWNSBNsQtAULfjioREGBWuI0EOvYUKjDnrCy8PW5Zlr1md5mw== + dependencies: + "@jest/expect-utils" "^29.1.2" + jest-get-type "^29.0.0" + jest-matcher-utils "^29.1.2" + jest-message-util "^29.1.2" + jest-util "^29.1.2" + express@4.17.3: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" @@ -11615,6 +11971,16 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-diff@^29.1.2: + version "29.1.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.1.2.tgz#bb7aaf5353227d6f4f96c5e7e8713ce576a607dc" + integrity sha512-4GQts0aUopVvecIT4IwD/7xsBaMhKTYoM4/njE/aVw9wpw+pIUVp8Vab/KnSzSilr84GnLBkaP3JLDnQYCKqVQ== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.0.0" + jest-get-type "^29.0.0" + pretty-format "^29.1.2" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -11663,6 +12029,11 @@ jest-get-type@^27.5.1: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.0.0: + version "29.0.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.0.0.tgz#843f6c50a1b778f7325df1129a0fd7aa713aef80" + integrity sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw== + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -11745,6 +12116,16 @@ jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-matcher-utils@^29.1.2: + version "29.1.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.1.2.tgz#e68c4bcc0266e70aa1a5c13fb7b8cd4695e318a1" + integrity sha512-MV5XrD3qYSW2zZSHRRceFzqJ39B2z11Qv0KPyZYxnzDHFeYZGJlgGi0SW+IXSJfOewgJp/Km/7lpcFT+cgZypw== + dependencies: + chalk "^4.0.0" + jest-diff "^29.1.2" + jest-get-type "^29.0.0" + pretty-format "^29.1.2" + jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -11760,7 +12141,22 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.0.6, jest-mock@^27.5.1: +jest-message-util@^29.1.2: + version "29.1.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.1.2.tgz#c21a33c25f9dc1ebfcd0f921d89438847a09a501" + integrity sha512-9oJ2Os+Qh6IlxLpmvshVbGUiSkZVc2FK+uGOm6tghafnB2RyjKAxMZhtxThRMxfX1J1SOMhTn9oK3/MutRWQJQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.1.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.1.2" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.0.6, jest-mock@^27.3.0, jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== @@ -11931,6 +12327,18 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.1.2: + version "29.1.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.1.2.tgz#ac5798e93cb6a6703084e194cfa0898d66126df1" + integrity sha512-vPCk9F353i0Ymx3WQq3+a4lZ07NXu9Ca8wya6o4Fe4/aO1e1awMMprZ3woPFpKwghEOW+UXgd15vVotuNN9ONQ== + dependencies: + "@jest/types" "^29.1.2" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" @@ -14642,6 +15050,15 @@ pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.0.0, pretty-format@^29.1.2: + version "29.1.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.1.2.tgz#b1f6b75be7d699be1a051f5da36e8ae9e76a8e6a" + integrity sha512-CGJ6VVGXVRP2o2Dorl4mAwwvDWT25luIsYhkyVQW32E4nL+TgW939J7LlKT/npq5Cpq6j3s+sy+13yk7xYpBmg== + dependencies: + "@jest/schemas" "^29.0.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -15033,7 +15450,7 @@ react-is@17.0.2, react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0": +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== @@ -15271,6 +15688,14 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + refractor@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" From d3167ffadca43c4a8c7fbc15b5e06eace08311d7 Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Tue, 4 Oct 2022 15:07:37 +0200 Subject: [PATCH 27/71] Clicking anywhere inside the TextField should focus it --- packages/design-system/package.json | 1 + .../src/components/text-field.stories.tsx | 28 +++++++++++++++++++ .../src/components/text-field.tsx | 14 +++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/design-system/package.json b/packages/design-system/package.json index cac926dd46..e42dd3a483 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -63,6 +63,7 @@ "@radix-ui/react-tooltip": "^1.0.0", "@radix-ui/react-use-layout-effect": "^1.0.0", "@react-aria/interactions": "^3.11.0", + "@react-aria/utils": "^3.13.3", "@stitches/react": "^1.2.8", "@webstudio-is/icons": "*", "react-hot-toast": "^2.3.0" diff --git a/packages/design-system/src/components/text-field.stories.tsx b/packages/design-system/src/components/text-field.stories.tsx index 055ce3e114..28b2bef61e 100644 --- a/packages/design-system/src/components/text-field.stories.tsx +++ b/packages/design-system/src/components/text-field.stories.tsx @@ -214,3 +214,31 @@ FocusEvents.play = async ({ args, canvasElement }) => { await waitFor(() => expect(args.onFocus).toHaveBeenCalledTimes(1)); await waitFor(() => expect(args.onBlur).toHaveBeenCalledTimes(1)); }; + +export const ClickCapture: ComponentStory = (args) => { + return ( + + + + } + suffix={ + + + + } + {...args} + /> + ); +}; +ClickCapture.play = async ({ canvasElement }) => { + const canvas = within(canvasElement); + const input = canvas.getByPlaceholderText("Click on the icon to focus input"); + await userEvent.click(canvas.getByTitle("icon")); + await waitFor(() => expect(input).toHaveFocus()); + await userEvent.click(canvas.getByRole("button")); + await waitFor(() => expect(input).toHaveFocus()); +}; diff --git a/packages/design-system/src/components/text-field.tsx b/packages/design-system/src/components/text-field.tsx index 68a1078cc6..c6e79b1ae8 100644 --- a/packages/design-system/src/components/text-field.tsx +++ b/packages/design-system/src/components/text-field.tsx @@ -1,3 +1,4 @@ +import { mergeRefs } from "@react-aria/utils"; import React from "react"; import { useFocusWithin } from "@react-aria/interactions"; import { styled } from "../stitches.config"; @@ -171,6 +172,12 @@ export const TextField = React.forwardRef( ...textFieldProps } = props; + const internalInputRef = React.useRef(null); + + const focusInnerInput = React.useCallback(() => { + internalInputRef.current?.focus(); + }, [internalInputRef]); + const { focusWithinProps } = useFocusWithin({ isDisabled: disabled, onFocusWithin: (event) => { @@ -192,9 +199,14 @@ export const TextField = React.forwardRef( variant={variant} css={css} {...focusWithinProps} + onClickCapture={focusInnerInput} > {/* We want input to be the first element in DOM so it receives the focus first */} - + {prefix && ( Date: Tue, 4 Oct 2022 19:03:55 +0400 Subject: [PATCH 28/71] fix a bug where user can't D&D a link on canvas --- .../src/components/primitives/dnd/use-drag.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/design-system/src/components/primitives/dnd/use-drag.ts b/packages/design-system/src/components/primitives/dnd/use-drag.ts index e544e23e07..4821ba0d2f 100644 --- a/packages/design-system/src/components/primitives/dnd/use-drag.ts +++ b/packages/design-system/src/components/primitives/dnd/use-drag.ts @@ -66,6 +66,11 @@ export const useDrag = ( } }; + const handleDragStart = (event: Event) => { + // Prevent default drag behavior, for example dragging a link. + event.preventDefault(); + }; + const handlePointerDown = (event: PointerEvent) => { if (event.button !== 0 || state.current.status !== "idle") { return; @@ -89,6 +94,7 @@ export const useDrag = ( window.addEventListener("pointermove", handlePointerMove); window.addEventListener("pointerup", handlePointerUp); window.addEventListener("pointercancel", handlePointerCancel); + window.addEventListener("dragstart", handleDragStart); }; const handlePointerMove = (event: PointerEvent) => { @@ -154,6 +160,7 @@ export const useDrag = ( window.removeEventListener("pointermove", handlePointerMove); window.removeEventListener("pointerup", handlePointerUp); window.removeEventListener("pointercancel", handlePointerCancel); + window.removeEventListener("dragstart", handleDragStart); }; const handlePointerUp = (event: PointerEvent) => { From ae9084cafa9524f19d395d9e8bd23e0dc5da371a Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Tue, 4 Oct 2022 19:05:18 +0400 Subject: [PATCH 29/71] minor --- .../design-system/src/components/primitives/dnd/use-drag.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/design-system/src/components/primitives/dnd/use-drag.ts b/packages/design-system/src/components/primitives/dnd/use-drag.ts index 4821ba0d2f..0d1d1190cf 100644 --- a/packages/design-system/src/components/primitives/dnd/use-drag.ts +++ b/packages/design-system/src/components/primitives/dnd/use-drag.ts @@ -67,7 +67,7 @@ export const useDrag = ( }; const handleDragStart = (event: Event) => { - // Prevent default drag behavior, for example dragging a link. + // Prevent default drag behavior. For example, dragging a link. event.preventDefault(); }; From b96e1500c607640f0defee53bdbe56017060fdf4 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Tue, 4 Oct 2022 18:46:34 +0200 Subject: [PATCH 30/71] default props and image placeholder --- .../react-sdk/src/components/image.props.json | 18 +++++--- packages/react-sdk/src/components/image.tsx | 42 +++++++++++++++++-- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/packages/react-sdk/src/components/image.props.json b/packages/react-sdk/src/components/image.props.json index f9ad6eb46a..12e6262e69 100644 --- a/packages/react-sdk/src/components/image.props.json +++ b/packages/react-sdk/src/components/image.props.json @@ -66,7 +66,9 @@ } }, "alt": { - "defaultValue": null, + "defaultValue": { + "value": "" + }, "description": "", "name": "alt", "parent": { @@ -158,7 +160,9 @@ "options": ["async", "auto", "sync"] }, "height": { - "defaultValue": null, + "defaultValue": { + "value": "auto" + }, "description": "", "name": "height", "parent": { @@ -180,7 +184,9 @@ } }, "loading": { - "defaultValue": null, + "defaultValue": { + "value": "lazy" + }, "description": "", "name": "loading", "parent": { @@ -298,7 +304,7 @@ }, "src": { "defaultValue": { - "value": "placeholder.webp" + "value": "" }, "description": "", "name": "src", @@ -365,7 +371,9 @@ } }, "width": { - "defaultValue": null, + "defaultValue": { + "value": "auto" + }, "description": "", "name": "width", "parent": { diff --git a/packages/react-sdk/src/components/image.tsx b/packages/react-sdk/src/components/image.tsx index dfe590ba20..b4011ecb86 100644 --- a/packages/react-sdk/src/components/image.tsx +++ b/packages/react-sdk/src/components/image.tsx @@ -2,13 +2,49 @@ import React, { forwardRef, type ElementRef, type ComponentProps } from "react"; const defaultTag = "img"; -type ButtonProps = ComponentProps; +type ImageProps = ComponentProps; -export const Image = forwardRef, ButtonProps>( +export const Image = forwardRef, ImageProps>( (props, ref) => ); Image.defaultProps = { - src: "placeholder.webp", // Match the platform default + src: "", + loading: "lazy", + width: "auto", + height: "auto", + alt: "", }; + Image.displayName = "Image"; + +const imagePlaceholderSvg = ` + + + + +`; + +// Defining src here prevents analyzer from putting the image into the UI. +// @todo it would be better to have the image hosted like a normal image, +// the problem is inside sdk bundler doesn't know how to do that. +Image.defaultProps.src = `data:image/svg+xml;base64,${btoa( + imagePlaceholderSvg +)}`; From f7268d648200277a5032efc4658bb45208a817e5 Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Wed, 5 Oct 2022 13:42:25 +0200 Subject: [PATCH 31/71] Update Combobox --- .../src/components/combobox.stories.tsx | 132 ++++--- .../design-system/src/components/combobox.tsx | 347 ++++++++---------- .../design-system/src/components/menu.tsx | 4 +- 3 files changed, 244 insertions(+), 239 deletions(-) diff --git a/packages/design-system/src/components/combobox.stories.tsx b/packages/design-system/src/components/combobox.stories.tsx index f4eccfd4f3..5c1abba546 100644 --- a/packages/design-system/src/components/combobox.stories.tsx +++ b/packages/design-system/src/components/combobox.stories.tsx @@ -1,7 +1,10 @@ -import { action } from "@storybook/addon-actions"; import { ComponentStory } from "@storybook/react"; -import React from "react"; -import { Combobox, ComboboxTextField } from "./combobox"; +import { MagnifyingGlassIcon } from "@webstudio-is/icons"; +import { useCombobox as useDownshiftCombobox } from "downshift"; +import React, { useCallback } from "react"; +import { TextField } from "./text-field"; +import { Combobox, ComboboxListboxItem, useCombobox } from "./combobox"; +import { Flex } from "./flex"; export default { component: Combobox, @@ -13,59 +16,90 @@ export const Simple: ComponentStory = () => { { label: "Banana" }, { label: "Orange" }, ] as const; - const [value, setValue] = React.useState(); + const [value, setValue] = React.useState(null); return ( - ); -}; - -export const CustomInput: ComponentStory = () => { - const items = [ - { label: "Apple", value: "apple", disabled: true }, - { label: "Banana", value: "banana" }, - { label: "Orange", value: "orange" }, - ]; - const [value, setValue] = React.useState( - items[0] - ); - - return ( - { - action("Highlight")(item); + onItemSelect={(value) => { + // If the value is cleared, we revert to previous state + if (value !== null) { + setValue(value); + } }} - renderTextField={({ inputProps, toggleProps }) => ( - { - inputProps.onKeyDown?.(event); - switch (event.key) { - case "Enter": { - action("Enter")(inputProps.value); - break; - } - } - }, - onBlur: (event) => { - inputProps.onBlur?.(event); - action("Blur")(inputProps.value); - }, - }} - /> - )} + itemToString={(item) => item?.label ?? ""} /> ); }; + +export const Complex: ComponentStory = () => { + const [value, setValue] = React.useState(null); + + const stateReducer = useCallback((state, actionAndChanges) => { + const { type, changes } = actionAndChanges; + switch (type) { + // on item selection. + case useDownshiftCombobox.stateChangeTypes.ItemClick: + case useDownshiftCombobox.stateChangeTypes.InputKeyDownEnter: + case useDownshiftCombobox.stateChangeTypes.InputBlur: + case useDownshiftCombobox.stateChangeTypes + .ControlledPropUpdatedSelectedItem: + return { + ...changes, + // if we have a selected item. + ...(changes.selectedItem && { + // we will set the input value to "" (empty string). + inputValue: "", + }), + }; + + // Remove "reset" action + case useDownshiftCombobox.stateChangeTypes.InputKeyDownEscape: { + return { + ...state, + }; + } + + default: + return changes; // otherwise business as usual. + } + }, []); + + const { items, getComboboxProps, getMenuProps, getItemProps, getInputProps } = + useCombobox({ + items: ["Apple", "Banana", "Orange"], + value, + itemToString: (item) => item ?? "", + stateReducer, + onItemSelect: (value) => { + setValue(value); + }, + }); + + return ( + + } + {...getInputProps({})} + /> +
+ Choose an item + + {items.map((item, index) => { + return ( + + {item} + + ); + })} + +
+
+ ); +}; diff --git a/packages/design-system/src/components/combobox.tsx b/packages/design-system/src/components/combobox.tsx index 2e0dc1c1f3..ae936d67d5 100644 --- a/packages/design-system/src/components/combobox.tsx +++ b/packages/design-system/src/components/combobox.tsx @@ -4,10 +4,16 @@ import { useCallback, type ComponentProps, type ForwardRefRenderFunction, + useEffect, + useRef, } from "react"; import { CheckIcon, ChevronDownIcon } from "@webstudio-is/icons"; import { Popper, PopperContent, PopperAnchor } from "@radix-ui/react-popper"; -import { useCombobox, type UseComboboxGetItemPropsOptions } from "downshift"; +import { + DownshiftState, + UseComboboxStateChangeOptions, + useCombobox as useDownshiftCombobox, +} from "downshift"; import { matchSorter } from "match-sorter"; import { styled } from "../stitches.config"; import { IconButton } from "./icon-button"; @@ -17,37 +23,6 @@ import { TextField } from "./text-field"; import { Box } from "./box"; import { Grid } from "./grid"; -type Label = string; - -type BaseItem = { label: Label; disabled?: boolean } | Label; - -type ComboboxTextFieldProps = { - inputProps: ComponentProps; - toggleProps: ComponentProps; - highlightedItem?: Item; -}; - -const ComboboxTextFieldBase: ForwardRefRenderFunction< - HTMLDivElement, - ComboboxTextFieldProps -> = ({ inputProps, toggleProps }, ref) => { - return ( - - - - } - {...inputProps} - /> - ); -}; - -export const ComboboxTextField = forwardRef(ComboboxTextFieldBase); - -ComboboxTextField.displayName = "ComboboxTextField"; - const Listbox = styled("ul", panelStyles, { padding: 0, margin: 0, @@ -62,145 +37,75 @@ const ListboxItem = styled("li", itemCss, { margin: 0, }); -type ListProps = { - containerProps: ComponentProps; - items: ReadonlyArray; - getItemProps: ( - options: UseComboboxGetItemPropsOptions - ) => ComponentProps; - highlightedIndex: number; - selectedItem: Item | null; - itemToString: (item: Item | null) => string; -}; - -export const List = ({ - containerProps, - items, - getItemProps, - highlightedIndex, - selectedItem, - itemToString, -}: ListProps) => { +export const ListboxItemBase: ForwardRefRenderFunction< + HTMLLIElement, + ComponentProps & { + disabled?: boolean; + selected?: boolean; + highlighted?: boolean; + } +> = (props, ref) => { + const { disabled, selected, highlighted, children, ...rest } = props; return ( - - {items.map((item, index) => { - const itemProps: Record = getItemProps({ - item, - index, - key: index, - ...(typeof item === "object" && item.disabled - ? { "data-disabled": true, disabled: true } - : {}), - ...(highlightedIndex === index ? { "data-found": true } : {}), - }); - - return ( - // eslint-disable-next-line react/jsx-key - - - {selectedItem === item && } - {itemToString(item)} - - - ); - })} - + + + {selected && } + {children} + + ); }; +export const ComboboxListboxItem = forwardRef(ListboxItemBase); + export const ComboboxPopperContent = PopperContent; -type ComboboxProps = { - name: string; - label?: string; - placeholder?: string; +type useComboboxProps = { items: ReadonlyArray; - value?: Item; - onItemSelect?: (value?: Item) => void; - onItemHighlight?: (value?: Item) => void; - itemToString?: (item?: Item | null) => string; - renderTextField?: ( - props: ComponentProps - ) => JSX.Element; - renderList?: (props: ListProps) => JSX.Element; - renderPopperContent?: ( - props: ComponentProps - ) => JSX.Element; + itemToString: (item: Item | null) => string; + value: Item | null; // This is to prevent: "downshift: A component has changed the uncontrolled prop "selectedItem" to be controlled." + onItemSelect?: (value: Item | null) => void; + onItemHighlight?: (value: Item | null) => void; + stateReducer?: ( + state: DownshiftState, + changes: UseComboboxStateChangeOptions + ) => Partial>; }; -export const Combobox = ({ +// eslint-disable-next-line func-style +export function useCombobox({ items, value, - name, - placeholder, - itemToString = (item) => - item != null && "label" in item ? item.label : item ?? "", + itemToString, onItemSelect, onItemHighlight, - renderTextField = (props) => , - // IMPORTANT! Without Item passed to list typescript is 10x slower! - renderList = (props) => {...props} />, - renderPopperContent = (props) => , -}: ComboboxProps) => { - const [foundItems, setFoundItems] = useState(items); - const stateReducer = useCallback((state, actionAndChanges) => { - const { type, changes } = actionAndChanges; - switch (type) { - // on item selection. - case useCombobox.stateChangeTypes.ItemClick: - case useCombobox.stateChangeTypes.InputKeyDownEnter: - case useCombobox.stateChangeTypes.InputBlur: - case useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem: - return { - ...changes, - // if we had an item selected. - ...(changes.selectedItem && { - // we will set the input value to be empty since we display it using prefix - inputValue: "", - }), - }; - // Clear input value on escape - case useCombobox.stateChangeTypes.InputKeyDownEscape: - return { - ...changes, - inputValue: "", - selectedItem: null, - }; - // Reset selectedItem when there is selection and we type something - case useCombobox.stateChangeTypes.InputChange: - return { - ...changes, - selectedItem: null, - }; - default: - return changes; // otherwise business as usual. - } - }, []); - const { - isOpen, - getToggleButtonProps, - // getLabelProps, - getMenuProps, - getInputProps, - getComboboxProps, - highlightedIndex, - getItemProps, - selectedItem, - } = useCombobox({ - items: foundItems as Item[], - selectedItem: value ?? null, // Avoid downshift warning about switching controlled mode + stateReducer, +}: useComboboxProps) { + const [filteredItems, setFilteredItems] = useState(items); + const cachedItems = useRef(items); + + const downshiftProps = useDownshiftCombobox({ + items: filteredItems as Item[], + selectedItem: value, // Avoid downshift warning about switching controlled mode stateReducer, itemToString, onInputValueChange({ inputValue }) { - const options = - typeof items[0] === "object" && "label" in items[0] - ? { keys: ["label"] } - : undefined; - const foundItems = matchSorter(items, inputValue ?? "", options); - setFoundItems(foundItems); + const foundItems: ReadonlyArray = matchSorter( + items, + inputValue ?? "", + { + keys: [(item) => itemToString(item)], + } + ); + setFilteredItems(foundItems); }, onSelectedItemChange({ selectedItem }) { - onItemSelect?.(selectedItem ?? undefined); + onItemSelect?.(selectedItem ?? null); }, onHighlightedIndexChange({ highlightedIndex }) { if (highlightedIndex !== undefined) { @@ -209,48 +114,114 @@ export const Combobox = ({ }, }); - const inputProps: Record = getInputProps({ - name, - placeholder: selectedItem ? "" : placeholder, // Placeholder should not be visible when we have selected item - prefix: itemToString?.(selectedItem ?? undefined), - onKeyDown: (event) => { - // When we press Backspace and the input is empty, - // we should to clear the selection - // @todo: it would be much better to handle this in reducer but downshift doesn't handle this event - if (event.key === "Backspace" && selectedItem !== null) { - onItemSelect?.(undefined); - } - }, - }); - const toggleProps: Record = getToggleButtonProps(); - const comboboxProps: Record = getComboboxProps(); - const menuProps: Record = getMenuProps(); - const highlightedItem = foundItems[highlightedIndex]; + const { isOpen, getItemProps, highlightedIndex, selectedItem } = + downshiftProps; + useEffect(() => { + cachedItems.current = items; + }, [items]); + + useEffect(() => { + if (!isOpen) { + setFilteredItems(cachedItems.current); + } + }, [isOpen]); + + const enhancedGetItemProps = useCallback( + (options) => { + return getItemProps({ + highlighted: highlightedIndex === options.index, + // We need to either deep compare objects here or use itemToString to get primitive types + selected: itemToString(selectedItem) === itemToString(options.item), + key: options.id, + ...options, + }); + }, + [getItemProps, highlightedIndex, itemToString, selectedItem] + ); + + return { + ...downshiftProps, + items: filteredItems, // Return filtered items + getItemProps: enhancedGetItemProps, + }; +} + +type ComboboxProps = useComboboxProps & { + name: string; + label?: string; + placeholder?: string; +}; + +// eslint-disable-next-line func-style +export function Combobox({ + items, + value = null, + name, + placeholder, + itemToString, + onItemSelect, + onItemHighlight, +}: ComboboxProps) { + const stateReducer = useCallback((state, actionAndChanges) => { + const { type, changes } = actionAndChanges; + switch (type) { + default: + return changes; // otherwise business as usual. + } + }, []); + + const { + items: foundItems, + getInputProps, + getComboboxProps, + getToggleButtonProps, + getMenuProps, + getItemProps, + isOpen, + } = useCombobox({ + items, + value, + itemToString, + onItemSelect, + onItemHighlight, + stateReducer, + }); return ( - + - {renderTextField({ - inputProps, - toggleProps, - highlightedItem, - })} + + + + } + /> - {renderPopperContent({ - style: { zIndex: 1 }, - children: renderList({ - containerProps: menuProps, - items: isOpen ? foundItems : [], - getItemProps, - highlightedIndex, - selectedItem, - itemToString, - }), - })} + + + {isOpen && + foundItems.map((item, index) => { + return ( + // eslint-disable-next-line react/jsx-key + + {itemToString(item)} + + ); + })} + + ); -}; +} Combobox.displayName = "Combobox"; diff --git a/packages/design-system/src/components/menu.tsx b/packages/design-system/src/components/menu.tsx index 903c3c77b4..cd0a2eea91 100644 --- a/packages/design-system/src/components/menu.tsx +++ b/packages/design-system/src/components/menu.tsx @@ -24,12 +24,12 @@ export const baseItemCss = css({ export const itemCss = css(baseItemCss, { position: "relative", color: "$hiContrast", - "&:focus, &[data-found]": { + "&:focus, &[data-found], &[aria-selected=true]": { outline: "none", backgroundColor: "$blue9", color: "white", }, - "&[data-disabled]": { + "&[data-disabled], &[aria-disabled]": { color: "$slate9", }, }); From 8ccad888f94f870805c94c91ae3f5573a4d49b63 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Wed, 5 Oct 2022 10:01:39 +0400 Subject: [PATCH 32/71] empty pages panel behing feature flag --- .../features/sidebar-left/panels/index.ts | 3 ++- .../sidebar-left/panels/pages/index.tsx | 22 +++++++++++++++++++ .../features/sidebar-left/sidebar-left.tsx | 2 ++ .../app/shared/feature-flags/flags.ts | 1 + 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx diff --git a/apps/designer/app/designer/features/sidebar-left/panels/index.ts b/apps/designer/app/designer/features/sidebar-left/panels/index.ts index db4a8af5dd..72410a298c 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/index.ts +++ b/apps/designer/app/designer/features/sidebar-left/panels/index.ts @@ -1,5 +1,6 @@ import * as components from "./components"; import * as navigator from "./navigator"; import * as assets from "./assets"; +import * as pages from "./pages"; -export const panels = { components, navigator, assets } as const; +export const panels = { components, navigator, assets, pages } as const; diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx new file mode 100644 index 0000000000..241e277922 --- /dev/null +++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx @@ -0,0 +1,22 @@ +// @todo: consider moving code from index.tsx to separate files similar to other panels + +import { type Publish } from "~/shared/pubsub"; +import { StackIcon } from "@webstudio-is/icons"; +import type { TabName } from "../../types"; +import { Header } from "../../lib/header"; + +type TabContentProps = { + onSetActiveTab: (tabName: TabName) => void; + publish: Publish; +}; + +export const TabContent = ({ onSetActiveTab }: TabContentProps) => { + return ( + <> +
onSetActiveTab("none")} /> + + ); +}; + +// @todo: proper icon +export const icon = ; diff --git a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx index 03f82bd628..201355ad6b 100644 --- a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx +++ b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx @@ -38,6 +38,8 @@ export const SidebarLeft = ({ publish }: SidebarLeftProps) => { switch (panel) { case "assets": return isFeatureEnabled("assets"); + case "pages": + return isFeatureEnabled("pages"); case "navigator": return clientSettings.navigatorLayout === "docked"; } diff --git a/apps/designer/app/shared/feature-flags/flags.ts b/apps/designer/app/shared/feature-flags/flags.ts index 56e8f1d294..32814f8cb7 100644 --- a/apps/designer/app/shared/feature-flags/flags.ts +++ b/apps/designer/app/shared/feature-flags/flags.ts @@ -1,2 +1,3 @@ export const assets = false; export const example = false; +export const pages = false; From 652775ca8c6b71dadac527986b5b654ddafdb6a2 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Wed, 5 Oct 2022 12:22:04 +0400 Subject: [PATCH 33/71] bring pages data to the panel --- apps/designer/app/designer/designer.tsx | 30 ++++++++++++++++--- .../sidebar-left/panels/pages/index.tsx | 4 +++ .../app/designer/shared/nano-states/index.ts | 4 +++ .../app/routes/designer/$projectId.tsx | 21 ++++++------- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 92ca3f0a41..43472b4a07 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -1,6 +1,10 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { type Publish, usePublish, useSubscribe } from "~/shared/pubsub"; -import type { Page, Project } from "@webstudio-is/project"; +import { + type Pages, + type Project, + utils as projectUtils, +} from "@webstudio-is/project"; import type { Config } from "~/config"; import { Box, type CSS, Flex, Grid } from "@webstudio-is/design-system"; import interStyles from "~/shared/font-faces/inter.css"; @@ -9,6 +13,7 @@ import { Inspector } from "./features/inspector"; import { useAssets, useHoveredInstanceData, + usePages, useSelectedInstanceData, useSyncStatus, } from "./shared/nano-states"; @@ -73,6 +78,13 @@ const useSetAssets = (assets?: Array) => { }, [assets, setAssets]); }; +const useSetPages = (pages: Pages) => { + const [, setPages] = usePages(); + useEffect(() => { + setPages(pages); + }, [pages, setPages]); +}; + const useNavigatorLayout = () => { // We need to render the detached state only once the setting was actually loaded from local storage. // Otherwise we may show the detached state because its the default and then hide it immediately. @@ -241,16 +253,18 @@ const NavigatorPanel = ({ publish, isPreviewMode }: NavigatorPanelProps) => { type DesignerProps = { config: Config; project: Project; - page: Page; + pages: Pages; + pageId: string; }; -export const Designer = ({ config, project, page }: DesignerProps) => { +export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { useSubscribeSyncStatus(); useSubscribeRootInstance(); useSubscribeSelectedInstanceData(); useSubscribeHoveredInstanceData(); useSubscribeBreakpoints(); useSetAssets(project.assets); + useSetPages(pages); const [publish, publishRef] = usePublish(); const [isPreviewMode] = useIsPreviewMode(); usePublishShortcuts(publish); @@ -268,6 +282,14 @@ export const Designer = ({ config, project, page }: DesignerProps) => { [publishRef, onRefReadCanvasWidth, onRefReadCanvas] ); + const page = useMemo(() => { + const page = projectUtils.pages.findById(pages, pageId); + if (page === undefined) { + throw new Error(`Page with id ${pageId} not found`); + } + return page; + }, [pages, pageId]); + return ( void; @@ -11,9 +12,12 @@ type TabContentProps = { }; export const TabContent = ({ onSetActiveTab }: TabContentProps) => { + const [pages] = usePages(); + return ( <>
onSetActiveTab("none")} /> +
{JSON.stringify(pages)}
); }; diff --git a/apps/designer/app/designer/shared/nano-states/index.ts b/apps/designer/app/designer/shared/nano-states/index.ts index 667f42f760..dea1b82554 100644 --- a/apps/designer/app/designer/shared/nano-states/index.ts +++ b/apps/designer/app/designer/shared/nano-states/index.ts @@ -6,6 +6,7 @@ import { } from "~/shared/canvas-components"; import { type SyncStatus } from "~/shared/sync"; import { Asset } from "@webstudio-is/asset-uploader"; +import { type Pages } from "@webstudio-is/project"; const selectedInstanceDataContainer = createValueContainer< SelectedInstanceData | undefined @@ -49,3 +50,6 @@ export const useSelectionRect = () => useValue(selectionRectContainer); const assetsContainer = createValueContainer>([]); export const useAssets = () => useValue(assetsContainer); + +const pagesContainer = createValueContainer(); +export const usePages = () => useValue(pagesContainer); diff --git a/apps/designer/app/routes/designer/$projectId.tsx b/apps/designer/app/routes/designer/$projectId.tsx index f0dd31fae9..1e2762709e 100644 --- a/apps/designer/app/routes/designer/$projectId.tsx +++ b/apps/designer/app/routes/designer/$projectId.tsx @@ -2,7 +2,7 @@ import { useLoaderData } from "@remix-run/react"; import { LoaderFunction } from "@remix-run/node"; import { Designer, links } from "~/designer"; import { db } from "@webstudio-is/project/index.server"; -import { type Project, type Page, utils } from "@webstudio-is/project"; +import { type Project, type Pages } from "@webstudio-is/project"; import config from "~/config"; import { ErrorMessage } from "~/shared/error"; import { action, useAction } from "./_assets"; @@ -26,16 +26,12 @@ export const loader: LoaderFunction = async ({ const devBuild = await db.build.loadByProjectId(project.id, "dev"); - const page = - params.pageId === undefined - ? devBuild.pages.homePage - : utils.pages.findById(devBuild.pages, params.pageId); - - if (page === undefined) { - throw new Error(`Page "${params.pageId}" not found`); - } - - return { config, project, page }; + return { + config, + project, + pages: devBuild.pages, + pageId: params.pageId || devBuild.pages.homePage.id, + }; } catch (error) { sentryException({ error }); return { errors: error instanceof Error ? error.message : String(error) }; @@ -45,7 +41,8 @@ export const loader: LoaderFunction = async ({ type Data = { config: typeof config; project: Project; - page: Page; + pages: Pages; + pageId: string; }; type Error = { From 851093891f53551a31b1816fcd58094a6582c245 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Wed, 5 Oct 2022 20:05:26 +0400 Subject: [PATCH 34/71] render pages list --- apps/designer/app/designer/designer.tsx | 8 +- .../sidebar-left/panels/pages/index.tsx | 110 +++++++++++++++++- .../app/designer/shared/nano-states/index.ts | 4 +- .../app/routes/designer/$projectId.tsx | 9 ++ .../src/components/tree/tree-node.tsx | 73 ++++++------ 5 files changed, 162 insertions(+), 42 deletions(-) diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 43472b4a07..c32d6c2e60 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -78,11 +78,11 @@ const useSetAssets = (assets?: Array) => { }, [assets, setAssets]); }; -const useSetPages = (pages: Pages) => { +const useSetPages = (pages: Pages, currentPageId: string) => { const [, setPages] = usePages(); useEffect(() => { - setPages(pages); - }, [pages, setPages]); + setPages({ pages, currentPageId }); + }, [pages, setPages, currentPageId]); }; const useNavigatorLayout = () => { @@ -264,7 +264,7 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { useSubscribeHoveredInstanceData(); useSubscribeBreakpoints(); useSetAssets(project.assets); - useSetPages(pages); + useSetPages(pages, pageId); const [publish, publishRef] = usePublish(); const [isPreviewMode] = useIsPreviewMode(); usePublishShortcuts(publish); diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx index 3462ec38d8..dbe3b6a168 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx @@ -1,26 +1,130 @@ // @todo: consider moving code from index.tsx to separate files similar to other panels +import { TreeNodeLabel, TreeNode } from "@webstudio-is/design-system"; import { type Publish } from "~/shared/pubsub"; import { StackIcon } from "@webstudio-is/icons"; import type { TabName } from "../../types"; import { Header } from "../../lib/header"; import { usePages } from "~/designer/shared/nano-states"; +import { type Page, type Pages } from "@webstudio-is/project"; +import { useMemo } from "react"; +import { useNavigate } from "react-router-dom"; type TabContentProps = { onSetActiveTab: (tabName: TabName) => void; publish: Publish; }; -export const TabContent = ({ onSetActiveTab }: TabContentProps) => { - const [pages] = usePages(); +type PagesTreeNode = + | { + // currently used only for root node + type: "folder"; + id: string; + children: PagesTreeNode[]; + } + | { + type: "page"; + id: string; + data: Page; + }; + +const toTreeData = (pages: Pages): PagesTreeNode => { + return { + type: "folder", + id: "root", + children: [pages.homePage, ...pages.pages].map((data) => ({ + type: "page", + id: data.id, + data, + })), + }; +}; + +const staticTreeProps = { + getItemChildren(node: PagesTreeNode) { + if (node.type === "folder") { + return node.children; + } + return []; + }, + getIsExpanded(_node: PagesTreeNode) { + return true; + }, + renderItem(props: { data: PagesTreeNode; isSelected: boolean }) { + if (props.data.type === "folder") { + return null; + } + + return ( + + ); + }, +}; + +const TabBody = ({ + pages, + currentPageId, +}: { + pages: Pages; + currentPageId: string; +}) => { + const pagesTree = useMemo(() => pages && toTreeData(pages), [pages]); + + const navigate = useNavigate(); + const handleSelect = (pageId: string) => { + navigate(`/todo/${pageId}`); + }; + return ( + <> + + + ); +}; + +export const TabContent = ({ onSetActiveTab }: TabContentProps) => { + const [pagesData] = usePages(); return ( <>
onSetActiveTab("none")} /> -
{JSON.stringify(pages)}
+ {pagesData && ( + + )} ); }; // @todo: proper icon export const icon = ; + +// export type TreeNodeProps = { +// itemData: Data; +// getItemChildren: (item: Data) => Data[]; +// renderItem: (props: { data: Data; isSelected: boolean }) => React.ReactNode; + +// getIsExpanded: (item: Data) => boolean; +// setIsExpanded?: (item: Data, expanded: boolean) => void; +// onExpandTransitionEnd?: () => void; + +// selectedItemId?: string; +// parentIsSelected?: boolean; +// onSelect?: (itemId: string) => void; +// onMouseEnter?: (item: Data) => void; +// onMouseLeave?: (item: Data) => void; + +// level?: number; +// animate?: boolean; +// forceHoverStateAtItem?: string; +// }; diff --git a/apps/designer/app/designer/shared/nano-states/index.ts b/apps/designer/app/designer/shared/nano-states/index.ts index dea1b82554..b4aefb35ec 100644 --- a/apps/designer/app/designer/shared/nano-states/index.ts +++ b/apps/designer/app/designer/shared/nano-states/index.ts @@ -51,5 +51,7 @@ export const useSelectionRect = () => useValue(selectionRectContainer); const assetsContainer = createValueContainer>([]); export const useAssets = () => useValue(assetsContainer); -const pagesContainer = createValueContainer(); +const pagesContainer = createValueContainer< + { pages: Pages; currentPageId: string } | undefined +>(); export const usePages = () => useValue(pagesContainer); diff --git a/apps/designer/app/routes/designer/$projectId.tsx b/apps/designer/app/routes/designer/$projectId.tsx index 1e2762709e..c829cc7351 100644 --- a/apps/designer/app/routes/designer/$projectId.tsx +++ b/apps/designer/app/routes/designer/$projectId.tsx @@ -13,6 +13,8 @@ export { action, links }; export const loader: LoaderFunction = async ({ params, }): Promise => { + console.log("params", params); + try { if (params.projectId === undefined) { throw new Error("Project id undefined"); @@ -52,6 +54,13 @@ type Error = { export const DesignerRoute = () => { const data = useLoaderData(); useAction(); + + console.log("data", data); + + if (data === undefined) { + return ; + } + if ("errors" in data) { return ; } diff --git a/packages/design-system/src/components/tree/tree-node.tsx b/packages/design-system/src/components/tree/tree-node.tsx index 8c24045244..5dcaba5de2 100644 --- a/packages/design-system/src/components/tree/tree-node.tsx +++ b/packages/design-system/src/components/tree/tree-node.tsx @@ -149,12 +149,15 @@ export type TreeNodeProps = { level?: number; animate?: boolean; forceHoverStateAtItem?: string; + + hideRoot?: boolean; }; export const TreeNode = ({ itemData, level = 0, parentIsSelected, + hideRoot, ...commonProps }: TreeNodeProps) => { const { @@ -216,42 +219,44 @@ export const TreeNode = ({ onOpenChange={(isOpen) => setIsExpanded?.(itemData, isOpen)} data-drop-target-id={itemData.id} > - onMouseEnter?.(itemData)} - onMouseLeave={() => onMouseLeave?.(itemData)} - isSelected={isSelected} - parentIsSelected={parentIsSelected} - enableHoverState={forceHoverStateAtItem === undefined} - forceHoverState={forceHoverStateAtItem === itemData.id} - > - {/* We want the main ItemButton to take the entire space, - * and then position the collapsible trigger on top of it using absolute positionning. - * When user clicks anywhere on a tree item, they should either hit the main button or the trigger. - */} - - onMouseEnter?.(itemData)} + onMouseLeave={() => onMouseLeave?.(itemData)} + isSelected={isSelected} + parentIsSelected={parentIsSelected} + enableHoverState={forceHoverStateAtItem === undefined} + forceHoverState={forceHoverStateAtItem === itemData.id} > - - {isAlwaysExpanded === false && } - {renderItem({ data: itemData, isSelected })} - + {/* We want the main ItemButton to take the entire space, + * and then position the collapsible trigger on top of it using absolute positionning. + * When user clicks anywhere on a tree item, they should either hit the main button or the trigger. + */} - {shouldRenderExpandButton && ( - - {isExpanded ? : } - - )} - + + {isAlwaysExpanded === false && } + {renderItem({ data: itemData, isSelected })} + + + {shouldRenderExpandButton && ( + + {isExpanded ? : } + + )} + + )} {isExpandable && ( ({ From eeef3ec56e94489c038b385e028114b4fb378cd9 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 10:59:09 +0400 Subject: [PATCH 35/71] do not re-export routes. use search param instead re-exporting confuses Remix in a way that it doesn't load data on page transition in the browser --- apps/designer/app/designer/designer.tsx | 2 +- apps/designer/app/designer/features/topbar/share.tsx | 2 +- apps/designer/app/routes/canvas/$projectId.$pageId.tsx | 2 -- apps/designer/app/routes/canvas/$projectId.tsx | 10 ++++++++-- .../app/routes/designer/$projectId.$pageId.tsx | 2 -- apps/designer/app/routes/designer/$projectId.tsx | 10 +++++----- .../designer/app/routes/preview/$projectId.$pageId.tsx | 2 -- apps/designer/app/routes/preview/$projectId.tsx | 10 ++++++++-- 8 files changed, 23 insertions(+), 17 deletions(-) delete mode 100644 apps/designer/app/routes/canvas/$projectId.$pageId.tsx delete mode 100644 apps/designer/app/routes/designer/$projectId.$pageId.tsx delete mode 100644 apps/designer/app/routes/preview/$projectId.$pageId.tsx diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index c32d6c2e60..9a4f599a12 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -303,7 +303,7 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { { return null; } const url = new URL( - `${location.protocol}//${location.host}${path}/${projectId}/${pageId}` + `${location.protocol}//${location.host}${path}/${projectId}?pageId=${pageId}` ); return ( { return { title: page.title, ...page.meta }; }; -export const loader: LoaderFunction = async ({ params }): Promise => { +export const loader: LoaderFunction = async ({ + params, + request, +}): Promise => { try { if (params.projectId === undefined) { throw new Error("Missing projectId"); } - return loadCanvasData(params.projectId, params.pageId); + const url = new URL(request.url); + const pageIdParam = url.searchParams.get("pageId"); + + return loadCanvasData(params.projectId, pageIdParam || undefined); } catch (error) { sentryException({ error }); return { errors: error instanceof Error ? error.message : String(error) }; diff --git a/apps/designer/app/routes/designer/$projectId.$pageId.tsx b/apps/designer/app/routes/designer/$projectId.$pageId.tsx deleted file mode 100644 index 673acba501..0000000000 --- a/apps/designer/app/routes/designer/$projectId.$pageId.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./$projectId"; -export { default } from "./$projectId"; diff --git a/apps/designer/app/routes/designer/$projectId.tsx b/apps/designer/app/routes/designer/$projectId.tsx index c829cc7351..055447a81f 100644 --- a/apps/designer/app/routes/designer/$projectId.tsx +++ b/apps/designer/app/routes/designer/$projectId.tsx @@ -12,14 +12,16 @@ export { action, links }; export const loader: LoaderFunction = async ({ params, + request, }): Promise => { - console.log("params", params); - try { if (params.projectId === undefined) { throw new Error("Project id undefined"); } + const url = new URL(request.url); + const pageIdParam = url.searchParams.get("pageId"); + const project = await db.project.loadById(params.projectId); if (project === null) { @@ -32,7 +34,7 @@ export const loader: LoaderFunction = async ({ config, project, pages: devBuild.pages, - pageId: params.pageId || devBuild.pages.homePage.id, + pageId: pageIdParam || devBuild.pages.homePage.id, }; } catch (error) { sentryException({ error }); @@ -55,8 +57,6 @@ export const DesignerRoute = () => { const data = useLoaderData(); useAction(); - console.log("data", data); - if (data === undefined) { return ; } diff --git a/apps/designer/app/routes/preview/$projectId.$pageId.tsx b/apps/designer/app/routes/preview/$projectId.$pageId.tsx deleted file mode 100644 index 673acba501..0000000000 --- a/apps/designer/app/routes/preview/$projectId.$pageId.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./$projectId"; -export { default } from "./$projectId"; diff --git a/apps/designer/app/routes/preview/$projectId.tsx b/apps/designer/app/routes/preview/$projectId.tsx index 3299a6d2af..6c5166ab27 100644 --- a/apps/designer/app/routes/preview/$projectId.tsx +++ b/apps/designer/app/routes/preview/$projectId.tsx @@ -14,13 +14,19 @@ export const meta: MetaFunction = ({ data }: { data: Data }) => { return { title: page.title, ...page.meta }; }; -export const loader: LoaderFunction = async ({ params }): Promise => { +export const loader: LoaderFunction = async ({ + params, + request, +}): Promise => { try { if (params.projectId === undefined) { throw new Error("Missing projectId"); } - return loadCanvasData(params.projectId, params.pageId); + const url = new URL(request.url); + const pageIdParam = url.searchParams.get("pageId"); + + return loadCanvasData(params.projectId, pageIdParam || undefined); } catch (error) { sentryException({ error }); return { errors: error instanceof Error ? error.message : String(error) }; From 8263f757bf933efcc81cd374063c7b899a59cd52 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 11:25:04 +0400 Subject: [PATCH 36/71] make clicking on page work & refactor --- apps/designer/app/designer/designer.tsx | 17 ++++----- .../sidebar-left/panels/pages/index.tsx | 36 +++++++------------ .../features/sidebar-left/sidebar-left.tsx | 23 ++++++++++-- .../app/designer/shared/nano-states/index.ts | 6 ---- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 9a4f599a12..50c009c714 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -13,7 +13,6 @@ import { Inspector } from "./features/inspector"; import { useAssets, useHoveredInstanceData, - usePages, useSelectedInstanceData, useSyncStatus, } from "./shared/nano-states"; @@ -78,13 +77,6 @@ const useSetAssets = (assets?: Array) => { }, [assets, setAssets]); }; -const useSetPages = (pages: Pages, currentPageId: string) => { - const [, setPages] = usePages(); - useEffect(() => { - setPages({ pages, currentPageId }); - }, [pages, setPages, currentPageId]); -}; - const useNavigatorLayout = () => { // We need to render the detached state only once the setting was actually loaded from local storage. // Otherwise we may show the detached state because its the default and then hide it immediately. @@ -264,7 +256,6 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { useSubscribeHoveredInstanceData(); useSubscribeBreakpoints(); useSetAssets(project.assets); - useSetPages(pages, pageId); const [publish, publishRef] = usePublish(); const [isPreviewMode] = useIsPreviewMode(); usePublishShortcuts(publish); @@ -318,7 +309,13 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { - + void; publish: Publish; + project: Project; + pages: Pages; + currentPageId: string; + config: Config; }; type PagesTreeNode = @@ -64,22 +68,23 @@ const staticTreeProps = { }, }; -const TabBody = ({ +export const TabContent = ({ + onSetActiveTab, + project, pages, currentPageId, -}: { - pages: Pages; - currentPageId: string; -}) => { + config, +}: TabContentProps) => { const pagesTree = useMemo(() => pages && toTreeData(pages), [pages]); const navigate = useNavigate(); const handleSelect = (pageId: string) => { - navigate(`/todo/${pageId}`); + navigate(`${config.designerPath}/${project.id}?pageId=${pageId}`); }; return ( <> +
onSetActiveTab("none")} /> { - const [pagesData] = usePages(); - return ( - <> -
onSetActiveTab("none")} /> - {pagesData && ( - - )} - - ); -}; - // @todo: proper icon export const icon = ; diff --git a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx index 201355ad6b..e8e4208012 100644 --- a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx +++ b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx @@ -13,14 +13,26 @@ import { isFeatureEnabled } from "~/shared/feature-flags"; import { useClientSettings } from "~/designer/shared/client-settings"; import { PANEL_WIDTH } from "~/designer/shared/constants"; import { Flex } from "@webstudio-is/design-system"; +import { type Project, type Pages } from "@webstudio-is/project"; +import { Config } from "~/config"; const none = { TabContent: () => null }; type SidebarLeftProps = { publish: Publish; + project: Project; + pages: Pages; + currentPageId: string; + config: Config; }; -export const SidebarLeft = ({ publish }: SidebarLeftProps) => { +export const SidebarLeft = ({ + publish, + project, + pages, + currentPageId, + config, +}: SidebarLeftProps) => { const [dragAndDropState] = useDragAndDropState(); const [activeTab, setActiveTab] = useState("none"); const { TabContent } = activeTab === "none" ? none : panels[activeTab]; @@ -78,7 +90,14 @@ export const SidebarLeft = ({ publish }: SidebarLeftProps) => { overflow: "auto", }} > - + diff --git a/apps/designer/app/designer/shared/nano-states/index.ts b/apps/designer/app/designer/shared/nano-states/index.ts index b4aefb35ec..667f42f760 100644 --- a/apps/designer/app/designer/shared/nano-states/index.ts +++ b/apps/designer/app/designer/shared/nano-states/index.ts @@ -6,7 +6,6 @@ import { } from "~/shared/canvas-components"; import { type SyncStatus } from "~/shared/sync"; import { Asset } from "@webstudio-is/asset-uploader"; -import { type Pages } from "@webstudio-is/project"; const selectedInstanceDataContainer = createValueContainer< SelectedInstanceData | undefined @@ -50,8 +49,3 @@ export const useSelectionRect = () => useValue(selectionRectContainer); const assetsContainer = createValueContainer>([]); export const useAssets = () => useValue(assetsContainer); - -const pagesContainer = createValueContainer< - { pages: Pages; currentPageId: string } | undefined ->(); -export const usePages = () => useValue(pagesContainer); From 849768f19565e9e65a5408b6961ce11da96cdb30 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 11:28:18 +0400 Subject: [PATCH 37/71] minor --- .../sidebar-left/panels/pages/index.tsx | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx index baa72872e1..90a8497546 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx @@ -98,23 +98,3 @@ export const TabContent = ({ // @todo: proper icon export const icon = ; - -// export type TreeNodeProps = { -// itemData: Data; -// getItemChildren: (item: Data) => Data[]; -// renderItem: (props: { data: Data; isSelected: boolean }) => React.ReactNode; - -// getIsExpanded: (item: Data) => boolean; -// setIsExpanded?: (item: Data, expanded: boolean) => void; -// onExpandTransitionEnd?: () => void; - -// selectedItemId?: string; -// parentIsSelected?: boolean; -// onSelect?: (itemId: string) => void; -// onMouseEnter?: (item: Data) => void; -// onMouseLeave?: (item: Data) => void; - -// level?: number; -// animate?: boolean; -// forceHoverStateAtItem?: string; -// }; From cbe1c123ca41b9978f5795a2c908ccca75e92fd5 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 11:33:38 +0400 Subject: [PATCH 38/71] minor --- apps/designer/app/routes/designer/$projectId.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/designer/app/routes/designer/$projectId.tsx b/apps/designer/app/routes/designer/$projectId.tsx index 055447a81f..7f15cab11b 100644 --- a/apps/designer/app/routes/designer/$projectId.tsx +++ b/apps/designer/app/routes/designer/$projectId.tsx @@ -57,10 +57,6 @@ export const DesignerRoute = () => { const data = useLoaderData(); useAction(); - if (data === undefined) { - return ; - } - if ("errors" in data) { return ; } From 84cbd7dc6d0e16cd4d646beb5dee435b51953e3c Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 13:51:32 +0400 Subject: [PATCH 39/71] remove node 14 support --- .github/workflows/main.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index efb144c5ed..f37e4cb4bf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node: ["14.x", "16.x"] + node: ["16.x"] os: [ubuntu-latest] steps: - name: Checkout repo diff --git a/package.json b/package.json index 8f2f00e5da..6183c444a6 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "engines": { "npm": ">=7.0.0", - "node": ">=14 <=16" + "node": "16" }, "dependencies": {}, "packageManager": "yarn@1.22.19", From 18f724aac7b7aa6d2713b10135a7a37787a54040 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 16:49:01 +0400 Subject: [PATCH 40/71] use page icon --- .../sidebar-left/panels/pages/index.tsx | 4 +-- packages/icons/src/index.ts | 1 + packages/icons/src/page.tsx | 28 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 packages/icons/src/page.tsx diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx index 90a8497546..1bf2b53503 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx @@ -2,7 +2,7 @@ import { TreeNodeLabel, TreeNode } from "@webstudio-is/design-system"; import { type Publish } from "~/shared/pubsub"; -import { StackIcon } from "@webstudio-is/icons"; +import { PageIcon } from "@webstudio-is/icons"; import type { TabName } from "../../types"; import { Header } from "../../lib/header"; import { type Page, type Pages, type Project } from "@webstudio-is/project"; @@ -97,4 +97,4 @@ export const TabContent = ({ }; // @todo: proper icon -export const icon = ; +export const icon = ; diff --git a/packages/icons/src/index.ts b/packages/icons/src/index.ts index a15ffb67ce..1897831629 100644 --- a/packages/icons/src/index.ts +++ b/packages/icons/src/index.ts @@ -48,3 +48,4 @@ export { TrashIcon } from "./trash"; export { SizeIcon } from "./size"; export { UploadIcon } from "./upload"; export { GearIcon, gearIconCssVars } from "./gear"; +export * from "./page"; diff --git a/packages/icons/src/page.tsx b/packages/icons/src/page.tsx new file mode 100644 index 0000000000..2cd745639b --- /dev/null +++ b/packages/icons/src/page.tsx @@ -0,0 +1,28 @@ +import * as React from "react"; +import { IconProps } from "./types"; + +export const PageIcon = React.forwardRef( + ({ color = "currentColor", ...props }, forwardedRef) => { + return ( + + + + ); + } +); + +PageIcon.displayName = "PageIcon"; From 77689e4dcdfe23c2b8de3890eaf124ffdcd38fcf Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 16:50:51 +0400 Subject: [PATCH 41/71] move code from index.ts --- .../app/designer/features/sidebar-left/panels/pages/index.ts | 1 + .../sidebar-left/panels/pages/{index.tsx => pages.tsx} | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) create mode 100644 apps/designer/app/designer/features/sidebar-left/panels/pages/index.ts rename apps/designer/app/designer/features/sidebar-left/panels/pages/{index.tsx => pages.tsx} (95%) diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.ts b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.ts new file mode 100644 index 0000000000..c4e34b2701 --- /dev/null +++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/index.ts @@ -0,0 +1 @@ +export * from "./pages"; diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx similarity index 95% rename from apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx rename to apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx index 1bf2b53503..f66e0d89b7 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/pages/index.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx @@ -1,5 +1,3 @@ -// @todo: consider moving code from index.tsx to separate files similar to other panels - import { TreeNodeLabel, TreeNode } from "@webstudio-is/design-system"; import { type Publish } from "~/shared/pubsub"; import { PageIcon } from "@webstudio-is/icons"; @@ -96,5 +94,4 @@ export const TabContent = ({ ); }; -// @todo: proper icon export const icon = ; From 573200405fde57618044434d513ebd6f7b0ab0ee Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Thu, 6 Oct 2022 17:33:54 +0400 Subject: [PATCH 42/71] use nano states --- apps/designer/app/designer/designer.tsx | 35 ++++++++++++--- .../sidebar-left/panels/pages/pages.tsx | 45 +++++++++++++++---- .../features/sidebar-left/sidebar-left.tsx | 15 +------ .../app/designer/shared/nano-states/index.ts | 10 +++++ 4 files changed, 76 insertions(+), 29 deletions(-) diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 50c009c714..0518f7cad9 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -13,6 +13,9 @@ import { Inspector } from "./features/inspector"; import { useAssets, useHoveredInstanceData, + usePages, + useProject, + useCurrentPageId, useSelectedInstanceData, useSyncStatus, } from "./shared/nano-states"; @@ -77,6 +80,27 @@ const useSetAssets = (assets?: Array) => { }, [assets, setAssets]); }; +const useSetProject = (project: Project) => { + const [, setProject] = useProject(); + useEffect(() => { + setProject(project); + }, [project, setProject]); +}; + +const useSetPages = (pages: Pages) => { + const [, setPages] = usePages(); + useEffect(() => { + setPages(pages); + }, [pages, setPages]); +}; + +const useSetCurrentPageId = (pageId: string) => { + const [, setCurrentPageId] = useCurrentPageId(); + useEffect(() => { + setCurrentPageId(pageId); + }, [pageId, setCurrentPageId]); +}; + const useNavigatorLayout = () => { // We need to render the detached state only once the setting was actually loaded from local storage. // Otherwise we may show the detached state because its the default and then hide it immediately. @@ -256,6 +280,9 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { useSubscribeHoveredInstanceData(); useSubscribeBreakpoints(); useSetAssets(project.assets); + useSetProject(project); + useSetPages(pages); + useSetCurrentPageId(pageId); const [publish, publishRef] = usePublish(); const [isPreviewMode] = useIsPreviewMode(); usePublishShortcuts(publish); @@ -309,13 +336,7 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { - + void; publish: Publish; - project: Project; - pages: Pages; - currentPageId: string; config: Config; }; @@ -66,14 +68,18 @@ const staticTreeProps = { }, }; -export const TabContent = ({ +export const TabContentWithData = ({ onSetActiveTab, - project, + config, pages, currentPageId, - config, -}: TabContentProps) => { - const pagesTree = useMemo(() => pages && toTreeData(pages), [pages]); + project, +}: TabContentProps & { + pages: Pages; + currentPageId: string; + project: Project; +}) => { + const pagesTree = useMemo(() => toTreeData(pages), [pages]); const navigate = useNavigate(); const handleSelect = (pageId: string) => { @@ -94,4 +100,27 @@ export const TabContent = ({ ); }; +export const TabContent = (props: TabContentProps) => { + const [pages] = usePages(); + const [currentPageId] = useCurrentPageId(); + const [project] = useProject(); + + if ( + pages === undefined || + currentPageId === undefined || + project === undefined + ) { + return null; + } + + return ( + + ); +}; + export const icon = ; diff --git a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx index e8e4208012..f82b138aaf 100644 --- a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx +++ b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx @@ -13,26 +13,16 @@ import { isFeatureEnabled } from "~/shared/feature-flags"; import { useClientSettings } from "~/designer/shared/client-settings"; import { PANEL_WIDTH } from "~/designer/shared/constants"; import { Flex } from "@webstudio-is/design-system"; -import { type Project, type Pages } from "@webstudio-is/project"; import { Config } from "~/config"; const none = { TabContent: () => null }; type SidebarLeftProps = { publish: Publish; - project: Project; - pages: Pages; - currentPageId: string; config: Config; }; -export const SidebarLeft = ({ - publish, - project, - pages, - currentPageId, - config, -}: SidebarLeftProps) => { +export const SidebarLeft = ({ publish, config }: SidebarLeftProps) => { const [dragAndDropState] = useDragAndDropState(); const [activeTab, setActiveTab] = useState("none"); const { TabContent } = activeTab === "none" ? none : panels[activeTab]; @@ -93,9 +83,6 @@ export const SidebarLeft = ({ diff --git a/apps/designer/app/designer/shared/nano-states/index.ts b/apps/designer/app/designer/shared/nano-states/index.ts index 667f42f760..f0e0e9a4b2 100644 --- a/apps/designer/app/designer/shared/nano-states/index.ts +++ b/apps/designer/app/designer/shared/nano-states/index.ts @@ -6,6 +6,7 @@ import { } from "~/shared/canvas-components"; import { type SyncStatus } from "~/shared/sync"; import { Asset } from "@webstudio-is/asset-uploader"; +import { type Pages, type Project } from "@webstudio-is/project"; const selectedInstanceDataContainer = createValueContainer< SelectedInstanceData | undefined @@ -49,3 +50,12 @@ export const useSelectionRect = () => useValue(selectionRectContainer); const assetsContainer = createValueContainer>([]); export const useAssets = () => useValue(assetsContainer); + +const pagesContainer = createValueContainer(); +export const usePages = () => useValue(pagesContainer); + +const currentPageIdContainer = createValueContainer(); +export const useCurrentPageId = () => useValue(currentPageIdContainer); + +const projectContainer = createValueContainer(); +export const useProject = () => useValue(projectContainer); From 5f5cfae96b90cbb3e83bc699694d43fc82a84c94 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 18:12:26 +0200 Subject: [PATCH 43/71] use new combobox in props --- apps/designer/app/auth/login.tsx | 3 - .../features/props-panel/props-panel.tsx | 118 ++++++++++++++---- .../design-system/src/components/combobox.tsx | 10 +- 3 files changed, 102 insertions(+), 29 deletions(-) diff --git a/apps/designer/app/auth/login.tsx b/apps/designer/app/auth/login.tsx index 8172ed3d86..9e2794fc14 100644 --- a/apps/designer/app/auth/login.tsx +++ b/apps/designer/app/auth/login.tsx @@ -1,9 +1,7 @@ import { LinksFunction, MetaFunction } from "@remix-run/node"; import { Form, useLoaderData } from "@remix-run/react"; import { useState } from "react"; - import { - Box, Card, Flex, Heading, @@ -12,7 +10,6 @@ import { TextField, } from "@webstudio-is/design-system"; import interStyles from "~/shared/font-faces/inter.css"; - import { GithubIcon, CommitIcon, GoogleIcon } from "@webstudio-is/icons"; import { LoginButton } from "./components/login-button"; import loginStyles from "./login.css"; diff --git a/apps/designer/app/designer/features/props-panel/props-panel.tsx b/apps/designer/app/designer/features/props-panel/props-panel.tsx index 74a599f36b..e698e2a6f0 100644 --- a/apps/designer/app/designer/features/props-panel/props-panel.tsx +++ b/apps/designer/app/designer/features/props-panel/props-panel.tsx @@ -10,19 +10,99 @@ import type { SelectedInstanceData } from "~/shared/canvas-components"; import { Box, Button, - Combobox, - ComboboxTextField, - ComboboxPopperContent, Grid, + TextField, Tooltip, + useCombobox, + ComboboxPrimitive, + ComboboxContent, + ComboboxAnchor, + ComboboxListbox, + ComboboxListboxItem, + IconButton, } from "@webstudio-is/design-system"; import { PlusIcon, TrashIcon, ExclamationTriangleIcon, + ChevronDownIcon, } from "@webstudio-is/icons"; import { handleChangePropType, usePropsLogic } from "./use-props-logic"; +type ComboboxProps = { + isReadonly: boolean; + isInvalid: boolean; + items: Array; + value: string; + onItemSelect: (value: string | null) => void; + onSubmit: (value: string) => void; +}; + +export const Combobox = ({ + isReadonly, + isInvalid, + items: itemsProp, + value, + onItemSelect, + onSubmit, +}: ComboboxProps) => { + const { + items, + getInputProps, + getComboboxProps, + getToggleButtonProps, + getMenuProps, + getItemProps, + isOpen, + } = useCombobox({ + items: itemsProp, + value, + itemToString: (item) => item ?? "", + onItemSelect, + }); + + return ( + + + + + + + } + onKeyPress={(event) => { + if (event.key === "Enter") { + onSubmit(event.currentTarget.value); + } + }} + /> + + + + {isOpen && + items.map((item, index) => { + return ( + + {item} + + ); + })} + + + + + ); +}; + type PropertyProps = UserProp & { component: Instance["component"]; onChange: handleChangePropType; @@ -33,14 +113,14 @@ const Property = ({ id, prop, value, - required, + required = false, component, onChange, onDelete, }: PropertyProps) => { const meta = componentsMeta[component]; const argType = meta.argTypes?.[prop as keyof typeof meta.argTypes]; - const isInvalidProp = + const isInvalid = prop != null && prop.length > 0 && typeof argType === "undefined" && @@ -56,30 +136,20 @@ const Property = ({ css={{ gridTemplateColumns: "1fr 1fr auto", alignItems: "center" }} > item ?? ""} - onItemSelect={(value) => { + onItemSelect={(value: PropertyProps["value"] | null) => { + if (value !== null) { + onChange(id, "prop", value); + } + }} + onSubmit={(value) => { onChange(id, "prop", value); }} - renderTextField={({ inputProps, toggleProps }) => ( - - )} - renderPopperContent={(props) => ( - - )} + isInvalid={isInvalid} + isReadonly={required} /> - - {isInvalidProp ? ( + {isInvalid ? ( diff --git a/packages/design-system/src/components/combobox.tsx b/packages/design-system/src/components/combobox.tsx index ae936d67d5..95123404b8 100644 --- a/packages/design-system/src/components/combobox.tsx +++ b/packages/design-system/src/components/combobox.tsx @@ -61,9 +61,15 @@ export const ListboxItemBase: ForwardRefRenderFunction< ); }; +export const ComboboxListbox = Listbox; + export const ComboboxListboxItem = forwardRef(ListboxItemBase); -export const ComboboxPopperContent = PopperContent; +export const ComboboxPrimitive = Popper; + +export const ComboboxContent = PopperContent; + +export const ComboboxAnchor = PopperAnchor; type useComboboxProps = { items: ReadonlyArray; @@ -84,7 +90,7 @@ export function useCombobox({ itemToString, onItemSelect, onItemHighlight, - stateReducer, + stateReducer = (state, { changes }) => changes, }: useComboboxProps) { const [filteredItems, setFilteredItems] = useState(items); const cachedItems = useRef(items); From 87737a4b83388a37c0fad1e0696e26a7ae40aaa6 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 21:35:09 +0200 Subject: [PATCH 44/71] remove unnecessary reducer --- packages/design-system/src/components/combobox.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/design-system/src/components/combobox.tsx b/packages/design-system/src/components/combobox.tsx index 95123404b8..80e4530a9b 100644 --- a/packages/design-system/src/components/combobox.tsx +++ b/packages/design-system/src/components/combobox.tsx @@ -169,14 +169,6 @@ export function Combobox({ onItemSelect, onItemHighlight, }: ComboboxProps) { - const stateReducer = useCallback((state, actionAndChanges) => { - const { type, changes } = actionAndChanges; - switch (type) { - default: - return changes; // otherwise business as usual. - } - }, []); - const { items: foundItems, getInputProps, @@ -191,7 +183,6 @@ export function Combobox({ itemToString, onItemSelect, onItemHighlight, - stateReducer, }); return ( From a4c303e78d9f1675568d040f48d77d00e1f9a3ea Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 21:38:06 +0200 Subject: [PATCH 45/71] use function expressions --- packages/design-system/src/components/combobox.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/design-system/src/components/combobox.tsx b/packages/design-system/src/components/combobox.tsx index 80e4530a9b..fa02112a64 100644 --- a/packages/design-system/src/components/combobox.tsx +++ b/packages/design-system/src/components/combobox.tsx @@ -83,15 +83,14 @@ type useComboboxProps = { ) => Partial>; }; -// eslint-disable-next-line func-style -export function useCombobox({ +export const useCombobox = ({ items, value, itemToString, onItemSelect, onItemHighlight, stateReducer = (state, { changes }) => changes, -}: useComboboxProps) { +}: useComboboxProps) => { const [filteredItems, setFilteredItems] = useState(items); const cachedItems = useRef(items); @@ -151,7 +150,7 @@ export function useCombobox({ items: filteredItems, // Return filtered items getItemProps: enhancedGetItemProps, }; -} +}; type ComboboxProps = useComboboxProps & { name: string; @@ -159,8 +158,7 @@ type ComboboxProps = useComboboxProps & { placeholder?: string; }; -// eslint-disable-next-line func-style -export function Combobox({ +export const Combobox = ({ items, value = null, name, @@ -168,7 +166,7 @@ export function Combobox({ itemToString, onItemSelect, onItemHighlight, -}: ComboboxProps) { +}: ComboboxProps) => { const { items: foundItems, getInputProps, @@ -219,6 +217,6 @@ export function Combobox({ ); -} +}; Combobox.displayName = "Combobox"; From c984d00603d07fca9febe22ad52bba5224fa36ca Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 21:42:40 +0200 Subject: [PATCH 46/71] keep original naming inside the mapped name for clarity --- .../features/props-panel/props-panel.tsx | 18 +++++++++--------- .../design-system/src/components/combobox.tsx | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/designer/app/designer/features/props-panel/props-panel.tsx b/apps/designer/app/designer/features/props-panel/props-panel.tsx index e698e2a6f0..888e8833d7 100644 --- a/apps/designer/app/designer/features/props-panel/props-panel.tsx +++ b/apps/designer/app/designer/features/props-panel/props-panel.tsx @@ -14,9 +14,9 @@ import { TextField, Tooltip, useCombobox, - ComboboxPrimitive, - ComboboxContent, - ComboboxAnchor, + ComboboxPopper, + ComboboxPopperContent, + ComboboxPopperAnchor, ComboboxListbox, ComboboxListboxItem, IconButton, @@ -62,9 +62,9 @@ export const Combobox = ({ }); return ( - + - + - - + + {isOpen && items.map((item, index) => { @@ -97,9 +97,9 @@ export const Combobox = ({ ); })} - + - + ); }; diff --git a/packages/design-system/src/components/combobox.tsx b/packages/design-system/src/components/combobox.tsx index fa02112a64..2851a86ce0 100644 --- a/packages/design-system/src/components/combobox.tsx +++ b/packages/design-system/src/components/combobox.tsx @@ -65,11 +65,11 @@ export const ComboboxListbox = Listbox; export const ComboboxListboxItem = forwardRef(ListboxItemBase); -export const ComboboxPrimitive = Popper; +export const ComboboxPopper = Popper; -export const ComboboxContent = PopperContent; +export const ComboboxPopperContent = PopperContent; -export const ComboboxAnchor = PopperAnchor; +export const ComboboxPopperAnchor = PopperAnchor; type useComboboxProps = { items: ReadonlyArray; From 1063cb743ef71fcd756edb223bf310b1e44648c2 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 21:47:13 +0200 Subject: [PATCH 47/71] move onKeyPress into props getter to make sure we don't override a handler --- .../designer/features/props-panel/props-panel.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/designer/app/designer/features/props-panel/props-panel.tsx b/apps/designer/app/designer/features/props-panel/props-panel.tsx index 888e8833d7..2b994f6d8d 100644 --- a/apps/designer/app/designer/features/props-panel/props-panel.tsx +++ b/apps/designer/app/designer/features/props-panel/props-panel.tsx @@ -66,7 +66,13 @@ export const Combobox = ({ { + if (event.key === "Enter") { + onSubmit(event.currentTarget.value); + } + }, + })} name="prop" placeholder="Property" readOnly={isReadonly} @@ -76,11 +82,6 @@ export const Combobox = ({ } - onKeyPress={(event) => { - if (event.key === "Enter") { - onSubmit(event.currentTarget.value); - } - }} /> From 311065fd637c0ede8e8640d2271325dc9832d2f3 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 21:47:26 +0200 Subject: [PATCH 48/71] make linter and jsx parser happy at the same time --- packages/design-system/src/components/combobox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/design-system/src/components/combobox.tsx b/packages/design-system/src/components/combobox.tsx index 2851a86ce0..e4fdd9b3f6 100644 --- a/packages/design-system/src/components/combobox.tsx +++ b/packages/design-system/src/components/combobox.tsx @@ -83,7 +83,7 @@ type useComboboxProps = { ) => Partial>; }; -export const useCombobox = ({ +export const useCombobox = ({ items, value, itemToString, @@ -158,7 +158,7 @@ type ComboboxProps = useComboboxProps & { placeholder?: string; }; -export const Combobox = ({ +export const Combobox = ({ items, value = null, name, From fe164432e8868a91b00ed200b3f873680e832d95 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 21:48:38 +0200 Subject: [PATCH 49/71] no need to export --- apps/designer/app/designer/features/props-panel/props-panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/designer/app/designer/features/props-panel/props-panel.tsx b/apps/designer/app/designer/features/props-panel/props-panel.tsx index 2b994f6d8d..084a501d25 100644 --- a/apps/designer/app/designer/features/props-panel/props-panel.tsx +++ b/apps/designer/app/designer/features/props-panel/props-panel.tsx @@ -38,7 +38,7 @@ type ComboboxProps = { onSubmit: (value: string) => void; }; -export const Combobox = ({ +const Combobox = ({ isReadonly, isInvalid, items: itemsProp, From 28394a16ee0bb10c2ac7d1447c7699667fde8d39 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 23:01:43 +0200 Subject: [PATCH 50/71] update lost pixel baseline --- .../components-combobox--custom-input.png | Bin 6430 -> 0 bytes .../baseline/components-combobox--simple.png | Bin 6430 -> 7001 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .lostpixel/baseline/components-combobox--custom-input.png diff --git a/.lostpixel/baseline/components-combobox--custom-input.png b/.lostpixel/baseline/components-combobox--custom-input.png deleted file mode 100644 index 572fc3220a45efea3016e4992e92fdaa555b7876..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6430 zcmeHL`%@EF6yBgUr~}$sQRERiYC9b$SOSTWN7U-1v>kBN5fmh%5*`|Y6JrP=glY$> z3^-clApyoZ)(#XXKEf*`lnM-zWD$^H5=d0Y7J(!fAmrU$^iSw--Jia@_nf_F?|1HZ z?>&3cFd*Cntd;~Y$2Hn@d0P@CLH7KaCLPsyM8hbWtQrBO7q?~`-TkZIJbxaV{w#)h+4)hy7# zoc)>t%Bv~TCAz9?;#1@;(V-||8wu;_E3oTOh*6)p7t(Df4?J5 zanlEx1wa!rR0Iucr13a4v5Je!NuLH<7a{;6fk1<93}~*oBTkX}pWz6-y}d)sGS7m< z#A4VvF?Ty+>Utw=XaYw^4>Jr^-c$^d(}J)MeASpS+06;kBBwos2G&z;Nem?OfcVWH zVL<+5ouo?Pc|$# zA-Rp5-Cq~H!m?)-e!^b%l5LX3<(${Zh@dwkf8aZL$4!`NA`!YX(^#u=&5+Y*G_!u( zMC5Z{h)imBo{DjutZ(H^1tv#Rt=Cnx7aiEx-LD3%^cG&eIM+PR>`yHR?`f8M^T7GO zrq`dW%;8U~Hnm({nO2S8ax&*H(cfsN)y588P`+E5wreyKPUd3yU~L<5!Ph65s$OlSP!ZR>~WSk56+f&u7#?S@bw)A zCu#mEF7Lz)*IW}y9ACKla%?Q2XB1)SRI1n;14^{r9Ec+c=U;p|6>KMdi{5(T)&2-ejQKhmw~1a00eN&(*yqx29k6<4kU9 z#KG&q?x2tfupTP-?Mq5aKVDuj9Or5#nP#aKrhFS)cQ2mGYYkaR)G^&iCs&8BRTE*A zHQI{mNbfD-VX3Kt}Qd#G94$ozlE>OXjma<{c}GT4-zcY8K{}P z&%|!_qe4PU&GS7-T8vVM1lv_oQqra`oPn6smFh{E3i*A=uMBv<36Q^XxbH%SG}?CpZZPhxcyCE@3qZPmT3dj? z#?MhBN&>nPP&vSB03`t>0X-c606ijM9iSwjB%n7&^xBNp4rq-8?}R7`C<*8<1GGj8 gfMxJMC=^}mqgyLV_HEmdfy_7NVC;d${iiPf3!HACIRF3v diff --git a/.lostpixel/baseline/components-combobox--simple.png b/.lostpixel/baseline/components-combobox--simple.png index 572fc3220a45efea3016e4992e92fdaa555b7876..9042b09cfedfd522797ea75426e03b3647ba4d3c 100644 GIT binary patch literal 7001 zcmeHLe^k=v8voi_b}MroyH+Z>=Bl%^YuXfBqSB_zl~YSW5(PQ6n+E=%FC=r`vOq*rbb^58F=wV!2q+3PO;He012s^%eCz&m|J?3&?mztXyw7>x=Y5{< z=Y5{f^M2o=V{y@L8@x6E0N{4y%cukZc-Jvp^K|_>hmho}k{rT?m=OIr&@kXL4FKLA zN1{GM6&hk={AAeUl&yuF`&`j4RpL%Xg(0$4|}pC)bHjTGp8 zk9Qwe7=B_OMK)*n@rrIAE=Ti@ve&h5Z`_4D6}YY(cWQeBao3YTkI8TNjT?FMoT~k; z5j0_vb|FloF=lEg1Fu@3jQ-Ouj#z+lCEVZ!4uEPOm-j&V-CyXSc<(Dco6G2PX`G+}aj~(; zT7fxc3+SBg?HTMjI5>#tBceCco16RbIRrwUA86d^1~C|*u#x8~E9-G0{S6E^{sTjV z8OYU{rY#I#d!$>*Kh<(l^x*)-#(-J2(7Jh4PI~iQ=PgldUex$S>#vF_A{$i_V-B#} zd`VMt1mD~lrHsvP!rA;eM9Y)m6xGR9?EuE?%9v%F35p9d#DQw@3An8n-rs8hV>t_Z zk_Y&Bi_unhQ9}Go?!Q{$Ej-&?Zx4IeBHWPk2{ardNK+Q{Y14f82n+_p%N%}QmLFG8 zkd|*v(NtBK-v%^QWi9Wws4AO6^G`P>anZ?0B;!C# zthPEWCfzrJQol~ZdaXoG4=k8V333xUs%cI=r}p*BVZkEocnG{aXv6Mi%Gi)({Bh)MNdiL`fd zIq~t~CLUK#6fY|*y%JmdW=Nj!hB0-`&&CN9e&WXo^W3Erg8A_TY3^w`nF)Js)hLRs z?t|l+QEZtpw`Fk_c1$Xj^6Z@-%cN4wh_4b|7QS%SAKP(P&K1{E1Ool9u5IeAnvkrH zI#ClTCQJQo4&)$ZTr}nW}dcFUrsY3X+o|;7|55P#VhDDOt+g9L@>RilvF% zAQ*q7hh?u|mb>Jxj<{dEBJ_B~%QsRNJB;Ljnc+m(&D@37S*^!Ms5H$&CBI-ioMalB zSFVo5K!d{&u+|**#%=p#LzPWlVhZmOH*WCZQtIk~u&@NFG>Dh3;j&ry;m+qbu2t1b zFKKlb`bB()O-gv(hy8mGEIEpJbnM^%OVsy`l|F_)>eQaSDnd-zb%@BV=s2S`Lmb@4yYV~H1y}Ijl zw!7-kpsA0<;1P}{F=oYFnr$>*f3L3k9*bq9X{X8E;@Bn4(iV?Nd6jEt`Lk#I;IIU5 z@ycx$1*Y=1jBR7F@VM#Ap?Nxv`zCsRelh;#!UUMqmWL13`f4t^y@N(w=vOwYgLxZ{xha_EY(DU`Aj#e@~q zQ1}8u#@9cW?t~pLdHsrDW>v~Yq=^!vI;BpsNj^34F%RFp;B`WowwFe{kENM;USZ_c z(~N$!DD&242#vEZwFB`^jFn(pX{${mlgW@U6P_8Ge`n90y@a`$*ZfocI$d^fgK_>P zqfR$9?;*P;zU3K!MnsoFxW!is@whVu=0y{tw&~U)B)2_|xwF1$ex9O#g5zMDIzY7| zr;4>Z##3?s;%%cK0z3r-(%*agUAWD*q!XW?tryz`Vh+#2LZ&0e{b7`Li`A3YQr0?Y zkEj{<6w<~@9LQ$HFhENn`~29;d;Ih+ep$p^QBp4^bI0?o{FiwK`10Ls{Wa=c-|0|ge*?|xC9io(YwvB@Ca!0<1 Li)#2B_uan%oBEnk literal 6430 zcmeHL`%@EF6yBgUr~}$sQRERiYC9b$SOSTWN7U-1v>kBN5fmh%5*`|Y6JrP=glY$> z3^-clApyoZ)(#XXKEf*`lnM-zWD$^H5=d0Y7J(!fAmrU$^iSw--Jia@_nf_F?|1HZ z?>&3cFd*Cntd;~Y$2Hn@d0P@CLH7KaCLPsyM8hbWtQrBO7q?~`-TkZIJbxaV{w#)h+4)hy7# zoc)>t%Bv~TCAz9?;#1@;(V-||8wu;_E3oTOh*6)p7t(Df4?J5 zanlEx1wa!rR0Iucr13a4v5Je!NuLH<7a{;6fk1<93}~*oBTkX}pWz6-y}d)sGS7m< z#A4VvF?Ty+>Utw=XaYw^4>Jr^-c$^d(}J)MeASpS+06;kBBwos2G&z;Nem?OfcVWH zVL<+5ouo?Pc|$# zA-Rp5-Cq~H!m?)-e!^b%l5LX3<(${Zh@dwkf8aZL$4!`NA`!YX(^#u=&5+Y*G_!u( zMC5Z{h)imBo{DjutZ(H^1tv#Rt=Cnx7aiEx-LD3%^cG&eIM+PR>`yHR?`f8M^T7GO zrq`dW%;8U~Hnm({nO2S8ax&*H(cfsN)y588P`+E5wreyKPUd3yU~L<5!Ph65s$OlSP!ZR>~WSk56+f&u7#?S@bw)A zCu#mEF7Lz)*IW}y9ACKla%?Q2XB1)SRI1n;14^{r9Ec+c=U;p|6>KMdi{5(T)&2-ejQKhmw~1a00eN&(*yqx29k6<4kU9 z#KG&q?x2tfupTP-?Mq5aKVDuj9Or5#nP#aKrhFS)cQ2mGYYkaR)G^&iCs&8BRTE*A zHQI{mNbfD-VX3Kt}Qd#G94$ozlE>OXjma<{c}GT4-zcY8K{}P z&%|!_qe4PU&GS7-T8vVM1lv_oQqra`oPn6smFh{E3i*A=uMBv<36Q^XxbH%SG}?CpZZPhxcyCE@3qZPmT3dj? z#?MhBN&>nPP&vSB03`t>0X-c606ijM9iSwjB%n7&^xBNp4rq-8?}R7`C<*8<1GGj8 gfMxJMC=^}mqgyLV_HEmdfy_7NVC;d${iiPf3!HACIRF3v From af1081780698bff9f1088c17caad1bb9804c427c Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Fri, 7 Oct 2022 13:18:28 +0400 Subject: [PATCH 51/71] potentially reusable component --- .../sidebar-left/panels/pages/pages.tsx | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx index e3ec08d88b..72ce1c953d 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx @@ -3,7 +3,7 @@ import { type Publish } from "~/shared/pubsub"; import { PageIcon } from "@webstudio-is/icons"; import type { TabName } from "../../types"; import { Header } from "../../lib/header"; -import { type Page, type Pages, type Project } from "@webstudio-is/project"; +import { type Page, type Pages } from "@webstudio-is/project"; import { useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { type Config } from "~/config"; @@ -68,31 +68,29 @@ const staticTreeProps = { }, }; -export const TabContentWithData = ({ - onSetActiveTab, - config, - pages, - currentPageId, - project, -}: TabContentProps & { - pages: Pages; - currentPageId: string; - project: Project; +const Pages = ({ + onClose, + onSelect, + selectedPageId, +}: { + onClose: () => void; + onSelect: (pageId: string) => void; + selectedPageId: string; }) => { - const pagesTree = useMemo(() => toTreeData(pages), [pages]); + const [pages] = usePages(); + const pagesTree = useMemo(() => pages && toTreeData(pages), [pages]); - const navigate = useNavigate(); - const handleSelect = (pageId: string) => { - navigate(`${config.designerPath}/${project.id}?pageId=${pageId}`); - }; + if (pagesTree === undefined) { + return null; + } return ( <> -
onSetActiveTab("none")} /> +
@@ -101,24 +99,26 @@ export const TabContentWithData = ({ }; export const TabContent = (props: TabContentProps) => { - const [pages] = usePages(); const [currentPageId] = useCurrentPageId(); const [project] = useProject(); - if ( - pages === undefined || - currentPageId === undefined || - project === undefined - ) { + const navigate = useNavigate(); + const handleSelect = (pageId: string) => { + if (project === undefined) { + return; + } + navigate(`${props.config.designerPath}/${project.id}?pageId=${pageId}`); + }; + + if (currentPageId === undefined) { return null; } return ( - props.onSetActiveTab("none")} + onSelect={handleSelect} + selectedPageId={currentPageId} /> ); }; From b663d8a4bdd492b625da091b2abb22eb2b5b78e7 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 6 Oct 2022 23:50:28 +0200 Subject: [PATCH 52/71] extract filtering logic into useFilter hook --- .../src/components/combobox.stories.tsx | 6 +- .../design-system/src/components/combobox.tsx | 68 +++++++++++++------ 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/packages/design-system/src/components/combobox.stories.tsx b/packages/design-system/src/components/combobox.stories.tsx index 5c1abba546..622ddea45a 100644 --- a/packages/design-system/src/components/combobox.stories.tsx +++ b/packages/design-system/src/components/combobox.stories.tsx @@ -11,11 +11,7 @@ export default { }; export const Simple: ComponentStory = () => { - const items = [ - { label: "Apple" }, - { label: "Banana" }, - { label: "Orange" }, - ] as const; + const items = [{ label: "Apple" }, { label: "Banana" }, { label: "Orange" }]; const [value, setValue] = React.useState(null); return ( ({ + items, + itemToString, +}: { + items: Array; + itemToString: (item: Item | null) => string; +}) => { + const [filteredItems, setFilteredItems] = useState>(items); + const cachedItems = useRef(items); + + useEffect(() => { + cachedItems.current = items; + }, [items]); + + const filter = useCallback( + (search?: string) => { + const foundItems: Array = matchSorter(items, search ?? "", { + keys: [(item) => itemToString(item)], + }); + setFilteredItems(foundItems); + }, + [itemToString, items] + ); + + const reset = useCallback(() => { + setFilteredItems(cachedItems.current); + }, []); + + return { + filteredItems, + filter, + reset, + }; +}; + type useComboboxProps = { - items: ReadonlyArray; + items: Array; itemToString: (item: Item | null) => string; value: Item | null; // This is to prevent: "downshift: A component has changed the uncontrolled prop "selectedItem" to be controlled." onItemSelect?: (value: Item | null) => void; @@ -91,23 +126,18 @@ export const useCombobox = ({ onItemHighlight, stateReducer = (state, { changes }) => changes, }: useComboboxProps) => { - const [filteredItems, setFilteredItems] = useState(items); - const cachedItems = useRef(items); + const { filteredItems, filter, reset } = useFilter({ + items, + itemToString, + }); const downshiftProps = useDownshiftCombobox({ - items: filteredItems as Item[], - selectedItem: value, // Avoid downshift warning about switching controlled mode + items: filteredItems, + selectedItem: value, // Prevent downshift warning about switching controlled mode stateReducer, itemToString, onInputValueChange({ inputValue }) { - const foundItems: ReadonlyArray = matchSorter( - items, - inputValue ?? "", - { - keys: [(item) => itemToString(item)], - } - ); - setFilteredItems(foundItems); + filter(inputValue); }, onSelectedItemChange({ selectedItem }) { onItemSelect?.(selectedItem ?? null); @@ -123,14 +153,10 @@ export const useCombobox = ({ downshiftProps; useEffect(() => { - cachedItems.current = items; - }, [items]); - - useEffect(() => { - if (!isOpen) { - setFilteredItems(cachedItems.current); + if (isOpen === false) { + reset(); } - }, [isOpen]); + }, [isOpen, reset]); const enhancedGetItemProps = useCallback( (options) => { @@ -147,7 +173,7 @@ export const useCombobox = ({ return { ...downshiftProps, - items: filteredItems, // Return filtered items + items: filteredItems, getItemProps: enhancedGetItemProps, }; }; From 9325b7082d6418296e9cd21ee3f1a70af5d2d27b Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 8 Oct 2022 09:32:51 +0200 Subject: [PATCH 53/71] reset > resetFilter --- packages/design-system/src/components/combobox.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/design-system/src/components/combobox.tsx b/packages/design-system/src/components/combobox.tsx index 862bae4fbc..e2916240f4 100644 --- a/packages/design-system/src/components/combobox.tsx +++ b/packages/design-system/src/components/combobox.tsx @@ -95,14 +95,14 @@ const useFilter = ({ [itemToString, items] ); - const reset = useCallback(() => { + const resetFilter = useCallback(() => { setFilteredItems(cachedItems.current); }, []); return { filteredItems, filter, - reset, + resetFilter, }; }; @@ -126,7 +126,7 @@ export const useCombobox = ({ onItemHighlight, stateReducer = (state, { changes }) => changes, }: useComboboxProps) => { - const { filteredItems, filter, reset } = useFilter({ + const { filteredItems, filter, resetFilter } = useFilter({ items, itemToString, }); @@ -154,9 +154,9 @@ export const useCombobox = ({ useEffect(() => { if (isOpen === false) { - reset(); + resetFilter(); } - }, [isOpen, reset]); + }, [isOpen, resetFilter]); const enhancedGetItemProps = useCallback( (options) => { From 02434049b37b2890a48873b64cdf62596aad8e69 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Fri, 7 Oct 2022 19:52:15 +0400 Subject: [PATCH 54/71] new catch-all route for canvas/preview/published --- apps/designer/app/config.ts | 2 - apps/designer/app/designer/designer.tsx | 2 +- .../app/designer/features/topbar/share.tsx | 11 +- .../app/designer/features/topbar/topbar.tsx | 6 +- apps/designer/app/entry.server.tsx | 8 +- apps/designer/app/routes/$.tsx | 130 ++++++++++++++++++ .../designer/app/routes/canvas/$projectId.tsx | 51 ------- apps/designer/app/routes/index.tsx | 99 ++----------- .../app/routes/preview/$projectId.tsx | 49 ------- apps/designer/app/shared/db/canvas.server.ts | 25 ++-- .../project/src/shared/pages/find-by-path.ts | 11 ++ packages/project/src/shared/pages/index.ts | 1 + 12 files changed, 180 insertions(+), 215 deletions(-) create mode 100644 apps/designer/app/routes/$.tsx delete mode 100644 apps/designer/app/routes/canvas/$projectId.tsx delete mode 100644 apps/designer/app/routes/preview/$projectId.tsx create mode 100644 packages/project/src/shared/pages/find-by-path.ts diff --git a/apps/designer/app/config.ts b/apps/designer/app/config.ts index 7bc8840d93..7bcd5c7519 100644 --- a/apps/designer/app/config.ts +++ b/apps/designer/app/config.ts @@ -1,6 +1,4 @@ const config = { - previewPath: "/preview", - canvasPath: "/canvas", designerPath: "/designer", dashboardPath: "/dashboard", loginPath: "/login", diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 0518f7cad9..72cf5459a3 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -321,7 +321,7 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { { +const Content = ({ projectId, pagePath }: ShareButtonProps) => { if (typeof location === "undefined") { return null; } const url = new URL( - `${location.protocol}//${location.host}${path}/${projectId}?pageId=${pageId}` + `${location.protocol}//${location.host}${ + pagePath || "/" + }?projectId=${projectId}&mode=preview` ); return ( - + diff --git a/apps/designer/app/entry.server.tsx b/apps/designer/app/entry.server.tsx index 2d217876da..969eed6c8e 100644 --- a/apps/designer/app/entry.server.tsx +++ b/apps/designer/app/entry.server.tsx @@ -4,7 +4,7 @@ import { initSentry } from "./shared/sentry"; import { prisma } from "@webstudio-is/prisma-client"; import { handleRequest as handleRequestDesigner } from "./shared/remix"; import { handleRequest as handleRequestCanvas } from "@webstudio-is/react-sdk"; -import config from "./config"; +import { isCanvasRequest } from "./routes/$"; initSentry({ integrations: [new Sentry.Integrations.Prisma({ client: prisma })], @@ -16,13 +16,9 @@ export default function handleRequest( responseHeaders: Headers, remixContext: EntryContext ) { - const { pathname } = new URL(request.url); let handle = handleRequestDesigner; - if ( - pathname.indexOf(config.previewPath) === 0 || - pathname.indexOf(config.canvasPath) === 0 - ) { + if (isCanvasRequest(request)) { handle = handleRequestCanvas; } diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx new file mode 100644 index 0000000000..2547d31f35 --- /dev/null +++ b/apps/designer/app/routes/$.tsx @@ -0,0 +1,130 @@ +import { type LoaderFunction, redirect, MetaFunction } from "@remix-run/node"; +import { useLoaderData } from "@remix-run/react"; +import { InstanceRoot, Root } from "@webstudio-is/react-sdk"; +import { loadCanvasData, type CanvasData } from "~/shared/db"; +import config from "~/config"; +import { db } from "@webstudio-is/project/index.server"; +import env, { Env } from "~/env.server"; +import { sentryException } from "~/shared/sentry"; +import { Canvas } from "~/canvas"; + +type Mode = "edit" | "preview" | "published"; + +type Data = + | (CanvasData & { env: Env; mode: Mode }) + | { errors: string; env: Env }; + +type ProjectIdentifier = { type: "id" | "domain"; value: string }; + +export const isCanvasRequest = ( + request: Request +): false | { projectId: ProjectIdentifier; mode: Mode; pathname: string } => { + const url = new URL(request.url); + + const pathname = url.pathname; + + if (Object.values(config).some((path) => pathname.startsWith(path))) { + return false; + } + + let projectId: ProjectIdentifier | undefined = undefined; + + // @todo all this subdomain logic is very hacky + const host = + request.headers.get("x-forwarded-host") || + request.headers.get("host") || + ""; + const [userDomain, wstdDomain] = host.split("."); + if (wstdDomain === "wstd" || wstdDomain?.includes("localhost")) { + projectId = { type: "domain", value: userDomain }; + } + + if (projectId === undefined) { + const projectIdParam = url.searchParams.get("projectId"); + if (projectIdParam !== null) { + projectId = { type: "id", value: projectIdParam }; + } + } + + if (projectId === undefined) { + return false; + } + + const modeParam = url.searchParams.get("mode"); + + if (modeParam === "edit") { + return { projectId, mode: "edit", pathname }; + } + + if (modeParam === "preview") { + return { projectId, mode: "preview", pathname }; + } + + return { projectId, mode: "published", pathname }; +}; + +export const meta: MetaFunction = ({ data }: { data: Data }) => { + if ("errors" in data) { + return { title: "Error" }; + } + const { page } = data; + return { title: page.title, ...page.meta }; +}; + +export const loader: LoaderFunction = async ({ + request, +}): Promise => { + try { + const canvasRequest = isCanvasRequest(request); + + if (canvasRequest === false) { + return redirect(config.dashboardPath); + } + + const { projectId, mode, pathname } = canvasRequest; + + const project = + projectId.type === "id" + ? await db.project.loadById(projectId.value) + : await db.project.loadByDomain(projectId.value); + + if (project === null) { + throw new Error("Project not found"); + } + + const canvasData = await loadCanvasData( + project.id, + mode === "published" ? "prod" : "dev", + pathname + ); + + return { ...canvasData, env, mode }; + } catch (error) { + sentryException({ error }); + return { + errors: error instanceof Error ? error.message : String(error), + env, + }; + } +}; + +const Content = () => { + const data = useLoaderData(); + + if ("errors" in data) { + return

{data.errors}

; + } + + const Outlet = + data.mode === "edit" + ? () => + : () => ; + + // @todo This is non-standard for Remix, is there a better way? + // We need to render essentially the preview route but from the index, + // so we have to know the layout and the outlet from here. + // Maybe there is a way to tell remix to use the right outlet somehow and avoid passing it? + return ; +}; + +export default Content; diff --git a/apps/designer/app/routes/canvas/$projectId.tsx b/apps/designer/app/routes/canvas/$projectId.tsx deleted file mode 100644 index b10818c1de..0000000000 --- a/apps/designer/app/routes/canvas/$projectId.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useLoaderData } from "@remix-run/react"; -import type { LoaderFunction, MetaFunction } from "@remix-run/node"; -import { Canvas } from "~/canvas"; -import { loadCanvasData, type CanvasData } from "~/shared/db"; -import { ErrorMessage } from "~/shared/error"; -import { sentryException } from "~/shared/sentry"; -import { Root } from "@webstudio-is/react-sdk"; - -type Data = CanvasData | { errors: string }; - -export const meta: MetaFunction = ({ data }: { data: Data }) => { - if ("errors" in data) { - return { title: "Error" }; - } - const { page } = data; - return { title: page.title, ...page.meta }; -}; - -export const loader: LoaderFunction = async ({ - params, - request, -}): Promise => { - try { - if (params.projectId === undefined) { - throw new Error("Missing projectId"); - } - - const url = new URL(request.url); - const pageIdParam = url.searchParams.get("pageId"); - - return loadCanvasData(params.projectId, pageIdParam || undefined); - } catch (error) { - sentryException({ error }); - return { errors: error instanceof Error ? error.message : String(error) }; - } -}; - -const Outlet = () => { - const data = useLoaderData(); - return ; -}; - -const CanvasRoute = () => { - const data = useLoaderData(); - if ("errors" in data) { - return ; - } - return ; -}; - -export default CanvasRoute; diff --git a/apps/designer/app/routes/index.tsx b/apps/designer/app/routes/index.tsx index 7ef1149563..476a48fc1f 100644 --- a/apps/designer/app/routes/index.tsx +++ b/apps/designer/app/routes/index.tsx @@ -1,87 +1,18 @@ -import { type LoaderFunction, redirect } from "@remix-run/node"; -import { useLoaderData } from "@remix-run/react"; -import { type Data, InstanceRoot, Root } from "@webstudio-is/react-sdk"; -import config from "~/config"; -import { db } from "@webstudio-is/project/index.server"; -import env, { Env } from "~/env.server"; +// We want our catch-all route to also handle `/`. +// Unfortunately, Remix doesn't do this by default, +// see https://github.com/remix-run/remix/issues/2098#issuecomment-1049262218 . +// To solve this, we're re-exporting the $.tsx route API in index.tsx -// @todo all this subdomain logic is very hacky +import { LoaderFunction, MetaFunction } from "@remix-run/node"; +import CatchAllContnet, { + loader as catchAllloader, + meta as catchAllmeta, +} from "./$"; -type Error = { errors: string }; -type LoaderReturnProps = Promise< - (Data & { env: Env }) | (Error & { env: Env }) | Response ->; +// We're wrapping functions in order for them to be distinct from the ones in $.tsx. +// If they are the same, Remix may get confused, and don't load data on page transitions. -export const loader: LoaderFunction = async ({ - request, -}): LoaderReturnProps => { - const host = - request.headers.get("x-forwarded-host") || - request.headers.get("host") || - ""; - - const [userDomain, wstdDomain] = host.split("."); - // We render the site from a subdomain - if ( - typeof userDomain === "string" && - (wstdDomain === "wstd" || wstdDomain?.includes("localhost")) - ) { - try { - const project = await db.project.loadByDomain(userDomain); - if (project === null) { - throw new Error(`Unknown domain "${userDomain}"`); - } - - const prodBuild = await db.build.loadByProjectId(project.id, "prod"); - - if (prodBuild === undefined) { - throw new Error(`Site is not published`); - } - - // @todo: use a correct page rather than homePage - - const [tree, props, breakpoints] = await Promise.all([ - db.tree.loadById(prodBuild.pages.homePage.treeId), - db.props.loadByTreeId(prodBuild.pages.homePage.treeId), - db.breakpoints.load(prodBuild.id), - ]); - if (tree === null) { - throw new Error(`Tree ${prodBuild.pages.homePage.treeId} not found`); - } - if (breakpoints === null) { - throw new Error( - `Breakpoints for tree ${prodBuild.pages.homePage.treeId} not found` - ); - } - return { tree, props, breakpoints: breakpoints.values, env }; - } catch (error) { - if (error instanceof Error) { - return { errors: error.message, env }; - } - } - } - - return redirect(config.dashboardPath); -}; - -const Index = () => { - const data = useLoaderData(); - if ("errors" in data) { - return

{data.errors}

; - } - - // We render the site from a subdomain - if (data.tree && data.props) { - const Outlet = () => ; - - // @todo This is non-standard for Remix, is there a better way? - // We need to render essentially the preview route but from the index, - // so we have to know the layout and the outlet from here. - // Maybe there is a way to tell remix to use the right outlet somehow and avoid passing it? - return ; - } - - return null; -}; - -export default Index; +export const meta: MetaFunction = (args) => catchAllmeta(args); +export const loader: LoaderFunction = (args) => catchAllloader(args); +const Content = () => ; +export default Content; diff --git a/apps/designer/app/routes/preview/$projectId.tsx b/apps/designer/app/routes/preview/$projectId.tsx deleted file mode 100644 index 6c5166ab27..0000000000 --- a/apps/designer/app/routes/preview/$projectId.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useLoaderData } from "@remix-run/react"; -import type { LoaderFunction, MetaFunction } from "@remix-run/node"; -import { loadCanvasData, type CanvasData } from "~/shared/db"; -import { InstanceRoot, Root } from "@webstudio-is/react-sdk"; -import { sentryException } from "~/shared/sentry"; - -type Data = CanvasData | { errors: string }; - -export const meta: MetaFunction = ({ data }: { data: Data }) => { - if ("errors" in data) { - return { title: "Error" }; - } - const { page } = data; - return { title: page.title, ...page.meta }; -}; - -export const loader: LoaderFunction = async ({ - params, - request, -}): Promise => { - try { - if (params.projectId === undefined) { - throw new Error("Missing projectId"); - } - - const url = new URL(request.url); - const pageIdParam = url.searchParams.get("pageId"); - - return loadCanvasData(params.projectId, pageIdParam || undefined); - } catch (error) { - sentryException({ error }); - return { errors: error instanceof Error ? error.message : String(error) }; - } -}; - -const Outlet = () => { - const data = useLoaderData(); - return ; -}; - -const PreviewRoute = () => { - const data = useLoaderData(); - if ("errors" in data) { - return

{data.errors}

; - } - return ; -}; - -export default PreviewRoute; diff --git a/apps/designer/app/shared/db/canvas.server.ts b/apps/designer/app/shared/db/canvas.server.ts index 1b05f12c13..9ed4fe0fa5 100644 --- a/apps/designer/app/shared/db/canvas.server.ts +++ b/apps/designer/app/shared/db/canvas.server.ts @@ -7,27 +7,28 @@ export type CanvasData = Data & { buildId: Build["id"]; page: Page }; export const loadCanvasData = async ( projectId: Project["id"], - pageId?: Page["id"] + env: "dev" | "prod", + pagePath = "" ): Promise => { - const project = await db.project.loadById(projectId); + const build = + env === "dev" + ? await db.build.loadByProjectId(projectId, "dev") + : await db.build.loadByProjectId(projectId, "prod"); - if (project === null) throw new Error(`Project "${projectId}" not found`); + if (build === undefined) { + throw new Error("The project is not published"); + } - const devBuild = await db.build.loadByProjectId(projectId, "dev"); - - const page = - pageId === undefined - ? devBuild.pages.homePage - : utils.pages.findById(devBuild.pages, pageId); + const page = utils.pages.findByPath(build.pages, pagePath); if (page === undefined) { - throw new Error(`Page "${pageId}" not found`); + throw new Error(`Page "${pagePath}" not found`); } const [tree, props, breakpoints] = await Promise.all([ db.tree.loadById(page.treeId), db.props.loadByTreeId(page.treeId), - db.breakpoints.load(devBuild.id), + db.breakpoints.load(build.id), ]); if (tree === null) { @@ -42,7 +43,7 @@ export const loadCanvasData = async ( tree, props, breakpoints: breakpoints.values, - buildId: devBuild.id, + buildId: build.id, page, }; }; diff --git a/packages/project/src/shared/pages/find-by-path.ts b/packages/project/src/shared/pages/find-by-path.ts new file mode 100644 index 0000000000..a286071248 --- /dev/null +++ b/packages/project/src/shared/pages/find-by-path.ts @@ -0,0 +1,11 @@ +import { type Pages, type Page } from "../../db/types"; + +export const findByPath = ( + pages: Pages, + pagePath: string +): Page | undefined => { + if (pagePath === "" || pagePath === "/") { + return pages.homePage; + } + return pages.pages.find((page) => page.path === pagePath); +}; diff --git a/packages/project/src/shared/pages/index.ts b/packages/project/src/shared/pages/index.ts index 41138b8354..d4526db190 100644 --- a/packages/project/src/shared/pages/index.ts +++ b/packages/project/src/shared/pages/index.ts @@ -1 +1,2 @@ export * from "./find-by-id"; +export * from "./find-by-path"; From 24bd4bd1af1364bcd1a8af7b19b15256575c827f Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 12:43:32 +0400 Subject: [PATCH 55/71] fix clonePages() --- packages/project/src/db/build.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/project/src/db/build.ts b/packages/project/src/db/build.ts index 0cddcc7c20..523b3a9359 100644 --- a/packages/project/src/db/build.ts +++ b/packages/project/src/db/build.ts @@ -2,7 +2,7 @@ import { prisma, Build as DbBuild } from "@webstudio-is/prisma-client"; import { type Breakpoint } from "@webstudio-is/react-sdk"; import { v4 as uuid } from "uuid"; import * as db from "."; -import { Build, Pages, PagesSchema } from "./types"; +import { Build, Page, Pages, PagesSchema } from "./types"; export const parseBuild = (build: DbBuild): Build => { const pages = PagesSchema.parse(JSON.parse(build.pages)); @@ -78,21 +78,23 @@ const createPages = async (breakpoints: Array) => { }); }; -const clonePages = async (source: Pages) => { - // @todo: clone pages other than home page - - const treeId = source.homePage.treeId; +const clonePage = async (source: Page) => { + const treeId = source.treeId; const tree = await db.tree.clone(treeId); await db.props.clone({ previousTreeId: treeId, nextTreeId: tree.id }); + return { ...source, id: uuid(), treeId: tree.id }; +}; + +const clonePages = async (source: Pages) => { + const clones = []; + for (const page of source.pages) { + clones.push(await clonePage(page)); + } return PagesSchema.parse({ - homePage: { - ...source.homePage, - id: uuid(), - treeId: tree.id, - }, - pages: [], + homePage: await clonePage(source.homePage), + pages: clones, }); }; From 26cc4985e861746eca630c4fc3b3b915c89560b1 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 13:42:37 +0400 Subject: [PATCH 56/71] reverse the ready event to account for canvas reloading --- .../app/canvas/shared/use-designer-ready.ts | 26 ++++++++++++++----- apps/designer/app/designer/designer.tsx | 20 +++----------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/designer/app/canvas/shared/use-designer-ready.ts b/apps/designer/app/canvas/shared/use-designer-ready.ts index 1a13769ca1..266143e8a0 100644 --- a/apps/designer/app/canvas/shared/use-designer-ready.ts +++ b/apps/designer/app/canvas/shared/use-designer-ready.ts @@ -1,18 +1,30 @@ import { useState } from "react"; +import { useInterval } from "react-use"; import { publish, useSubscribe } from "~/shared/pubsub"; declare module "~/shared/pubsub" { export interface PubsubMap { - designerReady: undefined; - designerReadyAck: undefined; + canvasReady: undefined; + canvasReadyAck: undefined; } } export const useSubscribeDesignerReady = () => { - const [isReady, setIsReady] = useState(false); - useSubscribe("designerReady", () => { - setIsReady(true); - publish({ type: "designerReadyAck" }); + const [isAcknowledged, setIsAcknowledged] = useState(false); + + useInterval( + () => { + // We publish this event to make sure designer is listening to the events, + // otherwise if canvas loads faster than designer, which is possible with SSR, + // we can miss the events and designer will just not connect to the canvas. + publish({ type: "canvasReady" }); + }, + isAcknowledged ? null : 100 + ); + + useSubscribe("canvasReadyAck", () => { + setIsAcknowledged(true); }); - return isReady; + + return isAcknowledged; }; diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 72cf5459a3..84a94da24e 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -42,7 +42,6 @@ import { useClientSettings } from "./shared/client-settings"; import { Navigator } from "./features/sidebar-left"; import { PANEL_WIDTH } from "./shared/constants"; import { Asset } from "@webstudio-is/asset-uploader"; -import { useInterval } from "react-use"; export const links = () => { return [ @@ -108,20 +107,9 @@ const useNavigatorLayout = () => { return isLoaded ? clientSettings.navigatorLayout : "docked"; }; -const usePublishDesignerReady = (publish: Publish) => { - const [isAcknowledged, setIsAcknowledged] = useState(false); - - useInterval( - () => { - // We publish this even to let canvas know that we are now listening to the events, otherwise if canvas loads faster than designer, which is possible with SSR, - // we can miss the events and designer will just not connect to the canvas. - publish({ type: "designerReady" }); - }, - isAcknowledged ? null : 100 - ); - - useSubscribe("designerReadyAck", () => { - setIsAcknowledged(true); +export const useSubscribeCanvasReady = (publish: Publish) => { + useSubscribe("canvasReady", () => { + publish({ type: "canvasReadyAck" }); }); }; @@ -289,7 +277,7 @@ export const Designer = ({ config, project, pages, pageId }: DesignerProps) => { const onRefReadCanvasWidth = useUpdateCanvasWidth(); const { onRef: onRefReadCanvas, onTransitionEnd } = useReadCanvasRect(); const [dragAndDropState] = useDragAndDropState(); - usePublishDesignerReady(publish); + useSubscribeCanvasReady(publish); const iframeRefCallback = useCallback( (ref) => { From 7250b169edde16d6ccb731e366e161b0d73450cc Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 13:55:06 +0400 Subject: [PATCH 57/71] remove outdated comment --- apps/designer/app/routes/$.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index 2547d31f35..fdbcf90d8c 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -121,8 +121,6 @@ const Content = () => { : () => ; // @todo This is non-standard for Remix, is there a better way? - // We need to render essentially the preview route but from the index, - // so we have to know the layout and the outlet from here. // Maybe there is a way to tell remix to use the right outlet somehow and avoid passing it? return ; }; From 9926695130fd287c6bd9915ef912df2c0d7776ca Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 13:57:06 +0400 Subject: [PATCH 58/71] fix import --- apps/designer/app/designer/designer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index 84a94da24e..bc6d149b11 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import { type Publish, usePublish, useSubscribe } from "~/shared/pubsub"; import { type Pages, From 0c010767965ef9b72ff1fb390898bd4d515e2c94 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 15:06:48 +0400 Subject: [PATCH 59/71] fix and use it for errors on canvas --- .../features/breakpoints/use-update-canvas-width.ts | 12 ++++++++++-- apps/designer/app/routes/$.tsx | 3 ++- apps/designer/app/shared/error/error-message.tsx | 12 ------------ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts b/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts index 24d9f98b0f..9f71a23802 100644 --- a/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts +++ b/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts @@ -1,4 +1,3 @@ -import { useSubscribe } from "~/shared/pubsub"; import { useCallback, useEffect } from "react"; import { useSelectedBreakpoint, @@ -32,7 +31,16 @@ export const useUpdateCanvasWidth = () => { setCanvasWidth(Math.max(selectedBreakpoint.minWidth, minWidth)); }, [selectedBreakpoint, setCanvasWidth]); - useSubscribe("canvasWidth", setCanvasWidth); + // Handle the case when canvas loads with a error + // And doesn't send any messages that it's supposed to send + useEffect(() => { + if (canvasWidth === 0) { + const timeout = setTimeout(() => { + setCanvasWidth(600); + }, 1000); + return () => clearTimeout(timeout); + } + }, [canvasWidth, setCanvasWidth]); // Set the initial canvas width based on the selected breakpoint upper bound, which starts where the next breakpoint begins. return useCallback( diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index fdbcf90d8c..522d4ee880 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -7,6 +7,7 @@ import { db } from "@webstudio-is/project/index.server"; import env, { Env } from "~/env.server"; import { sentryException } from "~/shared/sentry"; import { Canvas } from "~/canvas"; +import { ErrorMessage } from "~/shared/error"; type Mode = "edit" | "preview" | "published"; @@ -112,7 +113,7 @@ const Content = () => { const data = useLoaderData(); if ("errors" in data) { - return

{data.errors}

; + return ; } const Outlet = diff --git a/apps/designer/app/shared/error/error-message.tsx b/apps/designer/app/shared/error/error-message.tsx index daebed6b5f..7c6b6ddd07 100644 --- a/apps/designer/app/shared/error/error-message.tsx +++ b/apps/designer/app/shared/error/error-message.tsx @@ -1,16 +1,4 @@ -import { useEffect } from "react"; -import { publish } from "~/shared/pubsub"; - -declare module "~/shared/pubsub" { - export interface PubsubMap { - canvasWidth: number; - } -} - export const ErrorMessage = ({ message }: { message: string }) => { - useEffect(() => { - publish({ type: "canvasWidth", payload: 600 }); - }, []); return (

Error

From c06796fdf1125755b28d2cdf1f20d502a3df70b1 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 15:09:07 +0400 Subject: [PATCH 60/71] rename isCanvasRequest --- apps/designer/app/entry.server.tsx | 4 ++-- apps/designer/app/routes/$.tsx | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/designer/app/entry.server.tsx b/apps/designer/app/entry.server.tsx index 969eed6c8e..bb0a1594a9 100644 --- a/apps/designer/app/entry.server.tsx +++ b/apps/designer/app/entry.server.tsx @@ -4,7 +4,7 @@ import { initSentry } from "./shared/sentry"; import { prisma } from "@webstudio-is/prisma-client"; import { handleRequest as handleRequestDesigner } from "./shared/remix"; import { handleRequest as handleRequestCanvas } from "@webstudio-is/react-sdk"; -import { isCanvasRequest } from "./routes/$"; +import { getCanvasRequestParams } from "./routes/$"; initSentry({ integrations: [new Sentry.Integrations.Prisma({ client: prisma })], @@ -18,7 +18,7 @@ export default function handleRequest( ) { let handle = handleRequestDesigner; - if (isCanvasRequest(request)) { + if (getCanvasRequestParams(request) !== undefined) { handle = handleRequestCanvas; } diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index 522d4ee880..50b130a72e 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -17,15 +17,17 @@ type Data = type ProjectIdentifier = { type: "id" | "domain"; value: string }; -export const isCanvasRequest = ( +export const getCanvasRequestParams = ( request: Request -): false | { projectId: ProjectIdentifier; mode: Mode; pathname: string } => { +): + | { projectId: ProjectIdentifier; mode: Mode; pathname: string } + | undefined => { const url = new URL(request.url); const pathname = url.pathname; if (Object.values(config).some((path) => pathname.startsWith(path))) { - return false; + return undefined; } let projectId: ProjectIdentifier | undefined = undefined; @@ -48,7 +50,7 @@ export const isCanvasRequest = ( } if (projectId === undefined) { - return false; + return undefined; } const modeParam = url.searchParams.get("mode"); @@ -76,9 +78,9 @@ export const loader: LoaderFunction = async ({ request, }): Promise => { try { - const canvasRequest = isCanvasRequest(request); + const canvasRequest = getCanvasRequestParams(request); - if (canvasRequest === false) { + if (canvasRequest === undefined) { return redirect(config.dashboardPath); } From ac4ff9d46533070ee72e6c51cac58b9455c0a66a Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 15:11:27 +0400 Subject: [PATCH 61/71] rename projectId -> projectIdObject --- apps/designer/app/routes/$.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index 50b130a72e..9cd16a0dfc 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -20,7 +20,7 @@ type ProjectIdentifier = { type: "id" | "domain"; value: string }; export const getCanvasRequestParams = ( request: Request ): - | { projectId: ProjectIdentifier; mode: Mode; pathname: string } + | { projectIdObject: ProjectIdentifier; mode: Mode; pathname: string } | undefined => { const url = new URL(request.url); @@ -30,7 +30,7 @@ export const getCanvasRequestParams = ( return undefined; } - let projectId: ProjectIdentifier | undefined = undefined; + let projectIdObject: ProjectIdentifier | undefined = undefined; // @todo all this subdomain logic is very hacky const host = @@ -39,31 +39,31 @@ export const getCanvasRequestParams = ( ""; const [userDomain, wstdDomain] = host.split("."); if (wstdDomain === "wstd" || wstdDomain?.includes("localhost")) { - projectId = { type: "domain", value: userDomain }; + projectIdObject = { type: "domain", value: userDomain }; } - if (projectId === undefined) { + if (projectIdObject === undefined) { const projectIdParam = url.searchParams.get("projectId"); if (projectIdParam !== null) { - projectId = { type: "id", value: projectIdParam }; + projectIdObject = { type: "id", value: projectIdParam }; } } - if (projectId === undefined) { + if (projectIdObject === undefined) { return undefined; } const modeParam = url.searchParams.get("mode"); if (modeParam === "edit") { - return { projectId, mode: "edit", pathname }; + return { projectIdObject, mode: "edit", pathname }; } if (modeParam === "preview") { - return { projectId, mode: "preview", pathname }; + return { projectIdObject, mode: "preview", pathname }; } - return { projectId, mode: "published", pathname }; + return { projectIdObject, mode: "published", pathname }; }; export const meta: MetaFunction = ({ data }: { data: Data }) => { From f852cb434e27816029196ad6dc4a3bfbd84521c0 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 15:18:39 +0400 Subject: [PATCH 62/71] fix mode validation, fix projectIdObject --- apps/designer/app/routes/$.tsx | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index 9cd16a0dfc..c730ad30db 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -11,6 +11,8 @@ import { ErrorMessage } from "~/shared/error"; type Mode = "edit" | "preview" | "published"; +const modes = ["edit", "preview", "published"] as Mode[]; + type Data = | (CanvasData & { env: Env; mode: Mode }) | { errors: string; env: Env }; @@ -54,16 +56,13 @@ export const getCanvasRequestParams = ( } const modeParam = url.searchParams.get("mode"); - - if (modeParam === "edit") { - return { projectIdObject, mode: "edit", pathname }; + const mode = + modeParam === null ? "published" : modes.find((mode) => mode === modeParam); + if (mode === undefined) { + throw new Error(`Invalid mode "${modeParam}"`); } - if (modeParam === "preview") { - return { projectIdObject, mode: "preview", pathname }; - } - - return { projectIdObject, mode: "published", pathname }; + return { projectIdObject, mode, pathname }; }; export const meta: MetaFunction = ({ data }: { data: Data }) => { @@ -84,12 +83,12 @@ export const loader: LoaderFunction = async ({ return redirect(config.dashboardPath); } - const { projectId, mode, pathname } = canvasRequest; + const { projectIdObject, mode, pathname } = canvasRequest; const project = - projectId.type === "id" - ? await db.project.loadById(projectId.value) - : await db.project.loadByDomain(projectId.value); + projectIdObject.type === "id" + ? await db.project.loadById(projectIdObject.value) + : await db.project.loadByDomain(projectIdObject.value); if (project === null) { throw new Error("Project not found"); From df8bd6b7aad6fa693bbd911798f327ede9f0c371 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 16:20:04 +0400 Subject: [PATCH 63/71] add tests for getCanvasRequestParams --- apps/designer/app/entry.server.tsx | 2 +- apps/designer/app/routes/$.tsx | 58 +----------- .../canvas-request-params.test.ts | 91 +++++++++++++++++++ .../router-utils/canvas-request-params.ts | 58 ++++++++++++ .../designer/app/shared/router-utils/index.ts | 1 + 5 files changed, 156 insertions(+), 54 deletions(-) create mode 100644 apps/designer/app/shared/router-utils/canvas-request-params.test.ts create mode 100644 apps/designer/app/shared/router-utils/canvas-request-params.ts create mode 100644 apps/designer/app/shared/router-utils/index.ts diff --git a/apps/designer/app/entry.server.tsx b/apps/designer/app/entry.server.tsx index bb0a1594a9..207355a6c3 100644 --- a/apps/designer/app/entry.server.tsx +++ b/apps/designer/app/entry.server.tsx @@ -4,7 +4,7 @@ import { initSentry } from "./shared/sentry"; import { prisma } from "@webstudio-is/prisma-client"; import { handleRequest as handleRequestDesigner } from "./shared/remix"; import { handleRequest as handleRequestCanvas } from "@webstudio-is/react-sdk"; -import { getCanvasRequestParams } from "./routes/$"; +import { getCanvasRequestParams } from "./shared/router-utils"; initSentry({ integrations: [new Sentry.Integrations.Prisma({ client: prisma })], diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index c730ad30db..2741dd5b85 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -8,63 +8,15 @@ import env, { Env } from "~/env.server"; import { sentryException } from "~/shared/sentry"; import { Canvas } from "~/canvas"; import { ErrorMessage } from "~/shared/error"; - -type Mode = "edit" | "preview" | "published"; - -const modes = ["edit", "preview", "published"] as Mode[]; +import { + type CanvasRouteMode, + getCanvasRequestParams, +} from "~/shared/router-utils"; type Data = - | (CanvasData & { env: Env; mode: Mode }) + | (CanvasData & { env: Env; mode: CanvasRouteMode }) | { errors: string; env: Env }; -type ProjectIdentifier = { type: "id" | "domain"; value: string }; - -export const getCanvasRequestParams = ( - request: Request -): - | { projectIdObject: ProjectIdentifier; mode: Mode; pathname: string } - | undefined => { - const url = new URL(request.url); - - const pathname = url.pathname; - - if (Object.values(config).some((path) => pathname.startsWith(path))) { - return undefined; - } - - let projectIdObject: ProjectIdentifier | undefined = undefined; - - // @todo all this subdomain logic is very hacky - const host = - request.headers.get("x-forwarded-host") || - request.headers.get("host") || - ""; - const [userDomain, wstdDomain] = host.split("."); - if (wstdDomain === "wstd" || wstdDomain?.includes("localhost")) { - projectIdObject = { type: "domain", value: userDomain }; - } - - if (projectIdObject === undefined) { - const projectIdParam = url.searchParams.get("projectId"); - if (projectIdParam !== null) { - projectIdObject = { type: "id", value: projectIdParam }; - } - } - - if (projectIdObject === undefined) { - return undefined; - } - - const modeParam = url.searchParams.get("mode"); - const mode = - modeParam === null ? "published" : modes.find((mode) => mode === modeParam); - if (mode === undefined) { - throw new Error(`Invalid mode "${modeParam}"`); - } - - return { projectIdObject, mode, pathname }; -}; - export const meta: MetaFunction = ({ data }: { data: Data }) => { if ("errors" in data) { return { title: "Error" }; diff --git a/apps/designer/app/shared/router-utils/canvas-request-params.test.ts b/apps/designer/app/shared/router-utils/canvas-request-params.test.ts new file mode 100644 index 0000000000..6450791f12 --- /dev/null +++ b/apps/designer/app/shared/router-utils/canvas-request-params.test.ts @@ -0,0 +1,91 @@ +import { getCanvasRequestParams } from "./canvas-request-params"; + +const makeRequest = (url: string, headers: { [key: string]: string }) => ({ + url, + headers: { + get: (name: string) => (headers[name] === undefined ? null : headers[name]), + }, +}); + +describe("getCanvasRequestParams", () => { + test("returns undefined if the request is not an canvas request", () => { + expect( + getCanvasRequestParams(makeRequest("https://example.com", {})) + ).toBeUndefined(); + }); + + test("detects project domain on localhost via `x-forwarded-host` header", () => { + expect( + getCanvasRequestParams( + makeRequest("https://foo.localhost", { + "x-forwarded-host": "foo.localhost", + }) + )?.projectIdObject + ).toMatchInlineSnapshot(` + Object { + "type": "domain", + "value": "foo", + } + `); + }); + + test("detects project domain on localhost via `host` header", () => { + expect( + getCanvasRequestParams( + makeRequest("https://foo.localhost", { host: "foo.localhost" }) + )?.projectIdObject + ).toMatchInlineSnapshot(` + Object { + "type": "domain", + "value": "foo", + } + `); + }); + + test("detects projectId parameter", () => { + expect( + getCanvasRequestParams( + makeRequest("https://foo.localhost?projectId=bar", {}) + )?.projectIdObject + ).toMatchInlineSnapshot(` + Object { + "type": "id", + "value": "bar", + } + `); + }); + + test("detects path", () => { + expect( + getCanvasRequestParams( + makeRequest("https://foo.localhost/some/path?projectId=bar", {}) + )?.pathname + ).toMatchInlineSnapshot(`"/some/path"`); + }); + + test("detects mode", () => { + expect( + getCanvasRequestParams( + makeRequest( + "https://foo.localhost/some/path?projectId=bar&mode=edit", + {} + ) + )?.mode + ).toMatchInlineSnapshot(`"edit"`); + + expect( + getCanvasRequestParams( + makeRequest( + "https://foo.localhost/some/path?projectId=bar&mode=preview", + {} + ) + )?.mode + ).toMatchInlineSnapshot(`"preview"`); + + expect( + getCanvasRequestParams( + makeRequest("https://foo.localhost/some/path?projectId=bar", {}) + )?.mode + ).toMatchInlineSnapshot(`"published"`); + }); +}); diff --git a/apps/designer/app/shared/router-utils/canvas-request-params.ts b/apps/designer/app/shared/router-utils/canvas-request-params.ts new file mode 100644 index 0000000000..b4f18eb540 --- /dev/null +++ b/apps/designer/app/shared/router-utils/canvas-request-params.ts @@ -0,0 +1,58 @@ +import config from "~/config"; + +export type CanvasRouteMode = "edit" | "preview" | "published"; + +const modes = ["edit", "preview", "published"] as CanvasRouteMode[]; + +type ProjectIdentifier = { type: "id" | "domain"; value: string }; + +export const getCanvasRequestParams = (request: { + url: string; + headers: { get: (name: string) => string | null }; +}): + | { + projectIdObject: ProjectIdentifier; + mode: CanvasRouteMode; + pathname: string; + } + | undefined => { + const url = new URL(request.url); + + const pathname = url.pathname; + + if (Object.values(config).some((path) => pathname.startsWith(path))) { + return undefined; + } + + let projectIdObject: ProjectIdentifier | undefined = undefined; + + // @todo all this subdomain logic is very hacky + const host = + request.headers.get("x-forwarded-host") || + request.headers.get("host") || + ""; + const [userDomain, wstdDomain] = host.split("."); + if (wstdDomain === "wstd" || wstdDomain?.includes("localhost")) { + projectIdObject = { type: "domain", value: userDomain }; + } + + if (projectIdObject === undefined) { + const projectIdParam = url.searchParams.get("projectId"); + if (projectIdParam !== null) { + projectIdObject = { type: "id", value: projectIdParam }; + } + } + + if (projectIdObject === undefined) { + return undefined; + } + + const modeParam = url.searchParams.get("mode"); + const mode = + modeParam === null ? "published" : modes.find((mode) => mode === modeParam); + if (mode === undefined) { + throw new Error(`Invalid mode "${modeParam}"`); + } + + return { projectIdObject, mode, pathname }; +}; diff --git a/apps/designer/app/shared/router-utils/index.ts b/apps/designer/app/shared/router-utils/index.ts new file mode 100644 index 0000000000..79eef16bfe --- /dev/null +++ b/apps/designer/app/shared/router-utils/index.ts @@ -0,0 +1 @@ +export * from "./canvas-request-params"; From 20990cd4189b9e8b6100c865d576b3a0b11b0a5c Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 16:30:03 +0400 Subject: [PATCH 64/71] change return type of getCanvasRequestParams --- apps/designer/app/routes/$.tsx | 8 +-- .../canvas-request-params.test.ts | 21 ++++--- .../router-utils/canvas-request-params.ts | 58 ++++++++++++------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/apps/designer/app/routes/$.tsx b/apps/designer/app/routes/$.tsx index 2741dd5b85..21ff6aae15 100644 --- a/apps/designer/app/routes/$.tsx +++ b/apps/designer/app/routes/$.tsx @@ -35,12 +35,12 @@ export const loader: LoaderFunction = async ({ return redirect(config.dashboardPath); } - const { projectIdObject, mode, pathname } = canvasRequest; + const { mode, pathname } = canvasRequest; const project = - projectIdObject.type === "id" - ? await db.project.loadById(projectIdObject.value) - : await db.project.loadByDomain(projectIdObject.value); + "projectId" in canvasRequest + ? await db.project.loadById(canvasRequest.projectId) + : await db.project.loadByDomain(canvasRequest.projectDomain); if (project === null) { throw new Error("Project not found"); diff --git a/apps/designer/app/shared/router-utils/canvas-request-params.test.ts b/apps/designer/app/shared/router-utils/canvas-request-params.test.ts index 6450791f12..c09c7af2e3 100644 --- a/apps/designer/app/shared/router-utils/canvas-request-params.test.ts +++ b/apps/designer/app/shared/router-utils/canvas-request-params.test.ts @@ -20,11 +20,12 @@ describe("getCanvasRequestParams", () => { makeRequest("https://foo.localhost", { "x-forwarded-host": "foo.localhost", }) - )?.projectIdObject + ) ).toMatchInlineSnapshot(` Object { - "type": "domain", - "value": "foo", + "mode": "published", + "pathname": "/", + "projectDomain": "foo", } `); }); @@ -33,11 +34,12 @@ describe("getCanvasRequestParams", () => { expect( getCanvasRequestParams( makeRequest("https://foo.localhost", { host: "foo.localhost" }) - )?.projectIdObject + ) ).toMatchInlineSnapshot(` Object { - "type": "domain", - "value": "foo", + "mode": "published", + "pathname": "/", + "projectDomain": "foo", } `); }); @@ -46,11 +48,12 @@ describe("getCanvasRequestParams", () => { expect( getCanvasRequestParams( makeRequest("https://foo.localhost?projectId=bar", {}) - )?.projectIdObject + ) ).toMatchInlineSnapshot(` Object { - "type": "id", - "value": "bar", + "mode": "published", + "pathname": "/", + "projectId": "bar", } `); }); diff --git a/apps/designer/app/shared/router-utils/canvas-request-params.ts b/apps/designer/app/shared/router-utils/canvas-request-params.ts index b4f18eb540..e4720149c1 100644 --- a/apps/designer/app/shared/router-utils/canvas-request-params.ts +++ b/apps/designer/app/shared/router-utils/canvas-request-params.ts @@ -4,14 +4,17 @@ export type CanvasRouteMode = "edit" | "preview" | "published"; const modes = ["edit", "preview", "published"] as CanvasRouteMode[]; -type ProjectIdentifier = { type: "id" | "domain"; value: string }; - export const getCanvasRequestParams = (request: { url: string; headers: { get: (name: string) => string | null }; }): | { - projectIdObject: ProjectIdentifier; + projectId: string; + mode: CanvasRouteMode; + pathname: string; + } + | { + projectDomain: string; mode: CanvasRouteMode; pathname: string; } @@ -24,29 +27,26 @@ export const getCanvasRequestParams = (request: { return undefined; } - let projectIdObject: ProjectIdentifier | undefined = undefined; + let projectDomain = undefined; + let projectId = undefined; - // @todo all this subdomain logic is very hacky - const host = - request.headers.get("x-forwarded-host") || - request.headers.get("host") || - ""; - const [userDomain, wstdDomain] = host.split("."); - if (wstdDomain === "wstd" || wstdDomain?.includes("localhost")) { - projectIdObject = { type: "domain", value: userDomain }; + const projectIdParam = url.searchParams.get("projectId"); + if (projectIdParam !== null) { + projectId = projectIdParam; } - if (projectIdObject === undefined) { - const projectIdParam = url.searchParams.get("projectId"); - if (projectIdParam !== null) { - projectIdObject = { type: "id", value: projectIdParam }; + if (projectId === undefined) { + // @todo all this subdomain logic is very hacky + const host = + request.headers.get("x-forwarded-host") || + request.headers.get("host") || + ""; + const [userDomain, wstdDomain] = host.split("."); + if (wstdDomain === "wstd" || wstdDomain?.includes("localhost")) { + projectDomain = userDomain; } } - if (projectIdObject === undefined) { - return undefined; - } - const modeParam = url.searchParams.get("mode"); const mode = modeParam === null ? "published" : modes.find((mode) => mode === modeParam); @@ -54,5 +54,21 @@ export const getCanvasRequestParams = (request: { throw new Error(`Invalid mode "${modeParam}"`); } - return { projectIdObject, mode, pathname }; + if (projectId !== undefined) { + return { + projectId, + mode, + pathname, + }; + } + + if (projectDomain !== undefined) { + return { + projectDomain, + mode, + pathname, + }; + } + + return undefined; }; From b1f355f9413389bca8de55b1b5c24a629ccabb40 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 16:39:39 +0400 Subject: [PATCH 65/71] minor --- .../shared/router-utils/canvas-request-params.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/designer/app/shared/router-utils/canvas-request-params.ts b/apps/designer/app/shared/router-utils/canvas-request-params.ts index e4720149c1..5b6f0e7ecc 100644 --- a/apps/designer/app/shared/router-utils/canvas-request-params.ts +++ b/apps/designer/app/shared/router-utils/canvas-request-params.ts @@ -27,15 +27,10 @@ export const getCanvasRequestParams = (request: { return undefined; } + const projectId = url.searchParams.get("projectId"); + let projectDomain = undefined; - let projectId = undefined; - - const projectIdParam = url.searchParams.get("projectId"); - if (projectIdParam !== null) { - projectId = projectIdParam; - } - - if (projectId === undefined) { + if (projectId === null) { // @todo all this subdomain logic is very hacky const host = request.headers.get("x-forwarded-host") || @@ -54,7 +49,7 @@ export const getCanvasRequestParams = (request: { throw new Error(`Invalid mode "${modeParam}"`); } - if (projectId !== undefined) { + if (projectId !== null) { return { projectId, mode, @@ -70,5 +65,5 @@ export const getCanvasRequestParams = (request: { }; } - return undefined; + return; }; From f8904603a076757e015f99473d753d62ece69d31 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 17:13:53 +0400 Subject: [PATCH 66/71] don't use `config` in getCanvasRequestParams --- apps/designer/app/entry.server.tsx | 8 +++++++- .../app/shared/router-utils/canvas-request-params.ts | 6 ------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/designer/app/entry.server.tsx b/apps/designer/app/entry.server.tsx index 207355a6c3..286acca6ae 100644 --- a/apps/designer/app/entry.server.tsx +++ b/apps/designer/app/entry.server.tsx @@ -18,7 +18,13 @@ export default function handleRequest( ) { let handle = handleRequestDesigner; - if (getCanvasRequestParams(request) !== undefined) { + if ( + remixContext.matches.some( + (match) => + match.route.id === "routes/$" || match.route.id === "routes/index" + ) && + getCanvasRequestParams(request) !== undefined + ) { handle = handleRequestCanvas; } diff --git a/apps/designer/app/shared/router-utils/canvas-request-params.ts b/apps/designer/app/shared/router-utils/canvas-request-params.ts index 5b6f0e7ecc..bc10e28443 100644 --- a/apps/designer/app/shared/router-utils/canvas-request-params.ts +++ b/apps/designer/app/shared/router-utils/canvas-request-params.ts @@ -1,5 +1,3 @@ -import config from "~/config"; - export type CanvasRouteMode = "edit" | "preview" | "published"; const modes = ["edit", "preview", "published"] as CanvasRouteMode[]; @@ -23,10 +21,6 @@ export const getCanvasRequestParams = (request: { const pathname = url.pathname; - if (Object.values(config).some((path) => pathname.startsWith(path))) { - return undefined; - } - const projectId = url.searchParams.get("projectId"); let projectDomain = undefined; From 194a0a435067926bbed3d689961b9a7d420c2b67 Mon Sep 17 00:00:00 2001 From: Roman Pominov Date: Sat, 8 Oct 2022 17:19:09 +0400 Subject: [PATCH 67/71] change fallback width delay to 3000 and better code comment --- .../features/breakpoints/use-update-canvas-width.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts b/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts index 9f71a23802..1dc7a29e7b 100644 --- a/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts +++ b/apps/designer/app/designer/features/breakpoints/use-update-canvas-width.ts @@ -31,13 +31,16 @@ export const useUpdateCanvasWidth = () => { setCanvasWidth(Math.max(selectedBreakpoint.minWidth, minWidth)); }, [selectedBreakpoint, setCanvasWidth]); - // Handle the case when canvas loads with a error - // And doesn't send any messages that it's supposed to send + // This fallback is needed for cases when something unexpected loads in the iframe. + // In that case the width remains 0, and user is unable to see what has loaded, + // in particular any error messages. + // The delay is used to make sure we don't set the fallback width too early, + // because when canvas loads normally this will cause a jump in the width. useEffect(() => { if (canvasWidth === 0) { const timeout = setTimeout(() => { setCanvasWidth(600); - }, 1000); + }, 3000); return () => clearTimeout(timeout); } }, [canvasWidth, setCanvasWidth]); From 1ff68392396048b0a9d73f93c425130dd577c962 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 8 Oct 2022 16:36:59 +0200 Subject: [PATCH 68/71] fix $untitled font family --- packages/design-system/src/components/button.tsx | 2 +- packages/design-system/src/components/menu.tsx | 2 +- packages/design-system/src/components/text-field.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/design-system/src/components/button.tsx b/packages/design-system/src/components/button.tsx index eb0394f82d..edee5f240b 100644 --- a/packages/design-system/src/components/button.tsx +++ b/packages/design-system/src/components/button.tsx @@ -25,7 +25,7 @@ export const Button = styled("button", { // Custom height: "$5", px: "$2", - fontFamily: "$untitled", + fontFamily: "$sans", fontSize: "$2", fontWeight: 500, fontVariantNumeric: "tabular-nums", diff --git a/packages/design-system/src/components/menu.tsx b/packages/design-system/src/components/menu.tsx index cd0a2eea91..0019f338ad 100644 --- a/packages/design-system/src/components/menu.tsx +++ b/packages/design-system/src/components/menu.tsx @@ -10,7 +10,7 @@ export const baseItemCss = css({ display: "flex", alignItems: "center", justifyContent: "space-between", - fontFamily: "$untitled", + fontFamily: "$sans", fontSize: "$1", fontVariantNumeric: "tabular-nums", lineHeight: "1", diff --git a/packages/design-system/src/components/text-field.tsx b/packages/design-system/src/components/text-field.tsx index c6e79b1ae8..aadbcba47f 100644 --- a/packages/design-system/src/components/text-field.tsx +++ b/packages/design-system/src/components/text-field.tsx @@ -43,7 +43,7 @@ const InputBase = styled("input", { }, "&:-webkit-autofill::first-line": { - fontFamily: "$untitled", + fontFamily: "$sans", color: "$hiContrast", }, From e777b735f3cdc649a7bcaa33449d658a299d4599 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 8 Oct 2022 18:28:28 +0200 Subject: [PATCH 69/71] apply figma design to text-field, remove size variant --- .../src/components/text-field.stories.tsx | 32 +++----------- .../src/components/text-field.tsx | 44 +++++++++---------- 2 files changed, 27 insertions(+), 49 deletions(-) diff --git a/packages/design-system/src/components/text-field.stories.tsx b/packages/design-system/src/components/text-field.stories.tsx index 28b2bef61e..f49a1c0adb 100644 --- a/packages/design-system/src/components/text-field.stories.tsx +++ b/packages/design-system/src/components/text-field.stories.tsx @@ -2,7 +2,7 @@ import { expect } from "@storybook/jest"; import { userEvent, waitFor, within } from "@storybook/testing-library"; import React from "react"; import { ComponentStory } from "@storybook/react"; -import { BrushIcon, ChevronDownIcon } from "@webstudio-is/icons"; +import { RowGapIcon, ChevronDownIcon } from "@webstudio-is/icons"; import { Button } from "./button"; import { Flex } from "./flex"; import { IconButton } from "./icon-button"; @@ -36,15 +36,6 @@ export const NativeProps: ComponentStory = () => { ); }; -export const Sizes: ComponentStory = () => { - return ( - - - - - ); -}; - export const Variants: ComponentStory = () => { return ( @@ -68,7 +59,7 @@ export const PrefixSuffix: ComponentStory = () => { return ( } + prefix={} suffix={ @@ -77,25 +68,16 @@ export const PrefixSuffix: ComponentStory = () => { /> } + prefix={} suffix={ } /> - } - suffix={ - - - - } - /> } + prefix={} suffix={ @@ -112,7 +94,7 @@ export const Layout: ComponentStory = () => { } + prefix={} suffix={ @@ -147,7 +129,7 @@ export const Layout: ComponentStory = () => { } + prefix={} /> @@ -222,7 +204,7 @@ export const ClickCapture: ComponentStory = (args) => { placeholder="Click on the icon to focus input" prefix={ - + } suffix={ diff --git a/packages/design-system/src/components/text-field.tsx b/packages/design-system/src/components/text-field.tsx index aadbcba47f..d84988cee5 100644 --- a/packages/design-system/src/components/text-field.tsx +++ b/packages/design-system/src/components/text-field.tsx @@ -12,6 +12,7 @@ const InputBase = styled("input", { boxSizing: "border-box", fontFamily: "inherit", fontSize: "inherit", + color: "inherit", margin: "0", padding: "0", flexGrow: 1, @@ -69,9 +70,23 @@ const TextFieldBase = styled("div", { color: "$hiContrast", fontVariantNumeric: "tabular-nums", + px: "$2", + borderRadius: "$1", + fontFamily: "$sans", + fontSize: "$1", + height: 28, // @todo waiting for the sizing scale + lineHeight: 1, + "&[data-has-prefix]": { + paddingLeft: 2, + }, + + "&[data-has-suffix]": { + paddingRight: 2, + }, + "&:focus-within": { boxShadow: - "inset 0px 0px 0px 1px $colors$blue8, 0px 0px 0px 1px $colors$blue8", + "inset 0px 0px 0px 1px $colors$blue10, 0px 0px 0px 1px $colors$blue10", }, "&[aria-disabled=true]": { @@ -86,22 +101,6 @@ const TextFieldBase = styled("div", { }, variants: { - size: { - "1": { - px: "$1", - borderRadius: "$1", - fontSize: "$1", - height: "$5", - lineHeight: "$sizes$5", - }, - "2": { - px: "$2", - borderRadius: "$2", - fontSize: "$3", - height: "$6", - lineHeight: "$sizes$6", - }, - }, variant: { ghost: { boxShadow: "none", @@ -114,7 +113,7 @@ const TextFieldBase = styled("div", { "&:focus": { backgroundColor: "$loContrast", boxShadow: - "inset 0px 0px 0px 1px $colors$blue8, 0px 0px 0px 1px $colors$blue8", + "inset 0px 0px 0px 1px $colors$blue8, 0px 0px 0px 1px $colors$blue10", }, "&:disabled": { backgroundColor: "transparent", @@ -141,14 +140,11 @@ const TextFieldBase = styled("div", { }, }, }, - defaultVariants: { - size: "1", - }, }); type TextFieldProps = Pick< React.ComponentProps, - "size" | "variant" | "state" | "css" + "variant" | "state" | "css" > & Omit, "prefix" | "children"> & { inputRef?: React.Ref; @@ -164,7 +160,6 @@ export const TextField = React.forwardRef( css, disabled, inputRef, - size, state, variant, onFocus, @@ -194,11 +189,12 @@ export const TextField = React.forwardRef( {/* We want input to be the first element in DOM so it receives the focus first */} From 652fcea031dc91bccaa8c5293eb94d5e24427f2e Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 8 Oct 2022 19:51:35 +0200 Subject: [PATCH 70/71] remove size prop and fix search icon --- apps/designer/app/auth/login.tsx | 1 - apps/designer/app/dashboard/components/card.tsx | 1 - apps/designer/app/designer/features/style-panel/search.tsx | 6 +++++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/designer/app/auth/login.tsx b/apps/designer/app/auth/login.tsx index 9e2794fc14..f300aa904f 100644 --- a/apps/designer/app/auth/login.tsx +++ b/apps/designer/app/auth/login.tsx @@ -84,7 +84,6 @@ export const Login = ({ errorMessage }: { errorMessage: string }) => { }} > { type="search" value={search} inputRef={inputRef} - prefix={} + prefix={ + + + + } suffix={ search.length > 0 && ( Date: Sun, 9 Oct 2022 10:33:30 +0200 Subject: [PATCH 71/71] update lost pixel baseline --- .../baseline/components-combobox--complex.png | Bin 0 -> 10732 bytes .../baseline/components-combobox--simple.png | Bin 7001 -> 7450 bytes .../components-text-field--click-capture.png | Bin 0 -> 8950 bytes .../components-text-field--default.png | Bin 0 -> 5601 bytes .../components-text-field--focus-events.png | Bin 0 -> 8470 bytes .../components-text-field--interactive.png | Bin 0 -> 10014 bytes .../components-text-field--layout.png | Bin 0 -> 18943 bytes .../components-text-field--native-props.png | Bin 0 -> 16180 bytes .../components-text-field--prefix-suffix.png | Bin 0 -> 7058 bytes .../baseline/components-text-field--state.png | Bin 0 -> 6193 bytes .../components-text-field--variants.png | Bin 0 -> 5601 bytes 11 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .lostpixel/baseline/components-combobox--complex.png create mode 100644 .lostpixel/baseline/components-text-field--click-capture.png create mode 100644 .lostpixel/baseline/components-text-field--default.png create mode 100644 .lostpixel/baseline/components-text-field--focus-events.png create mode 100644 .lostpixel/baseline/components-text-field--interactive.png create mode 100644 .lostpixel/baseline/components-text-field--layout.png create mode 100644 .lostpixel/baseline/components-text-field--native-props.png create mode 100644 .lostpixel/baseline/components-text-field--prefix-suffix.png create mode 100644 .lostpixel/baseline/components-text-field--state.png create mode 100644 .lostpixel/baseline/components-text-field--variants.png diff --git a/.lostpixel/baseline/components-combobox--complex.png b/.lostpixel/baseline/components-combobox--complex.png new file mode 100644 index 0000000000000000000000000000000000000000..4e21c7d2876529e1d4b62aee2ba63347ca98d115 GIT binary patch literal 10732 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 literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-combobox--simple.png b/.lostpixel/baseline/components-combobox--simple.png index 9042b09cfedfd522797ea75426e03b3647ba4d3c..dae0b9a6e9e49d9a41ffb1de59e0f82600a0ea50 100644 GIT binary patch delta 2342 zcmX9=dsI@{8mFwB8Aq+vDoeAvHIuV$Y27hxe8Ds})!Y;-1;nQ_l3Bb!$Dws>!o|NW=OH-Ei3nB!AF$pbSR@LPMc zmxn}u)3pW96*^EZm(682f22Aq#5vvSURVqBfHo`o>j<(S z{m`K*g?u)yufJcscKb3Ikls3QPW~gn!gAA&UH9|0MsvApQz3QZdBbdRPhRmK?tOf0 ztQVxnniG8oUs>EupD2Jdj20$SYP=uDB2M4G64Pf~@OiwQDJAtm>-a6wD=-j&VH0?@ zyYNYs;=|&|?Or*~`U`gv4t6WO`fpzAfOfC-h^j!;F`22XajvKI9w9SRPk!B7ULmtUWx~_a z1o>`;Iz0$|qtE{PXb@WYwslZ{0Rwy6gYx?Z;nYcm2DL3FxuWWZJTbx5=Cn?6anw^I z@l|yb1qOy$YwMDg48=wf354^TDqzQKH2m9tChrjG*?%RM~!XDwlhPexWEC?&aGagzW52u>F~SRcH}FadlC6UkMS_+G3$ z$n@_!*z*9$$WE4sA&BF8Q5yUBT{O*+ zYE%CA+v_X^QNgS-x1pR*)$lj0blLRMls~=)+FU%=A+Yd(qX|eyTzq`IDxqP4D9L#J2f< z!LYzrb;Ins*W3(}6#xF{Cf7{)G+b0Wi83vXPg5%whdB-M&To$pyFyd9 zOK)Jn&{P_Y#-D(8%2bk#Fz@~e5o-Adl?vCb>Ev%No1I<+0p~wywo4Tx+jU8##eM^j zGFpr%QB#+;IF>d(XCmu#jgh4VXZrt>o)D9`6Ss?-Fx1JuSpL=N`G0F+5F0CYgZ~-Ca3DWkL?z_@>c4}x^J*uE) z-qTIybBLwY)U8+>Rm8A#2Nh1WC1guXm)k}~L(7se{9&4r^wh3Y4Kug%i_A{SJT*by zxc!iEbc^F~PpPhk7*4lNT6SYG7^*1U(>IV)v$M7#q6~0d!Vyp9b&5;OZr1cDk@#*zmF{9Nk{ z+}Fi^i_+aL8g1@|Of{snHZ4B6TXg50GdDvh6wfya4Gf`#vR`>Y>!Ewu(GdyID58** z?k;5;9z()%Au_4LD6ZJT4&X{S1ffP?QnK^4gQ|0TiOqu%7TaW2HkB6Ev_y-(71NbD+yAOX$Z<-ch|i)UeUil!v()sx1R`Tef?_iGa^ zzr&^RSoo}&#NhzcDJKz*6^BvyO^HsUm(&G)3coXfddC`FT^*Wc(RvzQy!cd??bfWd z=}2u7M)C1BK1C*J2mLE?w`Vk-Me>&&iTQ*!$>eYE9V0CJUjNF)0S;)m60=)DRkGPk zCX-Al6+uwhd;_Yr!8P&-f(+(dj8KEr(*d8rh~okKL|peYAc`~`Hy_DIV@hZ7vAfX` zSRRju!SMA>J^Bw^#Sn@s^nw+BVM$rBG;(_ilFYx7Koa{5D(%fYj=XFNk@o0Ri9_oy zNt8Gr@b;Ngjg1uq<3;Vro@^2ZlWiZ98tZMyWJzgRQG6U)UV}>mjvcwZi;?fCtL;82w&X1%Ft`p0v4Z)|W(uI#w*jUZdbbx_mD`l+@PD^&r8*7J=!-k+roJb;`Hra`z z>jL_-sC`moXVd1=v5piW;US?p(=cPhV-pi|O+Mn@OP2&Q9s!1(84g=Y`^o+3g)g6x z^Crhm*lB>yVKh433GcDbsZ8qJ5|#@Ir^nd<=fOJW+t4C$)(Z`{>}K}aSGDPHEH<)G zvk>iAyvoq@fZ=VDEWEu-y;gB9d^~!}PM@AG^!5(^!8q`KXzxEFPOu4<*HJEf=-@;W zI2b{iDi)@*39MDC7G-pWsgR{%04-O%D#rP9`@VONSck)rIdw8xk4#KdOcW4@kLK#( zcTJ}`Yvax-YXU-CrYP|>ubDMBRbM-vN6k9B7k+)Xvbm$H&X$ZPH6{B)V+UUjMK+LmMD> zuggb%j*XE~4=vvc-|`u#<{~_Jm%i#<&D-k5S?)IpAY9yb3$GF(hwZ=oj6T5m>Ez}A E0eqGL!2kdN delta 1882 zcmX9B@!7LWE!&90(7y0i$}H63TptYQdLL1vbzZ7teLq(X3N7ESzUClf!C$~T?P zm}4wf9qkY)#b zkpBh%9xj)nzl0YzYUjtR>vl>pR_fHX}3OSWQOe=?>i4i2#?CZ@8Tf zoGoT!U{558pQO@~h65p}U@)h% zmnM_T)WG6q&bVFYG7%IX7l&x&8Dme{*vTHQ!LHNO)9`T|%8A_7CBzh9u_Zn>>p@4b zRttg3U(cKAFB8c&^j{l5AKFyUBaNV9wPDRf@ji@lto)Bcf{(fAFLm(WVDQaOa}xZ! zixrxpk1VwK#AqH02m^qHI2CL3KeTw`S2wZV#p^OLgTca6Xdvf7uG@6q;`?|U`9stry%_+*pS|jk@rPW4rjnFh-#||sa>P%vxk`V|> zXkI&=US2*X>%$iLrOu>84Ye9)TIKX!nhKrm9Z6_EHbrG&CX){DpEC}w#6hfF0)piS zOt3pXPmRL+=fIKK+14mbckuni(%$eZXlf|wrBy%>i9|=o%ISx?0S2%!pqVUU-Zt!( za|^X7?fi}g)ms<`hHc5+&+t=YO|DXd0S=)cs8p(z-C6oP>PO8ovU`F~zQZGerB%~O0^0KP!je-fb}?v$z5SKI9c+hP=t zfMBNZxGO>mQc3taB~Mmdz`Q=NZ(AWpWD#h_zm2VXb*+}JL{H|p2 z{Pio!8SXM)2~3ajD;H*QDvi;_S6xrDJXM4Rt$re~rExBavLRrREOQB(Hd<2~ovtIP z){2G&aXZSLlP)WgM*G29{UoAZDC;E68>eJ+07J-Hd2+_3wid#|DxkW2?V1mj3dZAaEJjuD zH5=bOkHd?D5yTxy$2>8zZWoCVZ>@JtMao~2V~1MBy}K1sdB%AQ3JWnPGA!Ad}=@ZXsj7)+3#&lFDfbm zhZ``|u+k?Vf9#9hT;Jnn3f1cT;11o^8wyRmy5%DNS9f0!b-YMhBH#!=kJC?K7R!!W zP`J@9IOGYK9BVr=T+#)G6Z%YMS59|z&&pwB%aSXfv{-2kZeA?KW(mqKTzu2U%^u=b zM{Y(g&0mg`~Nam0_*= z2P%&317bvtjQFZwZ~hZaY3N7>?2lQYWUmy7LXq~C8^#b6+%|3pqaKC&zV~f6SAQy_ m%g*u0iLQX-6-V7>4ncoA{{^AawRg@&a_Ou1=#GD6eD{Aw5AOW{ diff --git a/.lostpixel/baseline/components-text-field--click-capture.png b/.lostpixel/baseline/components-text-field--click-capture.png new file mode 100644 index 0000000000000000000000000000000000000000..3511c42f5b3da6a9266eac7729d596566f67cd64 GIT binary patch literal 8950 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 literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-text-field--default.png b/.lostpixel/baseline/components-text-field--default.png new file mode 100644 index 0000000000000000000000000000000000000000..f7db5735a2a6f03c115e7c1ece416e06f7e86b75 GIT binary patch literal 5601 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 literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-text-field--interactive.png b/.lostpixel/baseline/components-text-field--interactive.png new file mode 100644 index 0000000000000000000000000000000000000000..ce559ea3f99368205e482b460e549b193a7d92f1 GIT binary patch literal 10014 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 R(@EwCl~s9CB>n95e*tr7Nk9Mq literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-text-field--native-props.png b/.lostpixel/baseline/components-text-field--native-props.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0764f33b6b8b08cd601f18bde7ab2a49513dea GIT binary patch literal 16180 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 I>%KGp0K@z23jhEB literal 0 HcmV?d00001 diff --git a/.lostpixel/baseline/components-text-field--variants.png b/.lostpixel/baseline/components-text-field--variants.png new file mode 100644 index 0000000000000000000000000000000000000000..f7db5735a2a6f03c115e7c1ece416e06f7e86b75 GIT binary patch literal 5601 zcmeAS@N?(olHy`uVBq!ia0y~yUX8DA@kRIFoWTrEt3LBq=mBqL!a1HE9X65iX1tgtPDT~{YgfP8aowm+F