diff --git a/web/package-lock.json b/web/package-lock.json index 5ec58978c1..1d61b57cd4 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -47,23 +47,18 @@ "yaml": "^2.4.2" }, "devDependencies": { - "@babel/core": "^7.24.5", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-decorators": "^7.24.1", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.5", - "@babel/plugin-transform-runtime": "^7.24.3", - "@babel/preset-env": "^7.24.5", - "@babel/preset-typescript": "^7.24.1", "@hcaptcha/types": "^1.0.3", "@jeysal/storybook-addon-css-user-preferences": "^0.2.0", "@lit/localize-tools": "^0.7.2", "@rollup/plugin-replace": "^5.0.5", "@spotlightjs/spotlight": "^1.2.17", + "@storybook/addon-controls": "^8.1.1", + "@storybook/addon-docs": "^8.1.1", "@storybook/addon-essentials": "^8.1.1", "@storybook/addon-links": "^8.1.1", "@storybook/api": "^7.6.17", "@storybook/blocks": "^8.0.8", + "@storybook/web-components-vite": "^8.1.1", "@testing-library/webdriverio": "^3.2.1", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chart.js": "^2.9.41", @@ -77,8 +72,6 @@ "@wdio/cli": "^8.36.1", "@wdio/mocha-framework": "^8.36.1", "@wdio/spec-reporter": "^8.36.1", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-tsconfig-paths": "^1.0.3", "chokidar": "^3.6.0", "cross-env": "^7.0.3", "esbuild": "^0.21.3", @@ -92,10 +85,9 @@ "glob": "^10.3.15", "lit-analyzer": "^2.0.3", "npm-run-all": "^4.1.5", + "polished": "^4.3.1", "prettier": "^3.2.5", "pseudolocale": "^2.0.0", - "react": "^18.2.0", - "react-dom": "^18.3.1", "rollup-plugin-modify": "^3.0.0", "rollup-plugin-postcss-lit": "^2.1.0", "storybook": "^8.1.1", @@ -626,23 +618,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.1.tgz", - "integrity": "sha512-zPEvzFijn+hRvJuX2Vu3KbEBN39LN3f7tW3MQO2LsIs57B26KU+kUc82BdAktS1VCM6libzh45eKGI65lg0cpA==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-decorators": "^7.24.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -694,21 +669,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.1.tgz", - "integrity": "sha512-05RJdO/cCrtVWuAaSn1tS3bH8jbsJa/Y1uD186u6J4C/1mnHFxseeuWpsqr9anvo7TUulev7tm7GDwRV+VuhDw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -1598,26 +1558,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.3.tgz", - "integrity": "sha512-J0BuRPNlNqlMTRJ72eVptpt9VcInbxO6iP3jaxr+1NPhC0UkKL+6oeX6VXMEYdADnuqmMmsBspt4d5w8Y/TCbQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.24.3", - "@babel/helper-plugin-utils": "^7.24.0", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.1", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", @@ -5739,6 +5679,151 @@ "node": ">=14.14" } }, + "node_modules/@storybook/builder-vite": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-8.1.1.tgz", + "integrity": "sha512-+BSmXuZ9j95oKCvHcKztzjZNzBVeXYMoRO2TuflLnknMUA0v9ySp1PhiQxHM4DgAW6t9db1akzc9HoTA5sjTWg==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.1.1", + "@storybook/client-logger": "8.1.1", + "@storybook/core-common": "8.1.1", + "@storybook/core-events": "8.1.1", + "@storybook/csf-plugin": "8.1.1", + "@storybook/node-logger": "8.1.1", + "@storybook/preview": "8.1.1", + "@storybook/preview-api": "8.1.1", + "@storybook/types": "8.1.1", + "@types/find-cache-dir": "^3.2.1", + "browser-assert": "^1.2.1", + "es-module-lexer": "^1.5.0", + "express": "^4.17.3", + "find-cache-dir": "^3.0.0", + "fs-extra": "^11.1.0", + "magic-string": "^0.30.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@preact/preset-vite": "*", + "typescript": ">= 4.3.x", + "vite": "^4.0.0 || ^5.0.0", + "vite-plugin-glimmerx": "*" + }, + "peerDependenciesMeta": { + "@preact/preset-vite": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vite-plugin-glimmerx": { + "optional": true + } + } + }, + "node_modules/@storybook/builder-vite/node_modules/@storybook/channels": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-8.1.1.tgz", + "integrity": "sha512-vG7y97QB++TRkuxYLNKaWJmgr9QBUHyjQgNCWvHIeSYW5zxum9sm6VSR2j1r2G3XUGFSxDwenYBTQuwZJLhWNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "8.1.1", + "@storybook/core-events": "8.1.1", + "@storybook/global": "^5.0.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/builder-vite/node_modules/@storybook/client-logger": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.1.1.tgz", + "integrity": "sha512-9AWPgIN3K0eLusChJUqB5Ft+9P2pW5/s4vOMoj3TCvu8lrdq8AH8ctvxk7x2Kw2wEwQ/g9DyE6C/rDQUARbxew==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/builder-vite/node_modules/@storybook/core-events": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-8.1.1.tgz", + "integrity": "sha512-WpeiBV6RWTZ6t8SI1YdQh8NlbvQtZs9WRr4CPfpzHAly+oxFy6PtPz0h5TMKsU5/kt/L9yL7tE9ZzPYzvFWH/A==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.1.7", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/builder-vite/node_modules/@storybook/preview-api": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.1.1.tgz", + "integrity": "sha512-5EcByqtJgj7a7ZWICMLif8mK3cRmdIMbdSPEDf4X6aTQ8LZOg6updLrkb/Eh6qfeYv46TK/MP8BXa89wfOxWGQ==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.1.1", + "@storybook/client-logger": "8.1.1", + "@storybook/core-events": "8.1.1", + "@storybook/csf": "^0.1.7", + "@storybook/global": "^5.0.0", + "@storybook/types": "8.1.1", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/builder-vite/node_modules/@storybook/types": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-8.1.1.tgz", + "integrity": "sha512-QSQ63aKr2IXrGjX2/Fg1oiGWk+2Nuf+TplaHRC2NKBMgvyn+M0BHUgMTDHQVrFaH4bpl2PkE0r0tzOKP4JI43A==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.1.1", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/builder-vite/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/@storybook/channels": { "version": "7.6.17", "dev": true, @@ -7088,6 +7173,16 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/preview": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-8.1.1.tgz", + "integrity": "sha512-P8iBi9v/62AhTztbCYjVxH6idNO0h9uO583GHwi3uq2Io7F1gUSgwG/HYZ7PnclOsMnmG0FJvAwrvdRc6sWSNw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "node_modules/@storybook/preview-api": { "version": "7.6.17", "dev": true, @@ -7289,6 +7384,197 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/web-components": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-8.1.1.tgz", + "integrity": "sha512-o7Lt5RSKlrHG86604exFir5KYl0aUkvwVhoMbqqIhzygMlEllSb8YqJVBlnAmRW3vKd3Bp+FoO0JiQQl8gREJw==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "8.1.1", + "@storybook/docs-tools": "8.1.1", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "8.1.1", + "@storybook/preview-api": "8.1.1", + "@storybook/types": "8.1.1", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "lit": "^2.0.0 || ^3.0.0" + } + }, + "node_modules/@storybook/web-components-vite": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-8.1.1.tgz", + "integrity": "sha512-96zWh51ugm3DiJlvWgqnFjNmq/6/XaxUoA+nPXHbVra6Y3MW6eUfi6qpErI2umNjjLrjVBPbkKIaMtOI5l90xA==", + "dev": true, + "dependencies": { + "@storybook/builder-vite": "8.1.1", + "@storybook/core-server": "8.1.1", + "@storybook/node-logger": "8.1.1", + "@storybook/types": "8.1.1", + "@storybook/web-components": "8.1.1", + "magic-string": "^0.30.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components-vite/node_modules/@storybook/channels": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-8.1.1.tgz", + "integrity": "sha512-vG7y97QB++TRkuxYLNKaWJmgr9QBUHyjQgNCWvHIeSYW5zxum9sm6VSR2j1r2G3XUGFSxDwenYBTQuwZJLhWNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "8.1.1", + "@storybook/core-events": "8.1.1", + "@storybook/global": "^5.0.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components-vite/node_modules/@storybook/client-logger": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.1.1.tgz", + "integrity": "sha512-9AWPgIN3K0eLusChJUqB5Ft+9P2pW5/s4vOMoj3TCvu8lrdq8AH8ctvxk7x2Kw2wEwQ/g9DyE6C/rDQUARbxew==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components-vite/node_modules/@storybook/core-events": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-8.1.1.tgz", + "integrity": "sha512-WpeiBV6RWTZ6t8SI1YdQh8NlbvQtZs9WRr4CPfpzHAly+oxFy6PtPz0h5TMKsU5/kt/L9yL7tE9ZzPYzvFWH/A==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.1.7", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components-vite/node_modules/@storybook/types": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-8.1.1.tgz", + "integrity": "sha512-QSQ63aKr2IXrGjX2/Fg1oiGWk+2Nuf+TplaHRC2NKBMgvyn+M0BHUgMTDHQVrFaH4bpl2PkE0r0tzOKP4JI43A==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.1.1", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components/node_modules/@storybook/channels": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-8.1.1.tgz", + "integrity": "sha512-vG7y97QB++TRkuxYLNKaWJmgr9QBUHyjQgNCWvHIeSYW5zxum9sm6VSR2j1r2G3XUGFSxDwenYBTQuwZJLhWNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "8.1.1", + "@storybook/core-events": "8.1.1", + "@storybook/global": "^5.0.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components/node_modules/@storybook/client-logger": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.1.1.tgz", + "integrity": "sha512-9AWPgIN3K0eLusChJUqB5Ft+9P2pW5/s4vOMoj3TCvu8lrdq8AH8ctvxk7x2Kw2wEwQ/g9DyE6C/rDQUARbxew==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components/node_modules/@storybook/core-events": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-8.1.1.tgz", + "integrity": "sha512-WpeiBV6RWTZ6t8SI1YdQh8NlbvQtZs9WRr4CPfpzHAly+oxFy6PtPz0h5TMKsU5/kt/L9yL7tE9ZzPYzvFWH/A==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.1.7", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components/node_modules/@storybook/preview-api": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.1.1.tgz", + "integrity": "sha512-5EcByqtJgj7a7ZWICMLif8mK3cRmdIMbdSPEDf4X6aTQ8LZOg6updLrkb/Eh6qfeYv46TK/MP8BXa89wfOxWGQ==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.1.1", + "@storybook/client-logger": "8.1.1", + "@storybook/core-events": "8.1.1", + "@storybook/csf": "^0.1.7", + "@storybook/global": "^5.0.0", + "@storybook/types": "8.1.1", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/web-components/node_modules/@storybook/types": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-8.1.1.tgz", + "integrity": "sha512-QSQ63aKr2IXrGjX2/Fg1oiGWk+2Nuf+TplaHRC2NKBMgvyn+M0BHUgMTDHQVrFaH4bpl2PkE0r0tzOKP4JI43A==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.1.1", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "node_modules/@swagger-api/apidom-ast": { "version": "0.96.0", "license": "Apache-2.0", @@ -8398,6 +8684,12 @@ "@types/send": "*" } }, + "node_modules/@types/find-cache-dir": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", + "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", + "dev": true + }, "node_modules/@types/grecaptcha": { "version": "3.0.9", "dev": true, @@ -8508,12 +8800,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true - }, "node_modules/@types/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -10603,21 +10889,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", @@ -10657,15 +10928,6 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/babel-plugin-tsconfig-paths": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/babel-plugin-tsconfig-paths/-/babel-plugin-tsconfig-paths-1.0.3.tgz", - "integrity": "sha512-eBTjzXpx0CXO2gooYPyIU1joS/eK1Vk2+oLhJDwRwIgh2+2kD/j649eYNtHjFKuXr36/4Y0ytPORLyiey7MLRA==", - "dev": true, - "peerDependencies": { - "@babel/core": "^7.9.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "license": "MIT" @@ -11554,31 +11816,6 @@ "layout-base": "^1.0.0" } }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/country-flag-icons": { "version": "1.5.11", "license": "MIT" @@ -12872,6 +13109,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-module-lexer": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", + "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "dev": true + }, "node_modules/es-set-tostringtag": { "version": "2.0.3", "dev": true, diff --git a/web/package.json b/web/package.json index 6d2303e18b..0d6c0c6c17 100644 --- a/web/package.json +++ b/web/package.json @@ -71,23 +71,18 @@ "yaml": "^2.4.2" }, "devDependencies": { - "@babel/core": "^7.24.5", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-decorators": "^7.24.1", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.5", - "@babel/plugin-transform-runtime": "^7.24.3", - "@babel/preset-env": "^7.24.5", - "@babel/preset-typescript": "^7.24.1", "@hcaptcha/types": "^1.0.3", "@jeysal/storybook-addon-css-user-preferences": "^0.2.0", "@lit/localize-tools": "^0.7.2", "@rollup/plugin-replace": "^5.0.5", "@spotlightjs/spotlight": "^1.2.17", + "@storybook/addon-controls": "^8.1.1", + "@storybook/addon-docs": "^8.1.1", "@storybook/addon-essentials": "^8.1.1", "@storybook/addon-links": "^8.1.1", "@storybook/api": "^7.6.17", "@storybook/blocks": "^8.0.8", + "@storybook/web-components-vite": "^8.1.1", "@testing-library/webdriverio": "^3.2.1", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chart.js": "^2.9.41", @@ -101,8 +96,6 @@ "@wdio/cli": "^8.36.1", "@wdio/mocha-framework": "^8.36.1", "@wdio/spec-reporter": "^8.36.1", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-tsconfig-paths": "^1.0.3", "chokidar": "^3.6.0", "cross-env": "^7.0.3", "esbuild": "^0.21.3", @@ -116,10 +109,9 @@ "glob": "^10.3.15", "lit-analyzer": "^2.0.3", "npm-run-all": "^4.1.5", + "polished": "^4.3.1", "prettier": "^3.2.5", "pseudolocale": "^2.0.0", - "react": "^18.2.0", - "react-dom": "^18.3.1", "rollup-plugin-modify": "^3.0.0", "rollup-plugin-postcss-lit": "^2.1.0", "storybook": "^8.1.1", diff --git a/web/src/elements/Alert.ts b/web/src/elements/Alert.ts index e75d2c3936..c0a642c52f 100644 --- a/web/src/elements/Alert.ts +++ b/web/src/elements/Alert.ts @@ -1,11 +1,20 @@ import { AKElement } from "@goauthentik/elements/Base"; -import { CSSResult, TemplateResult, html } from "lit"; +import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; import PFAlert from "@patternfly/patternfly/components/Alert/alert.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; +export interface IAlert { + inline?: boolean; + warning?: boolean; + info?: boolean; + success?: boolean; + danger?: boolean; +} + export enum Level { Warning = "pf-m-warning", Info = "pf-m-info", @@ -13,20 +22,88 @@ export enum Level { Danger = "pf-m-danger", } +/** + * @class Alert + * @element ak-alert + * + * Alerts are in-page elements intended to draw the user's attention and alert them to important + * details. Alerts are used alongside form elements to warn users of potential mistakes they can + * make, as well as in in-line documentation. + */ @customElement("ak-alert") -export class Alert extends AKElement { +export class Alert extends AKElement implements IAlert { + /** + * Whether or not to display the entire component's contents in-line or not. + * + * @attr + */ @property({ type: Boolean }) inline = false; + /** + * Fallback method of determining severity + * + * @attr + */ @property() level: Level = Level.Warning; - static get styles(): CSSResult[] { + /** + * Highest severity level. + * + * @attr + */ + @property({ type: Boolean }) + danger = false; + + /** + * Next severity level. + * + * @attr + */ + @property({ type: Boolean }) + warning = false; + + /** + * Next severity level. The default severity level. + * + * @attr + */ + @property({ type: Boolean }) + success = false; + + /** + * Lowest severity level. + * + * @attr + */ + @property({ type: Boolean }) + info = false; + + static get styles() { return [PFBase, PFAlert]; } - render(): TemplateResult { - return html`
+ get classmap() { + const leveltags = ["danger", "warning", "success", "info"].filter( + // @ts-ignore + (level) => this[level] && this[level] === true, + ); + + if (leveltags.length > 1) { + console.warn("ak-alert has multiple levels defined"); + } + const level = leveltags.length > 0 ? `pf-m-${leveltags[0]}` : this.level; + + return { + "pf-c-alert": true, + "pf-m-inline": this.inline, + [level]: true, + }; + } + + render() { + return html`
@@ -36,3 +113,9 @@ export class Alert extends AKElement {
`; } } + +declare global { + interface HTMLElementTagNameMap { + "ak-alert": Alert; + } +} diff --git a/web/src/elements/Divider.ts b/web/src/elements/Divider.ts index 3f90bb1c74..7fb18952ee 100644 --- a/web/src/elements/Divider.ts +++ b/web/src/elements/Divider.ts @@ -5,6 +5,14 @@ import { customElement } from "lit/decorators.js"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; +/** + * @class Divider + * @element ak-divider + * + * Divider is a horizontal rule, an in-page element to separate displayed items. + * + * @slot - HTML to display in-line in the middle of the horizontal rule. + */ @customElement("ak-divider") export class Divider extends AKElement { static get styles(): CSSResult[] { @@ -39,3 +47,9 @@ export class Divider extends AKElement { return html`
`; } } + +declare global { + interface HTMLElementTagNameMap { + "ak-divider": Divider; + } +} diff --git a/web/src/elements/EmptyState.ts b/web/src/elements/EmptyState.ts index 8842badea2..ba15b140f3 100644 --- a/web/src/elements/EmptyState.ts +++ b/web/src/elements/EmptyState.ts @@ -9,17 +9,64 @@ import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-sta import PFTitle from "@patternfly/patternfly/components/Title/title.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -@customElement("ak-empty-state") -export class EmptyState extends AKElement { - @property({ type: String }) - icon = ""; +export interface IEmptyState { + icon?: string; + loading?: boolean; + fullHeight?: boolean; + header?: string; +} +/** + * @class EmptyState + * @element ak-empty-state + * + * The EmptyState is an in-page element to indicate that something is either loading or unavailable. + * When "loading" is true it displays a spinner, otherwise it displays a static icon. The default + * icon is a question mark in a circle. + * + * @slot body - Optional low-priority text that appears beneath the state indicator. + * @slot primary - Optional high-priority text that appears some distance between the state indicator. + * + * The layout of the component is always centered, and from top to bottom: + * + * ``` + * icon or spinner + * header + * body + * primary + * ``` + */ +@customElement("ak-empty-state") +export class EmptyState extends AKElement implements IEmptyState { + /** + * The Font Awesome icon to display. Defaults to the � symbol. + * + * @attr + */ + @property({ type: String }) + icon = "fa-question-circle"; + + /** + * Whether or not to show the spinner, or the end icon + * + * @attr + */ @property({ type: Boolean }) loading = false; + /** + * If set, will attempt to occupy the full viewport. + * + * @attr + */ @property({ type: Boolean }) fullHeight = false; + /** + * [Optional] If set, will display a message in large text beneath the icon + * + * @attr + */ @property() header = ""; @@ -45,8 +92,7 @@ export class EmptyState extends AKElement {
` : html``}

${this.header}

@@ -60,3 +106,9 @@ export class EmptyState extends AKElement { `; } } + +declare global { + interface HTMLElementTagNameMap { + "ak-empty-state": EmptyState; + } +} diff --git a/web/src/elements/Expand.ts b/web/src/elements/Expand.ts index 1cc1629996..98f706c884 100644 --- a/web/src/elements/Expand.ts +++ b/web/src/elements/Expand.ts @@ -1,24 +1,53 @@ import { AKElement } from "@goauthentik/elements/Base"; import { msg } from "@lit/localize"; -import { CSSResult, TemplateResult, css, html } from "lit"; +import { css, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; +export interface IExpand { + expanded?: boolean; + textOpen?: string; + textClosed?: string; +} +/** + * @class Expand + * @element ak-expand + * + * An `ak-expand` is used to hide cluttering details that a user may wish to reveal, such as the raw + * details of an alert or event. + * + * slot - The contents to be hidden or displayed. + */ @customElement("ak-expand") export class Expand extends AKElement { - @property({ type: Boolean }) + /** + * The state of the expanded content + * + * @attr + */ + @property({ type: Boolean, reflect: true }) expanded = false; + /** + * The text to display next to the open/close control when the accordion is closed. + * + * @attr + */ @property() textOpen = msg("Show less"); + /** + * The text to display next to the open/close control when the accordion is . + * + * @attr + */ @property() textClosed = msg("Show more"); - static get styles(): CSSResult[] { + static get styles() { return [ PFBase, PFExpandableSection, @@ -30,7 +59,7 @@ export class Expand extends AKElement { ]; } - render(): TemplateResult { + render() { return html`
`; } } + +declare global { + interface HTMLElementTagNameMap { + "ak-expand": Expand; + } +} diff --git a/web/src/elements/buttons/ActionButton/ak-action-button.stories.ts b/web/src/elements/buttons/ActionButton/ak-action-button.stories.ts index 45430cce4a..150ab62d40 100644 --- a/web/src/elements/buttons/ActionButton/ak-action-button.stories.ts +++ b/web/src/elements/buttons/ActionButton/ak-action-button.stories.ts @@ -4,9 +4,9 @@ import { Meta } from "@storybook/web-components"; import { TemplateResult, html } from "lit"; import "./ak-action-button"; -import AKActionButton from "./ak-action-button"; +import { ActionButton } from "./ak-action-button"; -const metadata: Meta = { +const metadata: Meta = { title: "Elements / Action Button", component: "ak-action-button", parameters: { diff --git a/web/src/elements/buttons/ActionButton/ak-action-button.ts b/web/src/elements/buttons/ActionButton/ak-action-button.ts index e8681cf585..79b1e89cb4 100644 --- a/web/src/elements/buttons/ActionButton/ak-action-button.ts +++ b/web/src/elements/buttons/ActionButton/ak-action-button.ts @@ -1,9 +1,14 @@ import { MessageLevel } from "@goauthentik/common/messages"; -import { BaseTaskButton } from "@goauthentik/elements/buttons/SpinnerButton/BaseTaskButton"; +import { + BaseTaskButton, + type IBaseTaskButton, +} from "@goauthentik/elements/buttons/SpinnerButton/BaseTaskButton"; import { showMessage } from "@goauthentik/elements/messages/MessageContainer"; import { customElement, property } from "lit/decorators.js"; +type IActionButton = IBaseTaskButton & { apiRequest: () => Promise }; + /** * A button associated with an event handler for loading data. Takes an asynchronous function as its * only property. @@ -19,7 +24,7 @@ import { customElement, property } from "lit/decorators.js"; */ @customElement("ak-action-button") -export class ActionButton extends BaseTaskButton { +export class ActionButton extends BaseTaskButton implements IActionButton { /** * The command to run when the button is pressed. Must return a promise. If the promise is a * reject or throw, we process the content of the promise and deliver it to the Notification @@ -27,7 +32,6 @@ export class ActionButton extends BaseTaskButton { * * @attr */ - @property({ attribute: false }) apiRequest: () => Promise = () => { throw new Error(); @@ -52,4 +56,10 @@ export class ActionButton extends BaseTaskButton { } } +declare global { + interface HTMLElementTagNameMap { + "ak-action-button": ActionButton; + } +} + export default ActionButton; diff --git a/web/src/elements/buttons/SpinnerButton/BaseTaskButton.ts b/web/src/elements/buttons/SpinnerButton/BaseTaskButton.ts index 7e07dc3a4b..458e54e39d 100644 --- a/web/src/elements/buttons/SpinnerButton/BaseTaskButton.ts +++ b/web/src/elements/buttons/SpinnerButton/BaseTaskButton.ts @@ -36,6 +36,10 @@ const StatusMap = new Map([ const SPINNER_TIMEOUT = 1000 * 1.5; // milliseconds +export interface IBaseTaskButton { + disabled?: boolean; +} + /** * BaseTaskButton * @@ -46,7 +50,6 @@ const SPINNER_TIMEOUT = 1000 * 1.5; // milliseconds * `onFailure` call their `super.` equivalents. * */ - export abstract class BaseTaskButton extends CustomEmitterElement(AKElement) { eventPrefix = "ak-button"; diff --git a/web/src/elements/buttons/SpinnerButton/ak-spinner-button.ts b/web/src/elements/buttons/SpinnerButton/ak-spinner-button.ts index e3b07e2fb0..9cf75df297 100644 --- a/web/src/elements/buttons/SpinnerButton/ak-spinner-button.ts +++ b/web/src/elements/buttons/SpinnerButton/ak-spinner-button.ts @@ -1,7 +1,7 @@ import { customElement } from "lit/decorators.js"; import { property } from "lit/decorators.js"; -import { BaseTaskButton } from "./BaseTaskButton"; +import { BaseTaskButton, type IBaseTaskButton } from "./BaseTaskButton"; /** * A button associated with an event handler for loading data. Takes an asynchronous function as its @@ -17,8 +17,10 @@ import { BaseTaskButton } from "./BaseTaskButton"; * @fires ak-button-reset - When the button is reset after the async process completes */ +type ISpinnerButton = IBaseTaskButton & { callAction: () => Promise }; + @customElement("ak-spinner-button") -export class SpinnerButton extends BaseTaskButton { +export class SpinnerButton extends BaseTaskButton implements ISpinnerButton { /** * The command to run when the button is pressed. Must return a promise. We don't do anything * with that promise other than check if it's a resolve or reject, and rethrow the event after. @@ -29,4 +31,10 @@ export class SpinnerButton extends BaseTaskButton { callAction!: () => Promise; } +declare global { + interface HTMLElementTagNameMap { + "ak-spinner-button": SpinnerButton; + } +} + export default SpinnerButton; diff --git a/web/src/elements/cards/stories/AggregateCard.tests.ts b/web/src/elements/cards/stories/AggregateCard.test.ts similarity index 100% rename from web/src/elements/cards/stories/AggregateCard.tests.ts rename to web/src/elements/cards/stories/AggregateCard.test.ts diff --git a/web/src/elements/cards/stories/AggregatePromiseCard.tests.ts b/web/src/elements/cards/stories/AggregatePromiseCard.test.ts similarity index 100% rename from web/src/elements/cards/stories/AggregatePromiseCard.tests.ts rename to web/src/elements/cards/stories/AggregatePromiseCard.test.ts diff --git a/web/src/elements/cards/stories/QuickActionCard.tests.ts b/web/src/elements/cards/stories/QuickActionCard.test.ts similarity index 100% rename from web/src/elements/cards/stories/QuickActionCard.tests.ts rename to web/src/elements/cards/stories/QuickActionCard.test.ts diff --git a/web/src/elements/stories/Alert.docs.mdx b/web/src/elements/stories/Alert.docs.mdx new file mode 100644 index 0000000000..92f7ff7bf9 --- /dev/null +++ b/web/src/elements/stories/Alert.docs.mdx @@ -0,0 +1,43 @@ +import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks"; + +import * as AlertStories from "./Alert.stories"; + + + +# Alerts + +Alerts are in-page elements intended to draw the user's attention and alert them to important +details. Alerts are used alongside form elements to warn users of potential mistakes they can +make, as well as in in-line documentation. + +## Usage + +```Typescript +import "@goauthentik/elements/Alert.js"; +``` + +Note that the content of an alert _must_ be a valid HTML component; plain text does not work here. + +```html +

This is the content of your alert!

+``` + +## Demo + +### Default + +The default state of an alert is _warning_. + + + +### Info + + + +### Success + + + +### Danger + + diff --git a/web/src/elements/stories/Alert.stories.ts b/web/src/elements/stories/Alert.stories.ts new file mode 100644 index 0000000000..eea8e624d6 --- /dev/null +++ b/web/src/elements/stories/Alert.stories.ts @@ -0,0 +1,77 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +import { Alert, type IAlert } from "../Alert.js"; +import "../Alert.js"; + +type IAlertForTesting = IAlert & { message: string }; + +const metadata: Meta = { + title: "Elements/", + component: "ak-alert", + parameters: { + docs: { + description: "An alert", + }, + }, + argTypes: { + inline: { control: "boolean" }, + warning: { control: "boolean" }, + info: { control: "boolean" }, + success: { control: "boolean" }, + danger: { control: "boolean" }, + // @ts-ignore + message: { control: "text" }, + }, +}; + +export default metadata; + +export const DefaultStory: StoryObj = { + args: { + inline: false, + warning: false, + info: false, + success: false, + danger: false, + message: "You should be alarmed.", + }, + + // @ts-ignore + render: ({ inline, warning, info, success, danger, message }: IAlertForTesting) => { + return html`
+ + +

${message}

+
+
`; + }, +}; + +export const SuccessAlert = { + ...DefaultStory, + args: { ...DefaultStory, ...{ success: true, message: "He's a tribute to your genius!" } }, +}; + +export const InfoAlert = { + ...DefaultStory, + args: { ...DefaultStory, ...{ info: true, message: "An octopus has tastebuds on its arms." } }, +}; + +export const DangerAlert = { + ...DefaultStory, + args: { ...DefaultStory, ...{ danger: true, message: "Danger, Will Robinson! Danger!" } }, +}; diff --git a/web/src/elements/stories/Alert.test.ts b/web/src/elements/stories/Alert.test.ts new file mode 100644 index 0000000000..e8eff1abe3 --- /dev/null +++ b/web/src/elements/stories/Alert.test.ts @@ -0,0 +1,36 @@ +import { $, expect } from "@wdio/globals"; + +import { html, render } from "lit"; + +import "../Alert.js"; +import { Level } from "../Alert.js"; + +describe("ak-alert", () => { + it("should render an alert with the enum", async () => { + render(html`This is an alert`, document.body); + + await expect(await $("ak-alert").$(">>>div")).not.toHaveElementClass("pf-m-inline"); + await expect(await $("ak-alert").$(">>>div")).toHaveElementClass("pf-c-alert"); + await expect(await $("ak-alert").$(">>>div")).toHaveElementClass("pf-m-info"); + await expect(await $("ak-alert").$(">>>.pf-c-alert__title")).toHaveText("This is an alert"); + }); + + it("should render an alert with the attribute", async () => { + render(html`This is an alert`, document.body); + await expect(await $("ak-alert").$(">>>div")).toHaveElementClass("pf-m-info"); + await expect(await $("ak-alert").$(">>>.pf-c-alert__title")).toHaveText("This is an alert"); + }); + + it("should render an alert with conflicting attributes in priority order", async () => { + render(html`This is an alert`, document.body); + await expect(await $("ak-alert").$(">>>div")).toHaveElementClass("pf-m-danger"); + await expect(await $("ak-alert").$(">>>.pf-c-alert__title")).toHaveText("This is an alert"); + }); + + it("should render an alert with an inline class and the default level", async () => { + render(html`This is an alert`, document.body); + await expect(await $("ak-alert").$(">>>div")).toHaveElementClass("pf-m-warning"); + await expect(await $("ak-alert").$(">>>div")).toHaveElementClass("pf-m-inline"); + await expect(await $("ak-alert").$(">>>.pf-c-alert__title")).toHaveText("This is an alert"); + }); +}); diff --git a/web/src/elements/stories/Divider.docs.mdx b/web/src/elements/stories/Divider.docs.mdx new file mode 100644 index 0000000000..7b534a4de3 --- /dev/null +++ b/web/src/elements/stories/Divider.docs.mdx @@ -0,0 +1,51 @@ +import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks"; + +import * as DividerStories from "./Divider.stories"; + + + +# Divider + +Divider is a horizontal rule, an in-page element to separate displayed items. + +It has no configurable attributes. It does have a single unnamed slot, which is displayed in-line in +the center of the rule. If the CSS Base in loaded into the parent context, icons defined in the base +can be used here. + +## Usage + +```Typescript +import "@goauthentik/elements/Divider.js"; +``` + +```html + +``` + +With content: + +```html +

Your content here

+``` + +With an icon: + +```html + +``` + +## Demo + +Note that the Divider inherits its background from its parent component. + +### Default Horizontal Rule + + + +### With A Message + + + +### With an Icon + + diff --git a/web/src/elements/stories/Divider.stories.ts b/web/src/elements/stories/Divider.stories.ts new file mode 100644 index 0000000000..1d424294c7 --- /dev/null +++ b/web/src/elements/stories/Divider.stories.ts @@ -0,0 +1,41 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { TemplateResult, html } from "lit"; + +import { Divider } from "../Divider.js"; +import "../Divider.js"; + +const metadata: Meta = { + title: "Elements/", + component: "ak-divider", + parameters: { + docs: { + description: "our most simple divider", + }, + }, +}; + +export default metadata; + +const container = (content: TemplateResult) => + html`
+ ${content} +
`; + +export const DefaultStory: StoryObj = { + render: () => container(html` `), +}; + +export const DividerWithSlottedContent: StoryObj = { + render: () => container(html`

Time for bed!

`), +}; + +export const DividerWithSlottedIcon: StoryObj = { + render: () => container(html` `), +}; diff --git a/web/src/elements/stories/Divider.test.ts b/web/src/elements/stories/Divider.test.ts new file mode 100644 index 0000000000..3f87da04d9 --- /dev/null +++ b/web/src/elements/stories/Divider.test.ts @@ -0,0 +1,33 @@ +import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet.js"; +import { $, expect } from "@wdio/globals"; + +import { TemplateResult, html, render as litRender } from "lit"; + +import AKGlobal from "@goauthentik/common/styles/authentik.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +import "../Divider.js"; + +const render = (body: TemplateResult) => { + document.adoptedStyleSheets = [ + ...document.adoptedStyleSheets, + ensureCSSStyleSheet(PFBase), + ensureCSSStyleSheet(AKGlobal), + ]; + return litRender(body, document.body); +}; + +describe("ak-divider", () => { + it("should render the divider", async () => { + render(html``); + const empty = await $("ak-divider"); + await expect(empty).toExist(); + }); + + it("should render the divider with the specified text", async () => { + render(html`Your Message Here`); + const span = await $("ak-divider").$("span"); + await expect(span).toExist(); + await expect(span).toHaveText("Your Message Here"); + }); +}); diff --git a/web/src/elements/stories/EmptyState.docs.mdx b/web/src/elements/stories/EmptyState.docs.mdx new file mode 100644 index 0000000000..c5e770871d --- /dev/null +++ b/web/src/elements/stories/EmptyState.docs.mdx @@ -0,0 +1,59 @@ +import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks"; + +import * as EmptyStateStories from "./EmptyState.stories"; + + + +# EmptyState + +The EmptyState is an in-page element to indicate that something is either loading or unavailable. +When "loading" is true it displays a spinner, otherwise it displays a static icon. The default +icon is a question mark in a circle. + +It has two named slots, `body` and `primary`, to communicate further details about the current state +this element is meant to display. + +## Usage + +```Typescript +import "@goauthentik/elements/EmptyState.js"; +``` + +Note that the content of an alert _must_ be a valid HTML component; plain text does not work here. + +```html +This would display in the "primary" slot +``` + +## Demo + +### Default: Loading + +The default state is _loading_ + + + +### Done + + + +### Alternative "Done" Icon + +This also shows the "header" attribute filled, which is rendered in a large, dark typeface. + + + +### The Body Slot Filled + +The body content slot is rendered in a lighter typeface at default size. + + + +### The Body and Primary Slot Filled + +The primary content is rendered in the normal dark typeface at default size. It is also spaced +significantly below the spinner itself. + + diff --git a/web/src/elements/stories/EmptyState.stories.ts b/web/src/elements/stories/EmptyState.stories.ts new file mode 100644 index 0000000000..69d710f5bd --- /dev/null +++ b/web/src/elements/stories/EmptyState.stories.ts @@ -0,0 +1,108 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { TemplateResult, html } from "lit"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { EmptyState, type IEmptyState } from "../EmptyState.js"; +import "../EmptyState.js"; + +const metadata: Meta = { + title: "Elements/", + component: "ak-empty-state", + parameters: { + docs: { + description: "Our empty state spinner", + }, + }, + argTypes: { + icon: { control: "text" }, + loading: { control: "boolean" }, + fullHeight: { control: "boolean" }, + header: { control: "text" }, + }, +}; + +export default metadata; + +const container = (content: TemplateResult) => + html`
+ ${content} +
`; + +export const DefaultStory: StoryObj = { + args: { + icon: undefined, + loading: true, + fullHeight: false, + header: undefined, + }, + + render: ({ icon, loading, fullHeight, header }: IEmptyState) => + container( + html` + `, + ), +}; + +export const DefaultAndLoadingDone = { + ...DefaultStory, + args: { ...DefaultStory, ...{ loading: false } }, +}; + +export const DoneWithAlternativeIcon = { + ...DefaultStory, + args: { + ...DefaultStory, + ...{ loading: false, icon: "fa-space-shuttle", header: "The final frontier" }, + }, +}; + +export const WithBodySlotFilled = { + ...DefaultStory, + args: { + ...DefaultStory, + ...{ loading: false, icon: "fa-space-shuttle", header: "The final frontier" }, + }, + render: ({ icon, loading, fullHeight, header }: IEmptyState) => + container(html` + + This is the body content + + `), +}; + +export const WithBodyAndPrimarySlotsFilled = { + ...DefaultStory, + args: { + ...DefaultStory, + ...{ loading: false, icon: "fa-space-shuttle", header: "The final frontier" }, + }, + render: ({ icon, loading, fullHeight, header }: IEmptyState) => + container( + html` + This is the body content slot + This is the primary content slot + `, + ), +}; diff --git a/web/src/elements/EmptyState.test.ts b/web/src/elements/stories/EmptyState.test.ts similarity index 95% rename from web/src/elements/EmptyState.test.ts rename to web/src/elements/stories/EmptyState.test.ts index ac0d68c586..7ef5f46038 100644 --- a/web/src/elements/EmptyState.test.ts +++ b/web/src/elements/stories/EmptyState.test.ts @@ -7,7 +7,7 @@ import { TemplateResult, html, render as litRender } from "lit"; import AKGlobal from "@goauthentik/common/styles/authentik.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import "./EmptyState.js"; +import "../EmptyState.js"; const render = (body: TemplateResult) => { document.adoptedStyleSheets = [ @@ -58,6 +58,6 @@ describe("ak-empty-state", () => { ); const message = await $("ak-empty-state").$(">>>.pf-c-empty-state__body").$(">>>p"); - await expect(message).toHaveText("Try again with a different filter"); + await expect(message).toHaveText("Try again with a fucked filter"); }); }); diff --git a/web/src/elements/stories/Expand.docs.mdx b/web/src/elements/stories/Expand.docs.mdx new file mode 100644 index 0000000000..c5ee9b455a --- /dev/null +++ b/web/src/elements/stories/Expand.docs.mdx @@ -0,0 +1,38 @@ +import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks"; + +import * as ExpandStories from "./Expand.stories"; + + + +# Expand + +Expand is an in-page element used to hid cluttering details that a user may wish to reveal, such as raw +details of an alert or event. + +It has one unnamed slot for the content to be displayed. + +## Usage + +```Typescript +import "@goauthentik/elements/Expand.js"; +``` + +```html +

Your primary content goes here

+``` + +To show the expanded content on initial render: + +```html +

Your primary content goes here

+``` + +## Demo + +### Default: The content is hidden + + + +### Expanded + + diff --git a/web/src/elements/stories/Expand.stories.ts b/web/src/elements/stories/Expand.stories.ts new file mode 100644 index 0000000000..b7baa4eb69 --- /dev/null +++ b/web/src/elements/stories/Expand.stories.ts @@ -0,0 +1,60 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { TemplateResult, html } from "lit"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { Expand, type IExpand } from "../Expand.js"; +import "../Expand.js"; + +const metadata: Meta = { + title: "Elements/", + component: "ak-expand", + parameters: { + docs: { + description: "Our accordion component", + }, + }, + argTypes: { + expanded: { control: "boolean" }, + textOpen: { control: "text" }, + textClosed: { control: "text" }, + }, +}; + +export default metadata; + +const container = (content: TemplateResult) => + html`
+ ${content} +
`; + +export const DefaultStory: StoryObj = { + args: { + expanded: false, + textOpen: undefined, + textClosed: undefined, + }, + + render: ({ expanded, textOpen, textClosed }: IExpand) => + container( + html`
+

Μήτ᾽ ἔμοι μέλι μήτε μέλισσα

+

"Neither the bee nor the honey for me." - Sappho, 600 BC

+
+
`, + ), +}; +export const Expanded = { + ...DefaultStory, + args: { ...DefaultStory, ...{ expanded: true } }, +}; diff --git a/web/src/elements/stories/Expand.test.ts b/web/src/elements/stories/Expand.test.ts new file mode 100644 index 0000000000..3fe64a911d --- /dev/null +++ b/web/src/elements/stories/Expand.test.ts @@ -0,0 +1,52 @@ +import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet.js"; +import { $, expect } from "@wdio/globals"; + +import { TemplateResult, html, render as litRender } from "lit"; + +import AKGlobal from "@goauthentik/common/styles/authentik.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +import "../Expand.js"; + +const render = (body: TemplateResult) => { + document.adoptedStyleSheets = [ + ...document.adoptedStyleSheets, + ensureCSSStyleSheet(PFBase), + ensureCSSStyleSheet(AKGlobal), + ]; + return litRender(body, document.body); +}; + +describe("ak-expand", () => { + it("should render the expansion content hidden by default", async () => { + render(html`

This is the expanded text

`); + const text = await $("ak-expand").$(">>>.pf-c-expandable-section__content"); + await expect(text).not.toBeDisplayed(); + }); + + it("should render the expansion content visible on demand", async () => { + render(html`

This is the expanded text

`); + const paragraph = await $("ak-expand").$(">>>p"); + await expect(paragraph).toExist(); + await expect(paragraph).toBeDisplayed(); + await expect(paragraph).toHaveText("This is the expanded text"); + }); + + it("should respond to the click event", async () => { + render(html`

This is the expanded text

`); + let content = await $("ak-expand").$(">>>.pf-c-expandable-section__content"); + await expect(content).toExist(); + await expect(content).not.toBeDisplayed(); + const control = await $("ak-expand").$(">>>button"); + + await control.click(); + content = await $("ak-expand").$(">>>.pf-c-expandable-section__content"); + await expect(content).toExist(); + await expect(content).toBeDisplayed(); + + await control.click(); + content = await $("ak-expand").$(">>>.pf-c-expandable-section__content"); + await expect(content).toExist(); + await expect(content).not.toBeDisplayed(); + }); +}); diff --git a/web/wdio.conf.ts b/web/wdio.conf.ts index f1fd0c9f62..3c02e6a20c 100644 --- a/web/wdio.conf.ts +++ b/web/wdio.conf.ts @@ -87,7 +87,7 @@ export const config: Options.Testrunner = { // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // - maxInstances: 10, + maxInstances: 1, // // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: