diff --git a/package-lock.json b/package-lock.json index 6da10054600..cfe8299300e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3075,9 +3075,9 @@ } }, "node_modules/@formatjs/ts-transformer/node_modules/@types/node": { - "version": "16.18.94", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.94.tgz", - "integrity": "sha512-X8q3DoKq8t/QhA0Rk/9wJUajxtXRDiCK+cVaONKLxpsjPhu+xX6uZuEj4UKGLQ4p0obTdFxa0cP/BMvf9mOYZA==", + "version": "16.18.96", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.96.tgz", + "integrity": "sha512-84iSqGXoO+Ha16j8pRZ/L90vDMKX04QTYMTfYeE1WrjWaZXuchBehGUZEpNgx7JnmlrIHdnABmpjrQjhCnNldQ==", "dev": true }, "node_modules/@formatjs/ts-transformer/node_modules/ansi-styles": { @@ -8736,23 +8736,23 @@ } }, "node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz", + "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==", "dev": true, "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", + "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@testing-library/dom/node_modules/ansi-styles": { @@ -9141,9 +9141,9 @@ "dev": true }, "node_modules/@types/aws-lambda": { - "version": "8.10.136", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.136.tgz", - "integrity": "sha512-cmmgqxdVGhxYK9lZMYYXYRJk6twBo53ivtXjIUEFZxfxe4TkZTZBK3RRWrY2HjJcUIix0mdifn15yjOAat5lTA==", + "version": "8.10.137", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.137.tgz", + "integrity": "sha512-YNFwzVarXAOXkjuFxONyDw1vgRNzyH8AuyN19s0bM+ChSu/bzxb5XPxYFLXoqoM+tvgzwR3k7fXcEOW125yJxg==", "dev": true }, "node_modules/@types/babel__core": { @@ -9313,9 +9313,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", "dev": true, "dependencies": { "@types/node": "*", @@ -9789,9 +9789,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", - "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", + "version": "20.12.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.6.tgz", + "integrity": "sha512-3KurE8taB8GCvZBPngVbp0lk5CKi8M9f9k1rsADh0Evdz5SzJ+Q+Hx9uHoFGsLnLnd1xmkDQr2hVhlA0Mn0lKQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -9863,9 +9863,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.74", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz", - "integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==", + "version": "18.2.75", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.75.tgz", + "integrity": "sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -10284,9 +10284,9 @@ } }, "node_modules/@wdio/cli/node_modules/@types/node": { - "version": "18.19.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", - "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -10649,9 +10649,9 @@ } }, "node_modules/@wdio/reporter/node_modules/@types/node": { - "version": "18.19.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", - "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -10789,9 +10789,9 @@ } }, "node_modules/@wdio/sync/node_modules/@types/node": { - "version": "18.19.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", - "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -11160,9 +11160,9 @@ } }, "node_modules/@wdio/types/node_modules/@types/node": { - "version": "18.19.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", - "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -11863,12 +11863,12 @@ } }, "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/array-buffer-byte-length": { @@ -12277,9 +12277,9 @@ "optional": true }, "node_modules/aws-sdk": { - "version": "2.1592.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1592.0.tgz", - "integrity": "sha512-iwmS46jOEHMNodfrpNBJ5eHwjKAY05t/xYV2cp+KyzMX2yGgt2/EtWWnlcoMGBKR31qKTsjMj5ZPouC9/VeDOA==", + "version": "2.1595.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1595.0.tgz", + "integrity": "sha512-ee0FaplSMz9Y6XJnnyDCHv6SLziJ2YCI4SsO0VRFUKK4Jtk/KErp20CJI/4ZsS+oz7k2/vQ3JqGQXCz95nU8Ww==", "dev": true, "hasInstallScript": true, "optional": true, @@ -13070,9 +13070,9 @@ "dev": true }, "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "dependencies": { "semver": "^7.0.0" @@ -13752,9 +13752,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001607", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz", + "integrity": "sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w==", "dev": true, "funding": [ { @@ -15454,38 +15454,6 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -15751,9 +15719,9 @@ "dev": true }, "node_modules/devtools/node_modules/@types/node": { - "version": "18.19.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", - "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -16257,9 +16225,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.726", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.726.tgz", - "integrity": "sha512-xtjfBXn53RORwkbyKvDfTajtnTp0OJoPOIBzXvkNbb7+YYvCHJflba3L7Txyx/6Fov3ov2bGPr/n5MTixmPhdQ==", + "version": "1.4.730", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.730.tgz", + "integrity": "sha512-oJRPo82XEqtQAobHpJIR3zW5YO3sSRRkPz2an4yxi1UvqhsGm54vR/wzTFV74a3soDOJ8CKW7ajOOX5ESzddwg==", "dev": true }, "node_modules/email-addresses": { @@ -16384,9 +16352,9 @@ } }, "node_modules/envinfo": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz", - "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz", + "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==", "dev": true, "bin": { "envinfo": "dist/cli.js" @@ -16627,26 +16595,6 @@ "node": ">= 0.4" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-iterator-helpers": { "version": "1.0.18", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", @@ -17142,15 +17090,6 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/eslint-plugin-jsx-a11y/node_modules/axe-core": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", @@ -18659,9 +18598,9 @@ "dev": true }, "node_modules/focus-lock": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.4.tgz", - "integrity": "sha512-Gv0N3mvej3pD+HWkNryrF8sExzEHqhQ6OSFxD4DPxm9n5HGCaHme98ZMBZroNEAJcsdtHxk+skvThGKyUeoEGA==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.5.tgz", + "integrity": "sha512-QFaHbhv9WPUeLYBDe/PAuLKJ4Dd9OPvKs9xZBr3yLXnUrDNaVXKu2baDBXe3naPY30hgHYSsf2JW4jzas2mDEQ==", "dependencies": { "tslib": "^2.0.3" }, @@ -21087,6 +21026,7 @@ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, + "optional": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -34291,18 +34231,6 @@ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "dev": true }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/stream-buffers": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", @@ -38059,9 +37987,9 @@ } }, "node_modules/webdriver/node_modules/@types/node": { - "version": "18.19.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", - "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -38106,9 +38034,9 @@ } }, "node_modules/webdriverio/node_modules/@types/node": { - "version": "18.19.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", - "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -38132,15 +38060,6 @@ "node": ">= 10" } }, - "node_modules/webdriverio/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/webdriverio/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", diff --git a/packages/terra-framework-docs/CHANGELOG.md b/packages/terra-framework-docs/CHANGELOG.md index e8882335bed..74cd0854e21 100644 --- a/packages/terra-framework-docs/CHANGELOG.md +++ b/packages/terra-framework-docs/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Added + * Added documentation for subsections in `terra-table`. * Added examples and tests for `isAutoFocusEnabled` prop for `terra-data-grid`. * Changed diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/table/About.1.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/table/About.1.doc.mdx index 45f2e193190..46070ca1b72 100644 --- a/packages/terra-framework-docs/src/terra-dev-site/doc/table/About.1.doc.mdx +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/table/About.1.doc.mdx @@ -45,27 +45,27 @@ See the following example of a default table: * [Column Error Indicator](/components/cerner-terra-framework-docs/table/examples/table-column-states) ## Examples -|Link to Example| Description| -|-|-| -|[Default Table](/components/cerner-terra-framework-docs/table/examples/default-table)|An example demonstrating a basic table populated with tabular information and no interactive elements.| -|[Auto Layout Table](/components/cerner-terra-framework-docs/table/examples/auto-layout-table)|An example demonstrating a basic with tabular information and no interactive elements using the auto layout for column widths.| -|[Zebra Striped Table](/components/cerner-terra-framework-docs/table/examples/zebra-striped-table)|An example demonstrating the ability to enable zebra striping for table rows.| -|[Pinned Columns](/components/cerner-terra-framework-docs/table/examples/pinned-columns)|An example demonstrating pinned columns (frozen leftmost columns) and overflow columns.| -|[Sortable Table](/components/cerner-terra-framework-docs/table/examples/sortable-table)|An example demonstrating how to enable sorting on a table. Heading cells of sortable columns are interactive and include an icon indicating sort direction.| -|[Table Column States](/components/cerner-terra-framework-docs/table/examples/table-column-states)|An example demonstrating column states and interactions such as an error or sort. Header cells include an icon that represents the state of the information in the column.| -|[Table with Focusable Cells](/components/cerner-terra-framework-docs/table/examples/table-focusable-cell)|An example demonstrating tab stop behavior on a table with interactive elements. Each tabbable element on a table is a separate tab stop.| -|[Single Row Selection](/components/cerner-terra-framework-docs/table/examples/table-single-row-selection)|An example demonstrating a table with single row selection mode. Consumers control the selection state with the **onRowSelect** callback and **isSelected** row property.| -|[Multiple Row Selection](/components/cerner-terra-framework-docs/table/examples/table-multiple-row-selection)|An example demonstrating a table with multiple row selection mode. Consumers control the selection state with the **onRowSelect** callback and **isSelected** row property.| -|[Table with Sections](/components/cerner-terra-framework-docs/table/examples/table-with-sections)|An example demonstrating how to create a table with sections.| -|[Table Without Headers](/components/cerner-terra-framework-docs/table/examples/table-without-headers)|An example demonstrating how to hide table column headers. The column headers still exist in the Document Object Model (DOM) for accessibility.| +| Link to Example | Description | +| ------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Default Table](/components/cerner-terra-framework-docs/table/examples/default-table) | An example demonstrating a basic table populated with tabular information and no interactive elements. | +| [Auto Layout Table](/components/cerner-terra-framework-docs/table/examples/auto-layout-table) | An example demonstrating a basic with tabular information and no interactive elements using the auto layout for column widths. | +| [Zebra Striped Table](/components/cerner-terra-framework-docs/table/examples/zebra-striped-table) | An example demonstrating the ability to enable zebra striping for table rows. | +| [Pinned Columns](/components/cerner-terra-framework-docs/table/examples/pinned-columns) | An example demonstrating pinned columns (frozen leftmost columns) and overflow columns. | +| [Sortable Table](/components/cerner-terra-framework-docs/table/examples/sortable-table) | An example demonstrating how to enable sorting on a table. Heading cells of sortable columns are interactive and include an icon indicating sort direction. | +| [Table Column States](/components/cerner-terra-framework-docs/table/examples/table-column-states) | An example demonstrating column states and interactions such as an error or sort. Header cells include an icon that represents the state of the information in the column. | +| [Table with Focusable Cells](/components/cerner-terra-framework-docs/table/examples/table-focusable-cell) | An example demonstrating tab stop behavior on a table with interactive elements. Each tabbable element on a table is a separate tab stop. | +| [Single Row Selection](/components/cerner-terra-framework-docs/table/examples/table-single-row-selection) | An example demonstrating a table with single row selection mode. Consumers control the selection state with the **onRowSelect** callback and **isSelected** row property. | +| [Multiple Row Selection](/components/cerner-terra-framework-docs/table/examples/table-multiple-row-selection) | An example demonstrating a table with multiple row selection mode. Consumers control the selection state with the **onRowSelect** callback and **isSelected** row property. | +| [Table with Sections](/components/cerner-terra-framework-docs/table/examples/table-with-sections) | An example demonstrating how to create a table with sections. | +| [Table Without Headers](/components/cerner-terra-framework-docs/table/examples/table-without-headers) | An example demonstrating how to hide table column headers. The column headers still exist in the Document Object Model (DOM) for accessibility. | ## Accessibility ### Keyboard Interactions -|Keys|Description| -|---|---| -|HOME| Selects the first element on the table when the currently active element is not editable.| -|END | Selects the last element on the table when the currently active element is not editable.| +| Keys | Description | +| ---- | ----------------------------------------------------------------------------------------- | +| HOME | Selects the first element on the table when the currently active element is not editable. | +| END | Selects the last element on the table when the currently active element is not editable. | ### Assistive Technology Support #### WAI ARIA Roles and States @@ -112,33 +112,33 @@ In addition, ensure that you memoize callback functions using methods such as th ### Table Constants Enumeration: TableConstants -|Constant|Type|Description| -|---|---|---| -|**ROW_SELECTION_COLUMN_WIDTH**|Number|The width of the row selection column.| -|**TABLE_MARGIN_RIGHT**|Number|The margin right of the table to allow resizing of the last column.| +| Constant | Type | Description | +| ------------------------------ | ------ | ------------------------------------------------------------------- | +| **ROW_SELECTION_COLUMN_WIDTH** | Number | The width of the row selection column. | +| **TABLE_MARGIN_RIGHT** | Number | The margin right of the table to allow resizing of the last column. | Enumeration: RowSelectionModes -|Constant|Type|Description| -|---|---|---| -|**SINGLE**|String|Single row selection mode.| -|**MULTIPLE**|String|Multiple row selection mode.| +| Constant | Type | Description | +| ------------ | ------ | ---------------------------- | +| **SINGLE** | String | Single row selection mode. | +| **MULTIPLE** | String | Multiple row selection mode. | ### Column A column specifies the information to render a cell in the header row of the Worklist Data Grid component. -|Name|Type|Required|Default Value|Description| -|---|---|---|---|---| -|**id**|String|Required|None|An identifier to uniquely identify the column in the grid.| -|**displayName**|String|Optional|None|A string of text to render in the column header cell.| -|**action**|object|optional|none|An object containing label and onClick properties for column action button, which will be displayed in an additional row below the column header row. -|**hasError**|Bool|Optional|None|A Boolean value indicating whether the column information has an error.| -|**isResizable**|Bool|Optional|None|A Boolean value indicating whether the column header is resizable. This value is ignored when for a table with auto layout enabled.| -|**isSelectable**|Bool|Optional|None|A Boolean value indicating whether the column header is selectable.| -|**minimumWidth**|Number|Optional|None|A number that specifies the minimum column width in pixels.| -|**maximumWidth**|Number|Optional|None|A number that specifies the maximum column width in pixels.| -|**width**|unionOf: [type: 'number',type: 'string',],|Optional|None|The width can be either a numeric or string value. When a number is provided, it is the number (in px) specifying the width of the column. If not provided, the Table's default column width will be used. When a string is provided, it specifies the default column width and can be any valid CSS width value| -|**sortIndicator**|Custom|Optional|None|A string indicating which sorting indicator is rendered (`ascending` or `descending`). A sorting indicator is rendered only if a value is provided. +| Name | Type | Required | Default Value | Description | +| ----------------- | ------------------------------------------ | ----------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **id** | String | Required | None | An identifier to uniquely identify the column in the grid. | +| **displayName** | String | Optional | None | A string of text to render in the column header cell. | +| **action** | object | optional | none | An object containing label and onClick properties for column action button, which will be displayed in an additional row below the column header row. | +| **hasError** | Bool | Optional | None | A Boolean value indicating whether the column information has an error. | +| **isResizable** | Bool | Optional | None | A Boolean value indicating whether the column header is resizable. This value is ignored when for a table with auto layout enabled. | +| **isSelectable** | Bool | Optional | None | A Boolean value indicating whether the column header is selectable. | +| **minimumWidth** | Number | Optional | None | A number that specifies the minimum column width in pixels. | +| **maximumWidth** | Number | Optional | None | A number that specifies the maximum column width in pixels. | +| **width** | unionOf: [type: 'number',type: 'string',], | Optional | None | The width can be either a numeric or string value. When a number is provided, it is the number (in px) specifying the width of the column. If not provided, the Table's default column width will be used. When a string is provided, it specifies the default column width and can be any valid CSS width value | +| **sortIndicator** | Custom | Optional | None | A string indicating which sorting indicator is rendered (`ascending` or `descending`). A sorting indicator is rendered only if a value is provided. | ### Section @@ -147,32 +147,45 @@ Due to poor support in Apple VoiceOver for spanned table headings, Table compone A section defines the rows rendered in the section. -|Name|Type|Required|Default Value|Description| -|---|---|---|---|---| -|**id**|String|Required|None|An identifier to uniquely identify the section in the grid.| -|**isCollapsible**|Bool|Optional|False|A Boolean value indicating whether the the section is collapsible.| -|**isCollapsed**|Bool|Optional|False|A Boolean value indicating whether the section is collapsed. If true, the contents of the section is not displayed.| -|**text**|String|Optional|None|A text string to render in the section header.| -|**rows**|Array|Optional|[]|An array of row objects to be rendered in the section.| +| Name | Type | Required | Default Value | Description | +| ----------------- | ------ | ----------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------- | +| **id** | String | Required | None | An identifier to uniquely identify the section in the grid. | +| **isCollapsible** | Bool | Optional | False | A Boolean value indicating whether the the section is collapsible. | +| **isCollapsed** | Bool | Optional | False | A Boolean value indicating whether the section is collapsed. If true, the contents of the section is not displayed. | +| **text** | String | Optional | None | A text string to render in the section header. | +| **rows** | Array | Optional | [] | An array of row objects to be rendered in the section. | +| **subsections** | Array | Optional | [] | An array of subsections objects to be rendered within the section. | + +#### Subsection + +Subsections objects are virtually identical to section objects. + +| Name | Type | Required | Default Value | Description | +| ----------------- | ------ | ----------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------- | +| **id** | String | Required | None | An identifier to uniquely identify the subsection in the grid. | +| **isCollapsible** | Bool | Optional | False | A Boolean value indicating whether the the subsection is collapsible. | +| **isCollapsed** | Bool | Optional | False | A Boolean value indicating whether the subsection is collapsed. If true, the contents of the subsection is not displayed. | +| **text** | String | Optional | None | A text string to render in the subsection header. | +| **rows** | Array | Optional | [] | An array of row objects to be rendered in the subsection. | ### Row A row defines the cells rendered in the row. -|Name|Type|Required|Default Value|Description| -|---|---|---|---|---| -|**id**|String|Required|None|An identifier to uniquely identify the row in the grid.| -|**cells**|Array|Optional|[]|An array of cell objects that define the content to be rendered in the row. Cells are rendered in the order given and are expected to match the column order.| -|**isSelected**|Bool|Optional|None|A Boolean value indicating whether the row is rendered as selected.| -|**ariaLabel**|String|Optional|None|A string identifier used to describe the row contents. This value is used for accessibility when announcing the row selection or deselection.| +| Name | Type | Required | Default Value | Description | +| -------------- | ------ | ----------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **id** | String | Required | None | An identifier to uniquely identify the row in the grid. | +| **cells** | Array | Optional | [] | An array of cell objects that define the content to be rendered in the row. Cells are rendered in the order given and are expected to match the column order. | +| **isSelected** | Bool | Optional | None | A Boolean value indicating whether the row is rendered as selected. | +| **ariaLabel** | String | Optional | None | A string identifier used to describe the row contents. This value is used for accessibility when announcing the row selection or deselection. | ### Cell A cell defines the content rendered at the intersection of a row and a column. -|Name|Type|Required|Default Value|Description| -|---|---|---|---|---| -|**content**|Content|Optional|None|The content to render in the cell.| -|**isMasked**|Bool|Optional|None|A Boolean value indicating whether the cell content is masked.| -|**maskedLabel**|Bool|Optional|None|A Boolean value that provides a custom string for screen readers to read masked cells. This value is only applied if the cell is masked.| +| Name | Type | Required | Default Value | Description | +| --------------- | ------- | -------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| **content** | Content | Optional | None | The content to render in the cell. | +| **isMasked** | Bool | Optional | None | A Boolean value indicating whether the cell content is masked. | +| **maskedLabel** | Bool | Optional | None | A Boolean value that provides a custom string for screen readers to read masked cells. This value is only applied if the cell is masked. | ## Terra Standards * [Cross-Browser Support](https://engineering.cerner.com/terra-ui/about/terra-ui/component-standards#cross-browser-support) diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithCollapsibleSectionsAndSubSections.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithCollapsibleSectionsAndSubSections.test.jsx new file mode 100644 index 00000000000..015c318f621 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithCollapsibleSectionsAndSubSections.test.jsx @@ -0,0 +1,172 @@ +import React, { useState } from 'react'; +import Table from 'terra-table'; + +const tableData = { + cols: [ + { + id: 'Column-0', displayName: 'Patient', sortIndicator: 'ascending', isSelectable: true, + }, + { + id: 'Column-1', displayName: 'Location', isSelectable: true, + }, + { id: 'Column-2', displayName: 'Illness Severity', isSelectable: true }, + { id: 'Column-3', displayName: 'Visit' }, + { id: 'Column-4', displayName: 'Allergy' }, + { id: 'Column-5', displayName: 'Primary Contact' }, + + ], + sections: [{ + id: 'section-0', + isCollapsible: true, + isCollapsed: true, + text: 'Test Section', + subsections: [ + { + id: 'subsection-0', + text: 'Test SubSection', + rows: [ + { + id: '1', + cells: [ + { content: 'Fleck, Arthur' }, + { content: '1007-MTN' }, + { content: 'Unstable' }, + { content: 'Inpatient, 2 months' }, + { content: '' }, + { content: 'Quinzell, Harleen' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Wayne, Bruce' }, + { content: '1007-MTN-DR' }, + { content: 'Stable' }, + { content: 'Outpatient, 2 days' }, + { content: 'Phytochemicals' }, + { content: 'Grayson, Richard' }, + ], + }, + ], + }, + { + id: 'subsection-1', + text: 'Test SubSection #2', + rows: [ + { + id: '3', + cells: [ + { content: 'Fleck, Arthur' }, + { content: '1007-MTN' }, + { content: 'Unstable' }, + { content: 'Inpatient, 2 months' }, + { content: '' }, + { content: 'Quinzell, Harleen' }, + ], + }, + { + id: '4', + cells: [ + { content: 'Wayne, Bruce' }, + { content: '1007-MTN-DR' }, + { content: 'Stable' }, + { content: 'Outpatient, 2 days' }, + { content: 'Phytochemicals' }, + { content: 'Grayson, Richard' }, + ], + }, + ], + }, + ], + }, + { + id: 'section-1', + isCollapsible: true, + isCollapsed: true, + text: 'Test Section #2', + subsections: [ + { + id: 'subsection-2', + text: 'Test SubSection', + rows: [ + { + id: '5', + cells: [ + { content: 'Parker, Peter' }, + { content: '1007-MTN' }, + { content: 'Unstable' }, + { content: 'Inpatient, 2 months' }, + { content: '' }, + { content: 'Octopus, Doctor' }, + ], + }, + { + id: '6', + cells: [ + { content: 'Stark, Tony' }, + { content: '1007-MTN-DR' }, + { content: 'Stable' }, + { content: 'Outpatient, 2 days' }, + { content: 'Phytochemicals' }, + { content: 'America, Captain' }, + ], + }, + ], + }, + { + id: 'subsection-3', + text: 'Test SubSection #2', + rows: [ + { + id: '7', + cells: [ + { content: 'Parker, Peter' }, + { content: '1007-MTN' }, + { content: 'Unstable' }, + { content: 'Inpatient, 2 months' }, + { content: '' }, + { content: 'Octopus, Doctor' }, + ], + }, + { + id: '8', + cells: [ + { content: 'Stark, Tony' }, + { content: '1007-MTN-DR' }, + { content: 'Stable' }, + { content: 'Outpatient, 2 days' }, + { content: 'Phytochemicals' }, + { content: 'America, Captain' }, + ], + }, + ], + }, + ], + }], +}; + +const TableWithCollapsibleSections = () => { + const [tableSections, setTableSections] = useState(tableData.sections); + + const handleSectionSelect = (sectionId) => { + const newSections = [...tableSections]; + const sectionIndex = newSections.findIndex(section => section.id === sectionId); + + if (sectionIndex > -1) { + newSections[sectionIndex].isCollapsed = !newSections[sectionIndex].isCollapsed; + } + + setTableSections(newSections); + }; + + return ( + + ); +}; + +export default TableWithCollapsibleSections; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithSubSections.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithSubSections.test.jsx new file mode 100644 index 00000000000..7c10aedf78a --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithSubSections.test.jsx @@ -0,0 +1,135 @@ +import React, { useState } from 'react'; +import Table from 'terra-table'; + +const tableData = { + cols: [ + { + id: 'Column-0', displayName: 'Patient', sortIndicator: 'ascending', isSelectable: true, + }, + { + id: 'Column-1', displayName: 'Location', isSelectable: true, + }, + { id: 'Column-2', displayName: 'Illness Severity', isSelectable: true }, + { id: 'Column-3', displayName: 'Visit' }, + { id: 'Column-4', displayName: 'Allergy' }, + { id: 'Column-5', displayName: 'Primary Contact' }, + + ], + sections: [{ + id: 'section-0', + text: 'Test Section', + subsections: [ + { + id: 'subsection-0', + text: 'Test SubSection', + rows: [ + { + id: '1', + cells: [ + { content: 'Fleck, Arthur' }, + { content: '1007-MTN' }, + { content: 'Unstable' }, + { content: 'Inpatient, 2 months' }, + { content: '' }, + { content: 'Quinzell, Harleen' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Wayne, Bruce' }, + { content: '1007-MTN-DR' }, + { content: 'Stable' }, + { content: 'Outpatient, 2 days' }, + { content: 'Phytochemicals' }, + { content: 'Grayson, Richard' }, + ], + }, + ], + }, + { + id: 'subsection-1', + text: 'Test SubSection #2', + rows: [ + { + id: '3', + cells: [ + { content: 'Fleck, Arthur' }, + { content: '1007-MTN' }, + { content: 'Unstable' }, + { content: 'Inpatient, 2 months' }, + { content: '' }, + { content: 'Quinzell, Harleen' }, + ], + }, + { + id: '4', + cells: [ + { content: 'Wayne, Bruce' }, + { content: '1007-MTN-DR' }, + { content: 'Stable' }, + { content: 'Outpatient, 2 days' }, + { content: 'Phytochemicals' }, + { content: 'Grayson, Richard' }, + ], + }, + ], + }, + ], + }, + { + id: 'section-1', + text: 'Test Section #2', + rows: [ + { + id: '5', + cells: [ + { content: 'Parker, Peter' }, + { content: '1007-MTN' }, + { content: 'Unstable' }, + { content: 'Inpatient, 2 months' }, + { content: '' }, + { content: 'Octopus, Doctor' }, + ], + }, + { + id: '6', + cells: [ + { content: 'Stark, Tony' }, + { content: '1007-MTN-DR' }, + { content: 'Stable' }, + { content: 'Outpatient, 2 days' }, + { content: 'Phytochemicals' }, + { content: 'America, Captain' }, + ], + }, + ], + }], +}; + +const TableWithSections = () => { + const [tableSections, setTableSections] = useState(tableData.sections); + + const handleSectionSelect = (sectionId) => { + const newSections = [...tableSections]; + const sectionIndex = newSections.findIndex(section => section.id === sectionId); + + if (sectionIndex > -1) { + newSections[sectionIndex].isCollapsed = !newSections[sectionIndex].isCollapsed; + } + + setTableSections(newSections); + }; + + return ( +
+ ); +}; + +export default TableWithSections; diff --git a/packages/terra-table/CHANGELOG.md b/packages/terra-table/CHANGELOG.md index 81965911f32..a6c24482713 100644 --- a/packages/terra-table/CHANGELOG.md +++ b/packages/terra-table/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added + * (flowsheet-data-grid only) Added support for subsections. + * Changed * Updated the cell component auto focus logic to be configured by the grid context. diff --git a/packages/terra-table/src/Table.jsx b/packages/terra-table/src/Table.jsx index 1604bf60f78..33b49a2ca31 100644 --- a/packages/terra-table/src/Table.jsx +++ b/packages/terra-table/src/Table.jsx @@ -328,10 +328,18 @@ function Table(props) { const headerRowCount = hasVisibleColumnHeaders ? (hasColumnHeaderActions ? 2 : 1) : 0; // Calculate total table row count + const subSectionReducer = (rowCount, currentSubsection) => { + // eslint-disable-next-line no-param-reassign + currentSubsection.subSectionRowIndex = rowCount + 1; + return rowCount + currentSubsection.rows.length + 1; + }; const tableSectionReducer = (rowCount, currentSection) => { if (currentSection.id !== defaultSectionRef.current) { // eslint-disable-next-line no-param-reassign currentSection.sectionRowIndex = rowCount + 1; + if (currentSection.subsections) { + return currentSection.subsections.reduce(subSectionReducer, rowCount + 1); + } return rowCount + currentSection.rows.length + 1; } // eslint-disable-next-line no-param-reassign @@ -373,7 +381,12 @@ function Table(props) { // useEffect for row updates useEffect(() => { const previousSelectedRows = [...selectedRows.current]; - const selectableRows = tableSections.flatMap(section => (section.rows.map(row => (row)))); + const selectableRows = tableSections.flatMap(section => { + if (section.subsections) { + return section.subsections.flatMap(subsection => (subsection.rows.map(row => (row)))); + } + return section.rows.map(row => (row)); + }); selectedRows.current = selectableRows.filter((row) => row.isSelected).map(row => (row.id)); if (previousSelectedRows.length > 0 && selectedRows.current.length === 0) { @@ -685,6 +698,7 @@ function Table(props) { isTableStriped={isStriped} text={section.text} rows={section.rows} + subsections={section.subsections} rowHeight={rowHeight} rowSelectionMode={rowSelectionMode} displayedColumns={displayedColumns} diff --git a/packages/terra-table/src/clinical-lowlight-theme/Section.module.scss b/packages/terra-table/src/clinical-lowlight-theme/Section.module.scss index 34bf413272a..f721115a197 100644 --- a/packages/terra-table/src/clinical-lowlight-theme/Section.module.scss +++ b/packages/terra-table/src/clinical-lowlight-theme/Section.module.scss @@ -3,5 +3,8 @@ --terra-table-cell-border-left: 1px solid #181b1d; --terra-table-cell-border-right: 1px solid #181b1d; --terra-table-section-focus-outline: 3px dashed #fff; + + --terra-table-subsection-border-bottom: #161616; + --terra-section-header-background: #161616; } } diff --git a/packages/terra-table/src/orion-fusion-theme/Section.module.scss b/packages/terra-table/src/orion-fusion-theme/Section.module.scss index 188af1b770a..802a8d788c2 100644 --- a/packages/terra-table/src/orion-fusion-theme/Section.module.scss +++ b/packages/terra-table/src/orion-fusion-theme/Section.module.scss @@ -2,6 +2,7 @@ .orion-fusion-theme { --terra-table-cell-border-left: 1px solid #c8cacb; --terra-table-cell-border-right: 1px solid #c8cacb; + --terra-section-header-font-size: 1rem; /* stylelint-disable selector-max-compound-selectors */ &.header-row { @@ -15,6 +16,5 @@ --terra-table-section-focus-outline: none; } } - /* stylelint-enable selector-max-compound-selectors */ } } diff --git a/packages/terra-table/src/proptypes/sectionShape.js b/packages/terra-table/src/proptypes/sectionShape.js index f0d930090f1..983f56365c1 100644 --- a/packages/terra-table/src/proptypes/sectionShape.js +++ b/packages/terra-table/src/proptypes/sectionShape.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import rowShape from './rowShape'; +import subsectionShape from './subsectionShape'; const sectionShape = PropTypes.shape({ /** @@ -22,6 +23,10 @@ const sectionShape = PropTypes.shape({ * An array of row objects to be rendered within the section. */ rows: PropTypes.arrayOf(rowShape), + /** + * An array of subsections objects to be rendered within the section. + */ + subsections: PropTypes.arrayOf(subsectionShape), }); export default sectionShape; diff --git a/packages/terra-table/src/proptypes/subsectionShape.js b/packages/terra-table/src/proptypes/subsectionShape.js new file mode 100644 index 00000000000..8b8569ce3e0 --- /dev/null +++ b/packages/terra-table/src/proptypes/subsectionShape.js @@ -0,0 +1,19 @@ +import PropTypes from 'prop-types'; +import rowShape from './rowShape'; + +const sectionShape = PropTypes.shape({ + /** + * An identifier for the section. This identifier should be unique across the set of sections provided. + */ + id: PropTypes.string.isRequired, + /** + * A text string to render within the section header. + */ + text: PropTypes.string, + /** + * An array of row objects to be rendered within the section. + */ + rows: PropTypes.arrayOf(rowShape), +}); + +export default sectionShape; diff --git a/packages/terra-table/src/proptypes/validators.js b/packages/terra-table/src/proptypes/validators.js index 2661dcf9e7b..34a2f5d1c1a 100644 --- a/packages/terra-table/src/proptypes/validators.js +++ b/packages/terra-table/src/proptypes/validators.js @@ -1,4 +1,4 @@ -import ERRORS from '../utils/constants'; +import { ERRORS } from '../utils/constants'; // eslint-disable-next-line consistent-return const validateRowHeaderIndex = (props) => { diff --git a/packages/terra-table/src/subcomponents/Cell.jsx b/packages/terra-table/src/subcomponents/Cell.jsx index 62d3f0b1d80..6ade8ca9f9e 100644 --- a/packages/terra-table/src/subcomponents/Cell.jsx +++ b/packages/terra-table/src/subcomponents/Cell.jsx @@ -47,6 +47,11 @@ const propTypes = { */ sectionId: PropTypes.string, + /** + * An identifier for the subsection. + */ + subsectionId: PropTypes.string, + /** * Unique identifier for the parent table */ @@ -172,6 +177,7 @@ function Cell(props) { rowIndex, rowMinimumHeight, sectionId, + subsectionId, tableId, firstRowId, lastRowId, @@ -255,6 +261,7 @@ function Cell(props) { if (!isFocusTrapEnabled) { onCellSelect({ sectionId, + subsectionId, rowId, rowIndex: (rowIndex - 1), columnId, @@ -318,6 +325,7 @@ function Cell(props) { } onCellSelect({ sectionId, + subsectionId, rowId, rowIndex: (rowIndex - 1), columnId, @@ -387,6 +395,7 @@ function Cell(props) { const columnHeaderId = `${tableId}-${columnId}-headerCell`; const rowHeaderId = !isRowHeader && rowHeaderIndex !== -1 ? `${tableId}-rowheader-${rowId} ` : ''; const sectionHeaderId = sectionId ? `${tableId}-${sectionId} ` : ''; + const subsectionHeaderId = subsectionId ? `${tableId}-${sectionId}-${subsectionId} ` : ''; let columnHighlight = {}; if (columnHighlightColor) { @@ -414,7 +423,7 @@ function Cell(props) { ref={isGridContext || rowSelectionMode ? cellRef : undefined} aria-selected={isSelected || undefined} aria-label={ariaLabel} - headers={`${sectionHeaderId}${rowHeaderId}${columnHeaderId}`} + headers={`${sectionHeaderId}${subsectionHeaderId}${rowHeaderId}${columnHeaderId}`} tabIndex={isGridContext ? -1 : undefined} className={className} onMouseDown={onCellSelect ? handleMouseDown : undefined} diff --git a/packages/terra-table/src/subcomponents/Row.jsx b/packages/terra-table/src/subcomponents/Row.jsx index 1f138ece6e9..7394cb50436 100644 --- a/packages/terra-table/src/subcomponents/Row.jsx +++ b/packages/terra-table/src/subcomponents/Row.jsx @@ -33,6 +33,11 @@ const propTypes = { */ sectionId: PropTypes.string, + /** + * An identifier for the subsection. + */ + subsectionId: PropTypes.string, + /** * String that specifies height of the row. Any valid CSS width value is accepted. */ @@ -116,6 +121,7 @@ function Row(props) { id, tableId, sectionId, + subsectionId, isSelected, isTableStriped, cells, @@ -181,6 +187,7 @@ function Row(props) { rowIndex={rowIndex} columnIndex={cellColumnIndex} sectionId={sectionId} + subsectionId={subsectionId} tableId={tableId} key={`${id}_${columnId}`} isSelected={!rowSelectionMode && cellData.isSelected} diff --git a/packages/terra-table/src/subcomponents/Section.jsx b/packages/terra-table/src/subcomponents/Section.jsx index 2be07907c8b..26af903d429 100644 --- a/packages/terra-table/src/subcomponents/Section.jsx +++ b/packages/terra-table/src/subcomponents/Section.jsx @@ -1,4 +1,3 @@ -// TODO: fix linter error /* eslint-disable jsx-a11y/control-has-associated-label */ import React, { useCallback, useContext } from 'react'; @@ -8,9 +7,11 @@ import PropTypes from 'prop-types'; import SectionHeader from 'terra-section-header'; import ThemeContext from 'terra-theme-context'; import GridContext, { GridConstants } from '../utils/GridContext'; +import { SUBSECTION_HEADER_LEVEL } from '../utils/constants'; import Row from './Row'; import rowShape from '../proptypes/rowShape'; +import subsectionShape from '../proptypes/subsectionShape'; import columnShape from '../proptypes/columnShape'; import styles from './Section.module.scss'; @@ -58,6 +59,11 @@ const propTypes = { */ rows: PropTypes.arrayOf(rowShape), + /** + * An array of subsection objects to be rendered within the section. + */ + subsections: PropTypes.arrayOf(subsectionShape), + /** * Enables row selection capabilities for the table. * Use 'single' for single row selection and 'multiple' for multi-row selection. @@ -144,6 +150,7 @@ function Section(props) { rowHeight, rowHeaderIndex, rows, + subsections, onSectionSelect, rowMinimumHeight, boundingWidth, @@ -162,37 +169,109 @@ function Section(props) { onSectionSelect(id); }, [id, onSectionSelect]); + const sectionHeader = ( + + {/* Render section rows */} + {!isHidden && ( + + + + )} + + ); + + const rowProps = { + displayedColumns, + firstRowId, + height: rowHeight, + isTableStriped, + lastRowId, + onCellSelect, + rowHeaderIndex, + rowMinimumHeight, + rowSelectionMode, + sectionId: !isHidden ? id : undefined, + tableId, + }; + + if (subsections) { + return ( + <> + {sectionHeader} + {!isCollapsed && subsections.map((subsection) => ( + <> + + + + + + + {/* Render subsection rows */} + {!isCollapsed && subsection.rows.map((row, rowIndex) => ( + + ))} + + + ))} + + ); + } + return ( <> - - {/* Render section rows */} - {!isHidden && ( - - - - )} - + {sectionHeader} ))} diff --git a/packages/terra-table/src/subcomponents/Section.module.scss b/packages/terra-table/src/subcomponents/Section.module.scss index a2353edd1d0..1e0c42bd239 100644 --- a/packages/terra-table/src/subcomponents/Section.module.scss +++ b/packages/terra-table/src/subcomponents/Section.module.scss @@ -15,8 +15,8 @@ border-right: var(--terra-table-cell-border-right, 1px solid #dedfe0); padding: 0; - /* stylelint-disable selector-max-compound-selectors */ - &:focus { + /* stylelint-disable selector-max-compound-selectors, max-nesting-depth */ + &:focus { outline: none; } @@ -25,11 +25,23 @@ outline-offset: var(--terra-table-section-focus-outline-offset, -5px); } - .section-header:focus-within { --terra-section-header-focus-within-outline-offset: -5px; } + + .subsection-header-row{ + // stylelint-disable-next-line terra/custom-property-namespace + background: var(--terra-section-header-background, #dedfe0); + border-bottom: var(--terra-table-subsection-border-bottom, 1px solid #c8cacb); + + span { + // stylelint-disable-next-line terra/custom-property-namespace + font-size: var(--terra-section-header-font-size, 0.808571428571rem); + left: var(--terra-table-subsection-header-left, 1.2142857143rem); + } + } + /* stylelint-enable selector-max-compound-selectors */ } } -} +} \ No newline at end of file diff --git a/packages/terra-table/src/utils/constants.js b/packages/terra-table/src/utils/constants.js index 34a838deb3b..36aaa562337 100644 --- a/packages/terra-table/src/utils/constants.js +++ b/packages/terra-table/src/utils/constants.js @@ -7,4 +7,9 @@ const ERRORS = { ACTION_ONCLICK_IS_NOT_A_FUNCTION: 'Column action onClick property type is not a function.', }; -export default ERRORS; +const SUBSECTION_HEADER_LEVEL = 3; + +export { + ERRORS, + SUBSECTION_HEADER_LEVEL, +}; diff --git a/packages/terra-table/src/utils/tableUtils.js b/packages/terra-table/src/utils/tableUtils.js index 9104818e45d..50d7a212632 100644 --- a/packages/terra-table/src/utils/tableUtils.js +++ b/packages/terra-table/src/utils/tableUtils.js @@ -15,7 +15,12 @@ const getFirstAndLastVisibleRowData = (sections) => { return rowData; } - const findNotEmptyOrCollapsed = section => section.rows.length > 0 && !section.isCollapsed; + const findNotEmptyOrCollapsed = section => { + if (section.subsections) { + return section.subsections.length > 0 && !section.isCollapsed; + } + return section.rows.length > 0 && !section.isCollapsed; + }; const visibleSections = sections.filter(findNotEmptyOrCollapsed); if (visibleSections.length < 1) { diff --git a/packages/terra-table/tests/jest/Section.test.jsx b/packages/terra-table/tests/jest/Section.test.jsx index f282cb3bb57..e52989d2b81 100644 --- a/packages/terra-table/tests/jest/Section.test.jsx +++ b/packages/terra-table/tests/jest/Section.test.jsx @@ -9,6 +9,55 @@ const tableData = { { id: 'Column-1', displayName: 'March 16' }, { id: 'Column-2', displayName: 'March 17', isSelectable: false }, ], + subsections: [ + { + id: 'subsection-0', + text: 'Test SubSection', + subSectionRowIndex: 2, + rows: [ + { + id: '1', + cells: [ + { content: 'Heart Rate Monitored (bpm)', isSelectable: false }, + { content: '' }, + { content: '66', isMasked: true }, + ], + }, + { + id: '2', + cells: [ + { content: 'Temperature Oral (degC)' }, + { content: '36.7' }, + { content: '36.9', isMasked: true }, + ], + }, + ], + }, + { + id: 'subsection-1', + text: 'Test SubSection #2', + subSectionRowIndex: 5, + rows: [ + { + id: '3', + cells: [ + { content: 'Cardiac Index (L/min/m2)' }, + { content: '2.25' }, + { content: '2.28', isMasked: true }, + ], + }, + { + id: '4', + isSelected: true, + cells: [ + { content: 'Oxygen Flow Rate (L/min)' }, + { content: '63' }, + { content: '47' }, + ], + }, + ], + }, + ], rows: [ { id: '1', @@ -63,6 +112,24 @@ describe('Section', () => { expect(wrapper.find(Row)).toHaveLength(tableData.rows.length); }); + it('verifies that the section created is consistent with the subsections rows', () => { + const wrapper = enzyme.shallow( +
, + ); + + const subSectionReducer = (rowCount, currentSubsection) => rowCount + currentSubsection.rows.length; + + // The number of rows should match the given data. + expect(wrapper.find(Row)).toHaveLength(tableData.subsections.reduce(subSectionReducer, 0)); + }); + it('verifies the rows are created with the right props', () => { const verifyRow = (rowIndex, rowComponent, data, overflowColumns) => { expect(rowComponent.props.displayedColumns).toEqual(overflowColumns); @@ -93,6 +160,37 @@ describe('Section', () => { verifyRow(2, rows.get(2), tableData.rows[2], tableData.cols); }); + it('verifies the rows are created with the right props when in subsections', () => { + const verifyRow = (rowIndex, rowComponent, data, overflowColumns, subsectionIndex) => { + expect(rowComponent.props.displayedColumns).toEqual(overflowColumns); + expect(rowComponent.props.rowSelectionMode).toBeUndefined(); + expect(rowComponent.props.tableId).toBe('test-table'); + expect(rowComponent.key).toEqual(data.id); + expect(rowComponent.props.onCellSelect).toBeUndefined(); + expect(rowComponent.props.rowHeaderIndex).toEqual(0); + expect(rowComponent.props.rowIndex).toEqual(rowIndex + subsectionIndex); + expect(rowComponent.props.cells).toEqual(data.cells); + }; + + const wrapper = enzyme.shallow( +
, + ); + + const rows = wrapper.find(Row); + expect(rows).toHaveLength(tableData.rows.length); + verifyRow(1, rows.get(0), tableData.subsections[0].rows[0], tableData.cols, tableData.subsections[0].subSectionRowIndex); + verifyRow(2, rows.get(1), tableData.subsections[0].rows[1], tableData.cols, tableData.subsections[0].subSectionRowIndex); + verifyRow(1, rows.get(2), tableData.subsections[1].rows[0], tableData.cols, tableData.subsections[1].subSectionRowIndex); + verifyRow(2, rows.get(3), tableData.subsections[1].rows[1], tableData.cols, tableData.subsections[1].subSectionRowIndex); + }); + it('verifies the rows receive the correct props when table is zebra striped', () => { const wrapper = enzyme.shallow(
{ jest.spyOn(console, 'error').mockImplementation(); @@ -400,6 +491,75 @@ describe('Table', () => { expect(section2Row1Cell2.props().headers).toBe('test-terra-table-section-1 test-terra-table-rowheader-3 test-terra-table-Column-1-headerCell'); }); + it('verifies ARIA attributes for a table with subsections', () => { + const wrapper = enzymeIntl.mountWithIntl( +
+ +
+ + + +
- -
, + ); + + // Validate table properties + const table = wrapper.find('table'); + expect(table.props().id).toBe('test-terra-table'); + expect(table.props().role).toBe('table'); + expect(table.props()['aria-rowcount']).toBe(8); + + // Validate column header id attribute + const columnHeaderCell = wrapper.find('.column-header').at(0); + expect(columnHeaderCell.props().id).toBe('test-terra-table-Column-0-headerCell'); + + // Retrieve first section + const tableSections = wrapper.find(Section); + const section1 = tableSections.at(0); + + // Validate section header row of the first section + const section1HeaderRow = section1.find('.header-row'); + expect(section1HeaderRow.at(0).props()['aria-rowindex']).toBe(2); + expect(section1HeaderRow.at(1).props()['aria-rowindex']).toBe(3); + expect(section1HeaderRow.at(2).props()['aria-rowindex']).toBe(6); + + // Validate table header element of the first section + const sectionHeader1 = section1HeaderRow.find('th'); + expect(sectionHeader1.at(0).props().id).toBe('test-terra-table-section-0'); + expect(sectionHeader1.at(0).props().colSpan).toBe(tableSectionData.cols.length); + expect(sectionHeader1.at(0).props().scope).toBe('col'); + // Validate table header element of the first subsection + expect(sectionHeader1.at(1).props().id).toBe('test-terra-table-section-0-subsection-0'); + expect(sectionHeader1.at(1).props().colSpan).toBe(tableSectionData.cols.length); + expect(sectionHeader1.at(1).props().scope).toBe('col'); + // Validate table header element of the 2nd subsection + expect(sectionHeader1.at(2).props().id).toBe('test-terra-table-section-0-subsection-1'); + expect(sectionHeader1.at(2).props().colSpan).toBe(tableSectionData.cols.length); + expect(sectionHeader1.at(2).props().scope).toBe('col'); + + // Validate rows of the first subsection + const section1Row1 = section1.find('.row').at(0); + + expect(section1Row1.props()['aria-rowindex']).toBe(4); + expect(section1Row1.props()['data-row-id']).toBe('1'); + const section1Row2 = section1.find('.row').at(1); + expect(section1Row2.props()['aria-rowindex']).toBe(5); + expect(section1Row2.props()['data-row-id']).toBe('2'); + + // Validate cells of the first row + const row1Cell1 = section1Row1.find('.cell').at(0); + expect(row1Cell1.props().headers).toBe('test-terra-table-section-0 test-terra-table-section-0-subsection-0 test-terra-table-Column-0-headerCell'); + expect(row1Cell1.props().id).toBe('test-terra-table-rowheader-1'); + const row1Cell2 = section1Row1.find('.cell').at(1); + expect(row1Cell2.props().headers).toBe('test-terra-table-section-0 test-terra-table-section-0-subsection-0 test-terra-table-rowheader-1 test-terra-table-Column-1-headerCell'); + + // Validate rows of the second subsection + const section1Row3 = section1.find('.row').at(2); + + expect(section1Row3.props()['aria-rowindex']).toBe(7); + expect(section1Row3.props()['data-row-id']).toBe('3'); + const section1Row4 = section1.find('.row').at(3); + expect(section1Row4.props()['aria-rowindex']).toBe(8); + expect(section1Row4.props()['data-row-id']).toBe('4'); + }); + it('verifies ARIA rowcount and rowindex attributes for a table with header actions (with sections)', () => { const wrapper = enzymeIntl.mountWithIntl(
{ }); }); + describe('Table with subsections', () => { + const tableWithSubSectionsSelector = '#table-with-sub-sections'; + + it('validates a table with subsections', () => { + browser.url('/raw/tests/cerner-terra-framework-docs/table/table-with-sub-sections'); + expect(browser.$('//*[@id="table-with-sub-sections"]/tbody[3]')).not.toExist(); + Terra.validates.element('table-with-subsections', { selector: tableWithSubSectionsSelector }); + }); + + it('validates a table with with collapsed sections', () => { + browser.url('/raw/tests/cerner-terra-framework-docs/table/table-with-collapsible-sections-and-sub-sections'); + expect(browser.$('//*[@id="table-with-sub-sections"]/tbody[3]')).toHaveChildren(2); + Terra.validates.element('table-with-collasped-subsections', { selector: tableWithSubSectionsSelector }); + }); + }); + describe('With row selection', () => { const rowSelectionTableSelector = '#table-with-row-selections';