diff --git a/.gitignore b/.gitignore index db3d62b944..a5e65b6a46 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ vendor/generated-*.txt .vscode # System .DS_Store +.kiro/ diff --git a/build-tools/utils/pluralize.js b/build-tools/utils/pluralize.js index 5cacfb96f9..1c05660269 100644 --- a/build-tools/utils/pluralize.js +++ b/build-tools/utils/pluralize.js @@ -27,6 +27,7 @@ const pluralizationMap = { DateInput: 'DateInputs', DatePicker: 'DatePickers', DateRangePicker: 'DateRangePickers', + Divider: 'Dividers', Drawer: 'Drawers', ErrorBoundary: 'ErrorBoundaries', ExpandableSection: 'ExpandableSections', diff --git a/package-lock.json b/package-lock.json index 4a1eea1340..92ae1b198b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "@cloudscape-design/components", "version": "3.0.0", + "hasInstallScript": true, "dependencies": { "@cloudscape-design/collection-hooks": "^1.0.0", "@cloudscape-design/component-toolkit": "^1.0.0-beta", @@ -818,6 +819,7 @@ "version": "7.27.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -1271,6 +1273,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -1292,6 +1295,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -1339,6 +1343,7 @@ "node_modules/@dnd-kit/core": { "version": "6.3.1", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -2453,7 +2458,6 @@ "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -2517,7 +2521,6 @@ "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -3636,6 +3639,7 @@ "integrity": "sha512-2kpQq2DD/pRpx3Tal/qRW1SYwcIeQ0iq8li5CJHQgOC+FtPn2BVmuDtzUCgNnpCrbgtfEHqh+iWzxK+Tq6C+RQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "bytes-iec": "^3.1.1", "chokidar": "^4.0.3", @@ -4484,6 +4488,7 @@ "version": "9.6.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4685,6 +4690,7 @@ "version": "16.14.34", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4867,6 +4873,7 @@ "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.0", @@ -4896,6 +4903,7 @@ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -5146,7 +5154,6 @@ "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "tinyrainbow": "^3.0.3" }, @@ -5160,7 +5167,6 @@ "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/pretty-format": "4.0.18", "magic-string": "^0.30.21", @@ -5175,8 +5181,7 @@ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@wdio/config": { "version": "9.24.0", @@ -5251,6 +5256,7 @@ "integrity": "sha512-OmwPKV8c5ecLqo+EkytN7oUeYfNmRI4uOXGIR1ybP7AK5Zz+l9R0dGfoadEuwi1aZXAL0vwuhtq3p0OL3dfqHQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18.20.0" }, @@ -5273,6 +5279,7 @@ "integrity": "sha512-HdzDrRs+ywAqbXGKqe1i/bLtCv47plz4TvsHFH3j729OooT5VH38ctFn5aLXgECmiAKDkmH/A6kOq2Zh5DIxww==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chalk": "^5.1.2", "loglevel": "^1.6.0", @@ -5590,6 +5597,7 @@ "version": "8.15.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5655,6 +5663,7 @@ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6222,6 +6231,7 @@ "version": "29.7.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -6340,22 +6350,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bare-events": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, "node_modules/bare-fs": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.4.tgz", @@ -6645,6 +6639,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -8362,7 +8357,6 @@ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -9361,6 +9355,7 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -9421,6 +9416,7 @@ "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -10017,7 +10013,6 @@ "integrity": "sha512-Bkoqs+39fHwjos51qab7ZWmvZrYNBbzgSAIykH2CrgLOLhHJXzC30DP9lZq2MsmaUsbBnN5c5m8VqAhOHTrCRw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/snapshot": "^4.0.16", "deep-eql": "^5.0.2", @@ -10050,7 +10045,6 @@ "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/get-type": "30.1.0" }, @@ -10064,7 +10058,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -10078,7 +10071,6 @@ "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -10097,8 +10089,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/expect-webdriverio/node_modules/chalk": { "version": "4.1.2", @@ -10106,7 +10097,6 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -10130,7 +10120,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -10141,7 +10130,6 @@ "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/expect-utils": "30.2.0", "@jest/get-type": "30.1.0", @@ -10160,7 +10148,6 @@ "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.1.0", @@ -10177,7 +10164,6 @@ "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", @@ -10194,7 +10180,6 @@ "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.2.0", @@ -10216,7 +10201,6 @@ "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -10232,7 +10216,6 @@ "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -10251,7 +10234,6 @@ "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -10267,7 +10249,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -12913,6 +12894,7 @@ "version": "29.7.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -13269,6 +13251,7 @@ "version": "29.7.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", @@ -13872,6 +13855,7 @@ "version": "29.7.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", @@ -16478,6 +16462,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -17043,6 +17028,7 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -17200,6 +17186,7 @@ "integrity": "sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -17631,6 +17618,7 @@ "node_modules/react": { "version": "16.14.0", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -17643,6 +17631,7 @@ "node_modules/react-dom": { "version": "16.14.0", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -18317,6 +18306,7 @@ "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -18640,6 +18630,7 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -19756,6 +19747,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", @@ -20508,7 +20500,6 @@ "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=14.0.0" } @@ -20741,7 +20732,8 @@ }, "node_modules/tslib": { "version": "2.8.1", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/type-check": { "version": "0.4.0", @@ -20889,6 +20881,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21402,6 +21395,7 @@ "integrity": "sha512-LTJt6Z/iDM0ne/4ytd3BykoPv9CuJ+CAILOzlwFeMGn4Mj02i4Bk2Rg9o/jeJ89f52hnv4OPmNjD0e8nzWAy5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "^20.11.30", "@types/sinonjs__fake-timers": "^8.1.5", @@ -21455,6 +21449,7 @@ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -21502,6 +21497,7 @@ "version": "5.1.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", diff --git a/pages/divider/permutations.page.tsx b/pages/divider/permutations.page.tsx new file mode 100644 index 0000000000..843422c37e --- /dev/null +++ b/pages/divider/permutations.page.tsx @@ -0,0 +1,35 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import * as React from 'react'; + +import Divider, { DividerProps } from '~components/divider'; + +import createPermutations from '../utils/permutations'; +import PermutationsView from '../utils/permutations-view'; +import ScreenshotArea from '../utils/screenshot-area'; + +const permutations = createPermutations([ + { + decorative: [true, false], + }, +]); + +export default function DividerPermutations() { + return ( + <> +

Divider permutations

+ + ( +
+

Above

+ +

Below

+
+ )} + /> +
+ + ); +} diff --git a/pages/divider/style-api.page.tsx b/pages/divider/style-api.page.tsx new file mode 100644 index 0000000000..c1e41b1621 --- /dev/null +++ b/pages/divider/style-api.page.tsx @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import { Divider, SpaceBetween } from '~components'; + +import ScreenshotArea from '../utils/screenshot-area'; + +export default function DividerStyleApi() { + return ( + +

Divider Style API

+ +
+

Default

+ +
+
+

Custom color

+ +
+
+

Custom width

+ +
+
+

Custom color + width

+ +
+
+
+ ); +} diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index efe8988f9a..136d7585c2 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -11499,6 +11499,105 @@ with the input using \`ariaDescribedby\`.", } `; +exports[`Components definition for divider matches the snapshot: divider 1`] = ` +{ + "dashCaseName": "divider", + "events": [], + "functions": [], + "name": "Divider", + "properties": [ + { + "deprecatedTag": "Custom CSS is not supported. For testing and other use cases, use [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes).", + "description": "Adds the specified classes to the root element of the component.", + "name": "className", + "optional": true, + "type": "string", + }, + { + "defaultValue": "true", + "description": "When \`true\` (default), the divider is purely decorative and hidden from assistive technology (\`role="presentation"\`). +When \`false\`, the divider is announced by screen readers as a separator (\`role="separator"\`). + +Set to \`false\` only when the divider genuinely separates distinct, meaningful content regions +that a screen reader user needs to be aware of.", + "name": "decorative", + "optional": true, + "type": "boolean", + }, + { + "deprecatedTag": "The usage of the \`id\` attribute is reserved for internal use cases. For testing and other use cases, +use [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes). If you must +use the \`id\` attribute, consider setting it on a parent element instead.", + "description": "Adds the specified ID to the root element of the component.", + "name": "id", + "optional": true, + "type": "string", + }, + { + "description": "Attributes to add to the native element. +Some attributes will be automatically combined with internal attribute values: +- \`className\` will be appended. +- Event handlers will be chained, unless the default is prevented. + +We do not support using this attribute to apply custom styling.", + "inlineType": { + "name": "Omit, "children"> & Record<\`data-\${string}\`, string>", + "type": "union", + "values": [ + "Omit, "children">", + "Record<\`data-\${string}\`, string>", + ], + }, + "name": "nativeAttributes", + "optional": true, + "systemTags": [ + "core", + ], + "type": "Omit, "children"> & Record<\`data-\${string}\`, string>", + }, + { + "description": "An object containing CSS properties to customize the divider's visual appearance. +Refer to the [style](/components/divider/?tabId=style) tab for more details.", + "inlineType": { + "name": "DividerProps.Style", + "properties": [ + { + "inlineType": { + "name": "{ borderColor?: string | undefined; borderWidth?: string | undefined; }", + "properties": [ + { + "name": "borderColor", + "optional": true, + "type": "string", + }, + { + "name": "borderWidth", + "optional": true, + "type": "string", + }, + ], + "type": "object", + }, + "name": "root", + "optional": true, + "type": "{ borderColor?: string | undefined; borderWidth?: string | undefined; }", + }, + ], + "type": "object", + }, + "name": "style", + "optional": true, + "systemTags": [ + "core", + ], + "type": "DividerProps.Style", + }, + ], + "regions": [], + "releaseStatus": "stable", +} +`; + exports[`Components definition for drawer matches the snapshot: drawer 1`] = ` { "dashCaseName": "drawer", @@ -36942,6 +37041,10 @@ wrapper.selectOptionByValue('option_1'); ], "name": "SelectWrapper", }, + { + "methods": [], + "name": "DividerWrapper", + }, { "methods": [ { @@ -46918,6 +47021,10 @@ The mode selector is only rendered as a Select on narrow viewports. On wide view ], "name": "SelectWrapper", }, + { + "methods": [], + "name": "DividerWrapper", + }, { "methods": [ { diff --git a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap index 20ef11c8ed..363e54f65b 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap @@ -244,6 +244,9 @@ exports[`test-utils selectors 1`] = ` "awsui_today_1afkv", "awsui_validation-error_1afkv", ], + "divider": [ + "awsui_divider_1noo2", + ], "drawer": [ "awsui_drawer_1sxt8", "awsui_footer_1sxt8", diff --git a/src/__tests__/snapshot-tests/__snapshots__/test-utils-wrappers.test.tsx.snap b/src/__tests__/snapshot-tests/__snapshots__/test-utils-wrappers.test.tsx.snap index 35b13b6618..8b3a64a14c 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/test-utils-wrappers.test.tsx.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/test-utils-wrappers.test.tsx.snap @@ -36,6 +36,7 @@ import CopyToClipboardWrapper from './copy-to-clipboard'; import DateInputWrapper from './date-input'; import DatePickerWrapper from './date-picker'; import DateRangePickerWrapper from './date-range-picker'; +import DividerWrapper from './divider'; import DrawerWrapper from './drawer'; import ErrorBoundaryWrapper from './error-boundary'; import ExpandableSectionWrapper from './expandable-section'; @@ -126,6 +127,7 @@ export { CopyToClipboardWrapper }; export { DateInputWrapper }; export { DatePickerWrapper }; export { DateRangePickerWrapper }; +export { DividerWrapper }; export { DrawerWrapper }; export { ErrorBoundaryWrapper }; export { ExpandableSectionWrapper }; @@ -704,6 +706,25 @@ findDateRangePicker(selector?: string): DateRangePickerWrapper | null; * @returns {Array} */ findAllDateRangePickers(selector?: string): Array; +/** + * Returns the wrapper of the first Divider that matches the specified CSS selector. + * If no CSS selector is specified, returns the wrapper of the first Divider. + * If no matching Divider is found, returns \`null\`. + * + * @param {string} [selector] CSS Selector + * @returns {DividerWrapper | null} + */ +findDivider(selector?: string): DividerWrapper | null; + +/** + * Returns an array of Divider wrapper that matches the specified CSS selector. + * If no CSS selector is specified, returns all of the Dividers inside the current wrapper. + * If no matching Divider is found, returns an empty array. + * + * @param {string} [selector] CSS Selector + * @returns {Array} + */ +findAllDividers(selector?: string): Array; /** * Returns the wrapper of the first Drawer that matches the specified CSS selector. * If no CSS selector is specified, returns the wrapper of the first Drawer. @@ -2218,6 +2239,19 @@ ElementWrapper.prototype.findDateRangePicker = function(selector) { ElementWrapper.prototype.findAllDateRangePickers = function(selector) { return this.findAllComponents(DateRangePickerWrapper, selector); }; +ElementWrapper.prototype.findDivider = function(selector) { + let rootSelector = \`.\${DividerWrapper.rootSelector}\`; + if("legacyRootSelector" in DividerWrapper && DividerWrapper.legacyRootSelector){ + rootSelector = \`:is(.\${DividerWrapper.rootSelector}, .\${DividerWrapper.legacyRootSelector})\`; + } + // casting to 'any' is needed to avoid this issue with generics + // https://github.com/microsoft/TypeScript/issues/29132 + return (this as any).findComponent(selector ? appendSelector(selector, rootSelector) : rootSelector, DividerWrapper); +}; + +ElementWrapper.prototype.findAllDividers = function(selector) { + return this.findAllComponents(DividerWrapper, selector); +}; ElementWrapper.prototype.findDrawer = function(selector) { let rootSelector = \`.\${DrawerWrapper.rootSelector}\`; if("legacyRootSelector" in DrawerWrapper && DrawerWrapper.legacyRootSelector){ @@ -3058,6 +3092,7 @@ import CopyToClipboardWrapper from './copy-to-clipboard'; import DateInputWrapper from './date-input'; import DatePickerWrapper from './date-picker'; import DateRangePickerWrapper from './date-range-picker'; +import DividerWrapper from './divider'; import DrawerWrapper from './drawer'; import ErrorBoundaryWrapper from './error-boundary'; import ExpandableSectionWrapper from './expandable-section'; @@ -3148,6 +3183,7 @@ export { CopyToClipboardWrapper }; export { DateInputWrapper }; export { DatePickerWrapper }; export { DateRangePickerWrapper }; +export { DividerWrapper }; export { DrawerWrapper }; export { ErrorBoundaryWrapper }; export { ExpandableSectionWrapper }; @@ -3672,6 +3708,23 @@ findDateRangePicker(selector?: string): DateRangePickerWrapper; * @returns {MultiElementWrapper} */ findAllDateRangePickers(selector?: string): MultiElementWrapper; +/** + * Returns a wrapper that matches the Dividers with the specified CSS selector. + * If no CSS selector is specified, returns a wrapper that matches Dividers. + * + * @param {string} [selector] CSS Selector + * @returns {DividerWrapper} + */ +findDivider(selector?: string): DividerWrapper; + +/** + * Returns a multi-element wrapper that matches Dividers with the specified CSS selector. + * If no CSS selector is specified, returns a multi-element wrapper that matches Dividers. + * + * @param {string} [selector] CSS Selector + * @returns {MultiElementWrapper} + */ +findAllDividers(selector?: string): MultiElementWrapper; /** * Returns a wrapper that matches the Drawers with the specified CSS selector. * If no CSS selector is specified, returns a wrapper that matches Drawers. @@ -5064,6 +5117,19 @@ ElementWrapper.prototype.findDateRangePicker = function(selector) { ElementWrapper.prototype.findAllDateRangePickers = function(selector) { return this.findAllComponents(DateRangePickerWrapper, selector); }; +ElementWrapper.prototype.findDivider = function(selector) { + let rootSelector = \`.\${DividerWrapper.rootSelector}\`; + if("legacyRootSelector" in DividerWrapper && DividerWrapper.legacyRootSelector){ + rootSelector = \`:is(.\${DividerWrapper.rootSelector}, .\${DividerWrapper.legacyRootSelector})\`; + } + // casting to 'any' is needed to avoid this issue with generics + // https://github.com/microsoft/TypeScript/issues/29132 + return (this as any).findComponent(selector ? appendSelector(selector, rootSelector) : rootSelector, DividerWrapper); +}; + +ElementWrapper.prototype.findAllDividers = function(selector) { + return this.findAllComponents(DividerWrapper, selector); +}; ElementWrapper.prototype.findDrawer = function(selector) { let rootSelector = \`.\${DrawerWrapper.rootSelector}\`; if("legacyRootSelector" in DrawerWrapper && DrawerWrapper.legacyRootSelector){ diff --git a/src/divider/__tests__/divider.test.tsx b/src/divider/__tests__/divider.test.tsx new file mode 100644 index 0000000000..0aa68d5b88 --- /dev/null +++ b/src/divider/__tests__/divider.test.tsx @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; +import { render } from '@testing-library/react'; + +import Divider from '../../../lib/components/divider'; +import createWrapper from '../../../lib/components/test-utils/dom'; + +import styles from '../../../lib/components/divider/styles.css.js'; + +function renderDivider(jsx: React.ReactElement) { + const { container } = render(jsx); + return createWrapper(container).findDivider()!.getElement(); +} + +describe('Divider', () => { + test('renders an hr element', () => { + expect(renderDivider().tagName).toBe('HR'); + }); + + test('applies root CSS class', () => { + expect(renderDivider()).toHaveClass(styles.divider); + }); +}); + +describe('decorative prop', () => { + test('is decorative by default (role="presentation")', () => { + expect(renderDivider()).toHaveAttribute('role', 'presentation'); + }); + + test('decorative=true sets role="presentation" and no aria-orientation', () => { + const el = renderDivider(); + expect(el).toHaveAttribute('role', 'presentation'); + expect(el).not.toHaveAttribute('aria-orientation'); + }); + + test('decorative=false sets role="separator" and aria-orientation="horizontal"', () => { + const el = renderDivider(); + expect(el).toHaveAttribute('role', 'separator'); + expect(el).toHaveAttribute('aria-orientation', 'horizontal'); + }); +}); + +describe('Style API', () => { + test('applies borderColor', () => { + const el = renderDivider(); + expect(getComputedStyle(el).getPropertyValue('border-color')).toBe('rgb(255, 0, 0)'); + }); + + test('applies borderWidth', () => { + const el = renderDivider(); + expect(getComputedStyle(el).getPropertyValue('border-width')).toBe('4px'); + }); +}); + +describe('nativeAttributes', () => { + test('adds data attributes', () => { + const { container } = render(); + expect(container.querySelector('[data-testid="my-divider"]')).not.toBeNull(); + }); + + test('concatenates className', () => { + const { container } = render(); + expect(container.firstChild).toHaveClass(styles.divider); + expect(container.firstChild).toHaveClass('custom-class'); + }); +}); diff --git a/src/divider/index.tsx b/src/divider/index.tsx new file mode 100644 index 0000000000..d0e6c47d28 --- /dev/null +++ b/src/divider/index.tsx @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +'use client'; +import React from 'react'; +import clsx from 'clsx'; + +import { getBaseProps } from '../internal/base-component'; +import useBaseComponent from '../internal/hooks/use-base-component'; +import { applyDisplayName } from '../internal/utils/apply-display-name'; +import WithNativeAttributes from '../internal/utils/with-native-attributes'; +import { DividerProps } from './interfaces'; +import { getDividerStyles } from './style'; + +import styles from './styles.css.js'; + +export { DividerProps }; + +export default function Divider({ decorative = true, style, nativeAttributes, ...rest }: DividerProps) { + const { __internalRootRef } = useBaseComponent('Divider', { props: { decorative } }); + const baseProps = getBaseProps(rest); + + return ( + + ); +} + +applyDisplayName(Divider, 'Divider'); diff --git a/src/divider/interfaces.ts b/src/divider/interfaces.ts new file mode 100644 index 0000000000..120a096e0d --- /dev/null +++ b/src/divider/interfaces.ts @@ -0,0 +1,50 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import { BaseComponentProps } from '../internal/base-component'; +/** + * @awsuiSystem core + */ +import { NativeAttributes } from '../internal/utils/with-native-attributes'; + +export interface DividerProps extends BaseComponentProps { + /** + * When `true` (default), the divider is purely decorative and hidden from assistive technology (`role="presentation"`). + * When `false`, the divider is announced by screen readers as a separator (`role="separator"`). + * + * Set to `false` only when the divider genuinely separates distinct, meaningful content regions + * that a screen reader user needs to be aware of. + * + * @defaultValue `true` + */ + decorative?: boolean; + + /** + * An object containing CSS properties to customize the divider's visual appearance. + * Refer to the [style](/components/divider/?tabId=style) tab for more details. + * @awsuiSystem core + */ + style?: DividerProps.Style; + + /** + * Attributes to add to the native element. + * Some attributes will be automatically combined with internal attribute values: + * - `className` will be appended. + * - Event handlers will be chained, unless the default is prevented. + * + * We do not support using this attribute to apply custom styling. + * + * @awsuiSystem core + */ + nativeAttributes?: NativeAttributes>; +} + +export namespace DividerProps { + export interface Style { + root?: { + borderColor?: string; + borderWidth?: string; + }; + } +} diff --git a/src/divider/style.ts b/src/divider/style.ts new file mode 100644 index 0000000000..e0a3382a95 --- /dev/null +++ b/src/divider/style.ts @@ -0,0 +1,14 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { SYSTEM } from '../internal/environment'; +import { DividerProps } from './interfaces'; + +export function getDividerStyles(style: DividerProps['style']) { + if (style?.root && SYSTEM === 'core') { + return { + borderColor: style.root.borderColor, + borderWidth: style.root.borderWidth, + }; + } + return {}; +} diff --git a/src/divider/styles.scss b/src/divider/styles.scss new file mode 100644 index 0000000000..0f9c4b3963 --- /dev/null +++ b/src/divider/styles.scss @@ -0,0 +1,22 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + SPDX-License-Identifier: Apache-2.0 +*/ + +@use '../internal/styles' as styles; +@use '../internal/styles/tokens' as awsui; + +.divider { + @include styles.styles-reset; + + display: block; + border-block: none; + border-inline: none; + margin-block: 0; + margin-inline: 0; + border-block-start-style: solid; + border-block-start-width: awsui.$border-divider-section-width; + border-block-start-color: awsui.$color-border-divider-default; + inline-size: 100%; + block-size: 0; +} diff --git a/src/test-utils/dom/divider/index.ts b/src/test-utils/dom/divider/index.ts new file mode 100644 index 0000000000..264825b6e9 --- /dev/null +++ b/src/test-utils/dom/divider/index.ts @@ -0,0 +1,9 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { ComponentWrapper } from '@cloudscape-design/test-utils-core/dom'; + +import styles from '../../../divider/styles.selectors.js'; + +export default class DividerWrapper extends ComponentWrapper { + static rootSelector: string = styles.divider; +}