From 9844c508c24b7faaa1d2455db0ef5fd3b8bd2844 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 30 Mar 2023 12:56:59 +0200 Subject: [PATCH 01/64] Add Radix deps --- package-lock.json | 339 +++++++++++++++++++++++++++++++ packages/components/package.json | 2 + 2 files changed, 341 insertions(+) diff --git a/package-lock.json b/package-lock.json index 040fe3911f377..b5a41035e2bdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7125,6 +7125,67 @@ "@babel/runtime": "^7.13.10" } }, + "@radix-ui/react-arrow": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.2.tgz", + "integrity": "sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "dependencies": { + "@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + } + } + } + }, + "@radix-ui/react-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.2.tgz", + "integrity": "sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-slot": "1.0.1" + }, + "dependencies": { + "@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + } + } + } + }, "@radix-ui/react-compose-refs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", @@ -7163,6 +7224,14 @@ "react-remove-scroll": "2.5.4" } }, + "@radix-ui/react-direction": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz", + "integrity": "sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, "@radix-ui/react-dismissable-layer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz", @@ -7176,6 +7245,41 @@ "@radix-ui/react-use-escape-keydown": "1.0.0" } }, + "@radix-ui/react-dropdown-menu": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.4.tgz", + "integrity": "sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-menu": "2.0.4", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "dependencies": { + "@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + } + } + } + }, "@radix-ui/react-focus-guards": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz", @@ -7195,6 +7299,11 @@ "@radix-ui/react-use-callback-ref": "1.0.0" } }, + "@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==" + }, "@radix-ui/react-id": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", @@ -7204,6 +7313,166 @@ "@radix-ui/react-use-layout-effect": "1.0.0" } }, + "@radix-ui/react-menu": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.4.tgz", + "integrity": "sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-collection": "1.0.2", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-dismissable-layer": "1.0.3", + "@radix-ui/react-focus-guards": "1.0.0", + "@radix-ui/react-focus-scope": "1.0.2", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-popper": "1.1.1", + "@radix-ui/react-portal": "1.0.2", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-roving-focus": "1.0.3", + "@radix-ui/react-slot": "1.0.1", + "@radix-ui/react-use-callback-ref": "1.0.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "dependencies": { + "@radix-ui/react-dismissable-layer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.3.tgz", + "integrity": "sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-escape-keydown": "1.0.2" + } + }, + "@radix-ui/react-focus-scope": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.2.tgz", + "integrity": "sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0" + } + }, + "@radix-ui/react-portal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz", + "integrity": "sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + } + }, + "@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + } + }, + "@radix-ui/react-use-escape-keydown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz", + "integrity": "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + } + }, + "react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "requires": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + } + } + } + }, + "@radix-ui/react-popper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.1.tgz", + "integrity": "sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==", + "requires": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "0.7.2", + "@radix-ui/react-arrow": "1.0.2", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0", + "@radix-ui/react-use-rect": "1.0.0", + "@radix-ui/react-use-size": "1.0.0", + "@radix-ui/rect": "1.0.0" + }, + "dependencies": { + "@floating-ui/core": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", + "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==" + }, + "@floating-ui/dom": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz", + "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==", + "requires": { + "@floating-ui/core": "^0.7.3" + } + }, + "@floating-ui/react-dom": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz", + "integrity": "sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==", + "requires": { + "@floating-ui/dom": "^0.5.3", + "use-isomorphic-layout-effect": "^1.1.1" + } + }, + "@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + } + } + } + }, "@radix-ui/react-portal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.0.tgz", @@ -7232,6 +7501,43 @@ "@radix-ui/react-slot": "1.0.0" } }, + "@radix-ui/react-roving-focus": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.3.tgz", + "integrity": "sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-collection": "1.0.2", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "dependencies": { + "@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + } + } + } + }, "@radix-ui/react-slot": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.0.tgz", @@ -7275,6 +7581,32 @@ "@babel/runtime": "^7.13.10" } }, + "@radix-ui/react-use-rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz", + "integrity": "sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.0" + } + }, + "@radix-ui/react-use-size": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz", + "integrity": "sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + } + }, + "@radix-ui/rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz", + "integrity": "sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, "@react-native-clipboard/clipboard": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.9.0.tgz", @@ -17003,6 +17335,8 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "1.0.0", + "@radix-ui/react-dropdown-menu": "^2.0.4", + "@radix-ui/react-icons": "^1.3.0", "@use-gesture/react": "^10.2.24", "@wordpress/a11y": "file:packages/a11y", "@wordpress/compose": "file:packages/compose", @@ -56292,6 +56626,11 @@ "tslib": "^2.0.0" } }, + "use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" + }, "use-lilius": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/use-lilius/-/use-lilius-2.0.1.tgz", diff --git a/packages/components/package.json b/packages/components/package.json index 0077e73d3f1da..57a48e2340bc8 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -38,6 +38,8 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "1.0.0", + "@radix-ui/react-dropdown-menu": "^2.0.4", + "@radix-ui/react-icons": "^1.3.0", "@use-gesture/react": "^10.2.24", "@wordpress/a11y": "file:../a11y", "@wordpress/compose": "file:../compose", From 0e7974123e2fca6b1ec074d7201ab11d3dced1d5 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 30 Mar 2023 13:08:14 +0200 Subject: [PATCH 02/64] Re-export radix dropdown with some custom styles --- .../src/ui/radix-dropdown/index.tsx | 27 +++ .../src/ui/radix-dropdown/styles.ts | 158 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 packages/components/src/ui/radix-dropdown/index.tsx create mode 100644 packages/components/src/ui/radix-dropdown/styles.ts diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx new file mode 100644 index 0000000000000..ce76361da1f9b --- /dev/null +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -0,0 +1,27 @@ +/** + * External dependencies + */ +// Baseline: export all radix components +export * from '@radix-ui/react-dropdown-menu'; + +/** + * Internal dependencies + */ +// Overrides some radix components with our styled version +export { + Arrow, + CheckboxItem, + Content, + Item, + ItemIndicator, + Label, + RadioItem, + Root, + Separator, + SubContent, + SubTrigger, +} from './styles'; + +// Questions: +// - do we want to have our own JSDocs ? +// - do we want to more explicitly PICK prop types? diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts new file mode 100644 index 0000000000000..752e9f80721e3 --- /dev/null +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -0,0 +1,158 @@ +/** + * External dependencies + */ +import styled from '@emotion/styled'; +import { css, keyframes } from '@emotion/react'; + +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; + +const slideDownAndFade = keyframes` + from { + opacity: 0; + transform: translateY(-2px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; +const slideLeftAndFade = keyframes` + from { + opacity: 0; + transform: translateX(2px); + } + to { + opacity: 1; + transform: translateX(0); + } +`; +const slideUpAndFade = keyframes` + from { + opacity: 0; + transform: translateY(2px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; +const slideRightAndFade = keyframes` + from { + opacity: 0; + transform: translateX(-2px); + } + to { + opacity: 1; + transform: translateX(0); + } +`; + +const baseContent = css` + min-width: 220px; + background-color: white; + border-radius: 6px; + padding: 5px; + box-shadow: 0px 10px 38px -10px rgba( 22, 23, 24, 0.35 ), + 0px 10px 20px -15px rgba( 22, 23, 24, 0.2 ); + animation-duration: 400ms; + animation-timing-function: cubic-bezier( 0.16, 1, 0.3, 1 ); + will-change: transform, opacity; + + &[data-side='top'] { + animation-name: ${ slideDownAndFade }; + } + + &[data-side='right'] { + animation-name: ${ slideLeftAndFade }; + } + + &[data-side='bottom'] { + animation-name: ${ slideUpAndFade }; + } + + &[data-side='left'] { + animation-name: ${ slideRightAndFade }; + } +`; + +const baseItem = css` + font-size: 13px; + line-height: 1; + color: hsl( 250, 95%, 76.8% ); // violet11 + border-radius: 3px; + display: flex; + align-items: center; + height: 25px; + padding: 0 5px; + position: relative; + padding-left: 25px; + user-select: none; + outline: none; + + &[data-disabled] { + color: hsl( 247, 4.8%, 32.5% ); // mauve8 + pointer-events: none; + } + + &[data-highlighted] { + background-color: hsl( 252, 56%, 57.5% ); // violet9 + color: hsl( 250, 20%, 10.2% ); // violet1 + } +`; + +export const Root = styled( DropdownMenu.Root )` + button { + all: unset; + } +`; + +export const Content = styled( DropdownMenu.Content )` + ${ baseContent } +`; +export const SubContent = styled( DropdownMenu.SubContent )` + ${ baseContent } +`; + +export const Item = styled( DropdownMenu.Item )` + ${ baseItem } +`; +export const CheckboxItem = styled( DropdownMenu.CheckboxItem )` + ${ baseItem } +`; +export const RadioItem = styled( DropdownMenu.RadioItem )` + ${ baseItem } +`; +export const SubTrigger = styled( DropdownMenu.SubTrigger )` + ${ baseItem } + + &[data-state='open'] { + background-color: hsl( 252, 40.1%, 22.5% ); // violet4 + color: hsl( 250, 95%, 76.8% ); // violet11 + } +`; + +export const Label = styled( DropdownMenu.Label )` + padding-left: 25px; + font-size: 12px; + line-height: 25px; + color: hsl( 253, 4%, 63.7% ); // mauve11 +`; + +export const Separator = styled( DropdownMenu.Separator )` + height: 1px; + background-color: hsl(251, 44.3%, 31.1%) // violet6; + margin: 5px; +`; + +export const ItemIndicator = styled( DropdownMenu.ItemIndicator )` + position: absolute; + left: 0; + width: 25px; + display: inline-flex; + align-items: center; + justify-content: center; +`; + +export const Arrow = styled( DropdownMenu.Arrow )` + fill: white; +`; From 9b4c1712e5d320c71dda0cc64dcabb21ff365646 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 30 Mar 2023 13:09:13 +0200 Subject: [PATCH 03/64] Add sample story --- .../src/ui/radix-dropdown/stories/index.tsx | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 packages/components/src/ui/radix-dropdown/stories/index.tsx diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx new file mode 100644 index 0000000000000..7b74ea95f1d2c --- /dev/null +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -0,0 +1,201 @@ +/** + * External dependencies + */ +import type { ComponentMeta } from '@storybook/react'; +import { css } from '@emotion/react'; + +/** + * Internal dependencies + */ +import * as DropdownMenu from '../'; +import { useCx } from '../../../utils/hooks/use-cx'; + +// TODO: replace with wordpress/icons +import { + HamburgerMenuIcon, + DotFilledIcon, + CheckIcon, + ChevronRightIcon, +} from '@radix-ui/react-icons'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +const meta: ComponentMeta< typeof DropdownMenu.Root > = { + title: 'Components/RadixDropdown', + component: DropdownMenu.Root, + // subcomponents: { DropdownMenu. }, + argTypes: { + // focusOnMount: { + // options: [ 'firstElement', true, false ], + // control: { + // type: 'radio', + // }, + // }, + // position: { control: { type: null } }, + // renderContent: { control: { type: null } }, + // renderToggle: { control: { type: null } }, + }, + parameters: { + controls: { + expanded: true, + }, + }, +}; +export default meta; + +const iconButton = css` + font-family: inherit; + border-radius: 100%; + height: 35px; + width: 35px; + display: inline-flex; + align-items: center; + justify-content: center; + color: hsl( 250, 95%, 76.8% ); // violet11 + background-color: white; + box-shadow: 0 2px 10px hsla( 0, 0%, 0%, 0.141 ); //blackA7 + + &:hover { + background-color: hsl( 253, 37%, 18.4% ); // violet3 + } + + &:focus { + box-shadow: 0 0 0 2px black; + } +`; + +const rightSlot = css` + margin-left: auto; + padding-left: 20px; + color: hsl( 253, 4%, 63.7% ); // mauve11 + + [data-highlighted] > & { + color: white; + } + + [data-disabled] & { + color: hsl( 253, 4%, 63.7% ); // mauve8 + } +`; + +export const DropdownMenuDemo = () => { + const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); + const [ urlsChecked, setUrlsChecked ] = useState( false ); + const [ person, setPerson ] = useState( 'pedro' ); + + const cx = useCx(); + + const rightSlotClassName = cx( rightSlot ); + + return ( + + + + + + + + + New Tab
⌘+T
+
+ + New Window{ ' ' } +
⌘+N
+
+ + New Private Window{ ' ' } +
⇧+⌘+N
+
+ + + More Tools +
+ +
+
+ + + + Save Page As…{ ' ' } +
+ ⌘+S +
+
+ + Create Shortcut… + + + Name Window… + + + + Developer Tools + +
+
+
+ + + + + + + + Show Bookmarks{ ' ' } +
⌘+B
+
+ + + + + Show Full URLs + + + + + People + + + + + + Pedro Duarte + + + + + + Colm Tuite + + + + +
+
+
+ ); +}; From dc33f9843a0d7f00ea3665eafd3f1ea174015bd5 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 31 Mar 2023 10:51:33 +0200 Subject: [PATCH 04/64] Start aggregating some components --- .../src/ui/radix-dropdown/index.tsx | 115 +++++++++- .../src/ui/radix-dropdown/stories/index.tsx | 206 ++++++++---------- 2 files changed, 202 insertions(+), 119 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index ce76361da1f9b..7002c0ceed9a8 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -1,14 +1,18 @@ /** * External dependencies */ -// Baseline: export all radix components -export * from '@radix-ui/react-dropdown-menu'; +import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; +import { CheckIcon, DividerHorizontalIcon } from '@radix-ui/react-icons'; + +/** + * WordPress dependencies + */ +import { forwardRef } from '@wordpress/element'; /** * Internal dependencies */ -// Overrides some radix components with our styled version -export { +import { Arrow, CheckboxItem, Content, @@ -18,10 +22,103 @@ export { RadioItem, Root, Separator, - SubContent, - SubTrigger, + // SubContent, + // SubTrigger, } from './styles'; -// Questions: -// - do we want to have our own JSDocs ? -// - do we want to more explicitly PICK prop types? +type DropdownMenuProps = { + /** + * The props passed to the dropdown's root element + */ + rootProps?: Omit< DropdownMenuPrimitive.DropdownMenuProps, 'children' >; + /** + * The props passed to the dropdown's content + */ + contentProps?: Omit< + DropdownMenuPrimitive.DropdownMenuContentProps, + 'children' + >; + /** + * The contents rendered inside the trigger + */ + trigger: React.ReactNode; + /** + * The contents of the dropdown + */ + children: React.ReactNode; +}; + +// Observations: +// - is it enough to have only one forwarded ref? If we have only one, should it be to the root, or to the content? +// - Should we be consistent in using the same value for the `asChild` prop on both trigger and content? +// - Should we allow customizing the `asChild` prop on trigger and content? +// - Should we explicitly Pick<> every prop ? +export const DropdownMenu = forwardRef( + ( + { children, rootProps, contentProps, trigger }: DropdownMenuProps, + forwardedRef: React.ForwardedRef< any > + ) => { + return ( + + + { trigger } + + + + { children } + + + + + ); + } +); + +export const DropdownMenuLabel = Label; +export const DropdownMenuItem = Item; +export const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +export const DropdownMenuCheckboxItem = forwardRef( + ( + { + children, + ...props + }: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps, + forwardedRef: React.ForwardedRef< any > + ) => { + return ( + + { children } + + { props.checked === 'indeterminate' && ( + + ) } + { props.checked === true && } + + + ); + } +); + +export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +export const DropdownMenuRadioItem = forwardRef( + ( + { + children, + ...props + }: DropdownMenuPrimitive.DropdownMenuRadioItemProps, + forwardedRef: React.ForwardedRef< any > + ) => { + return ( + + { children } + + + + + ); + } +); + +export const DropdownMenuSeparator = Separator; diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 7b74ea95f1d2c..b954c823c2c16 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -7,26 +7,26 @@ import { css } from '@emotion/react'; /** * Internal dependencies */ -import * as DropdownMenu from '../'; +import { DropdownMenu, DropdownMenuItem } from '../'; import { useCx } from '../../../utils/hooks/use-cx'; // TODO: replace with wordpress/icons import { HamburgerMenuIcon, - DotFilledIcon, - CheckIcon, - ChevronRightIcon, + // DotFilledIcon, + // CheckIcon, + // ChevronRightIcon, } from '@radix-ui/react-icons'; /** * WordPress dependencies */ -import { useState } from '@wordpress/element'; +// import { useState } from '@wordpress/element'; -const meta: ComponentMeta< typeof DropdownMenu.Root > = { +const meta: ComponentMeta< typeof DropdownMenu > = { title: 'Components/RadixDropdown', - component: DropdownMenu.Root, - // subcomponents: { DropdownMenu. }, + component: DropdownMenu, + subcomponents: { DropdownMenuItem }, argTypes: { // focusOnMount: { // options: [ 'firstElement', true, false ], @@ -82,120 +82,106 @@ const rightSlot = css` `; export const DropdownMenuDemo = () => { - const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); - const [ urlsChecked, setUrlsChecked ] = useState( false ); - const [ person, setPerson ] = useState( 'pedro' ); + // const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); + // const [ urlsChecked, setUrlsChecked ] = useState( false ); + // const [ person, setPerson ] = useState( 'pedro' ); const cx = useCx(); const rightSlotClassName = cx( rightSlot ); return ( - - + - - - - - - New Tab
⌘+T
-
- - New Window{ ' ' } -
⌘+N
-
- - New Private Window{ ' ' } -
⇧+⌘+N
-
- - - More Tools -
- -
-
- - - - Save Page As…{ ' ' } -
- ⌘+S -
-
- - Create Shortcut… - - - Name Window… - - - - Developer Tools - -
-
-
- - - - + + New Tab
⌘+T
+
+ + New Window
⌘+N
+
+ + New Private Window{ ' ' } +
⇧+⌘+N
+
+ + { /* + + More Tools +
+ +
+
+ + - - - - Show Bookmarks{ ' ' } -
⌘+B
-
- - - - - Show Full URLs - - - - - People - - - - - - Pedro Duarte - - - - - - Colm Tuite - - - - -
-
-
+ + Save Page As…{ ' ' } +
⌘+S
+
+ Create Shortcut… + Name Window… + + Developer Tools + + + + + + + + + + + Show Bookmarks
⌘+B
+
+ + + + + Show Full URLs + + + + + People + + + + + + Pedro Duarte + + + + + + Colm Tuite + + */ } + ); }; From 786acc550dd4050a5cf615bda308dc09fddbcc5a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 31 Mar 2023 10:52:49 +0200 Subject: [PATCH 05/64] Improve storybook config --- packages/components/src/ui/radix-dropdown/stories/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index b954c823c2c16..305ff2357ead2 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -39,9 +39,9 @@ const meta: ComponentMeta< typeof DropdownMenu > = { // renderToggle: { control: { type: null } }, }, parameters: { - controls: { - expanded: true, - }, + actions: { argTypesRegex: '^on.*' }, + controls: { expanded: true }, + docs: { source: { state: 'open', excludeDecorators: true } }, }, }; export default meta; From 1ccabba97421ff67c9ecfa921809e7da6d29b058 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 10:45:49 +0200 Subject: [PATCH 06/64] Use better colors --- .../src/ui/radix-dropdown/stories/index.tsx | 8 ++++---- .../components/src/ui/radix-dropdown/styles.ts | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 305ff2357ead2..7afd5f6cf1452 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -54,12 +54,12 @@ const iconButton = css` display: inline-flex; align-items: center; justify-content: center; - color: hsl( 250, 95%, 76.8% ); // violet11 + color: hsl( 250, 43%, 48% ); // violet11 background-color: white; box-shadow: 0 2px 10px hsla( 0, 0%, 0%, 0.141 ); //blackA7 &:hover { - background-color: hsl( 253, 37%, 18.4% ); // violet3 + background-color: hsl( 252, 96.9%, 97.4% ); // violet3 } &:focus { @@ -70,14 +70,14 @@ const iconButton = css` const rightSlot = css` margin-left: auto; padding-left: 20px; - color: hsl( 253, 4%, 63.7% ); // mauve11 + color: hsl( 252, 4%, 44.8% ); // mauve11 [data-highlighted] > & { color: white; } [data-disabled] & { - color: hsl( 253, 4%, 63.7% ); // mauve8 + color: hsl( 255, 3.7%, 78.8% ); // mauve8 } `; diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 752e9f80721e3..53a09146bf819 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -78,7 +78,7 @@ const baseContent = css` const baseItem = css` font-size: 13px; line-height: 1; - color: hsl( 250, 95%, 76.8% ); // violet11 + color: hsl( 250, 43%, 48% ); // violet11 border-radius: 3px; display: flex; align-items: center; @@ -90,13 +90,13 @@ const baseItem = css` outline: none; &[data-disabled] { - color: hsl( 247, 4.8%, 32.5% ); // mauve8 + color: hsl( 255, 3.7%, 78.8% ); // mauve8 pointer-events: none; } &[data-highlighted] { background-color: hsl( 252, 56%, 57.5% ); // violet9 - color: hsl( 250, 20%, 10.2% ); // violet1 + color: hsl( 255, 65%, 99.4% ); // violet1 } `; @@ -126,8 +126,8 @@ export const SubTrigger = styled( DropdownMenu.SubTrigger )` ${ baseItem } &[data-state='open'] { - background-color: hsl( 252, 40.1%, 22.5% ); // violet4 - color: hsl( 250, 95%, 76.8% ); // violet11 + background-color: hsl( 252, 91.5%, 95.5% ); // violet4 + color: hsl( 250, 43%, 48% ); // violet11 } `; @@ -135,12 +135,12 @@ export const Label = styled( DropdownMenu.Label )` padding-left: 25px; font-size: 12px; line-height: 25px; - color: hsl( 253, 4%, 63.7% ); // mauve11 + color: hsl( 252, 4%, 44.8% ); // mauve11 `; export const Separator = styled( DropdownMenu.Separator )` height: 1px; - background-color: hsl(251, 44.3%, 31.1%) // violet6; + background-color: hsl( 252, 77.8%, 89.4% ); // violet6 margin: 5px; `; From d29919adc5672ec457d217364b14f3606f05952c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 10:46:16 +0200 Subject: [PATCH 07/64] Fix styles --- .../src/ui/radix-dropdown/stories/index.tsx | 1 + packages/components/src/ui/radix-dropdown/styles.ts | 11 +++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 7afd5f6cf1452..19951af06769a 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -47,6 +47,7 @@ const meta: ComponentMeta< typeof DropdownMenu > = { export default meta; const iconButton = css` + all: unset; font-family: inherit; border-radius: 100%; height: 35px; diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 53a09146bf819..1f1ac815c37e9 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -76,6 +76,7 @@ const baseContent = css` `; const baseItem = css` + all: unset; font-size: 13px; line-height: 1; color: hsl( 250, 43%, 48% ); // violet11 @@ -100,12 +101,6 @@ const baseItem = css` } `; -export const Root = styled( DropdownMenu.Root )` - button { - all: unset; - } -`; - export const Content = styled( DropdownMenu.Content )` ${ baseContent } `; @@ -123,12 +118,12 @@ export const RadioItem = styled( DropdownMenu.RadioItem )` ${ baseItem } `; export const SubTrigger = styled( DropdownMenu.SubTrigger )` - ${ baseItem } - &[data-state='open'] { background-color: hsl( 252, 91.5%, 95.5% ); // violet4 color: hsl( 250, 43%, 48% ); // violet11 } + + ${ baseItem } `; export const Label = styled( DropdownMenu.Label )` From 37eb61f7afe9f73f9a75ecbdfb4f0b0aec21d1ea Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 11:00:39 +0200 Subject: [PATCH 08/64] Move types to external file --- .../src/ui/radix-dropdown/index.tsx | 23 +-------- .../components/src/ui/radix-dropdown/types.ts | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 packages/components/src/ui/radix-dropdown/types.ts diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 7002c0ceed9a8..d89eebf39709b 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -13,7 +13,7 @@ import { forwardRef } from '@wordpress/element'; * Internal dependencies */ import { - Arrow, +import type { DropdownMenuProps, DropdownSubMenuProps } from './types'; CheckboxItem, Content, Item, @@ -26,27 +26,6 @@ import { // SubTrigger, } from './styles'; -type DropdownMenuProps = { - /** - * The props passed to the dropdown's root element - */ - rootProps?: Omit< DropdownMenuPrimitive.DropdownMenuProps, 'children' >; - /** - * The props passed to the dropdown's content - */ - contentProps?: Omit< - DropdownMenuPrimitive.DropdownMenuContentProps, - 'children' - >; - /** - * The contents rendered inside the trigger - */ - trigger: React.ReactNode; - /** - * The contents of the dropdown - */ - children: React.ReactNode; -}; // Observations: // - is it enough to have only one forwarded ref? If we have only one, should it be to the root, or to the content? diff --git a/packages/components/src/ui/radix-dropdown/types.ts b/packages/components/src/ui/radix-dropdown/types.ts new file mode 100644 index 0000000000000..c2c5408344569 --- /dev/null +++ b/packages/components/src/ui/radix-dropdown/types.ts @@ -0,0 +1,48 @@ +/** + * External dependencies + */ +import type * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; + +export type DropdownMenuProps = { + /** + * The props passed to the dropdown's root element + */ + rootProps?: Omit< DropdownMenuPrimitive.DropdownMenuProps, 'children' >; + /** + * The props passed to the dropdown's content + */ + contentProps?: Omit< + DropdownMenuPrimitive.DropdownMenuContentProps, + 'children' + >; + portalProps?: Omit< + DropdownMenuPrimitive.DropdownMenuPortalProps, + 'children' + >; + /** + * The contents rendered inside the trigger + */ + trigger: React.ReactNode; + /** + * The contents of the dropdown + */ + children: React.ReactNode; +}; + +export type DropdownSubMenuProps = { + subProps?: Omit< DropdownMenuPrimitive.DropdownMenuSubProps, 'children' >; + subContentProps?: Omit< + DropdownMenuPrimitive.DropdownMenuSubContentProps, + 'children' + >; + portalProps?: Omit< + DropdownMenuPrimitive.DropdownMenuPortalProps, + 'children' + >; + triggerProps?: Omit< + DropdownMenuPrimitive.DropdownMenuSubTriggerProps, + 'children' + >; + trigger: React.ReactNode; + children: React.ReactNode; +}; From 37582a290ac2d97e5bb90d9cfad304c4c33019f3 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 11:06:15 +0200 Subject: [PATCH 09/64] Namespace all styled radix components under "DropdownMenuStyled" namespace --- .../src/ui/radix-dropdown/index.tsx | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index d89eebf39709b..ae441945d5a07 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -12,20 +12,8 @@ import { forwardRef } from '@wordpress/element'; /** * Internal dependencies */ -import { +import * as DropdownMenuStyled from './styles'; import type { DropdownMenuProps, DropdownSubMenuProps } from './types'; - CheckboxItem, - Content, - Item, - ItemIndicator, - Label, - RadioItem, - Root, - Separator, - // SubContent, - // SubTrigger, -} from './styles'; - // Observations: // - is it enough to have only one forwarded ref? If we have only one, should it be to the root, or to the content? @@ -38,23 +26,26 @@ export const DropdownMenu = forwardRef( forwardedRef: React.ForwardedRef< any > ) => { return ( - + { trigger } - + { children } - - + + - + ); } ); -export const DropdownMenuLabel = Label; -export const DropdownMenuItem = Item; +export const DropdownMenuLabel = DropdownMenuStyled.Label; +export const DropdownMenuItem = DropdownMenuStyled.Item; export const DropdownMenuGroup = DropdownMenuPrimitive.Group; export const DropdownMenuCheckboxItem = forwardRef( @@ -66,15 +57,15 @@ export const DropdownMenuCheckboxItem = forwardRef( forwardedRef: React.ForwardedRef< any > ) => { return ( - + { children } - + { props.checked === 'indeterminate' && ( ) } { props.checked === true && } - - + + ); } ); @@ -90,14 +81,14 @@ export const DropdownMenuRadioItem = forwardRef( forwardedRef: React.ForwardedRef< any > ) => { return ( - + { children } - + - - + + ); } ); -export const DropdownMenuSeparator = Separator; +export const DropdownMenuSeparator = DropdownMenuStyled.Separator; From ddda5c1d100f015d2577a976ac7545c8a717b22a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 11:06:42 +0200 Subject: [PATCH 10/64] Use dot icon for radio items instead of check icon --- packages/components/src/ui/radix-dropdown/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index ae441945d5a07..b38520bb482c7 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -2,7 +2,11 @@ * External dependencies */ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; -import { CheckIcon, DividerHorizontalIcon } from '@radix-ui/react-icons'; +import { + CheckIcon, + DotFilledIcon, + DividerHorizontalIcon, +} from '@radix-ui/react-icons'; /** * WordPress dependencies @@ -84,7 +88,7 @@ export const DropdownMenuRadioItem = forwardRef( { children } - + ); From 4b446dbf048056711d6b0823c994f94dbd6590a8 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 11:07:30 +0200 Subject: [PATCH 11/64] Pass portal props to portal --- packages/components/src/ui/radix-dropdown/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index b38520bb482c7..46a465076efb6 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -26,7 +26,13 @@ import type { DropdownMenuProps, DropdownSubMenuProps } from './types'; // - Should we explicitly Pick<> every prop ? export const DropdownMenu = forwardRef( ( - { children, rootProps, contentProps, trigger }: DropdownMenuProps, + { + children, + rootProps, + portalProps, + contentProps, + trigger, + }: DropdownMenuProps, forwardedRef: React.ForwardedRef< any > ) => { return ( @@ -34,7 +40,7 @@ export const DropdownMenu = forwardRef( { trigger } - + Date: Tue, 4 Apr 2023 11:08:05 +0200 Subject: [PATCH 12/64] Add DropdownSubMenu component --- .../src/ui/radix-dropdown/index.tsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 46a465076efb6..c1a9cee3f4bc4 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -54,6 +54,37 @@ export const DropdownMenu = forwardRef( } ); +export const DropdownSubMenu = forwardRef( + ( + { + children, + subProps, + subContentProps, + portalProps, + trigger, + triggerProps, + }: DropdownSubMenuProps, + forwardedRef: React.ForwardedRef< any > + ) => { + return ( + + + { trigger } + { /* Arrow? */ } + + + + { children } + + + + ); + } +); + export const DropdownMenuLabel = DropdownMenuStyled.Label; export const DropdownMenuItem = DropdownMenuStyled.Item; export const DropdownMenuGroup = DropdownMenuPrimitive.Group; From 377c61c89269c075e714296576e746e950304150 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 11:47:04 +0200 Subject: [PATCH 13/64] Improve stories to use high-level components --- .../src/ui/radix-dropdown/stories/index.tsx | 116 ++++++++---------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 19951af06769a..d79179cd3ce68 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -7,21 +7,26 @@ import { css } from '@emotion/react'; /** * Internal dependencies */ -import { DropdownMenu, DropdownMenuItem } from '../'; +import { + DropdownMenu, + DropdownMenuItem, + DropdownSubMenu, + DropdownMenuSeparator, + DropdownMenuCheckboxItem, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, +} from '../'; import { useCx } from '../../../utils/hooks/use-cx'; // TODO: replace with wordpress/icons -import { - HamburgerMenuIcon, - // DotFilledIcon, - // CheckIcon, - // ChevronRightIcon, -} from '@radix-ui/react-icons'; +import { HamburgerMenuIcon, ChevronRightIcon } from '@radix-ui/react-icons'; /** * WordPress dependencies */ -// import { useState } from '@wordpress/element'; +import { useState } from '@wordpress/element'; const meta: ComponentMeta< typeof DropdownMenu > = { title: 'Components/RadixDropdown', @@ -83,9 +88,9 @@ const rightSlot = css` `; export const DropdownMenuDemo = () => { - // const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); - // const [ urlsChecked, setUrlsChecked ] = useState( false ); - // const [ person, setPerson ] = useState( 'pedro' ); + const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); + const [ urlsChecked, setUrlsChecked ] = useState( false ); + const [ person, setPerson ] = useState( 'pedro' ); const cx = useCx(); @@ -114,75 +119,62 @@ export const DropdownMenuDemo = () => {
⇧+⌘+N
- { /* - - More Tools -
- -
-
- - - - Save Page As…{ ' ' } -
⌘+S
-
- Create Shortcut… - Name Window… - - Developer Tools -
-
-
- - - - + More Tools +
+ +
+ + } + subContentProps={ { + sideOffset: 2, + } } + > + + Save Page As…{ ' ' } +
⌘+S
+
+ Create Shortcut… + Name Window… + + Developer Tools + + + + + - - - Show Bookmarks
⌘+B
-
- + + - - - Show Full URLs - + - + - People - People + - - - - + Pedro Duarte - - + - - - Colm Tuite - - */ } + + ); }; From c3d36c7d45ef6c30c56c8c3ab457f074a09d9f10 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 11:47:42 +0200 Subject: [PATCH 14/64] Update notes --- .../src/ui/radix-dropdown/index.tsx | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index c1a9cee3f4bc4..2da0c1d756e2f 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -19,11 +19,23 @@ import { forwardRef } from '@wordpress/element'; import * as DropdownMenuStyled from './styles'; import type { DropdownMenuProps, DropdownSubMenuProps } from './types'; -// Observations: -// - is it enough to have only one forwarded ref? If we have only one, should it be to the root, or to the content? -// - Should we be consistent in using the same value for the `asChild` prop on both trigger and content? +// Observations / Questions: +// - is it enough on the larger components to have only one forwarded ref? +// If we have only one, should it be to the "root", the "trigger", or the "content"? +// - Should we be consistent in using the same value for the `asChild` prop on: +// - trigger +// - content +// - sub trigger +// - sub content // - Should we allow customizing the `asChild` prop on trigger and content? -// - Should we explicitly Pick<> every prop ? +// - Props & customisability: +// - Which props should we expose? +// - Should props be "namespaced" for each subcomponent? +// - We should probably explicitly `Pick<>` every prop that we want to expose +// - Subtrigger arrow: +// - Should it always be there (ie. an internal implementation)? +// - Should we just expect that the the consumers handle it themselves? +// - Should we expose it as a separate component that consumers could use? export const DropdownMenu = forwardRef( ( { From 76ffab2e1c626166e9520477c903471b9a8dffc1 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 11:48:20 +0200 Subject: [PATCH 15/64] Comment out unused import --- packages/components/src/ui/radix-dropdown/stories/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index d79179cd3ce68..30179398faf6a 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -13,7 +13,7 @@ import { DropdownSubMenu, DropdownMenuSeparator, DropdownMenuCheckboxItem, - DropdownMenuGroup, + // DropdownMenuGroup, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, From 0a3acf8cc9eb52b9911575c87f2d7240d3d4ad06 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 12:25:42 +0200 Subject: [PATCH 16/64] Use menu groups --- .../src/ui/radix-dropdown/stories/index.tsx | 99 ++++++++++--------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 30179398faf6a..eec57bb6f483d 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -13,7 +13,7 @@ import { DropdownSubMenu, DropdownMenuSeparator, DropdownMenuCheckboxItem, - // DropdownMenuGroup, + DropdownMenuGroup, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, @@ -108,63 +108,68 @@ export const DropdownMenuDemo = () => { } contentProps={ { sideOffset: 5 } } > - - New Tab
⌘+T
-
- - New Window
⌘+N
-
- - New Private Window{ ' ' } -
⇧+⌘+N
-
- - - More Tools -
- -
- - } - subContentProps={ { - sideOffset: 2, - } } - > + - Save Page As…{ ' ' } -
⌘+S
+ New Tab
⌘+T
+
+ + New Window
⌘+N
+
+ + New Private Window{ ' ' } +
⇧+⌘+N
- Create Shortcut… - Name Window… - - Developer Tools -
- + + More Tools +
+ +
+ + } + subContentProps={ { + sideOffset: 2, + } } + > + + Save Page As…{ ' ' } +
⌘+S
+
+ Create Shortcut… + Name Window… + + Developer Tools +
- - Show Bookmarks
⌘+B
-
+ + - - Show Full URLs - + + + Show Bookmarks{ ' ' } +
⌘+B
+
- + + Show Full URLs + + + +
- People + People Pedro Duarte From 6665504fb8bca63174ef7273509a04a93ec68270 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 12:30:14 +0200 Subject: [PATCH 17/64] draft section in contributing guidelines --- packages/components/CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 8464a4a732744..bf7569a19ddba 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -20,6 +20,7 @@ For an example of a component that follows these requirements, take a look at [` - [README example](#README-example) - [Folder structure](#folder-structure) - [TypeScript migration guide](#refactoring-a-component-to-typescript) +- [Using Radix UI primitives](#using-radix-ui-primitives) ## Introducing new components @@ -639,3 +640,12 @@ Given a component folder (e.g. `packages/components/src/unit-control`): 11. Convert unit tests. 1. Rename test file extensions from `.js` to `.tsx`. 2. Fix all TypeScript errors. + +## Using Radix UI primitives + +Useful links: + +- [online docs](https://www.radix-ui.com/docs/primitives/overview/introduction) +- [repo](https://github.com/radix-ui/primitives) — useful for: + - inspecting source code + - running storybook examples (`yarn install && yarn dev`) From e93beff3fdb46c3976ebb8de51945ed3734fd49a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 15:16:03 +0200 Subject: [PATCH 18/64] Use wordpress icons instead of `@radix-ui/react-icons` --- package-lock.json | 6 ----- packages/components/package.json | 3 +-- .../src/ui/radix-dropdown/index.tsx | 22 ++++++++++++------- .../src/ui/radix-dropdown/stories/index.tsx | 13 ++++++----- .../src/ui/radix-dropdown/styles.ts | 4 ++++ 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5a41035e2bdb..041a64c8600e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7299,11 +7299,6 @@ "@radix-ui/react-use-callback-ref": "1.0.0" } }, - "@radix-ui/react-icons": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", - "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==" - }, "@radix-ui/react-id": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", @@ -17336,7 +17331,6 @@ "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "1.0.0", "@radix-ui/react-dropdown-menu": "^2.0.4", - "@radix-ui/react-icons": "^1.3.0", "@use-gesture/react": "^10.2.24", "@wordpress/a11y": "file:packages/a11y", "@wordpress/compose": "file:packages/compose", diff --git a/packages/components/package.json b/packages/components/package.json index 57a48e2340bc8..0339100948a01 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -39,7 +39,6 @@ "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "1.0.0", "@radix-ui/react-dropdown-menu": "^2.0.4", - "@radix-ui/react-icons": "^1.3.0", "@use-gesture/react": "^10.2.24", "@wordpress/a11y": "file:../a11y", "@wordpress/compose": "file:../compose", @@ -87,4 +86,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 2da0c1d756e2f..bf9b20bd09112 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -2,20 +2,18 @@ * External dependencies */ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; -import { - CheckIcon, - DotFilledIcon, - DividerHorizontalIcon, -} from '@radix-ui/react-icons'; /** * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import { SVG, Circle } from '@wordpress/primitives'; +import { check, lineSolid } from '@wordpress/icons'; /** * Internal dependencies */ +import Icon from '../../icon'; import * as DropdownMenuStyled from './styles'; import type { DropdownMenuProps, DropdownSubMenuProps } from './types'; @@ -114,9 +112,11 @@ export const DropdownMenuCheckboxItem = forwardRef( { children } { props.checked === 'indeterminate' && ( - + + ) } + { props.checked === true && ( + ) } - { props.checked === true && } ); @@ -125,6 +125,12 @@ export const DropdownMenuCheckboxItem = forwardRef( export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; +const radioDot = ( + + + +); + export const DropdownMenuRadioItem = forwardRef( ( { @@ -137,7 +143,7 @@ export const DropdownMenuRadioItem = forwardRef( { children } - + ); diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index eec57bb6f483d..7321c8e215064 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -20,13 +20,16 @@ import { } from '../'; import { useCx } from '../../../utils/hooks/use-cx'; -// TODO: replace with wordpress/icons -import { HamburgerMenuIcon, ChevronRightIcon } from '@radix-ui/react-icons'; - /** * WordPress dependencies */ import { useState } from '@wordpress/element'; +import { chevronRightSmall, menu } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import Icon from '../../../icon'; const meta: ComponentMeta< typeof DropdownMenu > = { title: 'Components/RadixDropdown', @@ -103,7 +106,7 @@ export const DropdownMenuDemo = () => { className={ cx( iconButton ) } aria-label="Customise options" > - + } contentProps={ { sideOffset: 5 } } @@ -125,7 +128,7 @@ export const DropdownMenuDemo = () => { <> More Tools
- +
} diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 1f1ac815c37e9..a24debba17241 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -99,6 +99,10 @@ const baseItem = css` background-color: hsl( 252, 56%, 57.5% ); // violet9 color: hsl( 255, 65%, 99.4% ); // violet1 } + + svg { + fill: currentColor; + } `; export const Content = styled( DropdownMenu.Content )` From 6437b35e71b92e3278a7cc3d2fc87e505a52285a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 15:44:19 +0200 Subject: [PATCH 19/64] Add custom `icon` prop to `DropdownMenuItem` --- .../src/ui/radix-dropdown/index.tsx | 28 +++++++++++++++++-- .../src/ui/radix-dropdown/stories/index.tsx | 4 +-- .../src/ui/radix-dropdown/styles.ts | 20 +++++++++---- .../components/src/ui/radix-dropdown/types.ts | 9 ++++++ 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index bf9b20bd09112..46af5e69f0308 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -15,7 +15,11 @@ import { check, lineSolid } from '@wordpress/icons'; */ import Icon from '../../icon'; import * as DropdownMenuStyled from './styles'; -import type { DropdownMenuProps, DropdownSubMenuProps } from './types'; +import type { + DropdownMenuProps, + DropdownSubMenuProps, + DropdownItemProps, +} from './types'; // Observations / Questions: // - is it enough on the larger components to have only one forwarded ref? @@ -34,6 +38,10 @@ import type { DropdownMenuProps, DropdownSubMenuProps } from './types'; // - Should it always be there (ie. an internal implementation)? // - Should we just expect that the the consumers handle it themselves? // - Should we expose it as a separate component that consumers could use? +// - DropdowmMenuItem icon prop: +// - added to mirror previous menu item component +// - should we expect consumers to provide this directly with children? +// - should we expose prefix / suffix to help? export const DropdownMenu = forwardRef( ( { @@ -96,9 +104,25 @@ export const DropdownSubMenu = forwardRef( ); export const DropdownMenuLabel = DropdownMenuStyled.Label; -export const DropdownMenuItem = DropdownMenuStyled.Item; export const DropdownMenuGroup = DropdownMenuPrimitive.Group; +export const DropdownMenuItem = forwardRef( + ( + { children, icon, ...props }: DropdownItemProps, + forwardedRef: React.ForwardedRef< any > + ) => { + return ( + + { children } + { icon && ( + + + + ) } + + ); + } +); export const DropdownMenuCheckboxItem = forwardRef( ( { diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 7321c8e215064..8a6a991a75752 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -24,7 +24,7 @@ import { useCx } from '../../../utils/hooks/use-cx'; * WordPress dependencies */ import { useState } from '@wordpress/element'; -import { chevronRightSmall, menu } from '@wordpress/icons'; +import { chevronRightSmall, menu, wordpress } from '@wordpress/icons'; /** * Internal dependencies @@ -112,7 +112,7 @@ export const DropdownMenuDemo = () => { contentProps={ { sideOffset: 5 } } > - + New Tab
⌘+T
diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index a24debba17241..0c2a0bba187b4 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -105,6 +105,15 @@ const baseItem = css` } `; +const itemPrefix = css` + position: absolute; + left: 0; + width: 25px; + display: inline-flex; + align-items: center; + justify-content: center; +`; + export const Content = styled( DropdownMenu.Content )` ${ baseContent } `; @@ -144,12 +153,11 @@ export const Separator = styled( DropdownMenu.Separator )` `; export const ItemIndicator = styled( DropdownMenu.ItemIndicator )` - position: absolute; - left: 0; - width: 25px; - display: inline-flex; - align-items: center; - justify-content: center; + ${ itemPrefix } +`; + +export const ItemPrefixWrapper = styled.span` + ${ itemPrefix } `; export const Arrow = styled( DropdownMenu.Arrow )` diff --git a/packages/components/src/ui/radix-dropdown/types.ts b/packages/components/src/ui/radix-dropdown/types.ts index c2c5408344569..623476cb5637e 100644 --- a/packages/components/src/ui/radix-dropdown/types.ts +++ b/packages/components/src/ui/radix-dropdown/types.ts @@ -3,6 +3,11 @@ */ import type * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; +/** + * Internal dependencies + */ +import type { IconType } from '../../icon'; + export type DropdownMenuProps = { /** * The props passed to the dropdown's root element @@ -46,3 +51,7 @@ export type DropdownSubMenuProps = { trigger: React.ReactNode; children: React.ReactNode; }; + +export type DropdownItemProps = DropdownMenuPrimitive.DropdownMenuItemProps & { + icon?: IconType; +}; From 557b9553ca70b2bcc34b9ea4712a2a17bf4dfd3a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 15:49:56 +0200 Subject: [PATCH 20/64] Add missing alignment property for sub trigger --- packages/components/src/ui/radix-dropdown/stories/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 8a6a991a75752..1665b5f633440 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -134,6 +134,7 @@ export const DropdownMenuDemo = () => { } subContentProps={ { sideOffset: 2, + alignOffset: -5, } } > From 76ffa6b87e85fbf8183275c3012530931a5cbdc5 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 16:28:14 +0200 Subject: [PATCH 21/64] Extract animation variables --- .../src/ui/radix-dropdown/styles.ts | 81 +++++++++---------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 0c2a0bba187b4..6a40dc7739600 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -6,46 +6,43 @@ import { css, keyframes } from '@emotion/react'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; -const slideDownAndFade = keyframes` - from { - opacity: 0; - transform: translateY(-2px); - } - to { - opacity: 1; - transform: translateY(0); - } -`; -const slideLeftAndFade = keyframes` - from { - opacity: 0; - transform: translateX(2px); - } - to { - opacity: 1; - transform: translateX(0); - } -`; -const slideUpAndFade = keyframes` - from { - opacity: 0; - transform: translateY(2px); - } - to { - opacity: 1; - transform: translateY(0); - } -`; -const slideRightAndFade = keyframes` - from { - opacity: 0; - transform: translateX(-2px); - } - to { - opacity: 1; - transform: translateX(0); - } -`; +const ANIMATION_PARAMS = { + SLIDE_AMOUNT: '2px', + DURATION: '400ms', + EASING: 'cubic-bezier( 0.16, 1, 0.3, 1 )', +}; + +const slideUpAndFade = keyframes( { + '0%': { + opacity: 0, + transform: `translateY(${ ANIMATION_PARAMS.SLIDE_AMOUNT })`, + }, + '100%': { opacity: 1, transform: 'translateY(0)' }, +} ); + +const slideRightAndFade = keyframes( { + '0%': { + opacity: 0, + transform: `translateX(-${ ANIMATION_PARAMS.SLIDE_AMOUNT })`, + }, + '100%': { opacity: 1, transform: 'translateX(0)' }, +} ); + +const slideDownAndFade = keyframes( { + '0%': { + opacity: 0, + transform: `translateY(-${ ANIMATION_PARAMS.SLIDE_AMOUNT })`, + }, + '100%': { opacity: 1, transform: 'translateY(0)' }, +} ); + +const slideLeftAndFade = keyframes( { + '0%': { + opacity: 0, + transform: `translateX(${ ANIMATION_PARAMS.SLIDE_AMOUNT })`, + }, + '100%': { opacity: 1, transform: 'translateX(0)' }, +} ); const baseContent = css` min-width: 220px; @@ -54,8 +51,8 @@ const baseContent = css` padding: 5px; box-shadow: 0px 10px 38px -10px rgba( 22, 23, 24, 0.35 ), 0px 10px 20px -15px rgba( 22, 23, 24, 0.2 ); - animation-duration: 400ms; - animation-timing-function: cubic-bezier( 0.16, 1, 0.3, 1 ); + animation-duration: ${ ANIMATION_PARAMS.DURATION }; + animation-timing-function: ${ ANIMATION_PARAMS.EASING }; will-change: transform, opacity; &[data-side='top'] { From a1318bf0a46d3b91326d7ed72fd011a2dbdc12fc Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 16:28:37 +0200 Subject: [PATCH 22/64] Align colors more closely to WordPress styles --- .../src/ui/radix-dropdown/stories/index.tsx | 27 +++++++++---------- .../src/ui/radix-dropdown/styles.ts | 24 +++++++++-------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 1665b5f633440..87ba6550c2b80 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -7,6 +7,7 @@ import { css } from '@emotion/react'; /** * Internal dependencies */ +import { COLORS } from '../../../utils'; import { DropdownMenu, DropdownMenuItem, @@ -63,31 +64,27 @@ const iconButton = css` display: inline-flex; align-items: center; justify-content: center; - color: hsl( 250, 43%, 48% ); // violet11 + color: ${ COLORS.gray[ 900 ] }; background-color: white; - box-shadow: 0 2px 10px hsla( 0, 0%, 0%, 0.141 ); //blackA7 + box-shadow: 0 2px 10px hsla( 0, 0%, 0%, 0.141 ); - &:hover { - background-color: hsl( 252, 96.9%, 97.4% ); // violet3 + &:hover, + &:focus { + color: ${ COLORS.ui.theme }; } &:focus { - box-shadow: 0 0 0 2px black; + box-shadow: 0 0 0 2px ${ COLORS.ui.theme }; + } + + svg { + fill: currentColor; } `; const rightSlot = css` margin-left: auto; - padding-left: 20px; - color: hsl( 252, 4%, 44.8% ); // mauve11 - - [data-highlighted] > & { - color: white; - } - - [data-disabled] & { - color: hsl( 255, 3.7%, 78.8% ); // mauve8 - } + padding-left: 24px; `; export const DropdownMenuDemo = () => { diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 6a40dc7739600..189dcdd5c76a8 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -3,9 +3,13 @@ */ import styled from '@emotion/styled'; import { css, keyframes } from '@emotion/react'; - import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +/** + * Internal dependencies + */ +import { COLORS } from '../../utils'; + const ANIMATION_PARAMS = { SLIDE_AMOUNT: '2px', DURATION: '400ms', @@ -46,7 +50,7 @@ const slideLeftAndFade = keyframes( { const baseContent = css` min-width: 220px; - background-color: white; + background-color: ${ COLORS.ui.background }; border-radius: 6px; padding: 5px; box-shadow: 0px 10px 38px -10px rgba( 22, 23, 24, 0.35 ), @@ -76,7 +80,7 @@ const baseItem = css` all: unset; font-size: 13px; line-height: 1; - color: hsl( 250, 43%, 48% ); // violet11 + color: ${ COLORS.gray[ 900 ] }; border-radius: 3px; display: flex; align-items: center; @@ -88,13 +92,12 @@ const baseItem = css` outline: none; &[data-disabled] { - color: hsl( 255, 3.7%, 78.8% ); // mauve8 + color: ${ COLORS.ui.textDisabled }; pointer-events: none; } &[data-highlighted] { - background-color: hsl( 252, 56%, 57.5% ); // violet9 - color: hsl( 255, 65%, 99.4% ); // violet1 + color: ${ COLORS.ui.theme }; } svg { @@ -129,8 +132,7 @@ export const RadioItem = styled( DropdownMenu.RadioItem )` `; export const SubTrigger = styled( DropdownMenu.SubTrigger )` &[data-state='open'] { - background-color: hsl( 252, 91.5%, 95.5% ); // violet4 - color: hsl( 250, 43%, 48% ); // violet11 + color: ${ COLORS.ui.theme }; } ${ baseItem } @@ -140,12 +142,12 @@ export const Label = styled( DropdownMenu.Label )` padding-left: 25px; font-size: 12px; line-height: 25px; - color: hsl( 252, 4%, 44.8% ); // mauve11 + color: ${ COLORS.ui.textDisabled }; `; export const Separator = styled( DropdownMenu.Separator )` height: 1px; - background-color: hsl( 252, 77.8%, 89.4% ); // violet6 + background-color: ${ COLORS.ui.borderDisabled }; margin: 5px; `; @@ -158,5 +160,5 @@ export const ItemPrefixWrapper = styled.span` `; export const Arrow = styled( DropdownMenu.Arrow )` - fill: white; + fill: ${ COLORS.ui.background }; `; From 4e328469501fc53dde9d88634d2690d6c0125c2c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 17:12:26 +0200 Subject: [PATCH 23/64] Add border to dropdown container --- packages/components/src/ui/radix-dropdown/styles.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 189dcdd5c76a8..64ea8312cd396 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -51,6 +51,7 @@ const slideLeftAndFade = keyframes( { const baseContent = css` min-width: 220px; background-color: ${ COLORS.ui.background }; + border: 1px solid ${ COLORS.ui.border }; border-radius: 6px; padding: 5px; box-shadow: 0px 10px 38px -10px rgba( 22, 23, 24, 0.35 ), @@ -161,4 +162,11 @@ export const ItemPrefixWrapper = styled.span` export const Arrow = styled( DropdownMenu.Arrow )` fill: ${ COLORS.ui.background }; + + /* The following styles aim at adding a border to the arrow*/ + stroke: ${ COLORS.ui.border }; + stroke-dasharray: 36 28; + stroke-dashoffset: 34; + stroke-linejoin: round; + stroke-width: 2.5; `; From 4d15f2289ac94f228e1262da7c5f42b9d48dfe86 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 17:14:37 +0200 Subject: [PATCH 24/64] Increase spacing, use `space` util --- .../src/ui/radix-dropdown/styles.ts | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 64ea8312cd396..3e499b2a63e98 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -9,6 +9,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; * Internal dependencies */ import { COLORS } from '../../utils'; +import { space } from '../utils/space'; const ANIMATION_PARAMS = { SLIDE_AMOUNT: '2px', @@ -53,7 +54,7 @@ const baseContent = css` background-color: ${ COLORS.ui.background }; border: 1px solid ${ COLORS.ui.border }; border-radius: 6px; - padding: 5px; + padding: ${ space( 2 ) }; box-shadow: 0px 10px 38px -10px rgba( 22, 23, 24, 0.35 ), 0px 10px 20px -15px rgba( 22, 23, 24, 0.2 ); animation-duration: ${ ANIMATION_PARAMS.DURATION }; @@ -85,10 +86,9 @@ const baseItem = css` border-radius: 3px; display: flex; align-items: center; - height: 25px; - padding: 0 5px; + height: ${ space( 9 ) }; + padding: 0 ${ space( 2 ) }; position: relative; - padding-left: 25px; user-select: none; outline: none; @@ -106,10 +106,14 @@ const baseItem = css` } `; +const itemLeftSpace = css` + padding-left: ${ space( 8 ) }; +`; + const itemPrefix = css` position: absolute; left: 0; - width: 25px; + width: ${ space( 8 ) }; display: inline-flex; align-items: center; justify-content: center; @@ -124,12 +128,19 @@ export const SubContent = styled( DropdownMenu.SubContent )` export const Item = styled( DropdownMenu.Item )` ${ baseItem } + + /* Should the standard item have larger left padding ? */ + ${ itemLeftSpace } `; export const CheckboxItem = styled( DropdownMenu.CheckboxItem )` ${ baseItem } + + ${ itemLeftSpace } `; export const RadioItem = styled( DropdownMenu.RadioItem )` ${ baseItem } + + ${ itemLeftSpace } `; export const SubTrigger = styled( DropdownMenu.SubTrigger )` &[data-state='open'] { @@ -137,19 +148,22 @@ export const SubTrigger = styled( DropdownMenu.SubTrigger )` } ${ baseItem } + + /* Should the standard item have larger left padding ? */ + ${ itemLeftSpace } `; export const Label = styled( DropdownMenu.Label )` - padding-left: 25px; + ${ itemLeftSpace } font-size: 12px; - line-height: 25px; + line-height: ${ space( 7 ) }; color: ${ COLORS.ui.textDisabled }; `; export const Separator = styled( DropdownMenu.Separator )` height: 1px; background-color: ${ COLORS.ui.borderDisabled }; - margin: 5px; + margin: ${ space( 2 ) }; `; export const ItemIndicator = styled( DropdownMenu.ItemIndicator )` From 76682ec61428e372fd7a2f7e21349dc872a0fc89 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 17:14:54 +0200 Subject: [PATCH 25/64] Use font utils --- packages/components/src/ui/radix-dropdown/styles.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 3e499b2a63e98..96d2cc1e8c2d2 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -8,7 +8,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; /** * Internal dependencies */ -import { COLORS } from '../../utils'; +import { COLORS, font } from '../../utils'; import { space } from '../utils/space'; const ANIMATION_PARAMS = { @@ -80,7 +80,9 @@ const baseContent = css` const baseItem = css` all: unset; - font-size: 13px; + font-size: ${ font( 'default.fontSize' ) }; + font-family: inherit; + font-weight: normal; line-height: 1; color: ${ COLORS.gray[ 900 ] }; border-radius: 3px; @@ -155,7 +157,7 @@ export const SubTrigger = styled( DropdownMenu.SubTrigger )` export const Label = styled( DropdownMenu.Label )` ${ itemLeftSpace } - font-size: 12px; + font-size: ${ font( 'helpText.fontSize' ) }; line-height: ${ space( 7 ) }; color: ${ COLORS.ui.textDisabled }; `; From d637c506e3becce11fd9bf072d0b5a40a93a231d Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 17:15:20 +0200 Subject: [PATCH 26/64] Add focus-visible outline --- packages/components/src/ui/radix-dropdown/styles.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 96d2cc1e8c2d2..88a526b1845cf 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -103,6 +103,10 @@ const baseItem = css` color: ${ COLORS.ui.theme }; } + &[data-highlighted]:focus-visible { + outline: 1px solid ${ COLORS.ui.theme }; + } + svg { fill: currentColor; } From 7c4769af0c0f7630a4ce757495b92aa75af5a569 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 4 Apr 2023 17:26:28 +0200 Subject: [PATCH 27/64] Alternative styling for submenu triggers --- packages/components/src/ui/radix-dropdown/styles.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 88a526b1845cf..74f820c4b3b7b 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -149,8 +149,8 @@ export const RadioItem = styled( DropdownMenu.RadioItem )` ${ itemLeftSpace } `; export const SubTrigger = styled( DropdownMenu.SubTrigger )` - &[data-state='open'] { - color: ${ COLORS.ui.theme }; + &[data-state='open']:not( [data-highlighted] ) { + background-color: ${ COLORS.ui.backgroundDisabled }; } ${ baseItem } From ae83a5e327b6488f0029fd3a4d8753c5395081f9 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 18:29:14 +0200 Subject: [PATCH 28/64] Pick only selected props for DropdownMenu --- .../src/ui/radix-dropdown/index.tsx | 69 +++++++++++-------- .../components/src/ui/radix-dropdown/types.ts | 57 ++++++++++++--- 2 files changed, 86 insertions(+), 40 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 46af5e69f0308..0a5a09f5cccfd 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -42,35 +42,46 @@ import type { // - added to mirror previous menu item component // - should we expect consumers to provide this directly with children? // - should we expose prefix / suffix to help? -export const DropdownMenu = forwardRef( - ( - { - children, - rootProps, - portalProps, - contentProps, - trigger, - }: DropdownMenuProps, - forwardedRef: React.ForwardedRef< any > - ) => { - return ( - - - { trigger } - - - - { children } - - - - - ); - } -); +export const DropdownMenu = ( { + // Root props + defaultOpen, + open, + onOpenChange, + modal = true, + // Content positioning props + side = 'bottom', + sideOffset = 0, + align = 'center', + alignOffset = 0, + // Render props + children, + trigger, +}: DropdownMenuProps ) => { + return ( + + + { trigger } + + + + { children } + + + + + ); +}; export const DropdownSubMenu = forwardRef( ( diff --git a/packages/components/src/ui/radix-dropdown/types.ts b/packages/components/src/ui/radix-dropdown/types.ts index 623476cb5637e..71cd1b00a6e9d 100644 --- a/packages/components/src/ui/radix-dropdown/types.ts +++ b/packages/components/src/ui/radix-dropdown/types.ts @@ -10,20 +10,55 @@ import type { IconType } from '../../icon'; export type DropdownMenuProps = { /** - * The props passed to the dropdown's root element + * The open state of the dropdown menu when it is initially rendered. Use when + * you do not need to control its open state. + * */ - rootProps?: Omit< DropdownMenuPrimitive.DropdownMenuProps, 'children' >; + defaultOpen?: DropdownMenuPrimitive.DropdownMenuProps[ 'defaultOpen' ]; /** - * The props passed to the dropdown's content + * The controlled open state of the dropdown menu. Must be used in conjunction + * with `onOpenChange`. */ - contentProps?: Omit< - DropdownMenuPrimitive.DropdownMenuContentProps, - 'children' - >; - portalProps?: Omit< - DropdownMenuPrimitive.DropdownMenuPortalProps, - 'children' - >; + open?: DropdownMenuPrimitive.DropdownMenuProps[ 'open' ]; + /** + * Event handler called when the open state of the dropdown menu changes. + */ + onOpenChange?: DropdownMenuPrimitive.DropdownMenuProps[ 'onOpenChange' ]; + /** + * The modality of the dropdown menu. When set to true, interaction with + * outside elements will be disabled and only menu content will be visible to + * screen readers. + * + * @default true + */ + modal?: DropdownMenuPrimitive.DropdownMenuProps[ 'modal' ]; + /** + * The preferred side of the trigger to render against when open. + * Will be reversed when collisions occur and avoidCollisions is enabled. + * + * @default 'bottom' + */ + side?: DropdownMenuPrimitive.DropdownMenuContentProps[ 'side' ]; + /** + * The distance in pixels from the trigger. + * + * @default 0 + */ + sideOffset?: DropdownMenuPrimitive.DropdownMenuContentProps[ 'sideOffset' ]; + /** + * The preferred alignment against the trigger. + * May change when collisions occur. + * + * @default 'center' + */ + align?: DropdownMenuPrimitive.DropdownMenuContentProps[ 'align' ]; + /** + * An offset in pixels from the "start" or "end" alignment options. + * + * @default 0 + */ + alignOffset?: DropdownMenuPrimitive.DropdownMenuContentProps[ 'alignOffset' ]; + /** * The contents rendered inside the trigger */ From 6dfa27d67efe76c6687e51ce13dd164f23beb424 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 18:34:02 +0200 Subject: [PATCH 29/64] Rewrite Storybook example to support controls --- .../src/ui/radix-dropdown/stories/index.tsx | 195 +++++++++++------- 1 file changed, 116 insertions(+), 79 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 87ba6550c2b80..47340df62cd18 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -1,8 +1,8 @@ /** * External dependencies */ -import type { ComponentMeta } from '@storybook/react'; -import { css } from '@emotion/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import styled from '@emotion/styled'; /** * Internal dependencies @@ -19,12 +19,11 @@ import { DropdownMenuRadioGroup, DropdownMenuRadioItem, } from '../'; -import { useCx } from '../../../utils/hooks/use-cx'; /** * WordPress dependencies */ -import { useState } from '@wordpress/element'; +import { useState, createContext, useContext } from '@wordpress/element'; import { chevronRightSmall, menu, wordpress } from '@wordpress/icons'; /** @@ -32,30 +31,63 @@ import { chevronRightSmall, menu, wordpress } from '@wordpress/icons'; */ import Icon from '../../../icon'; +const DropdownMenuStoryContext = createContext< { + bookmarksChecked?: boolean; + setBookmarksChecked?: ( v: boolean ) => void; + urlsChecked?: boolean; + setUrlsChecked?: ( v: boolean ) => void; + person?: string; + setPerson?: ( v: string ) => void; +} >( {} ); + const meta: ComponentMeta< typeof DropdownMenu > = { - title: 'Components/RadixDropdown', + title: 'Components/Radix DropdownMenu', component: DropdownMenu, - subcomponents: { DropdownMenuItem }, + subcomponents: { + DropdownMenuItem, + DropdownSubMenu, + DropdownMenuSeparator, + DropdownMenuCheckboxItem, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + }, argTypes: { - // focusOnMount: { - // options: [ 'firstElement', true, false ], - // control: { - // type: 'radio', - // }, - // }, - // position: { control: { type: null } }, - // renderContent: { control: { type: null } }, - // renderToggle: { control: { type: null } }, + children: { control: { type: null } }, + trigger: { control: { type: null } }, }, parameters: { actions: { argTypesRegex: '^on.*' }, controls: { expanded: true }, docs: { source: { state: 'open', excludeDecorators: true } }, }, + decorators: [ + ( Story ) => { + const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); + const [ urlsChecked, setUrlsChecked ] = useState( false ); + const [ person, setPerson ] = useState( 'pedro' ); + + return ( + + + + ); + }, + ], }; export default meta; -const iconButton = css` +const MenuButton = styled.div` all: unset; font-family: inherit; border-radius: 100%; @@ -82,51 +114,89 @@ const iconButton = css` } `; -const rightSlot = css` +const ItemSuffix = styled.div` margin-left: auto; padding-left: 24px; `; -export const DropdownMenuDemo = () => { - const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); - const [ urlsChecked, setUrlsChecked ] = useState( false ); - const [ person, setPerson ] = useState( 'pedro' ); +const CheckboxItemsGroup = () => { + const { + bookmarksChecked, + setBookmarksChecked, + urlsChecked, + setUrlsChecked, + } = useContext( DropdownMenuStoryContext ); + + return ( + + + Show Bookmarks ⌘+B + + + + Show Full URLs + - const cx = useCx(); + + + ); +}; - const rightSlotClassName = cx( rightSlot ); +const RadioItemsGroup = () => { + const { person, setPerson } = useContext( DropdownMenuStoryContext ); return ( - - - - } - contentProps={ { sideOffset: 5 } } - > + + People + + Pedro Duarte + + + Colm Tuite + + + ); +}; + +const Template: ComponentStory< typeof DropdownMenu > = ( props ) => ( + +); +export const Default = Template.bind( {} ); +Default.args = { + trigger: ( + + + + ), + sideOffset: 5, + children: ( + <> - New Tab
⌘+T
+ New Tab ⌘+T
- New Window
⌘+N
+ New Window ⌘+N
- New Private Window{ ' ' } -
⇧+⌘+N
+ New Private Window ⇧+⌘+N
- More Tools -
+ -
+ } subContentProps={ { @@ -135,52 +205,19 @@ export const DropdownMenuDemo = () => { } } > - Save Page As…{ ' ' } -
⌘+S
+ Save Page As… ⌘+S
Create Shortcut… Name Window… Developer Tools
-
- - - Show Bookmarks{ ' ' } -
⌘+B
-
- - - Show Full URLs - - - -
+ - - People - - Pedro Duarte - - - Colm Tuite - - -
- ); + + + ), }; From f3762298e8a92f9b89feac4d5ca9a64e22a7ee6c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 20:18:59 +0200 Subject: [PATCH 30/64] DropdownMenuItem: Pick only selected props, support prefix/suffix --- .../src/ui/radix-dropdown/index.tsx | 43 +++++++++++------- .../src/ui/radix-dropdown/stories/index.tsx | 44 ++++++++++++------- .../src/ui/radix-dropdown/styles.ts | 38 +++++++--------- .../components/src/ui/radix-dropdown/types.ts | 38 +++++++++++++--- 4 files changed, 101 insertions(+), 62 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 0a5a09f5cccfd..162270f916a8c 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -42,6 +42,7 @@ import type { // - added to mirror previous menu item component // - should we expect consumers to provide this directly with children? // - should we expose prefix / suffix to help? +// - Props: should we export HTML-inherited props? export const DropdownMenu = ( { // Root props defaultOpen, @@ -97,9 +98,8 @@ export const DropdownSubMenu = forwardRef( ) => { return ( - + { trigger } - { /* Arrow? */ } ) => { return ( - { children } - { icon && ( + { prefix && ( - + { prefix } ) } + { children } + { suffix && ( + + { suffix } + + ) } ); } @@ -144,15 +149,17 @@ export const DropdownMenuCheckboxItem = forwardRef( ) => { return ( + + + { props.checked === 'indeterminate' && ( + + ) } + { props.checked === true && ( + + ) } + + { children } - - { props.checked === 'indeterminate' && ( - - ) } - { props.checked === true && ( - - ) } - ); } @@ -176,10 +183,12 @@ export const DropdownMenuRadioItem = forwardRef( ) => { return ( + + + + + { children } - - - ); } diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 47340df62cd18..b1c9369f04f82 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -114,9 +114,8 @@ const MenuButton = styled.div` } `; -const ItemSuffix = styled.div` - margin-left: auto; - padding-left: 24px; +const KeyboardShortcut = styled.span` + opacity: 0.8; `; const CheckboxItemsGroup = () => { @@ -133,7 +132,7 @@ const CheckboxItemsGroup = () => { checked={ bookmarksChecked } onCheckedChange={ setBookmarksChecked } > - Show Bookmarks ⌘+B + Show Bookmarks - - New Tab ⌘+T + } + suffix={ ⌘+T } + > + New Tab - - New Window ⌘+N + ⌘+N } + > + New Window - - New Private Window ⇧+⌘+N + ⇧+⌘+N } + > + New Private Window - More Tools - + - - + } + > + More Tools + } subContentProps={ { sideOffset: 2, alignOffset: -5, } } > - - Save Page As… ⌘+S + ⌘+S } + > + Save Page As… Create Shortcut… Name Window… diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 74f820c4b3b7b..af08b0d37f1a5 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -17,6 +17,8 @@ const ANIMATION_PARAMS = { EASING: 'cubic-bezier( 0.16, 1, 0.3, 1 )', }; +const ITEM_HORIZONTAL_PADDING = space( 2 ); + const slideUpAndFade = keyframes( { '0%': { opacity: 0, @@ -89,7 +91,7 @@ const baseItem = css` display: flex; align-items: center; height: ${ space( 9 ) }; - padding: 0 ${ space( 2 ) }; + padding: 0 ${ ITEM_HORIZONTAL_PADDING }; position: relative; user-select: none; outline: none; @@ -112,17 +114,21 @@ const baseItem = css` } `; -const itemLeftSpace = css` - padding-left: ${ space( 8 ) }; -`; - const itemPrefix = css` - position: absolute; - left: 0; width: ${ space( 8 ) }; display: inline-flex; align-items: center; justify-content: center; + margin-left: calc( -1 * ${ ITEM_HORIZONTAL_PADDING } ); +`; + +const itemSuffix = css` + width: max-content; + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: auto; + padding-left: ${ space( 6 ) }; `; export const Content = styled( DropdownMenu.Content )` @@ -134,19 +140,12 @@ export const SubContent = styled( DropdownMenu.SubContent )` export const Item = styled( DropdownMenu.Item )` ${ baseItem } - - /* Should the standard item have larger left padding ? */ - ${ itemLeftSpace } `; export const CheckboxItem = styled( DropdownMenu.CheckboxItem )` ${ baseItem } - - ${ itemLeftSpace } `; export const RadioItem = styled( DropdownMenu.RadioItem )` ${ baseItem } - - ${ itemLeftSpace } `; export const SubTrigger = styled( DropdownMenu.SubTrigger )` &[data-state='open']:not( [data-highlighted] ) { @@ -154,13 +153,10 @@ export const SubTrigger = styled( DropdownMenu.SubTrigger )` } ${ baseItem } - - /* Should the standard item have larger left padding ? */ - ${ itemLeftSpace } `; export const Label = styled( DropdownMenu.Label )` - ${ itemLeftSpace } + padding: 0 ${ ITEM_HORIZONTAL_PADDING }; font-size: ${ font( 'helpText.fontSize' ) }; line-height: ${ space( 7 ) }; color: ${ COLORS.ui.textDisabled }; @@ -172,12 +168,12 @@ export const Separator = styled( DropdownMenu.Separator )` margin: ${ space( 2 ) }; `; -export const ItemIndicator = styled( DropdownMenu.ItemIndicator )` +export const ItemPrefixWrapper = styled.span` ${ itemPrefix } `; -export const ItemPrefixWrapper = styled.span` - ${ itemPrefix } +export const ItemSuffixWrapper = styled.span` + ${ itemSuffix } `; export const Arrow = styled( DropdownMenu.Arrow )` diff --git a/packages/components/src/ui/radix-dropdown/types.ts b/packages/components/src/ui/radix-dropdown/types.ts index 71cd1b00a6e9d..16afe17132990 100644 --- a/packages/components/src/ui/radix-dropdown/types.ts +++ b/packages/components/src/ui/radix-dropdown/types.ts @@ -3,11 +3,6 @@ */ import type * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; -/** - * Internal dependencies - */ -import type { IconType } from '../../icon'; - export type DropdownMenuProps = { /** * The open state of the dropdown menu when it is initially rendered. Use when @@ -87,6 +82,35 @@ export type DropdownSubMenuProps = { children: React.ReactNode; }; -export type DropdownItemProps = DropdownMenuPrimitive.DropdownMenuItemProps & { - icon?: IconType; +export type DropdownItemProps = { + /** + * When true, prevents the user from interacting with the item. + * + * @default false + */ + disabled?: DropdownMenuPrimitive.DropdownMenuItemProps[ 'disabled' ]; + /** + * Event handler called when the user selects an item (via mouse or keyboard). + * Calling `event.preventDefault` in this handler will prevent the dropdown + * menu from closing when selecting that item. + */ + onSelect?: DropdownMenuPrimitive.DropdownMenuItemProps[ 'onSelect' ]; + /** + * Optional text used for typeahead purposes. By default the typeahead + * behavior will use the `.textContent` of the item. Use this when the content + * is complex, or you have non-textual content inside. + */ + textValue?: DropdownMenuPrimitive.DropdownMenuItemProps[ 'textValue' ]; + /** + * The contents of the item + */ + children: React.ReactNode; + /** + * The contents of the item's prefix + */ + prefix?: React.ReactNode; + /** + * The contents of the item's suffix + */ + suffix?: React.ReactNode; }; From 8f666b1dc5462cfcf43e43a4ecee76c6c03dae47 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 20:21:57 +0200 Subject: [PATCH 31/64] Remove forwarded refs --- .../src/ui/radix-dropdown/index.tsx | 176 ++++++++---------- 1 file changed, 80 insertions(+), 96 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 162270f916a8c..c6ff4dabd0674 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -6,7 +6,6 @@ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; /** * WordPress dependencies */ -import { forwardRef } from '@wordpress/element'; import { SVG, Circle } from '@wordpress/primitives'; import { check, lineSolid } from '@wordpress/icons'; @@ -84,88 +83,78 @@ export const DropdownMenu = ( { ); }; -export const DropdownSubMenu = forwardRef( - ( - { - children, - subProps, - subContentProps, - portalProps, - trigger, - triggerProps, - }: DropdownSubMenuProps, - forwardedRef: React.ForwardedRef< any > - ) => { - return ( - - - { trigger } - - - - { children } - - - - ); - } -); +export const DropdownSubMenu = ( { + children, + subProps, + subContentProps, + portalProps, + trigger, + triggerProps, +}: DropdownSubMenuProps ) => { + return ( + + + { trigger } + + + + { children } + + + + ); +}; export const DropdownMenuLabel = DropdownMenuStyled.Label; export const DropdownMenuGroup = DropdownMenuPrimitive.Group; -export const DropdownMenuItem = forwardRef( - ( - { children, prefix, suffix, ...props }: DropdownItemProps, - forwardedRef: React.ForwardedRef< any > - ) => { - return ( - - { prefix && ( - - { prefix } - - ) } - { children } - { suffix && ( - - { suffix } - - ) } - - ); - } -); -export const DropdownMenuCheckboxItem = forwardRef( - ( - { - children, - ...props - }: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps, - forwardedRef: React.ForwardedRef< any > - ) => { - return ( - +export const DropdownMenuItem = ( { + children, + prefix, + suffix, + ...props +}: DropdownItemProps ) => { + return ( + + { prefix && ( - - { props.checked === 'indeterminate' && ( - - ) } - { props.checked === true && ( - - ) } - + { prefix } - { children } - - ); - } -); + ) } + { children } + { suffix && ( + + { suffix } + + ) } + + ); +}; -export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; +export const DropdownMenuCheckboxItem = ( { + children, + ...props +}: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps ) => { + return ( + + + + { props.checked === 'indeterminate' && ( + + ) } + { props.checked === true && ( + + ) } + + + { children } + + ); +}; + +export const DropdownMenuRadioGroup = () => ( + +); const radioDot = ( @@ -173,25 +162,20 @@ const radioDot = ( ); -export const DropdownMenuRadioItem = forwardRef( - ( - { - children, - ...props - }: DropdownMenuPrimitive.DropdownMenuRadioItemProps, - forwardedRef: React.ForwardedRef< any > - ) => { - return ( - - - - - - - { children } - - ); - } -); +export const DropdownMenuRadioItem = ( { + children, + ...props +}: DropdownMenuPrimitive.DropdownMenuRadioItemProps ) => { + return ( + + + + + + + { children } + + ); +}; export const DropdownMenuSeparator = DropdownMenuStyled.Separator; From 15744061cbe300b5bbbea3865f89314b1d641d5c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 21:16:53 +0200 Subject: [PATCH 32/64] Playing around with asChild and ref forwarding --- .../src/ui/radix-dropdown/index.tsx | 72 +++++++++++-------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index c6ff4dabd0674..5040d7caf4d44 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -6,6 +6,7 @@ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; /** * WordPress dependencies */ +import { forwardRef } from '@wordpress/element'; import { SVG, Circle } from '@wordpress/primitives'; import { check, lineSolid } from '@wordpress/icons'; @@ -108,42 +109,49 @@ export const DropdownSubMenu = ( { export const DropdownMenuLabel = DropdownMenuStyled.Label; export const DropdownMenuGroup = DropdownMenuPrimitive.Group; -export const DropdownMenuItem = ( { - children, - prefix, - suffix, - ...props -}: DropdownItemProps ) => { - return ( - - { prefix && ( - - { prefix } - - ) } - { children } - { suffix && ( - - { suffix } - - ) } - - ); -}; +export const DropdownMenuItem = forwardRef( + ( + { children, prefix, suffix, ...props }: DropdownItemProps, + forwardedRef: React.ForwardedRef< any > + ) => { + return ( + + { prefix && ( + + { prefix } + + ) } + { children } + { suffix && ( + + { suffix } + + ) } + + ); + } +); export const DropdownMenuCheckboxItem = ( { children, + checked = false, ...props }: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps ) => { return ( - + - - { props.checked === 'indeterminate' && ( - - ) } - { props.checked === true && ( - + { /* + TODO: adding `asChild` seems to trigger an error due to the fact + that `Icon` doesn't forward refs to the underlying dom elements. + */ } + + { ( checked === 'indeterminate' || checked === true ) && ( + ) } @@ -169,7 +177,11 @@ export const DropdownMenuRadioItem = ( { return ( - + { /* + TODO: adding `asChild` seems to trigger an error due to the fact + that `Icon` doesn't forward refs to the underlying dom elements. + */ } + From b30c4c8f8ffc6f43b7ffe394bcb60fabcb5d2236 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 21:35:03 +0200 Subject: [PATCH 33/64] Updated remaining props --- .../src/ui/radix-dropdown/index.tsx | 61 +++++-- .../src/ui/radix-dropdown/stories/index.tsx | 10 +- .../components/src/ui/radix-dropdown/types.ts | 150 ++++++++++++++++-- 3 files changed, 183 insertions(+), 38 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 5040d7caf4d44..33a80f8a66942 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -19,6 +19,12 @@ import type { DropdownMenuProps, DropdownSubMenuProps, DropdownItemProps, + DropdownMenuLabelProps, + DropdownMenuGroupProps, + DropdownMenuCheckboxItemProps, + DropdownMenuRadioGroupProps, + DropdownMenuRadioItemProps, + DropdownMenuSeparatorProps, } from './types'; // Observations / Questions: @@ -42,7 +48,7 @@ import type { // - added to mirror previous menu item component // - should we expect consumers to provide this directly with children? // - should we expose prefix / suffix to help? -// - Props: should we export HTML-inherited props? +// - Props: should we export HTML-inherited props? (e.g styles, classnames, hidden, etc..) export const DropdownMenu = ( { // Root props defaultOpen, @@ -85,20 +91,36 @@ export const DropdownMenu = ( { }; export const DropdownSubMenu = ( { + // Sub props + defaultOpen, + open, + onOpenChange, + // Sub trigger props + disabled, + textValue, + // Render props children, - subProps, - subContentProps, - portalProps, trigger, - triggerProps, }: DropdownSubMenuProps ) => { return ( - - + + { trigger } - - + + { children } @@ -106,8 +128,13 @@ export const DropdownSubMenu = ( { ); }; -export const DropdownMenuLabel = DropdownMenuStyled.Label; -export const DropdownMenuGroup = DropdownMenuPrimitive.Group; +export const DropdownMenuLabel = ( props: DropdownMenuLabelProps ) => ( + +); + +export const DropdownMenuGroup = ( props: DropdownMenuGroupProps ) => ( + +); export const DropdownMenuItem = forwardRef( ( @@ -160,9 +187,9 @@ export const DropdownMenuCheckboxItem = ( { ); }; -export const DropdownMenuRadioGroup = () => ( - -); +export const DropdownMenuRadioGroup = ( + props: DropdownMenuRadioGroupProps +) => ; const radioDot = ( @@ -173,7 +200,7 @@ const radioDot = ( export const DropdownMenuRadioItem = ( { children, ...props -}: DropdownMenuPrimitive.DropdownMenuRadioItemProps ) => { +}: DropdownMenuRadioItemProps ) => { return ( @@ -190,4 +217,6 @@ export const DropdownMenuRadioItem = ( { ); }; -export const DropdownMenuSeparator = DropdownMenuStyled.Separator; +export const DropdownMenuSeparator = ( props: DropdownMenuSeparatorProps ) => ( + +); diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index b1c9369f04f82..f74b19bf43c51 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -128,6 +128,7 @@ const CheckboxItemsGroup = () => { return ( + Options { Pedro Duarte - + Colm Tuite
@@ -207,10 +205,6 @@ Default.args = { More Tools } - subContentProps={ { - sideOffset: 2, - alignOffset: -5, - } } > ⌘+S } diff --git a/packages/components/src/ui/radix-dropdown/types.ts b/packages/components/src/ui/radix-dropdown/types.ts index 16afe17132990..0ed61a3150032 100644 --- a/packages/components/src/ui/radix-dropdown/types.ts +++ b/packages/components/src/ui/radix-dropdown/types.ts @@ -53,7 +53,6 @@ export type DropdownMenuProps = { * @default 0 */ alignOffset?: DropdownMenuPrimitive.DropdownMenuContentProps[ 'alignOffset' ]; - /** * The contents rendered inside the trigger */ @@ -62,24 +61,47 @@ export type DropdownMenuProps = { * The contents of the dropdown */ children: React.ReactNode; + + // TODO: slot name ? }; export type DropdownSubMenuProps = { - subProps?: Omit< DropdownMenuPrimitive.DropdownMenuSubProps, 'children' >; - subContentProps?: Omit< - DropdownMenuPrimitive.DropdownMenuSubContentProps, - 'children' - >; - portalProps?: Omit< - DropdownMenuPrimitive.DropdownMenuPortalProps, - 'children' - >; - triggerProps?: Omit< - DropdownMenuPrimitive.DropdownMenuSubTriggerProps, - 'children' - >; + /** + * The open state of the submenu when it is initially rendered. Use when you + * do not need to control its open state. + */ + defaultOpen?: DropdownMenuPrimitive.DropdownMenuSubProps[ 'defaultOpen' ]; + /** + * The controlled open state of the submenu. Must be used in conjunction with + * `onOpenChange`. + */ + open?: DropdownMenuPrimitive.DropdownMenuSubProps[ 'open' ]; + /** + * Event handler called when the open state of the submenu changes. + */ + onOpenChange?: DropdownMenuPrimitive.DropdownMenuSubProps[ 'onOpenChange' ]; + + // TODO: conflict/duplication when using `asChild` and ? + /** + * When `true`, prevents the user from interacting with the item. + */ + disabled?: DropdownMenuPrimitive.DropdownMenuSubTriggerProps[ 'disabled' ]; + /** + * Optional text used for typeahead purposes. By default the typeahead + * behavior will use the `.textContent` of the item. Use this when the content + * is complex, or you have non-textual content inside. + */ + textValue?: DropdownMenuPrimitive.DropdownMenuSubTriggerProps[ 'textValue' ]; + /** + * The contents rendered inside the trigger + */ trigger: React.ReactNode; + /** + * The contents of the dropdown sub menu + */ children: React.ReactNode; + + // TODO: slot name ? }; export type DropdownItemProps = { @@ -114,3 +136,103 @@ export type DropdownItemProps = { */ suffix?: React.ReactNode; }; + +export type DropdownMenuCheckboxItemProps = { + /** + * The controlled checked state of the item. + * Must be used in conjunction with `onCheckedChange`. + * + * @default false + */ + checked?: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps[ 'checked' ]; + /** + * Event handler called when the checked state changes. + */ + onCheckedChange?: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps[ 'onCheckedChange' ]; + /** + * When `true`, prevents the user from interacting with the item. + */ + disabled?: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps[ 'disabled' ]; + /** + * Event handler called when the user selects an item (via mouse or keyboard). + * Calling `event.preventDefault` in this handler will prevent the dropdown + * menu from closing when selecting that item. + */ + onSelect?: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps[ 'onSelect' ]; + /** + * Optional text used for typeahead purposes. By default the typeahead + * behavior will use the `.textContent` of the item. Use this when the content + * is complex, or you have non-textual content inside. + */ + textValue?: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps[ 'textValue' ]; + /** + * The contents of the checkbox item + */ + children: React.ReactNode; + /** + * The contents of the checkbox item's suffix + */ + suffix?: React.ReactNode; +}; + +export type DropdownMenuRadioGroupProps = { + /** + * The value of the selected item in the group. + */ + value?: DropdownMenuPrimitive.DropdownMenuRadioGroupProps[ 'value' ]; + /** + * Event handler called when the value changes. + */ + onValueChange?: DropdownMenuPrimitive.DropdownMenuRadioGroupProps[ 'onValueChange' ]; + /** + * The contents of the radio group + */ + children: React.ReactNode; +}; + +export type DropdownMenuRadioItemProps = { + /** + * The unique value of the item. + */ + value: DropdownMenuPrimitive.DropdownMenuRadioItemProps[ 'value' ]; + /** + * When `true`, prevents the user from interacting with the item. + */ + disabled?: DropdownMenuPrimitive.DropdownMenuRadioItemProps[ 'disabled' ]; + /** + * Event handler called when the user selects an item (via mouse or keyboard). + * Calling `event.preventDefault` in this handler will prevent the dropdown + * menu from closing when selecting that item. + */ + onSelect?: DropdownMenuPrimitive.DropdownMenuRadioItemProps[ 'onSelect' ]; + /** + * Optional text used for typeahead purposes. By default the typeahead + * behavior will use the `.textContent` of the item. Use this when the content + * is complex, or you have non-textual content inside. + */ + textValue?: DropdownMenuPrimitive.DropdownMenuRadioItemProps[ 'textValue' ]; + /** + * The contents of the radio item + */ + children: React.ReactNode; + /** + * The contents of the radio item's suffix + */ + suffix?: React.ReactNode; +}; + +export type DropdownMenuLabelProps = { + /** + * The contents of the label + */ + children: React.ReactNode; +}; + +export type DropdownMenuGroupProps = { + /** + * The contents of the group + */ + children: React.ReactNode; +}; + +export type DropdownMenuSeparatorProps = {}; From c41f58aaaf0f23683c61c4dd87e77441f16fc56b Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 21:35:35 +0200 Subject: [PATCH 34/64] Add suffix to checkbox / radio items --- .../components/src/ui/radix-dropdown/index.tsx | 14 +++++++++++++- .../src/ui/radix-dropdown/stories/index.tsx | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 33a80f8a66942..17218ed62846f 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -162,8 +162,9 @@ export const DropdownMenuItem = forwardRef( export const DropdownMenuCheckboxItem = ( { children, checked = false, + suffix, ...props -}: DropdownMenuPrimitive.DropdownMenuCheckboxItemProps ) => { +}: DropdownMenuCheckboxItemProps ) => { return ( @@ -183,6 +184,11 @@ export const DropdownMenuCheckboxItem = ( { { children } + { suffix && ( + + { suffix } + + ) } ); }; @@ -199,6 +205,7 @@ const radioDot = ( export const DropdownMenuRadioItem = ( { children, + suffix, ...props }: DropdownMenuRadioItemProps ) => { return ( @@ -213,6 +220,11 @@ export const DropdownMenuRadioItem = ( { { children } + { suffix && ( + + { suffix } + + ) } ); }; diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index f74b19bf43c51..f211b33f72bc4 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -132,6 +132,7 @@ const CheckboxItemsGroup = () => { ⌘+B } > Show Bookmarks From 77585cdb3e3264150a9018c234f50b14e8365429 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 12 Apr 2023 21:45:18 +0200 Subject: [PATCH 35/64] Tweak styles (remove arrow, box shadow, center Storybook example) --- .../components/src/ui/radix-dropdown/index.tsx | 1 - .../src/ui/radix-dropdown/stories/index.tsx | 14 ++++++++++++-- .../components/src/ui/radix-dropdown/styles.ts | 16 +++------------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 17218ed62846f..b8923b577dcf9 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -83,7 +83,6 @@ export const DropdownMenu = ( { loop={ true } > { children } -
diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index f211b33f72bc4..c248c683457b8 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -166,7 +166,17 @@ const RadioItemsGroup = () => { }; const Template: ComponentStory< typeof DropdownMenu > = ( props ) => ( - +
+ +
); export const Default = Template.bind( {} ); Default.args = { @@ -175,7 +185,7 @@ Default.args = { ), - sideOffset: 5, + sideOffset: 12, children: ( <> diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index af08b0d37f1a5..2fa5b3bed9339 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -57,8 +57,9 @@ const baseContent = css` border: 1px solid ${ COLORS.ui.border }; border-radius: 6px; padding: ${ space( 2 ) }; - box-shadow: 0px 10px 38px -10px rgba( 22, 23, 24, 0.35 ), - 0px 10px 20px -15px rgba( 22, 23, 24, 0.2 ); + box-shadow: 0 0.7px 1px rgba( 0, 0, 0, 0.1 ), + 0 1.2px 1.7px -0.2px rgba( 0, 0, 0, 0.1 ), + 0 2.3px 3.3px -0.5px rgba( 0, 0, 0, 0.1 ); animation-duration: ${ ANIMATION_PARAMS.DURATION }; animation-timing-function: ${ ANIMATION_PARAMS.EASING }; will-change: transform, opacity; @@ -175,14 +176,3 @@ export const ItemPrefixWrapper = styled.span` export const ItemSuffixWrapper = styled.span` ${ itemSuffix } `; - -export const Arrow = styled( DropdownMenu.Arrow )` - fill: ${ COLORS.ui.background }; - - /* The following styles aim at adding a border to the arrow*/ - stroke: ${ COLORS.ui.border }; - stroke-dasharray: 36 28; - stroke-dashoffset: 34; - stroke-linejoin: round; - stroke-width: 2.5; -`; From 8dedbc1a6774b61247bc86956ad0e93b388ebe27 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Thu, 27 Apr 2023 03:35:23 +0900 Subject: [PATCH 36/64] Try separate submenu trigger component --- .../src/ui/radix-dropdown/index.tsx | 26 +++++++++++++++++-- .../src/ui/radix-dropdown/stories/index.tsx | 12 ++++----- .../components/src/ui/radix-dropdown/types.ts | 21 +++++++++++++-- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index b8923b577dcf9..5f75d1bc555d3 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -8,7 +8,7 @@ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; */ import { forwardRef } from '@wordpress/element'; import { SVG, Circle } from '@wordpress/primitives'; -import { check, lineSolid } from '@wordpress/icons'; +import { check, chevronRightSmall, lineSolid } from '@wordpress/icons'; /** * Internal dependencies @@ -25,6 +25,7 @@ import type { DropdownMenuRadioGroupProps, DropdownMenuRadioItemProps, DropdownMenuSeparatorProps, + DropdownSubMenuTriggerProps, } from './types'; // Observations / Questions: @@ -89,6 +90,28 @@ export const DropdownMenu = ( { ); }; +export const DropdownSubMenuTrigger = ( { + prefix, + suffix = , + children, +}: DropdownSubMenuTriggerProps ) => { + return ( + <> + { prefix && ( + + { prefix } + + ) } + { children } + { suffix && ( + + { suffix } + + ) } + + ); +}; + export const DropdownSubMenu = ( { // Sub props defaultOpen, @@ -110,7 +133,6 @@ export const DropdownSubMenu = ( { { trigger } diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index c248c683457b8..57ea750618048 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -18,13 +18,14 @@ import { DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, + DropdownSubMenuTrigger, } from '../'; /** * WordPress dependencies */ import { useState, createContext, useContext } from '@wordpress/element'; -import { chevronRightSmall, menu, wordpress } from '@wordpress/icons'; +import { menu, wordpress } from '@wordpress/icons'; /** * Internal dependencies @@ -46,6 +47,7 @@ const meta: ComponentMeta< typeof DropdownMenu > = { subcomponents: { DropdownMenuItem, DropdownSubMenu, + DropdownSubMenuTrigger, DropdownMenuSeparator, DropdownMenuCheckboxItem, DropdownMenuGroup, @@ -208,13 +210,9 @@ Default.args = { - } - > + More Tools - + } > Date: Tue, 9 May 2023 20:05:30 +0200 Subject: [PATCH 37/64] Move layout wrapper to decorator --- .../src/ui/radix-dropdown/stories/index.tsx | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 57ea750618048..50a37a9e5eede 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -65,6 +65,7 @@ const meta: ComponentMeta< typeof DropdownMenu > = { docs: { source: { state: 'open', excludeDecorators: true } }, }, decorators: [ + // Shared story state ( Story ) => { const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); const [ urlsChecked, setUrlsChecked ] = useState( false ); @@ -85,11 +86,25 @@ const meta: ComponentMeta< typeof DropdownMenu > = { ); }, + // Layout wrapper + ( Story ) => ( +
+ +
+ ), ], }; export default meta; -const MenuButton = styled.div` +const MenuButton = styled.button` all: unset; font-family: inherit; border-radius: 100%; @@ -168,17 +183,7 @@ const RadioItemsGroup = () => { }; const Template: ComponentStory< typeof DropdownMenu > = ( props ) => ( -
- -
+ ); export const Default = Template.bind( {} ); Default.args = { From c6a1c40888f5c400b6b61717ff8af336dbb6dbe8 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 9 May 2023 21:46:07 +0200 Subject: [PATCH 38/64] Do not use `asChild` for indicator wrappers --- packages/components/src/ui/radix-dropdown/index.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 5f75d1bc555d3..4ec31a11db9bd 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -189,11 +189,7 @@ export const DropdownMenuCheckboxItem = ( { return ( - { /* - TODO: adding `asChild` seems to trigger an error due to the fact - that `Icon` doesn't forward refs to the underlying dom elements. - */ } - + { ( checked === 'indeterminate' || checked === true ) && ( - { /* - TODO: adding `asChild` seems to trigger an error due to the fact - that `Icon` doesn't forward refs to the underlying dom elements. - */ } - + From d4b9addb62eba9d2b96d382b8b5797da0cb46b38 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 9 May 2023 21:46:24 +0200 Subject: [PATCH 39/64] Remove solved TODOs --- .../src/ui/radix-dropdown/index.tsx | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 4ec31a11db9bd..230eace26fb5a 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -28,28 +28,6 @@ import type { DropdownSubMenuTriggerProps, } from './types'; -// Observations / Questions: -// - is it enough on the larger components to have only one forwarded ref? -// If we have only one, should it be to the "root", the "trigger", or the "content"? -// - Should we be consistent in using the same value for the `asChild` prop on: -// - trigger -// - content -// - sub trigger -// - sub content -// - Should we allow customizing the `asChild` prop on trigger and content? -// - Props & customisability: -// - Which props should we expose? -// - Should props be "namespaced" for each subcomponent? -// - We should probably explicitly `Pick<>` every prop that we want to expose -// - Subtrigger arrow: -// - Should it always be there (ie. an internal implementation)? -// - Should we just expect that the the consumers handle it themselves? -// - Should we expose it as a separate component that consumers could use? -// - DropdowmMenuItem icon prop: -// - added to mirror previous menu item component -// - should we expect consumers to provide this directly with children? -// - should we expose prefix / suffix to help? -// - Props: should we export HTML-inherited props? (e.g styles, classnames, hidden, etc..) export const DropdownMenu = ( { // Root props defaultOpen, From a143f60521a410417835247b20d366a0bb7e0841 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 9 May 2023 23:23:02 +0200 Subject: [PATCH 40/64] RTL support --- .../src/ui/radix-dropdown/index.tsx | 13 ++++++++++-- .../src/ui/radix-dropdown/styles.ts | 20 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 230eace26fb5a..dc32840907de9 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -7,8 +7,9 @@ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; -import { SVG, Circle } from '@wordpress/primitives'; +import { isRTL } from '@wordpress/i18n'; import { check, chevronRightSmall, lineSolid } from '@wordpress/icons'; +import { SVG, Circle } from '@wordpress/primitives'; /** * Internal dependencies @@ -49,6 +50,7 @@ export const DropdownMenu = ( { open={ open } onOpenChange={ onOpenChange } modal={ modal } + dir={ isRTL() ? 'rtl' : 'ltr' } > { trigger } @@ -68,9 +70,16 @@ export const DropdownMenu = ( { ); }; +const ChevronIcon = () => ( + +); + export const DropdownSubMenuTrigger = ( { prefix, - suffix = , + suffix = , children, }: DropdownSubMenuTriggerProps ) => { return ( diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 2fa5b3bed9339..ecbd2b3cb5b1c 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -8,8 +8,9 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; /** * Internal dependencies */ -import { COLORS, font } from '../../utils'; +import { COLORS, font, rtl } from '../../utils'; import { space } from '../utils/space'; +import Icon from '../../icon'; const ANIMATION_PARAMS = { SLIDE_AMOUNT: '2px', @@ -120,7 +121,7 @@ const itemPrefix = css` display: inline-flex; align-items: center; justify-content: center; - margin-left: calc( -1 * ${ ITEM_HORIZONTAL_PADDING } ); + margin-inline-start: calc( -1 * ${ ITEM_HORIZONTAL_PADDING } ); `; const itemSuffix = css` @@ -128,8 +129,8 @@ const itemSuffix = css` display: inline-flex; align-items: center; justify-content: center; - margin-left: auto; - padding-left: ${ space( 6 ) }; + margin-inline-start: auto; + padding-inline-start: ${ space( 6 ) }; `; export const Content = styled( DropdownMenu.Content )` @@ -176,3 +177,14 @@ export const ItemPrefixWrapper = styled.span` export const ItemSuffixWrapper = styled.span` ${ itemSuffix } `; + +export const RTLFlippableIcon = styled( Icon )` + ${ rtl( + { + transform: 'scaleX(1)', + }, + { + transform: 'scaleX(-1)', + } + )() } +`; From a6bc304f4a6d7eebcef9e2dcba1278dc4109892f Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 9 May 2023 23:28:04 +0200 Subject: [PATCH 41/64] Add support for reduced motion --- packages/components/src/ui/radix-dropdown/styles.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index ecbd2b3cb5b1c..183b369efcc96 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -80,6 +80,10 @@ const baseContent = css` &[data-side='left'] { animation-name: ${ slideRightAndFade }; } + + @media ( prefers-reduced-motion ) { + animation-duration: 0s; + } `; const baseItem = css` From a4b27b49eb5c256bfd564c0ad9f40155a5236e21 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 9 May 2023 23:37:49 +0200 Subject: [PATCH 42/64] Expose component via lock APIs --- packages/components/src/private-apis.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index e114559e5088c..e51aba7d03c77 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -9,6 +9,18 @@ import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/pri import { default as CustomSelectControl } from './custom-select-control'; import { positionToPlacement as __experimentalPopoverLegacyPositionToPlacement } from './popover/utils'; import { createPrivateSlotFill } from './slot-fill'; +import { + DropdownMenu as DropdownMenuV2, + DropdownMenuCheckboxItem as DropdownMenuCheckboxItemV2, + DropdownMenuGroup as DropdownMenuGroupV2, + DropdownMenuItem as DropdownMenuItemV2, + DropdownMenuLabel as DropdownMenuLabelV2, + DropdownMenuRadioGroup as DropdownMenuRadioGroupV2, + DropdownMenuRadioItem as DropdownMenuRadioItemV2, + DropdownMenuSeparator as DropdownMenuSeparatorV2, + DropdownSubMenu as DropdownSubMenuV2, + DropdownSubMenuTrigger as DropdownSubMenuTriggerV2, +} from './ui/radix-dropdown'; export const { lock, unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules( @@ -21,4 +33,14 @@ lock( privateApis, { CustomSelectControl, __experimentalPopoverLegacyPositionToPlacement, createPrivateSlotFill, + DropdownMenuV2, + DropdownMenuCheckboxItemV2, + DropdownMenuGroupV2, + DropdownMenuItemV2, + DropdownMenuLabelV2, + DropdownMenuRadioGroupV2, + DropdownMenuRadioItemV2, + DropdownMenuSeparatorV2, + DropdownSubMenuV2, + DropdownSubMenuTriggerV2, } ); From d8518c095beea647c488319392284f001cf0ac40 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 10 May 2023 17:00:19 +0200 Subject: [PATCH 43/64] Polish styles --- .../src/ui/radix-dropdown/index.tsx | 22 +++++------ .../src/ui/radix-dropdown/styles.ts | 38 ++++++++++++++----- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index dc32840907de9..8341fc3b6c751 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -70,16 +70,14 @@ export const DropdownMenu = ( { ); }; -const ChevronIcon = () => ( - -); - export const DropdownSubMenuTrigger = ( { prefix, - suffix = , + suffix = ( + + ), children, }: DropdownSubMenuTriggerProps ) => { return ( @@ -176,7 +174,7 @@ export const DropdownMenuCheckboxItem = ( { return ( - + { ( checked === 'indeterminate' || checked === true ) && ( ) } - + { children } { suffix && ( @@ -215,9 +213,9 @@ export const DropdownMenuRadioItem = ( { return ( - + - + { children } { suffix && ( diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 183b369efcc96..9557363e659cd 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -56,7 +56,7 @@ const baseContent = css` min-width: 220px; background-color: ${ COLORS.ui.background }; border: 1px solid ${ COLORS.ui.border }; - border-radius: 6px; + border-radius: 2px; padding: ${ space( 2 ) }; box-shadow: 0 0.7px 1px rgba( 0, 0, 0, 0.1 ), 0 1.2px 1.7px -0.2px rgba( 0, 0, 0, 0.1 ), @@ -91,13 +91,12 @@ const baseItem = css` font-size: ${ font( 'default.fontSize' ) }; font-family: inherit; font-weight: normal; - line-height: 1; + line-height: 20px; color: ${ COLORS.gray[ 900 ] }; border-radius: 3px; display: flex; align-items: center; - height: ${ space( 9 ) }; - padding: 0 ${ ITEM_HORIZONTAL_PADDING }; + padding: ${ space( 2 ) } ${ ITEM_HORIZONTAL_PADDING }; position: relative; user-select: none; outline: none; @@ -126,6 +125,13 @@ const itemPrefix = css` align-items: center; justify-content: center; margin-inline-start: calc( -1 * ${ ITEM_HORIZONTAL_PADDING } ); + /* + Negative margin allows the suffix to be as tall as the whole item + (incl. padding) before increasing the items' height. This can be useful, + e.g., when using icons that are bigger than 20x20 px + */ + margin-top: ${ space( -2 ) }; + margin-bottom: ${ space( -2 ) }; `; const itemSuffix = css` @@ -135,6 +141,13 @@ const itemSuffix = css` justify-content: center; margin-inline-start: auto; padding-inline-start: ${ space( 6 ) }; + /* + Negative margin allows the suffix to be as tall as the whole item + (incl. padding) before increasing the items' height. This can be useful, + e.g., when using icons that are bigger than 20x20 px + */ + margin-top: ${ space( -2 ) }; + margin-bottom: ${ space( -2 ) }; `; export const Content = styled( DropdownMenu.Content )` @@ -170,8 +183,15 @@ export const Label = styled( DropdownMenu.Label )` export const Separator = styled( DropdownMenu.Separator )` height: 1px; - background-color: ${ COLORS.ui.borderDisabled }; - margin: ${ space( 2 ) }; + background-color: ${ COLORS.ui.border }; + /* Negative horizontal margin to make separator go from side to side */ + margin: ${ space( 2 ) } ${ space( -2 ) }; +`; + +export const ItemIndicator = styled( DropdownMenu.ItemIndicator )` + display: inline-flex; + align-items: center; + justify-content: center; `; export const ItemPrefixWrapper = styled.span` @@ -182,13 +202,13 @@ export const ItemSuffixWrapper = styled.span` ${ itemSuffix } `; -export const RTLFlippableIcon = styled( Icon )` +export const SubmenuRtlChevronIcon = styled( Icon )` ${ rtl( { - transform: 'scaleX(1)', + transform: `scaleX(1) translateX(${ space( 2 ) })`, }, { - transform: 'scaleX(-1)', + transform: `scaleX(-1) translateX(${ space( 2 ) })`, } )() } `; From c96e44951dadb59dd2428179232cb9d300a6be6c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 10 May 2023 17:00:32 +0200 Subject: [PATCH 44/64] Generalize storybook --- .../src/ui/radix-dropdown/stories/index.tsx | 135 ++++++++++-------- 1 file changed, 75 insertions(+), 60 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 50a37a9e5eede..65369bfc1dbe3 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -33,12 +33,12 @@ import { menu, wordpress } from '@wordpress/icons'; import Icon from '../../../icon'; const DropdownMenuStoryContext = createContext< { - bookmarksChecked?: boolean; - setBookmarksChecked?: ( v: boolean ) => void; - urlsChecked?: boolean; - setUrlsChecked?: ( v: boolean ) => void; - person?: string; - setPerson?: ( v: string ) => void; + itemOneChecked?: boolean; + setItemOneChecked?: ( v: boolean ) => void; + itemTwoChecked?: boolean; + setItemTwoChecked?: ( v: boolean ) => void; + radioValue?: string; + setRadioValue?: ( v: string ) => void; } >( {} ); const meta: ComponentMeta< typeof DropdownMenu > = { @@ -67,19 +67,19 @@ const meta: ComponentMeta< typeof DropdownMenu > = { decorators: [ // Shared story state ( Story ) => { - const [ bookmarksChecked, setBookmarksChecked ] = useState( true ); - const [ urlsChecked, setUrlsChecked ] = useState( false ); - const [ person, setPerson ] = useState( 'pedro' ); + const [ itemOneChecked, setItemOneChecked ] = useState( true ); + const [ itemTwoChecked, setItemTwoChecked ] = useState( false ); + const [ radioValue, setRadioValue ] = useState( 'radio-one' ); return ( @@ -131,34 +131,30 @@ const MenuButton = styled.button` } `; -const KeyboardShortcut = styled.span` - opacity: 0.8; -`; - const CheckboxItemsGroup = () => { const { - bookmarksChecked, - setBookmarksChecked, - urlsChecked, - setUrlsChecked, + itemOneChecked, + setItemOneChecked, + itemTwoChecked, + setItemTwoChecked, } = useContext( DropdownMenuStoryContext ); return ( - Options + Checkbox group label ⌘+B } + checked={ itemOneChecked } + onCheckedChange={ setItemOneChecked } + suffix={ ⌘+B } > - Show Bookmarks + Checkbox item one - Show Full URLs + Checkbox item two @@ -167,16 +163,21 @@ const CheckboxItemsGroup = () => { }; const RadioItemsGroup = () => { - const { person, setPerson } = useContext( DropdownMenuStoryContext ); + const { radioValue, setRadioValue } = useContext( + DropdownMenuStoryContext + ); return ( - - People - - Pedro Duarte + + Radio group label + + Radio item one - - Colm Tuite + + Radio item two ); @@ -196,39 +197,53 @@ Default.args = { children: ( <> + Menu item } - suffix={ ⌘+T } > - New Tab + Menu item with prefix - ⌘+N } - > - New Window - - ⇧+⌘+N } - > - New Private Window + ⌥⌘T }> + Menu item with suffix + Disabled menu item - More Tools - + Submenu } > - ⌘+S } - > - Save Page As… + ⌘+S }> + Submenu with suffix + + +
+ Submenu item + + With additional custom text + +
- Create Shortcut… - Name Window… - Developer Tools + + Submenu + + } + > + Submenu item + Submenu item +
From 0c6bce7675cd44c036e9eaeb1cad6c20673f4345 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 10 May 2023 17:18:53 +0200 Subject: [PATCH 45/64] Focus/hover styles --- .../components/src/ui/radix-dropdown/styles.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index 9557363e659cd..fc93699cbf5ed 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -107,11 +107,13 @@ const baseItem = css` } &[data-highlighted] { - color: ${ COLORS.ui.theme }; - } + /* + TODO: reconcile with global focus styles + (incl high contrast mode fallbacks) + */ - &[data-highlighted]:focus-visible { - outline: 1px solid ${ COLORS.ui.theme }; + background-color: ${ COLORS.ui.theme }; + color: white; } svg { @@ -168,7 +170,9 @@ export const RadioItem = styled( DropdownMenu.RadioItem )` `; export const SubTrigger = styled( DropdownMenu.SubTrigger )` &[data-state='open']:not( [data-highlighted] ) { - background-color: ${ COLORS.ui.backgroundDisabled }; + /* TODO: use variable */ + background-color: rgba( 56, 88, 233, 0.04 ); + color: ${ COLORS.ui.theme }; } ${ baseItem } From 203b6a96143f83258e1f387425bcedbf45535d7a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 11 May 2023 16:09:22 +0200 Subject: [PATCH 46/64] Refine styles --- .../src/ui/radix-dropdown/index.tsx | 13 +- .../src/ui/radix-dropdown/stories/index.tsx | 21 ++- .../src/ui/radix-dropdown/styles.ts | 149 ++++++++++++------ 3 files changed, 120 insertions(+), 63 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/ui/radix-dropdown/index.tsx index 8341fc3b6c751..8a2c9f1d1d4a5 100644 --- a/packages/components/src/ui/radix-dropdown/index.tsx +++ b/packages/components/src/ui/radix-dropdown/index.tsx @@ -29,6 +29,11 @@ import type { DropdownSubMenuTriggerProps, } from './types'; +// Menu content's side padding + 4px +const SUB_MENU_OFFSET_SIDE = 12; +// Opposite amount of the top padding of the menu item +const SUB_MENU_OFFSET_ALIGN = -8; + export const DropdownMenu = ( { // Root props defaultOpen, @@ -124,8 +129,8 @@ export const DropdownSubMenu = ( { { children } @@ -180,7 +185,7 @@ export const DropdownMenuCheckboxItem = ( { icon={ checked === 'indeterminate' ? lineSolid : check } - size={ 20 } + size={ 24 } /> ) } @@ -214,7 +219,7 @@ export const DropdownMenuRadioItem = ( { - + { children } diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 65369bfc1dbe3..0d90fa9a32b27 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -131,6 +131,18 @@ const MenuButton = styled.button` } `; +const ItemHelpText = styled.span` + font-size: 10px; + color: #777; + + /* "> * > &" syntax is to target only immediate parent menu item */ + [data-highlighted] > * > &, + [data-state='open'] > * > &, + [data-disabled] > * & { + color: inherit; + } +`; + const CheckboxItemsGroup = () => { const { itemOneChecked, @@ -223,14 +235,9 @@ Default.args = { } } > Submenu item - + With additional custom text - +
diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/ui/radix-dropdown/styles.ts index fc93699cbf5ed..8eeb5a4137bd6 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/ui/radix-dropdown/styles.ts @@ -18,7 +18,9 @@ const ANIMATION_PARAMS = { EASING: 'cubic-bezier( 0.16, 1, 0.3, 1 )', }; -const ITEM_HORIZONTAL_PADDING = space( 2 ); +const ITEM_PREFIX_WIDTH = space( 7 ); +const ITEM_PADDING_INLINE_START = space( 2 ); +const ITEM_PADDING_INLINE_END = space( 2.5 ); const slideUpAndFade = keyframes( { '0%': { @@ -55,12 +57,12 @@ const slideLeftAndFade = keyframes( { const baseContent = css` min-width: 220px; background-color: ${ COLORS.ui.background }; - border: 1px solid ${ COLORS.ui.border }; - border-radius: 2px; + border-radius: 6px; padding: ${ space( 2 ) }; - box-shadow: 0 0.7px 1px rgba( 0, 0, 0, 0.1 ), - 0 1.2px 1.7px -0.2px rgba( 0, 0, 0, 0.1 ), - 0 2.3px 3.3px -0.5px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0.1px 4px 16.4px -0.5px rgba( 0, 0, 0, 0.1 ), + 0px 5.5px 7.8px -0.3px rgba( 0, 0, 0, 0.1 ), + 0px 2.7px 3.8px -0.2px rgba( 0, 0, 0, 0.1 ), + 0px 0.7px 1px rgba( 0, 0, 0, 0.1 ); animation-duration: ${ ANIMATION_PARAMS.DURATION }; animation-timing-function: ${ ANIMATION_PARAMS.EASING }; will-change: transform, opacity; @@ -86,6 +88,66 @@ const baseContent = css` } `; +const itemPrefix = css` + width: ${ ITEM_PREFIX_WIDTH }; + display: inline-flex; + align-items: center; + justify-content: center; + /* Prefixes don't get affected by the item's inline start padding */ + margin-inline-start: calc( -1 * ${ ITEM_PADDING_INLINE_START } ); + /* + Negative margin allows the suffix to be as tall as the whole item + (incl. padding) before increasing the items' height. This can be useful, + e.g., when using icons that are bigger than 20x20 px + */ + margin-top: ${ space( -2 ) }; + margin-bottom: ${ space( -2 ) }; +`; + +const itemSuffix = css` + width: max-content; + display: inline-flex; + align-items: center; + justify-content: center; + /* Push prefix to the inline-end of the item */ + margin-inline-start: auto; + /* Minimum space between the item's content and suffix */ + padding-inline-start: ${ space( 6 ) }; + /* + Negative margin allows the suffix to be as tall as the whole item + (incl. padding) before increasing the items' height. This can be useful, + e.g., when using icons that are bigger than 20x20 px + */ + margin-top: ${ space( -2 ) }; + margin-bottom: ${ space( -2 ) }; + + /* + Override color in normal conditions, but inherit the item's color + for altered conditions. + + TODO: + - For now, used opacity like for disabled item, which makes it work + regardless of the theme + - how do we translate this for themes? Should we have a new variable + for "secondary" text? + */ + opacity: 0.6; + + [data-highlighted] > &, + [data-state='open'] > &, + [data-disabled] > & { + opacity: 1; + } +`; + +export const ItemPrefixWrapper = styled.span` + ${ itemPrefix } +`; + +export const ItemSuffixWrapper = styled.span` + ${ itemSuffix } +`; + const baseItem = css` all: unset; font-size: ${ font( 'default.fontSize' ) }; @@ -96,13 +158,19 @@ const baseItem = css` border-radius: 3px; display: flex; align-items: center; - padding: ${ space( 2 ) } ${ ITEM_HORIZONTAL_PADDING }; + padding: ${ space( 2 ) } ${ ITEM_PADDING_INLINE_END } ${ space( 2 ) } + ${ ITEM_PADDING_INLINE_START }; position: relative; user-select: none; outline: none; &[data-disabled] { - color: ${ COLORS.ui.textDisabled }; + /* + TODO: + - we need a disabled color in the Theme variables + - design specs use opacity instead of setting a new text color + */ + opacity: 0.5; pointer-events: none; } @@ -119,37 +187,10 @@ const baseItem = css` svg { fill: currentColor; } -`; -const itemPrefix = css` - width: ${ space( 8 ) }; - display: inline-flex; - align-items: center; - justify-content: center; - margin-inline-start: calc( -1 * ${ ITEM_HORIZONTAL_PADDING } ); - /* - Negative margin allows the suffix to be as tall as the whole item - (incl. padding) before increasing the items' height. This can be useful, - e.g., when using icons that are bigger than 20x20 px - */ - margin-top: ${ space( -2 ) }; - margin-bottom: ${ space( -2 ) }; -`; - -const itemSuffix = css` - width: max-content; - display: inline-flex; - align-items: center; - justify-content: center; - margin-inline-start: auto; - padding-inline-start: ${ space( 6 ) }; - /* - Negative margin allows the suffix to be as tall as the whole item - (incl. padding) before increasing the items' height. This can be useful, - e.g., when using icons that are bigger than 20x20 px - */ - margin-top: ${ space( -2 ) }; - margin-bottom: ${ space( -2 ) }; + &:not( :has( ${ ItemPrefixWrapper } ) ) { + padding-inline-start: ${ ITEM_PREFIX_WIDTH }; + } `; export const Content = styled( DropdownMenu.Content )` @@ -179,17 +220,29 @@ export const SubTrigger = styled( DropdownMenu.SubTrigger )` `; export const Label = styled( DropdownMenu.Label )` - padding: 0 ${ ITEM_HORIZONTAL_PADDING }; - font-size: ${ font( 'helpText.fontSize' ) }; - line-height: ${ space( 7 ) }; - color: ${ COLORS.ui.textDisabled }; + box-sizing: border-box; + display: flex; + align-items: center; + min-height: ${ space( 8 ) }; + + padding: ${ space( 2 ) } ${ ITEM_PADDING_INLINE_END } ${ space( 2 ) } + ${ ITEM_PREFIX_WIDTH }; + /* TODO: color doesn't match available UI variables */ + color: ${ COLORS.gray[ 700 ] }; + + /* TODO: font size doesn't match available ones via "font" utils */ + font-size: 11px; + line-height: 1.4; + font-weight: 500; + text-transform: uppercase; `; export const Separator = styled( DropdownMenu.Separator )` height: 1px; - background-color: ${ COLORS.ui.border }; + /* TODO: doesn't match border color from variables */ + background-color: ${ COLORS.ui.borderDisabled }; /* Negative horizontal margin to make separator go from side to side */ - margin: ${ space( 2 ) } ${ space( -2 ) }; + margin: ${ space( 2 ) } 0; `; export const ItemIndicator = styled( DropdownMenu.ItemIndicator )` @@ -198,14 +251,6 @@ export const ItemIndicator = styled( DropdownMenu.ItemIndicator )` justify-content: center; `; -export const ItemPrefixWrapper = styled.span` - ${ itemPrefix } -`; - -export const ItemSuffixWrapper = styled.span` - ${ itemSuffix } -`; - export const SubmenuRtlChevronIcon = styled( Icon )` ${ rtl( { From 2bf337974982cc5b53cd546f4e6479bc403eec28 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 12 May 2023 14:00:00 +0200 Subject: [PATCH 47/64] Tweak storybook example (move separators out of groups, better item text) --- .../src/ui/radix-dropdown/stories/index.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/ui/radix-dropdown/stories/index.tsx index 0d90fa9a32b27..7cad262b856b6 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/ui/radix-dropdown/stories/index.tsx @@ -168,8 +168,6 @@ const CheckboxItemsGroup = () => { > Checkbox item two - -
); }; @@ -225,7 +223,7 @@ Default.args = { } > ⌘+S }> - Submenu with suffix + Submenu item with suffix
- Submenu + Second level submenu } > @@ -252,11 +250,14 @@ Default.args = { Submenu item - + + + + ), From 6a7ff1450eea3cac9a1ceac05d38e3b11c426fe0 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 12 May 2023 14:00:12 +0200 Subject: [PATCH 48/64] Add unit tests --- .../src/ui/radix-dropdown/test/index.tsx | 816 ++++++++++++++++++ 1 file changed, 816 insertions(+) create mode 100644 packages/components/src/ui/radix-dropdown/test/index.tsx diff --git a/packages/components/src/ui/radix-dropdown/test/index.tsx b/packages/components/src/ui/radix-dropdown/test/index.tsx new file mode 100644 index 0000000000000..3138c74557ae1 --- /dev/null +++ b/packages/components/src/ui/radix-dropdown/test/index.tsx @@ -0,0 +1,816 @@ +/** + * External dependencies + */ +import { render, screen, waitFor } from '@testing-library/react'; +import { + default as userEvent, + PointerEventsCheckLevel, +} from '@testing-library/user-event'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownSubMenu, + DropdownSubMenuTrigger, +} from '..'; + +const delay = ( delayInMs: number ) => { + return new Promise( ( resolve ) => setTimeout( resolve, delayInMs ) ); +}; + +describe( 'DropdownMenu', () => { + // See https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ + it( 'should follow the WAI-ARIA spec', async () => { + // Radio and Checkbox items' + const user = userEvent.setup(); + + render( + Open dropdown }> + Dropdown menu item + + + Dropdown submenu + + } + > + Dropdown submenu item 1 + Dropdown submenu item 2 + + + ); + + const toggleButton = screen.getByRole( 'button', { + name: 'Open dropdown', + } ); + + expect( toggleButton ).toHaveAttribute( 'aria-haspopup', 'menu' ); + expect( toggleButton ).toHaveAttribute( 'aria-expanded', 'false' ); + + await user.click( toggleButton ); + + expect( toggleButton ).toHaveAttribute( 'aria-expanded', 'true' ); + + expect( screen.getByRole( 'menu' ) ).toHaveFocus(); + expect( screen.getByRole( 'separator' ) ).toHaveAttribute( + 'aria-orientation', + 'horizontal' + ); + expect( screen.getAllByRole( 'menuitem' ) ).toHaveLength( 2 ); + + const submenuTrigger = screen.getByRole( 'menuitem', { + name: 'Dropdown submenu', + } ); + expect( submenuTrigger ).toHaveAttribute( 'aria-haspopup', 'menu' ); + expect( submenuTrigger ).toHaveAttribute( 'aria-expanded', 'false' ); + + await user.hover( submenuTrigger ); + + // Wait for the open animation after hovering + await waitFor( () => + expect( screen.getAllByRole( 'menu' ) ).toHaveLength( 2 ) + ); + + expect( submenuTrigger ).toHaveAttribute( 'aria-expanded', 'true' ); + expect( submenuTrigger ).toHaveAttribute( + 'aria-controls', + screen.getAllByRole( 'menu' )[ 1 ].id + ); + } ); + + describe( 'pointer and keyboard interactions', () => { + it( 'should open when clicking the trigger', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + Dropdown menu item + + ); + + const toggleButton = screen.getByRole( 'button', { + name: 'Open dropdown', + } ); + + // DropdownMenu closed, the content is not displayed + expect( screen.queryByRole( 'menu' ) ).not.toBeInTheDocument(); + expect( screen.queryByRole( 'menuitem' ) ).not.toBeInTheDocument(); + + // Click to open the menu + await user.click( toggleButton ); + + // DropdownMenu open, the content is displayed + expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); + expect( screen.getByRole( 'menuitem' ) ).toBeInTheDocument(); + } ); + + it( 'should open when pressing the arrow down key on the trigger', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + Dropdown menu item + + ); + + const toggleButton = screen.getByRole( 'button', { + name: 'Open dropdown', + } ); + + // Move focus on the toggle + await user.keyboard( '{Tab}' ); + + expect( toggleButton ).toHaveFocus(); + + // DropdownMenu closed, the content is not displayed + expect( screen.queryByRole( 'menuitem' ) ).not.toBeInTheDocument(); + + await user.keyboard( '{ArrowDown}' ); + + // DropdownMenu open, the content is displayed + expect( screen.getByRole( 'menuitem' ) ).toBeInTheDocument(); + } ); + + it( 'should close when pressing the escape key', async () => { + const user = userEvent.setup(); + + render( + Open dropdown } + > + Dropdown menu item + + ); + + // The menu is focused automatically when `defaultOpen` is set. + expect( screen.getByRole( 'menu' ) ).toHaveFocus(); + + // Pressing esc will close the menu and move focus to the toggle + await user.keyboard( '{Escape}' ); + + expect( screen.queryByRole( 'menu' ) ).not.toBeInTheDocument(); + expect( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ).toHaveFocus(); + } ); + + it( 'should close when clicking outside of the content', async () => { + const user = userEvent.setup( { + // Disabling this check otherwise testing-library would complain + // when clicking on document.body to close the dropdown menu. + pointerEventsCheck: PointerEventsCheckLevel.Never, + } ); + + render( + Open dropdown } + > + Dropdown menu item + + ); + + expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); + + // Click on the body (ie. outside of the dropdown menu) + await user.click( document.body ); + + expect( screen.queryByRole( 'menu' ) ).not.toBeInTheDocument(); + } ); + + it( 'should close when clicking on a menu item', async () => { + const user = userEvent.setup(); + + render( + Open dropdown } + > + Dropdown menu item + + ); + + expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); + + // Clicking a menu item will close the menu + await user.click( screen.getByRole( 'menuitem' ) ); + + expect( screen.queryByRole( 'menu' ) ).not.toBeInTheDocument(); + } ); + + it( 'should not close when clicking on a disabled menu item', async () => { + const user = userEvent.setup( { + // Disabling this check otherwise testing-library would complain + // when clicking on a disabled element with pointer-events: none + pointerEventsCheck: PointerEventsCheckLevel.Never, + } ); + + render( + Open dropdown } + > + + Dropdown menu item + + + ); + + expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); + + // Clicking a disabled menu item won't close the menu + await user.click( screen.getByRole( 'menuitem' ) ); + + expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); + } ); + + it( 'should reveal submenu content when hovering over the submenu trigger', async () => { + const user = userEvent.setup(); + + render( + Open dropdown } + > + Dropdown menu item 1 + Dropdown menu item 2 + + Dropdown submenu + + } + > + + Dropdown submenu item 1 + + + Dropdown submenu item 2 + + + Dropdown menu item 3 + + ); + + // Before hover, submenu items are not rendered + expect( + screen.queryByRole( 'menuitem', { + name: 'Dropdown submenu item 1', + } ) + ).not.toBeInTheDocument(); + + await user.hover( + screen.getByRole( 'menuitem', { name: 'Dropdown submenu' } ) + ); + + // After hover, submenu items are rendered + // Reason for `findByRole`: due to the animation, we've got to wait + // a short amount of time for the submenu to appear + await screen.findByRole( 'menuitem', { + name: 'Dropdown submenu item 1', + } ); + } ); + + it( 'should navigate menu items and subitems using the arrow, spacebar and enter keys', async () => { + const user = userEvent.setup(); + + render( + Open dropdown } + > + Dropdown menu item 1 + Dropdown menu item 2 + + Dropdown submenu + + } + > + + Dropdown submenu item 1 + + + Dropdown submenu item 2 + + + Dropdown menu item 3 + + ); + + // The menu is focused automatically when `defaultOpen` is set. + expect( screen.getByRole( 'menu' ) ).toHaveFocus(); + + // Arrow up/down selects menu items + // The selection wraps around from last to first and viceversa + await user.keyboard( '{ArrowDown}' ); + expect( + screen.getByRole( 'menuitem', { name: 'Dropdown menu item 1' } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowDown}' ); + expect( + screen.getByRole( 'menuitem', { name: 'Dropdown menu item 2' } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowDown}' ); + expect( + screen.getByRole( 'menuitem', { name: 'Dropdown submenu' } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowDown}' ); + expect( + screen.getByRole( 'menuitem', { name: 'Dropdown menu item 3' } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowDown}' ); + expect( + screen.getByRole( 'menuitem', { name: 'Dropdown menu item 1' } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowUp}' ); + expect( + screen.getByRole( 'menuitem', { name: 'Dropdown menu item 3' } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowUp}' ); + expect( + screen.getByRole( 'menuitem', { name: 'Dropdown submenu' } ) + ).toHaveFocus(); + + // Arrow right/left can be used to enter/leave submenus + await user.keyboard( '{ArrowRight}' ); + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown submenu item 1', + } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowDown}' ); + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown submenu item 2', + } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowLeft}' ); + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown submenu', + } ) + ).toHaveFocus(); + + // Spacebar or enter key can also be used to enter a submenu + await user.keyboard( '{Enter}' ); + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown submenu item 1', + } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowLeft}' ); + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown submenu', + } ) + ).toHaveFocus(); + + await user.keyboard( '{Spacebar}' ); + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown submenu item 1', + } ) + ).toHaveFocus(); + + await user.keyboard( '{ArrowLeft}' ); + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown submenu', + } ) + ).toHaveFocus(); + } ); + + it( 'should check menu radio items', async () => { + const user = userEvent.setup(); + + const onRadioValueChangeSpy = jest.fn(); + + const ControlledRadioGroup = () => { + const [ radioValue, setRadioValue ] = useState< string >(); + return ( + Open dropdown }> + { + onRadioValueChangeSpy( value ); + setRadioValue( value ); + } } + > + + Radio group label + + + Radio item one + + + Radio item two + + + + ); + }; + + render( ); + + // Open dropdown + await user.click( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ); + + // No radios should be checked at this point + expect( screen.getAllByRole( 'menuitemradio' ) ).toHaveLength( 2 ); + expect( + screen.getByRole( 'menuitemradio', { name: 'Radio item one' } ) + ).not.toBeChecked(); + expect( + screen.getByRole( 'menuitemradio', { name: 'Radio item two' } ) + ).not.toBeChecked(); + + // Click first radio item, make sure that the callback fires + await user.click( + screen.getByRole( 'menuitemradio', { name: 'Radio item one' } ) + ); + expect( onRadioValueChangeSpy ).toHaveBeenCalledTimes( 1 ); + expect( onRadioValueChangeSpy ).toHaveBeenLastCalledWith( + 'radio-one' + ); + + // Open dropdown + await user.click( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ); + + // Make sure that first radio is checked + expect( + screen.getByRole( 'menuitemradio', { name: 'Radio item one' } ) + ).toBeChecked(); + expect( + screen.getByRole( 'menuitemradio', { name: 'Radio item two' } ) + ).not.toBeChecked(); + + // Click second radio item, make sure that the callback fires + await user.click( + screen.getByRole( 'menuitemradio', { name: 'Radio item two' } ) + ); + expect( onRadioValueChangeSpy ).toHaveBeenCalledTimes( 2 ); + expect( onRadioValueChangeSpy ).toHaveBeenLastCalledWith( + 'radio-two' + ); + + // Open dropdown + await user.click( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ); + + // Make sure that second radio is selected + expect( + screen.getByRole( 'menuitemradio', { name: 'Radio item one' } ) + ).not.toBeChecked(); + expect( + screen.getByRole( 'menuitemradio', { name: 'Radio item two' } ) + ).toBeChecked(); + } ); + + it( 'should check menu checkbox items', async () => { + const user = userEvent.setup(); + + const onCheckboxValueChangeSpy = jest.fn(); + + const ControlledRadioGroup = () => { + const [ itemOneChecked, setItemOneChecked ] = + useState< boolean >(); + const [ itemTwoChecked, setItemTwoChecked ] = + useState< boolean >(); + return ( + Open dropdown }> + + Checkbox group label + + { + setItemOneChecked( checked ); + onCheckboxValueChangeSpy( 'item-one', checked ); + } } + > + Checkbox item one + + + { + setItemTwoChecked( checked ); + onCheckboxValueChangeSpy( 'item-two', checked ); + } } + > + Checkbox item two + + + ); + }; + + render( ); + + // Open dropdown + await user.click( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ); + + // No checkboxes should be checked at this point + expect( screen.getAllByRole( 'menuitemcheckbox' ) ).toHaveLength( + 2 + ); + expect( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item one', + } ) + ).not.toBeChecked(); + expect( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item two', + } ) + ).not.toBeChecked(); + + // Click first checkbox item, make sure that the callback fires + await user.click( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item one', + } ) + ); + expect( onCheckboxValueChangeSpy ).toHaveBeenCalledTimes( 1 ); + expect( onCheckboxValueChangeSpy ).toHaveBeenLastCalledWith( + 'item-one', + true + ); + + // Open dropdown + await user.click( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ); + + // Make sure that first checkbox is checked + expect( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item one', + } ) + ).toBeChecked(); + + // Click second checkbox item, make sure that the callback fires + await user.click( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item two', + } ) + ); + expect( onCheckboxValueChangeSpy ).toHaveBeenCalledTimes( 2 ); + expect( onCheckboxValueChangeSpy ).toHaveBeenLastCalledWith( + 'item-two', + true + ); + + // Open dropdown + await user.click( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ); + + // Make sure that second checkbox is selected + expect( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item two', + } ) + ).toBeChecked(); + + // Click second checkbox item, make sure that the callback fires + await user.click( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item two', + } ) + ); + expect( onCheckboxValueChangeSpy ).toHaveBeenCalledTimes( 3 ); + expect( onCheckboxValueChangeSpy ).toHaveBeenLastCalledWith( + 'item-two', + false + ); + + // Open dropdown + await user.click( + screen.getByRole( 'button', { name: 'Open dropdown' } ) + ); + + // Make sure that second checkbox is unselected + expect( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item two', + } ) + ).not.toBeChecked(); + } ); + } ); + + describe( 'items prefix and suffix', () => { + it( 'should display a prefix on regular items', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + Item prefix }> + Dropdown menu item + + + ); + + // Click to open the menu + await user.click( + screen.getByRole( 'button', { + name: 'Open dropdown', + } ) + ); + + // The contents of the prefix are rendered before the item's children + expect( + screen.getByRole( 'menuitem', { + name: 'Item prefix Dropdown menu item', + } ) + ).toBeInTheDocument(); + } ); + + it( 'should display a suffix on regular items', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + Item suffix }> + Dropdown menu item + + + ); + + // Click to open the menu + await user.click( + screen.getByRole( 'button', { + name: 'Open dropdown', + } ) + ); + + // The contents of the suffix are rendered after the item's children + expect( + screen.getByRole( 'menuitem', { + name: 'Dropdown menu item Item suffix', + } ) + ).toBeInTheDocument(); + } ); + + it( 'should display a suffix on radio items', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + + + Radio item one + + + + ); + + // Click to open the menu + await user.click( + screen.getByRole( 'button', { + name: 'Open dropdown', + } ) + ); + + // The contents of the suffix are rendered after the item's children + expect( + screen.getByRole( 'menuitemradio', { + name: 'Radio item one Radio suffix', + } ) + ).toBeInTheDocument(); + } ); + + it( 'should display a suffix on checkbox items', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + + Checkbox item one + + + ); + + // Click to open the menu + await user.click( + screen.getByRole( 'button', { + name: 'Open dropdown', + } ) + ); + + // The contents of the suffix are rendered after the item's children + expect( + screen.getByRole( 'menuitemcheckbox', { + name: 'Checkbox item one Checkbox suffix', + } ) + ).toBeInTheDocument(); + } ); + } ); + + describe( 'typeahead', () => { + it( 'should highlight matching item', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + One + Two + + ); + + // Click to open the menu + await user.click( + screen.getByRole( 'button', { + name: 'Open dropdown', + } ) + ); + expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); + + // Type "tw", it should match and focus the item with content "Two" + await user.keyboard( 'tw' ); + expect( + screen.getByRole( 'menuitem', { name: 'Two' } ) + ).toHaveFocus(); + + // Wait for the typeahead timer to reset and interpret + // the next keystrokes as a new search + await delay( 1000 ); + + // Type "on", it should match and focus the item with content "One" + await user.keyboard( 'on' ); + expect( + screen.getByRole( 'menuitem', { name: 'One' } ) + ).toHaveFocus(); + } ); + + it( 'should use the textValue prop if specificied', async () => { + const user = userEvent.setup(); + + render( + Open dropdown }> + One + Two + + ); + + // Click to open the menu + await user.click( + screen.getByRole( 'button', { + name: 'Open dropdown', + } ) + ); + expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); + + // Type "tw", it should not match the item with content "Two" because it + // that item specifies the "textValue" prop. Therefore, the menu container + // retains focus. + await user.keyboard( 'tw' ); + expect( screen.getByRole( 'menu' ) ).toHaveFocus(); + + // Wait for the typeahead timer to reset and interpret + // the next keystrokes as a new search + await delay( 1000 ); + + // Type "fo", it should match and focus the item with textValue "Four" + await user.keyboard( 'fo' ); + expect( + screen.getByRole( 'menuitem', { name: 'Two' } ) + ).toHaveFocus(); + } ); + } ); +} ); From 82b526ad139c12a823bce121b3243a2fd060346c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 12 May 2023 15:38:29 +0200 Subject: [PATCH 49/64] Move legacy implementation to v1 subfolder, move new implementation to v2 folder --- .../components/src/dropdown-menu/{ => v1}/README.md | 0 .../src/dropdown-menu/{ => v1}/index.native.js | 4 ++-- .../components/src/dropdown-menu/{ => v1}/index.tsx | 6 +++--- .../src/dropdown-menu/{ => v1}/stories/index.tsx | 0 .../components/src/dropdown-menu/{ => v1}/style.scss | 0 .../src/dropdown-menu/{ => v1}/test/index.tsx | 0 .../components/src/dropdown-menu/{ => v1}/types.ts | 10 +++++----- .../{ui/radix-dropdown => dropdown-menu/v2}/index.tsx | 0 .../v2}/stories/index.tsx | 2 +- .../{ui/radix-dropdown => dropdown-menu/v2}/styles.ts | 2 +- .../radix-dropdown => dropdown-menu/v2}/test/index.tsx | 0 .../{ui/radix-dropdown => dropdown-menu/v2}/types.ts | 0 packages/components/src/index.ts | 2 +- packages/components/src/private-apis.ts | 2 +- packages/components/src/style.scss | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) rename packages/components/src/dropdown-menu/{ => v1}/README.md (100%) rename packages/components/src/dropdown-menu/{ => v1}/index.native.js (97%) rename packages/components/src/dropdown-menu/{ => v1}/index.tsx (98%) rename packages/components/src/dropdown-menu/{ => v1}/stories/index.tsx (100%) rename packages/components/src/dropdown-menu/{ => v1}/style.scss (100%) rename packages/components/src/dropdown-menu/{ => v1}/test/index.tsx (100%) rename packages/components/src/dropdown-menu/{ => v1}/types.ts (93%) rename packages/components/src/{ui/radix-dropdown => dropdown-menu/v2}/index.tsx (100%) rename packages/components/src/{ui/radix-dropdown => dropdown-menu/v2}/stories/index.tsx (99%) rename packages/components/src/{ui/radix-dropdown => dropdown-menu/v2}/styles.ts (99%) rename packages/components/src/{ui/radix-dropdown => dropdown-menu/v2}/test/index.tsx (100%) rename packages/components/src/{ui/radix-dropdown => dropdown-menu/v2}/types.ts (100%) diff --git a/packages/components/src/dropdown-menu/README.md b/packages/components/src/dropdown-menu/v1/README.md similarity index 100% rename from packages/components/src/dropdown-menu/README.md rename to packages/components/src/dropdown-menu/v1/README.md diff --git a/packages/components/src/dropdown-menu/index.native.js b/packages/components/src/dropdown-menu/v1/index.native.js similarity index 97% rename from packages/components/src/dropdown-menu/index.native.js rename to packages/components/src/dropdown-menu/v1/index.native.js index 1f60e0f67cf08..966552b4bf5f4 100644 --- a/packages/components/src/dropdown-menu/index.native.js +++ b/packages/components/src/dropdown-menu/v1/index.native.js @@ -13,8 +13,8 @@ import { menu } from '@wordpress/icons'; /** * Internal dependencies */ -import Button from '../button'; -import Dropdown from '../dropdown'; +import Button from '../../button'; +import Dropdown from '../../dropdown'; function mergeProps( defaultProps = {}, props = {} ) { const mergedProps = { diff --git a/packages/components/src/dropdown-menu/index.tsx b/packages/components/src/dropdown-menu/v1/index.tsx similarity index 98% rename from packages/components/src/dropdown-menu/index.tsx rename to packages/components/src/dropdown-menu/v1/index.tsx index 805bcd0661179..948bf702f090c 100644 --- a/packages/components/src/dropdown-menu/index.tsx +++ b/packages/components/src/dropdown-menu/v1/index.tsx @@ -11,9 +11,9 @@ import { menu } from '@wordpress/icons'; /** * Internal dependencies */ -import Button from '../button'; -import Dropdown from '../dropdown'; -import { NavigableMenu } from '../navigable-container'; +import Button from '../../button'; +import Dropdown from '../../dropdown'; +import { NavigableMenu } from '../../navigable-container'; import type { DropdownMenuProps, DropdownOption } from './types'; function mergeProps< diff --git a/packages/components/src/dropdown-menu/stories/index.tsx b/packages/components/src/dropdown-menu/v1/stories/index.tsx similarity index 100% rename from packages/components/src/dropdown-menu/stories/index.tsx rename to packages/components/src/dropdown-menu/v1/stories/index.tsx diff --git a/packages/components/src/dropdown-menu/style.scss b/packages/components/src/dropdown-menu/v1/style.scss similarity index 100% rename from packages/components/src/dropdown-menu/style.scss rename to packages/components/src/dropdown-menu/v1/style.scss diff --git a/packages/components/src/dropdown-menu/test/index.tsx b/packages/components/src/dropdown-menu/v1/test/index.tsx similarity index 100% rename from packages/components/src/dropdown-menu/test/index.tsx rename to packages/components/src/dropdown-menu/v1/test/index.tsx diff --git a/packages/components/src/dropdown-menu/types.ts b/packages/components/src/dropdown-menu/v1/types.ts similarity index 93% rename from packages/components/src/dropdown-menu/types.ts rename to packages/components/src/dropdown-menu/v1/types.ts index badfcb54d6072..6c91c3268965d 100644 --- a/packages/components/src/dropdown-menu/types.ts +++ b/packages/components/src/dropdown-menu/v1/types.ts @@ -5,11 +5,11 @@ import type { ReactNode } from 'react'; /** * Internal dependencies */ -import type { ButtonAsButtonProps } from '../button/types'; -import type { WordPressComponentProps } from '../ui/context'; -import type { DropdownProps } from '../dropdown/types'; -import type { Props as IconProps } from '../icon'; -import type { NavigableMenuProps } from '../navigable-container/types'; +import type { ButtonAsButtonProps } from '../../button/types'; +import type { WordPressComponentProps } from '../../ui/context'; +import type { DropdownProps } from '../../dropdown/types'; +import type { Props as IconProps } from '../../icon'; +import type { NavigableMenuProps } from '../../navigable-container/types'; export type DropdownOption = { /** diff --git a/packages/components/src/ui/radix-dropdown/index.tsx b/packages/components/src/dropdown-menu/v2/index.tsx similarity index 100% rename from packages/components/src/ui/radix-dropdown/index.tsx rename to packages/components/src/dropdown-menu/v2/index.tsx diff --git a/packages/components/src/ui/radix-dropdown/stories/index.tsx b/packages/components/src/dropdown-menu/v2/stories/index.tsx similarity index 99% rename from packages/components/src/ui/radix-dropdown/stories/index.tsx rename to packages/components/src/dropdown-menu/v2/stories/index.tsx index 7cad262b856b6..41ca0970352d5 100644 --- a/packages/components/src/ui/radix-dropdown/stories/index.tsx +++ b/packages/components/src/dropdown-menu/v2/stories/index.tsx @@ -19,7 +19,7 @@ import { DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownSubMenuTrigger, -} from '../'; +} from '..'; /** * WordPress dependencies diff --git a/packages/components/src/ui/radix-dropdown/styles.ts b/packages/components/src/dropdown-menu/v2/styles.ts similarity index 99% rename from packages/components/src/ui/radix-dropdown/styles.ts rename to packages/components/src/dropdown-menu/v2/styles.ts index 8eeb5a4137bd6..988b600b96127 100644 --- a/packages/components/src/ui/radix-dropdown/styles.ts +++ b/packages/components/src/dropdown-menu/v2/styles.ts @@ -9,7 +9,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; * Internal dependencies */ import { COLORS, font, rtl } from '../../utils'; -import { space } from '../utils/space'; +import { space } from '../../ui/utils/space'; import Icon from '../../icon'; const ANIMATION_PARAMS = { diff --git a/packages/components/src/ui/radix-dropdown/test/index.tsx b/packages/components/src/dropdown-menu/v2/test/index.tsx similarity index 100% rename from packages/components/src/ui/radix-dropdown/test/index.tsx rename to packages/components/src/dropdown-menu/v2/test/index.tsx diff --git a/packages/components/src/ui/radix-dropdown/types.ts b/packages/components/src/dropdown-menu/v2/types.ts similarity index 100% rename from packages/components/src/ui/radix-dropdown/types.ts rename to packages/components/src/dropdown-menu/v2/types.ts diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index b17d82a81bee6..4c0b311e7d0b8 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -71,7 +71,7 @@ export { default as DropZone } from './drop-zone'; export { default as DropZoneProvider } from './drop-zone/provider'; export { default as Dropdown } from './dropdown'; export { default as __experimentalDropdownContentWrapper } from './dropdown/dropdown-content-wrapper'; -export { default as DropdownMenu } from './dropdown-menu'; +export { default as DropdownMenu } from './dropdown-menu/v1'; export { DuotoneSwatch, DuotonePicker } from './duotone-picker'; export { Elevation as __experimentalElevation } from './elevation'; export { default as ExternalLink } from './external-link'; diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index e51aba7d03c77..e27c34aff3d7b 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -20,7 +20,7 @@ import { DropdownMenuSeparator as DropdownMenuSeparatorV2, DropdownSubMenu as DropdownSubMenuV2, DropdownSubMenuTrigger as DropdownSubMenuTriggerV2, -} from './ui/radix-dropdown'; +} from './dropdown-menu/v2'; export const { lock, unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules( diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 02dce83f66dcc..0aa7884f821c6 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -24,7 +24,7 @@ @import "./draggable/style.scss"; @import "./drop-zone/style.scss"; @import "./dropdown/style.scss"; -@import "./dropdown-menu/style.scss"; +@import "./dropdown-menu/v1/style.scss"; @import "./duotone-picker/style.scss"; @import "./duotone-picker/color-list-picker/style.scss"; @import "./form-toggle/style.scss"; From 20552e759b13e6584a58611b685db6206c7cac73 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 12 May 2023 16:03:41 +0200 Subject: [PATCH 50/64] READMEs --- docs/manifest.json | 10 +- .../components/src/dropdown-menu/v1/README.md | 2 +- .../components/src/dropdown-menu/v2/README.md | 388 ++++++++++++++++++ 3 files changed, 397 insertions(+), 3 deletions(-) create mode 100644 packages/components/src/dropdown-menu/v2/README.md diff --git a/docs/manifest.json b/docs/manifest.json index d6759f051a679..19a796000325d 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -818,7 +818,13 @@ { "title": "DropdownMenu", "slug": "dropdown-menu", - "markdown_source": "../packages/components/src/dropdown-menu/README.md", + "markdown_source": "../packages/components/src/dropdown-menu/v1/README.md", + "parent": "components" + }, + { + "title": "DropdownMenu", + "slug": "dropdown-menu", + "markdown_source": "../packages/components/src/dropdown-menu/v2/README.md", "parent": "components" }, { @@ -2315,4 +2321,4 @@ "markdown_source": "../docs/contributors/roadmap.md", "parent": "contributors" } -] +] \ No newline at end of file diff --git a/packages/components/src/dropdown-menu/v1/README.md b/packages/components/src/dropdown-menu/v1/README.md index e1e4c7bf031b0..8792b9b2df5ff 100644 --- a/packages/components/src/dropdown-menu/v1/README.md +++ b/packages/components/src/dropdown-menu/v1/README.md @@ -1,4 +1,4 @@ -# DropdownMenu +# DropdownMenu (v1) The DropdownMenu displays a list of actions (each contained in a MenuItem, MenuItemsChoice, or MenuGroup) in a compact way. It appears in a Popover after the user has interacted with an element (a button or icon) or when they perform a specific action. diff --git a/packages/components/src/dropdown-menu/v2/README.md b/packages/components/src/dropdown-menu/v2/README.md new file mode 100644 index 0000000000000..d238c4764afc4 --- /dev/null +++ b/packages/components/src/dropdown-menu/v2/README.md @@ -0,0 +1,388 @@ +# `DropdownMenu` (v2) + +`DropdownMenu` aisplays a menu to the user (such as a set of actions or functions) triggered by a button. + + +## Design guidelines + +### Usage + +#### When to use a DropdownMenu + +Use a DropdownMenu when you want users to: + +- Choose an action or change a setting from a list, AND +- Only see the available choices contextually. + +`DropdownMenu` is a React component to render an expandable menu of buttons. It is similar in purpose to a `