From 9deb6d128319e9c6bf90627734a75172cf1a5235 Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Mon, 18 Dec 2017 23:24:33 -0500 Subject: [PATCH 01/15] feat(tables): adds reactabular and patternfly tables examples --- .babelrc | 1 + package-lock.json | 368 ++++-- package.json | 7 + sass/patternfly-react/_dropdown.scss | 3 + sass/patternfly-react/_pagination.scss | 8 + sass/patternfly-react/_patternfly-react.scss | 3 +- src/components/DataTable/DataTable.stories.js | 26 + .../DataTable/Stories/PaginationTableStory.js | 20 + .../DataTable/Stories/SortableTableStory.js | 20 + src/components/DataTable/Stories/index.js | 2 + .../DataTable/__mocks__/mockDataTableRows.js | 225 ++++ .../__mocks__/mockPaginationTable.js | 321 +++++ .../DataTable/__mocks__/mockSortableTable.js | 248 ++++ src/components/DataTable/constants.js | 10 + src/components/DataTable/index.js | 1 + .../Pagination/Pagination.stories.js | 66 + src/components/Pagination/Paginator.js | 169 +++ src/components/Pagination/index.js | 2 + src/components/Pagination/paginate.js | 20 + .../Table/Stories/BootstrapTableStory.js | 77 ++ .../Table/Stories/PatternflyTableStory.js | 25 + src/components/Table/Stories/index.js | 2 + src/components/Table/Table.js | 1 + src/components/Table/Table.stories.js | 25 + src/components/Table/Table.test.js | 123 ++ src/components/Table/TableActions.js | 22 + src/components/Table/TableButton.js | 25 + src/components/Table/TableCell.js | 30 + src/components/Table/TableHeading.js | 52 + src/components/Table/TablePfProvider.js | 69 ++ src/components/Table/__mocks__/mockColumns.js | 140 +++ src/components/Table/__mocks__/mockRows.js | 72 ++ .../Table/__snapshots__/Table.test.js.snap | 1104 +++++++++++++++++ src/components/Table/constants.js | 16 + src/components/Table/index.js | 14 + src/index.js | 3 + storybook/.babelrc | 1 + 37 files changed, 3206 insertions(+), 115 deletions(-) create mode 100644 sass/patternfly-react/_dropdown.scss create mode 100644 sass/patternfly-react/_pagination.scss create mode 100644 src/components/DataTable/DataTable.stories.js create mode 100644 src/components/DataTable/Stories/PaginationTableStory.js create mode 100644 src/components/DataTable/Stories/SortableTableStory.js create mode 100644 src/components/DataTable/Stories/index.js create mode 100644 src/components/DataTable/__mocks__/mockDataTableRows.js create mode 100644 src/components/DataTable/__mocks__/mockPaginationTable.js create mode 100644 src/components/DataTable/__mocks__/mockSortableTable.js create mode 100644 src/components/DataTable/constants.js create mode 100644 src/components/DataTable/index.js create mode 100644 src/components/Pagination/Pagination.stories.js create mode 100644 src/components/Pagination/Paginator.js create mode 100644 src/components/Pagination/index.js create mode 100644 src/components/Pagination/paginate.js create mode 100644 src/components/Table/Stories/BootstrapTableStory.js create mode 100644 src/components/Table/Stories/PatternflyTableStory.js create mode 100644 src/components/Table/Stories/index.js create mode 100644 src/components/Table/Table.js create mode 100644 src/components/Table/Table.stories.js create mode 100644 src/components/Table/Table.test.js create mode 100644 src/components/Table/TableActions.js create mode 100644 src/components/Table/TableButton.js create mode 100644 src/components/Table/TableCell.js create mode 100644 src/components/Table/TableHeading.js create mode 100644 src/components/Table/TablePfProvider.js create mode 100644 src/components/Table/__mocks__/mockColumns.js create mode 100644 src/components/Table/__mocks__/mockRows.js create mode 100644 src/components/Table/__snapshots__/Table.test.js.snap create mode 100644 src/components/Table/constants.js create mode 100644 src/components/Table/index.js diff --git a/.babelrc b/.babelrc index d5e14244ad2..24fd1912fa9 100644 --- a/.babelrc +++ b/.babelrc @@ -2,6 +2,7 @@ "presets": ["env", "react"], "plugins": [ "transform-class-properties", + "transform-export-extensions", "transform-object-rest-spread", "transform-object-assign" ], diff --git a/package-lock.json b/package-lock.json index a8906727747..9189612a3fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5636,13 +5636,15 @@ "dependencies": { "abbrev": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", "dev": true, "optional": true }, "ajv": { "version": "4.11.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "optional": true, "requires": { @@ -5652,18 +5654,21 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "aproba": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", + "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, "requires": { @@ -5673,42 +5678,49 @@ }, "asn1": { "version": "0.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", "dev": true, "optional": true }, "assert-plus": { "version": "0.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", "dev": true, "optional": true }, "asynckit": { "version": "0.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true, "optional": true }, "aws-sign2": { "version": "0.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", "dev": true, "optional": true }, "aws4": { "version": "1.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", "dev": true, "optional": true }, "balanced-match": { "version": "0.4.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", "dev": true }, "bcrypt-pbkdf": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "dev": true, "optional": true, "requires": { @@ -5717,7 +5729,8 @@ }, "block-stream": { "version": "0.0.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, "requires": { "inherits": "2.0.3" @@ -5725,7 +5738,8 @@ }, "boom": { "version": "2.10.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { "hoek": "2.16.3" @@ -5733,7 +5747,8 @@ }, "brace-expansion": { "version": "1.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", "dev": true, "requires": { "balanced-match": "0.4.2", @@ -5742,29 +5757,34 @@ }, "buffer-shims": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", "dev": true }, "caseless": { "version": "0.12.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true, "optional": true }, "co": { "version": "4.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "combined-stream": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, "requires": { "delayed-stream": "1.0.0" @@ -5772,22 +5792,26 @@ }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "cryptiles": { "version": "2.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, "requires": { "boom": "2.10.1" @@ -5795,7 +5819,8 @@ }, "dashdash": { "version": "1.14.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "optional": true, "requires": { @@ -5804,7 +5829,8 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true, "optional": true } @@ -5812,7 +5838,8 @@ }, "debug": { "version": "2.6.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", "dev": true, "optional": true, "requires": { @@ -5821,30 +5848,35 @@ }, "deep-extend": { "version": "0.4.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", "dev": true, "optional": true }, "delayed-stream": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.2.tgz", + "integrity": "sha1-ca1dIEvxempsqPRQxhRUBm70YeE=", "dev": true, "optional": true }, "ecc-jsbn": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "dev": true, "optional": true, "requires": { @@ -5853,24 +5885,28 @@ }, "extend": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", "dev": true, "optional": true }, "extsprintf": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", "dev": true }, "forever-agent": { "version": "0.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true, "optional": true }, "form-data": { "version": "2.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", "dev": true, "optional": true, "requires": { @@ -5881,12 +5917,14 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "fstream": { "version": "1.0.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, "requires": { "graceful-fs": "4.1.11", @@ -5897,7 +5935,8 @@ }, "fstream-ignore": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", "dev": true, "optional": true, "requires": { @@ -5908,7 +5947,8 @@ }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -5924,7 +5964,8 @@ }, "getpass": { "version": "0.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "optional": true, "requires": { @@ -5933,7 +5974,8 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true, "optional": true } @@ -5941,7 +5983,8 @@ }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -5954,18 +5997,21 @@ }, "graceful-fs": { "version": "4.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "har-schema": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", "dev": true, "optional": true }, "har-validator": { "version": "4.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", "dev": true, "optional": true, "requires": { @@ -5975,13 +6021,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "hawk": { "version": "3.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, "requires": { "boom": "2.10.1", @@ -5992,12 +6040,14 @@ }, "hoek": { "version": "2.16.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", "dev": true }, "http-signature": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "dev": true, "optional": true, "requires": { @@ -6008,7 +6058,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "1.4.0", @@ -6017,18 +6068,21 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ini": { "version": "1.3.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "1.0.1" @@ -6036,24 +6090,28 @@ }, "is-typedarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true, "optional": true }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isstream": { "version": "0.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true, "optional": true }, "jodid25519": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", "dev": true, "optional": true, "requires": { @@ -6062,19 +6120,22 @@ }, "jsbn": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true, "optional": true }, "json-schema": { "version": "0.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true, "optional": true }, "json-stable-stringify": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "optional": true, "requires": { @@ -6083,19 +6144,22 @@ }, "json-stringify-safe": { "version": "5.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true, "optional": true }, "jsonify": { "version": "0.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true, "optional": true }, "jsprim": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", "dev": true, "optional": true, "requires": { @@ -6107,7 +6171,8 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true, "optional": true } @@ -6115,12 +6180,14 @@ }, "mime-db": { "version": "1.27.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", "dev": true }, "mime-types": { "version": "2.1.15", - "bundled": true, + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", "dev": true, "requires": { "mime-db": "1.27.0" @@ -6128,7 +6195,8 @@ }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "1.1.7" @@ -6136,12 +6204,14 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -6149,13 +6219,15 @@ }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "node-pre-gyp": { "version": "0.6.39", - "bundled": true, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", + "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", "dev": true, "optional": true, "requires": { @@ -6174,7 +6246,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -6184,7 +6257,8 @@ }, "npmlog": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", + "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", "dev": true, "optional": true, "requires": { @@ -6196,24 +6270,28 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "oauth-sign": { "version": "0.8.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1.0.2" @@ -6221,19 +6299,22 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", "dev": true, "optional": true, "requires": { @@ -6243,35 +6324,41 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "performance-now": { "version": "0.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", "dev": true, "optional": true }, "process-nextick-args": { "version": "1.0.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", "dev": true }, "punycode": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true, "optional": true }, "qs": { "version": "6.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", "dev": true, "optional": true }, "rc": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", "dev": true, "optional": true, "requires": { @@ -6283,7 +6370,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -6291,7 +6379,8 @@ }, "readable-stream": { "version": "2.2.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", "dev": true, "requires": { "buffer-shims": "1.0.0", @@ -6305,7 +6394,8 @@ }, "request": { "version": "2.81.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", "dev": true, "optional": true, "requires": { @@ -6335,7 +6425,8 @@ }, "rimraf": { "version": "2.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", "dev": true, "requires": { "glob": "7.1.2" @@ -6343,30 +6434,35 @@ }, "safe-buffer": { "version": "5.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", "dev": true }, "semver": { "version": "5.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "sntp": { "version": "1.0.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "dev": true, "requires": { "hoek": "2.16.3" @@ -6374,7 +6470,8 @@ }, "sshpk": { "version": "1.13.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", + "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", "dev": true, "optional": true, "requires": { @@ -6391,7 +6488,8 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true, "optional": true } @@ -6399,7 +6497,8 @@ }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "1.1.0", @@ -6409,7 +6508,8 @@ }, "string_decoder": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", + "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", "dev": true, "requires": { "safe-buffer": "5.0.1" @@ -6417,13 +6517,15 @@ }, "stringstream": { "version": "0.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", "dev": true, "optional": true }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "2.1.1" @@ -6431,13 +6533,15 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "2.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { "block-stream": "0.0.9", @@ -6447,7 +6551,8 @@ }, "tar-pack": { "version": "3.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", "dev": true, "optional": true, "requires": { @@ -6463,7 +6568,8 @@ }, "tough-cookie": { "version": "2.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", "dev": true, "optional": true, "requires": { @@ -6472,7 +6578,8 @@ }, "tunnel-agent": { "version": "0.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "optional": true, "requires": { @@ -6481,30 +6588,35 @@ }, "tweetnacl": { "version": "0.14.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true, "optional": true }, "uid-number": { "version": "0.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", "dev": true, "optional": true }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, "uuid": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", "dev": true, "optional": true }, "verror": { "version": "1.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", "dev": true, "optional": true, "requires": { @@ -6513,7 +6625,8 @@ }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, "requires": { @@ -6522,7 +6635,8 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true } } @@ -8795,6 +8909,12 @@ "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", "dev": true }, + "lodash.orderby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.orderby/-/lodash.orderby-4.6.0.tgz", + "integrity": "sha1-5pfwTOXXhSL1TZM4syuBozk+TrM=", + "optional": true + }, "lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", @@ -13041,6 +13161,14 @@ "velocity-react": "1.3.3" } }, + "reactabular-table": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/reactabular-table/-/reactabular-table-8.12.0.tgz", + "integrity": "sha512-Y+EuCPYLHV1OXlp3PwcyHWJrzRtlJ9VmKZv0Ok/wUWHcs34cYxj9QGTbotQL8th5RYpvDf/UmZ5jJNWQdIXBMQ==", + "requires": { + "classnames": "2.2.5" + } + }, "reactcss": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", @@ -14137,6 +14265,12 @@ "is-plain-obj": "1.1.0" } }, + "sortabular": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/sortabular/-/sortabular-1.5.1.tgz", + "integrity": "sha512-heKAIwXf0VAC8LIfYH+8DiJ4HTRqzfPgOOZwpxFFYQXy+qwAey5CUirCKtue2ocgKnaJkertj+3ZL5DDKifSQg==", + "optional": true + }, "source-list-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", @@ -14524,6 +14658,12 @@ "string-width": "2.1.1" } }, + "table-resolver": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/table-resolver/-/table-resolver-3.2.0.tgz", + "integrity": "sha512-DQrDHFdJPnvIhyjAcTqF4vhu/Uhp5eNRst9Url9KmBNqxYSMrPXOJoxhU7HPCd3efi1Hua7lMIDnBAphsdhPQw==", + "optional": true + }, "tapable": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", diff --git a/package.json b/package.json index 4421450f951..3eb331a029c 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,14 @@ "react-bootstrap-switch": "^15.5.3", "react-c3js": "^0.1.20", "react-fontawesome": "^1.6.1", + "reactabular-table": "^8.12.0", "recompose": "^0.26.0" }, + "optionalDependencies": { + "lodash.orderby": "^4.6.0", + "sortabular": "^1.5.1", + "table-resolver": "^3.2.0" + }, "devDependencies": { "@storybook/addon-actions": "^3.2.12", "@storybook/addon-info": "3.2.12", @@ -38,6 +44,7 @@ "babel-eslint": "^8.1.2", "babel-jest": "^22.0.4", "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-export-extensions": "^6.22.0", "babel-plugin-transform-object-assign": "^6.22.0", "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-preset-env": "^1.6.1", diff --git a/sass/patternfly-react/_dropdown.scss b/sass/patternfly-react/_dropdown.scss new file mode 100644 index 00000000000..d9a9b302cdc --- /dev/null +++ b/sass/patternfly-react/_dropdown.scss @@ -0,0 +1,3 @@ +.dropdown-menu { + min-width: auto; +} diff --git a/sass/patternfly-react/_pagination.scss b/sass/patternfly-react/_pagination.scss new file mode 100644 index 00000000000..e3d2b7bddf5 --- /dev/null +++ b/sass/patternfly-react/_pagination.scss @@ -0,0 +1,8 @@ +.content-view-pf-pagination .form-group .btn-group { + display: flex; + margin-left: 0; + margin-right: 5px; + margin-bottom: 0; + width: auto; + float: none; +} diff --git a/sass/patternfly-react/_patternfly-react.scss b/sass/patternfly-react/_patternfly-react.scss index 91f53ee3a07..e4b7cb2d309 100644 --- a/sass/patternfly-react/_patternfly-react.scss +++ b/sass/patternfly-react/_patternfly-react.scss @@ -1,4 +1,5 @@ - /** Patternfly React Partials */ +@import '_dropdown'; +@import '_pagination'; diff --git a/src/components/DataTable/DataTable.stories.js b/src/components/DataTable/DataTable.stories.js new file mode 100644 index 00000000000..4fbef9af746 --- /dev/null +++ b/src/components/DataTable/DataTable.stories.js @@ -0,0 +1,26 @@ +import { storiesOf } from '@storybook/react'; +import { withKnobs } from '@storybook/addon-knobs'; +import { + sortableTableAddWithInfo, + paginationTableAddWithInfo +} from './Stories'; +import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../storybook/constants'; + +const stories = storiesOf('DataTable', module); + +stories.addDecorator(withKnobs); +stories.addDecorator( + defaultTemplate({ + title: 'Data Table', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/' + }) +); + +/** + * DataTable stories + */ + +sortableTableAddWithInfo(stories); +paginationTableAddWithInfo(stories); diff --git a/src/components/DataTable/Stories/PaginationTableStory.js b/src/components/DataTable/Stories/PaginationTableStory.js new file mode 100644 index 00000000000..02317dbc8b2 --- /dev/null +++ b/src/components/DataTable/Stories/PaginationTableStory.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { withInfo } from '@storybook/addon-info'; +import { MockPaginationTable } from '../__mocks__/mockPaginationTable'; + +/** + * Pagination Table stories + */ + +const paginationTableAddWithInfo = stories => { + stories.add( + 'Paginated Table', + withInfo({ + source: false, + propTablesExclude: [MockPaginationTable], + text:
+ })(() => ) + ); +}; + +export default paginationTableAddWithInfo; diff --git a/src/components/DataTable/Stories/SortableTableStory.js b/src/components/DataTable/Stories/SortableTableStory.js new file mode 100644 index 00000000000..d2642c1e211 --- /dev/null +++ b/src/components/DataTable/Stories/SortableTableStory.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { withInfo } from '@storybook/addon-info'; +import { MockSortableTable } from '../__mocks__/mockSortableTable'; + +/** + * Sortable Table stories + */ + +const sortableTableAddWithInfo = stories => { + stories.add( + 'Sortable Table', + withInfo({ + source: false, + propTablesExclude: [MockSortableTable], + text:
+ })(() => ) + ); +}; + +export default sortableTableAddWithInfo; diff --git a/src/components/DataTable/Stories/index.js b/src/components/DataTable/Stories/index.js new file mode 100644 index 00000000000..d3bc182c812 --- /dev/null +++ b/src/components/DataTable/Stories/index.js @@ -0,0 +1,2 @@ +export { default as sortableTableAddWithInfo } from './SortableTableStory'; +export { default as paginationTableAddWithInfo } from './PaginationTableStory'; diff --git a/src/components/DataTable/__mocks__/mockDataTableRows.js b/src/components/DataTable/__mocks__/mockDataTableRows.js new file mode 100644 index 00000000000..e9f7af8c324 --- /dev/null +++ b/src/components/DataTable/__mocks__/mockDataTableRows.js @@ -0,0 +1,225 @@ +/** + * mockRows made courtesy of https://swapi.co/ + */ +export const mockRows = [ + { + id: 0, + name: 'Luke Skywalker', + height: 172, + mass: 77, + hair_color: 'blond', + skin_color: 'fair', + eye_color: 'blue', + birth_year: '19BBY', + gender: 'male' + }, + { + id: 1, + name: 'C-3PO', + height: 167, + mass: 75, + hair_color: 'n/a', + skin_color: 'gold', + eye_color: 'yellow', + birth_year: '112BBY', + gender: 'n/a' + }, + { + id: 2, + name: 'R2-D2', + height: 96, + mass: 32, + hair_color: 'n/a', + skin_color: 'white, blue', + eye_color: 'red', + birth_year: '33BBY', + gender: 'n/a' + }, + { + id: 3, + name: 'Darth Vader', + height: 202, + mass: 136, + hair_color: 'none', + skin_color: 'white', + eye_color: 'yellow', + birth_year: '41.9BBY', + gender: 'male' + }, + { + id: 4, + name: 'Leia Organa', + height: 150, + mass: 49, + hair_color: 'brown', + skin_color: 'light', + eye_color: 'brown', + birth_year: '19BBY', + gender: 'female' + }, + { + id: 5, + name: 'Owen Lars', + height: 178, + mass: 120, + hair_color: 'brown, grey', + skin_color: 'light', + eye_color: 'blue', + birth_year: '52BBY', + gender: 'male' + }, + { + id: 6, + name: 'Beru Whitesun lars', + height: 165, + mass: 75, + hair_color: 'brown', + skin_color: 'light', + eye_color: 'blue', + birth_year: '47BBY', + gender: 'female' + }, + { + id: 7, + name: 'R5-D4', + height: 97, + mass: 32, + hair_color: 'n/a', + skin_color: 'white, red', + eye_color: 'red', + birth_year: 'unknown', + gender: 'n/a' + }, + { + id: 8, + name: 'Biggs Darklighter', + height: 183, + mass: 84, + hair_color: 'black', + skin_color: 'light', + eye_color: 'brown', + birth_year: '24BBY', + gender: 'male' + }, + { + id: 9, + name: 'Obi-Wan Kenobi', + height: 182, + mass: 77, + hair_color: 'auburn, white', + skin_color: 'fair', + eye_color: 'blue-gray', + birth_year: '57BBY', + gender: 'male' + }, + { + id: 10, + name: 'Anakin Skywalker', + height: 188, + mass: 84, + hair_color: 'blond', + skin_color: 'fair', + eye_color: 'blue', + birth_year: '41.9BBY', + gender: 'male' + }, + { + id: 11, + name: 'Wilhuff Tarkin', + height: 180, + mass: 'unknown', + hair_color: 'auburn, grey', + skin_color: 'fair', + eye_color: 'blue', + birth_year: '64BBY', + gender: 'male' + }, + { + id: 12, + name: 'Chewbacca', + height: 228, + mass: 112, + hair_color: 'brown', + skin_color: 'unknown', + eye_color: 'blue', + birth_year: '200BBY', + gender: 'male' + }, + { + id: 13, + name: 'Han Solo', + height: 180, + mass: 80, + hair_color: 'brown', + skin_color: 'fair', + eye_color: 'brown', + birth_year: '29BBY', + gender: 'male' + }, + { + id: 14, + name: 'Greedo', + height: 173, + mass: 74, + hair_color: 'n/a', + skin_color: 'green', + eye_color: 'black', + birth_year: '44BBY', + gender: 'male' + }, + { + id: 15, + name: 'Jabba Desilijic Tiure', + height: 175, + mass: 1358, + hair_color: 'n/a', + skin_color: 'green-tan, brown', + eye_color: 'orange', + birth_year: '600BBY', + gender: 'hermaphrodite' + }, + { + id: 16, + name: 'Wedge Antilles', + height: 170, + mass: 77, + hair_color: 'brown', + skin_color: 'fair', + eye_color: 'hazel', + birth_year: '21BBY', + gender: 'male' + }, + { + id: 17, + name: 'Jek Tono Porkins', + height: 180, + mass: 110, + hair_color: 'brown', + skin_color: 'fair', + eye_color: 'blue', + birth_year: 'unknown', + gender: 'male' + }, + { + id: 18, + name: 'Yoda', + height: 66, + mass: 17, + hair_color: 'white', + skin_color: 'green', + eye_color: 'brown', + birth_year: '896BBY', + gender: 'male' + }, + { + id: 19, + name: 'Palpatine', + height: 170, + mass: 75, + hair_color: 'grey', + skin_color: 'pale', + eye_color: 'yellow', + birth_year: '82BBY', + gender: 'male' + } +]; diff --git a/src/components/DataTable/__mocks__/mockPaginationTable.js b/src/components/DataTable/__mocks__/mockPaginationTable.js new file mode 100644 index 00000000000..8ea6426fdf3 --- /dev/null +++ b/src/components/DataTable/__mocks__/mockPaginationTable.js @@ -0,0 +1,321 @@ +import React from 'react'; +import orderBy from 'lodash.orderby'; +import * as sort from 'sortabular'; +import * as resolve from 'table-resolver'; +import { bindMethods } from '../../../common/helpers'; +import { defaultSortingOrder } from '../index'; +import { Table } from '../../Table'; +import { DropdownKebab } from '../../DropdownKebab'; +import { MenuItem } from '../../MenuItem'; +import { Grid } from '../../Grid'; +import { Paginator, paginate } from '../../Pagination'; +import { compose } from 'recompose'; +import { mockRows } from './mockDataTableRows'; + +/** + * Reactabular client side paging based on the following api docs: + * https://reactabular.js.org/#/data/pagination + */ + +export class MockPaginationTable extends React.Component { + constructor(props) { + super(props); + + // Point the transform to your rows. React state can work for this purpose + // but you can use a state manager as well. + const getSortingColumns = () => this.state.sortingColumns || {}; + + // sortable transform + const sortable = sort.sort({ + getSortingColumns, + onSort: selectedColumn => { + this.setState({ + sortingColumns: sort.byColumn({ + sortingColumns: this.state.sortingColumns, + sortingOrder: defaultSortingOrder, + selectedColumn + }) + }); + }, + // Use property or index dependening on the `sortingColumns` structure specified + strategy: sort.strategies.byProperty + }); + + // sorting fortmatter + const sortingFormat = sort.header({ + sortable, + getSortingColumns, + strategy: sort.strategies.byProperty + }); + + // cell formatter + const cellFormat = value => { + return {value}; + }; + + this.state = { + // Sort the first column in an ascending way by default. + sortingColumns: { + name: { + direction: 'asc', + position: 0 + } + }, + columns: [ + { + property: 'name', + header: { + label: 'Name', + props: { + index: 0, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 0 + }, + formatters: [cellFormat] + } + }, + { + property: 'height', + header: { + label: 'Height', + props: { + index: 1, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 1 + }, + formatters: [cellFormat] + } + }, + { + property: 'eye_color', + header: { + label: 'Eye Color', + props: { + index: 2, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 2 + }, + formatters: [cellFormat] + } + }, + { + property: 'gender', + header: { + label: 'Gender', + props: { + index: 3, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 3 + }, + formatters: [cellFormat] + } + }, + { + property: 'birth_year', + header: { + label: 'Birth Year', + props: { + index: 4, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 4 + }, + formatters: [cellFormat] + } + }, + { + property: 'actions', + header: { + label: 'Actions', + props: { + index: 5, + rowSpan: 1, + colSpan: 2 + } + }, + cell: { + props: { + index: 5 + }, + formatters: [ + (value, { rowData }) => { + return [ + + alert('clicked ' + rowData.name)} + > + Actions + + , + + + Action + Another Action + Something else here + + Separated link + + + ]; + } + ] + } + } + ], + rows: mockRows, + pagination: { + page: 1, + perPage: 6, + perPageOptions: [6, 10, 15] + } + }; + + bindMethods(this, [ + 'onPageInput', + 'onPerPageSelect', + 'onFirstPage', + 'onPreviousPage', + 'onNextPage', + 'onLastPage' + ]); + } + onPageInput(e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = e.target.value; + this.setState({ pagination: newPaginationState }); + } + onPerPageSelect(eventKey, e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.perPage = eventKey; + newPaginationState.page = 1; + this.setState({ pagination: newPaginationState }); + } + onFirstPage() { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = 1; + this.setState({ pagination: newPaginationState }); + } + onPreviousPage() { + const { page } = this.state.pagination; + if (page > 1) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page--; + this.setState({ pagination: newPaginationState }); + } + } + onNextPage() { + const { page, perPage } = this.state.pagination; + const totalPages = Math.ceil(mockRows.length / perPage); + if (page < totalPages) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page++; + this.setState({ pagination: newPaginationState }); + } + } + onLastPage() { + const { page, perPage } = this.state.pagination; + const totalPages = Math.ceil(mockRows.length / perPage); + if (page < totalPages) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = totalPages; + this.setState({ pagination: newPaginationState }); + } + } + render() { + const { rows, sortingColumns, columns, pagination } = this.state; + + const sortedPaginatedRows = compose( + paginate(pagination), + sort.sorter({ + columns: columns, + sortingColumns, + sort: orderBy, + strategy: sort.strategies.byProperty + }) + )(rows); + + const customHeaderCell = cellProps => { + const { index } = cellProps; + const column = columns[index]; + const sortDirection = + sortingColumns[column.property] && + sortingColumns[column.property].direction; + return ( + + {column.header.label} + + ); + }; + + const customTableCell = cellProps => { + return cellProps.children; + }; + + return ( + + + + + + + + ); + } +} diff --git a/src/components/DataTable/__mocks__/mockSortableTable.js b/src/components/DataTable/__mocks__/mockSortableTable.js new file mode 100644 index 00000000000..e62ac1c625a --- /dev/null +++ b/src/components/DataTable/__mocks__/mockSortableTable.js @@ -0,0 +1,248 @@ +import React from 'react'; +import orderBy from 'lodash.orderby'; +import * as sort from 'sortabular'; +import * as resolve from 'table-resolver'; +import { defaultSortingOrder } from '../index'; +import { Table } from '../../Table'; +import { DropdownKebab } from '../../DropdownKebab'; +import { MenuItem } from '../../MenuItem'; +import { compose } from 'recompose'; +import { mockRows } from './mockDataTableRows'; + +/** + * Reactabular client side data sorting based on the following api docs: + * https://reactabular.js.org/#/data/sorting + */ + +export class MockSortableTable extends React.Component { + constructor(props) { + super(props); + + // Point the transform to your rows. React state can work for this purpose + // but you can use a state manager as well. + const getSortingColumns = () => this.state.sortingColumns || {}; + + // sortable transform + const sortable = sort.sort({ + getSortingColumns, + onSort: selectedColumn => { + this.setState({ + sortingColumns: sort.byColumn({ + sortingColumns: this.state.sortingColumns, + sortingOrder: defaultSortingOrder, + selectedColumn + }) + }); + }, + // Use property or index dependening on the `sortingColumns` structure specified + strategy: sort.strategies.byProperty + }); + + // sorting fortmatter + const sortingFormat = sort.header({ + sortable, + getSortingColumns, + strategy: sort.strategies.byProperty + }); + + // cell formatter + const cellFormat = value => { + return {value}; + }; + + this.state = { + // Sort the first column in an ascending way by default. + sortingColumns: { + name: { + direction: 'asc', + position: 0 + } + }, + columns: [ + { + property: 'name', + header: { + label: 'Name', + props: { + index: 0, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 0 + }, + formatters: [cellFormat] + } + }, + { + property: 'height', + header: { + label: 'Height', + props: { + index: 1, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 1 + }, + formatters: [cellFormat] + } + }, + { + property: 'eye_color', + header: { + label: 'Eye Color', + props: { + index: 2, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 2 + }, + formatters: [cellFormat] + } + }, + { + property: 'gender', + header: { + label: 'Gender', + props: { + index: 3, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 3 + }, + formatters: [cellFormat] + } + }, + { + property: 'birth_year', + header: { + label: 'Birth Year', + props: { + index: 4, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortable], + formatters: [sortingFormat] + }, + cell: { + props: { + index: 4 + }, + formatters: [cellFormat] + } + }, + { + property: 'actions', + header: { + label: 'Actions', + props: { + index: 5, + rowSpan: 1, + colSpan: 2 + } + }, + cell: { + props: { + index: 5 + }, + formatters: [ + (value, { rowData }) => { + return [ + + alert('clicked ' + rowData.name)} + > + Actions + + , + + + Action + Another Action + Something else here + + Separated link + + + ]; + } + ] + } + } + ], + rows: mockRows.slice(0, 6) + }; + } + render() { + const { rows, sortingColumns, columns } = this.state; + + const sortedRows = compose( + sort.sorter({ + columns: columns, + sortingColumns, + sort: orderBy, + strategy: sort.strategies.byProperty + }) + )(rows); + + const customHeaderCell = cellProps => { + const { index } = cellProps; + const column = columns[index]; + const sortDirection = + sortingColumns[column.property] && + sortingColumns[column.property].direction; + return ( + + {column.header.label} + + ); + }; + + const customTableCell = cellProps => { + // just return the cell formatters `td` content and remove the default cell wrapper + return cellProps.children; + }; + + return ( +
+ + + + +
+ ); + } +} diff --git a/src/components/DataTable/constants.js b/src/components/DataTable/constants.js new file mode 100644 index 00000000000..8606fed71b4 --- /dev/null +++ b/src/components/DataTable/constants.js @@ -0,0 +1,10 @@ +// Reactabular sorting order allows you to specifiy sort asc/desc only and removes +// the unsorted state. This is consistent with current PF Data Table but should +// be better spelled out in our design docs. +// https://github.com/patternfly/patternfly-design/blob/master/pattern-library/content-views/table-view/design/design.md +// https://reactabular.js.org/#/data/sorting?a=customizing-sorting-order +export const defaultSortingOrder = { + FIRST: 'asc', + asc: 'desc', + desc: 'asc' +}; diff --git a/src/components/DataTable/index.js b/src/components/DataTable/index.js new file mode 100644 index 00000000000..e6ea5fd0ff7 --- /dev/null +++ b/src/components/DataTable/index.js @@ -0,0 +1 @@ +export { defaultSortingOrder } from './constants'; diff --git a/src/components/Pagination/Pagination.stories.js b/src/components/Pagination/Pagination.stories.js new file mode 100644 index 00000000000..86f40a3024b --- /dev/null +++ b/src/components/Pagination/Pagination.stories.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { withKnobs, number } from '@storybook/addon-knobs'; +import { bindMethods } from '../../common/helpers'; +import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../storybook/constants'; +import { Paginator } from './index'; + +const stories = storiesOf('Pagination', module); +stories.addDecorator(withKnobs); +stories.addDecorator( + defaultTemplate({ + title: 'Pagination', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_NAVIGATION + 'pagination/' + }) +); + +class PaginationStoryWrapper extends React.Component { + constructor(props) { + super(props); + this.state = { + pagination: { + page: 1, + perPage: 6, + perPageOptions: [6, 10, 15, 25, 50] + } + }; + bindMethods(this, ['onPageInput', 'onPerPageSelect']); + } + onPageInput(e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = e.target.value; + this.setState({ pagination: newPaginationState }); + } + onPerPageSelect(eventKey, e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.perPage = eventKey; + this.setState({ pagination: newPaginationState }); + } + render() { + return ( + + ); + } +} + +stories.addWithInfo( + 'Pagination row', + `Pagination row for card, list, and table views`, + () => +); diff --git a/src/components/Pagination/Paginator.js b/src/components/Pagination/Paginator.js new file mode 100644 index 00000000000..5c1186b64e5 --- /dev/null +++ b/src/components/Pagination/Paginator.js @@ -0,0 +1,169 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Form, FormControl, FormGroup, ControlLabel } from '../Form'; +import { DropdownButton } from '../Button'; +import { ListGroup, ListGroupItem } from '../ListGroup'; +import { MenuItem } from '../MenuItem'; +import { Icon } from '../Icon'; + +/** + * Paginator component for Patternfly React + */ +const Paginator = ({ + className, + pagination, + amountOfPages, + itemCount, + itemsStart, + itemsEnd, + onPerPageSelect, + onFirstPage, + onPreviousPage, + onPageInput, + onNextPage, + onLastPage +}) => { + const { page, perPage, perPageOptions } = pagination; + + return ( +
+ + + {perPageOptions && + perPageOptions.length && + perPageOptions.map((option, i) => { + return ( + + {option} + + ); + })} + + per page + + + + + {itemsStart}-{itemsEnd} + +  of  + {itemCount} + + + + { + e.preventDefault(); + onFirstPage(e); + }} + > + + + + + { + e.preventDefault(); + onPreviousPage(e); + }} + > + + + + + Current Page + + +  of  + {amountOfPages} + + + + { + e.preventDefault(); + onNextPage(e); + }} + > + + + + + { + e.preventDefault(); + onLastPage(e); + }} + > + + + + + +
+ ); +}; +Paginator.propTypes = { + /** Additional css classes */ + className: PropTypes.string, + /** user pagination settings */ + pagination: PropTypes.object, + /** calculated amount of pages */ + amountOfPages: PropTypes.number, + /** calculated number of rows */ + itemCount: PropTypes.number, + /** calculated items start */ + itemsStart: PropTypes.number, + /** calculated items end */ + itemsEnd: PropTypes.number, + /** per page selection callback */ + onPerPageSelect: PropTypes.func, + /** first page callback */ + onFirstPage: PropTypes.func, + /** previous page selection callback */ + onPreviousPage: PropTypes.func, + /** user page input callback */ + onPageInput: PropTypes.func, + /** next page callback */ + onNextPage: PropTypes.func, + /** last page callback */ + onLastPage: PropTypes.func +}; +export default Paginator; diff --git a/src/components/Pagination/index.js b/src/components/Pagination/index.js new file mode 100644 index 00000000000..76469176b99 --- /dev/null +++ b/src/components/Pagination/index.js @@ -0,0 +1,2 @@ +export { default as Paginator } from './Paginator'; +export { default as paginate } from './paginate'; diff --git a/src/components/Pagination/paginate.js b/src/components/Pagination/paginate.js new file mode 100644 index 00000000000..212f96e29c6 --- /dev/null +++ b/src/components/Pagination/paginate.js @@ -0,0 +1,20 @@ +/** + * Client Side Pagination helper which returns amountOfPages, itemCount, + * itemsStart, itemsEnd, and paginated rows + */ +export default function paginate({ page, perPage, perPageOptions }) { + return (rows = []) => { + // adapt to zero indexed logic + const p = page - 1 || 0; + const amountOfPages = Math.ceil(rows.length / perPage); + const startPage = p < amountOfPages ? p : 0; + const endOfPage = startPage * perPage + perPage; + return { + amountOfPages: amountOfPages, + itemCount: rows.length, + itemsStart: startPage * perPage + 1, + itemsEnd: endOfPage > rows.length ? rows.length : endOfPage, + rows: rows.slice(startPage * perPage, endOfPage) + }; + }; +} diff --git a/src/components/Table/Stories/BootstrapTableStory.js b/src/components/Table/Stories/BootstrapTableStory.js new file mode 100644 index 00000000000..3d0b74ce19a --- /dev/null +++ b/src/components/Table/Stories/BootstrapTableStory.js @@ -0,0 +1,77 @@ +import React from 'react'; +import { Table } from '../index'; +import { mockRows } from '../__mocks__/mockRows'; +import { mockBootstrapColumns } from '../__mocks__/mockColumns'; + +/** + * Bootstrap Table stories + */ + +const bootstrapTableAddWithInfo = stories => { + stories.addWithInfo('Bootstrap Table Styles', '', () => ( +
+

Boostrap examples

+

Basic example

+ + Optional table caption. + + + + +

Striped Rows

+ + + + + +

Bordered Table

+ + + + + +

Hover Rows

+ + + + + +

Condensed table

+ + + + + +

Contextual Classes

+ + + { + switch (rowIndex) { + case 0: + return { className: 'active' }; + case 2: + return { className: 'success' }; + case 4: + return { className: 'warning' }; + case 6: + return { className: 'danger' }; + } + }} + /> + + +

Responsive Tables

+
+ + + + +
+
+ )); +}; + +export default bootstrapTableAddWithInfo; diff --git a/src/components/Table/Stories/PatternflyTableStory.js b/src/components/Table/Stories/PatternflyTableStory.js new file mode 100644 index 00000000000..2fb27aef24d --- /dev/null +++ b/src/components/Table/Stories/PatternflyTableStory.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { Table } from '../index'; +import { mockRows } from '../__mocks__/mockRows'; +import { mockPatternflyColumns } from '../__mocks__/mockColumns'; + +/** + * Patternfly Table stories + */ + +const patternflyTableAddWithInfo = stories => { + stories.addWithInfo('PatternFly Table Styles', '', () => ( +
+

+ PatternFly recommendation: Bootstrap striped, bordered, hover, and + responsive +

+ + + + +
+ )); +}; + +export default patternflyTableAddWithInfo; diff --git a/src/components/Table/Stories/index.js b/src/components/Table/Stories/index.js new file mode 100644 index 00000000000..f2041a98bba --- /dev/null +++ b/src/components/Table/Stories/index.js @@ -0,0 +1,2 @@ +export { default as bootstrapTableAddWithInfo } from './BootstrapTableStory'; +export { default as patternflyTableAddWithInfo } from './PatternflyTableStory'; diff --git a/src/components/Table/Table.js b/src/components/Table/Table.js new file mode 100644 index 00000000000..f6f810e67f4 --- /dev/null +++ b/src/components/Table/Table.js @@ -0,0 +1 @@ +export * as Table from 'reactabular-table'; diff --git a/src/components/Table/Table.stories.js b/src/components/Table/Table.stories.js new file mode 100644 index 00000000000..5bf5795be23 --- /dev/null +++ b/src/components/Table/Table.stories.js @@ -0,0 +1,25 @@ +import { storiesOf } from '@storybook/react'; +import { withKnobs } from '@storybook/addon-knobs'; +import { + bootstrapTableAddWithInfo, + patternflyTableAddWithInfo +} from './Stories'; +import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../storybook/constants'; + +const stories = storiesOf('Table', module); + +stories.addDecorator(withKnobs); +stories.addDecorator( + defaultTemplate({ + title: 'Table', + documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_WIDGETS + '#tables' + }) +); + +/** + * Table stories + */ + +bootstrapTableAddWithInfo(stories); +patternflyTableAddWithInfo(stories); diff --git a/src/components/Table/Table.test.js b/src/components/Table/Table.test.js new file mode 100644 index 00000000000..c85b6657433 --- /dev/null +++ b/src/components/Table/Table.test.js @@ -0,0 +1,123 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import { Table } from './index'; + +import { mockRows } from './__mocks__/mockRows'; +import { + mockBootstrapColumns, + mockPatternflyColumns +} from './__mocks__/mockColumns'; + +test('Bootstrap basic table renders properly', () => { + const component = renderer.create( + + Optional table caption. + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Bootstrap striped table renders properly', () => { + const component = renderer.create( + + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Bootstrap bordered table renders properly', () => { + const component = renderer.create( + + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Bootstrap hover table renders properly', () => { + const component = renderer.create( + + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Bootstrap condensed table renders properly', () => { + const component = renderer.create( + + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Bootstrap contextual classes table renders properly', () => { + const component = renderer.create( + + + { + switch (rowIndex) { + case 0: + return { className: 'active' }; + case 2: + return { className: 'success' }; + case 4: + return { className: 'warning' }; + case 6: + return { className: 'danger' }; + } + }} + /> + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Bootstrap responsive table renders properly', () => { + const component = renderer.create( +
+ + + + +
+ ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Patternfly table renders properly', () => { + const component = renderer.create( + + + + + ); + + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/Table/TableActions.js b/src/components/Table/TableActions.js new file mode 100644 index 00000000000..ab963cd69c1 --- /dev/null +++ b/src/components/Table/TableActions.js @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; + +/** + * TableActions component for Patternfly React + */ +const TableActions = ({ children, className, ...props }) => { + const classes = cx('table-view-pf-actions', className); + return ( + + {children} + + ); +}; +TableActions.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string +}; +export default TableActions; diff --git a/src/components/Table/TableButton.js b/src/components/Table/TableButton.js new file mode 100644 index 00000000000..dfaedd45b7b --- /dev/null +++ b/src/components/Table/TableButton.js @@ -0,0 +1,25 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { Button } from '../Button'; + +/** + * TableButton component for Patternfly React + */ +const TableButton = ({ children, className, onClick, ...props }) => { + const classes = cx('table-view-pf-btn', className); + return ( +
+ +
+ ); +}; +TableButton.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string, + /** onClick callback for button */ + onClick: PropTypes.func +}; +export default TableButton; diff --git a/src/components/Table/TableCell.js b/src/components/Table/TableCell.js new file mode 100644 index 00000000000..25f5b059083 --- /dev/null +++ b/src/components/Table/TableCell.js @@ -0,0 +1,30 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { TABLE_ALIGN, TABLE_ALIGNMENT_TYPES } from './constants'; +/** + * TableCell component for Patternfly React + */ +const TableCell = ({ children, className, align, ...props }) => { + const classes = cx( + { + 'text-right': align === TABLE_ALIGN.RIGHT, + 'text-center': align === TABLE_ALIGN.CENTER + }, + className + ); + return ( + + {children} + + ); +}; +TableCell.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string, + /** Cell alignment */ + align: PropTypes.oneOf(TABLE_ALIGNMENT_TYPES) +}; +export default TableCell; diff --git a/src/components/Table/TableHeading.js b/src/components/Table/TableHeading.js new file mode 100644 index 00000000000..332e815b2f5 --- /dev/null +++ b/src/components/Table/TableHeading.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { + TABLE_ALIGN, + TABLE_ALIGNMENT_TYPES, + TABLE_SORT_DIRECTION, + TABLE_SORT_DIRECTIONS +} from './constants'; + +/** + * TableHeading component for Patternfly React + */ +const TableHeading = ({ + children, + className, + align, + sort, + sortDirection, + ...props +}) => { + const sortingClass = cx({ + sorting_asc: sortDirection === TABLE_SORT_DIRECTION.ASC, + sorting_desc: sortDirection === TABLE_SORT_DIRECTION.DESC + }); + const classes = cx( + { + 'text-right': align === TABLE_ALIGN.RIGHT, + 'text-center': align === TABLE_ALIGN.CENTER + }, + sort ? sortingClass || 'sorting' : '', + className + ); + return ( + + {children} + + ); +}; +TableHeading.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string, + /** Heading alignment */ + align: PropTypes.oneOf(TABLE_ALIGNMENT_TYPES), + /** sortable heading */ + sort: PropTypes.bool, + /** sort direction */ + sortDirection: PropTypes.oneOf(TABLE_SORT_DIRECTIONS) +}; +export default TableHeading; diff --git a/src/components/Table/TablePfProvider.js b/src/components/Table/TablePfProvider.js new file mode 100644 index 00000000000..ab2a79a9d35 --- /dev/null +++ b/src/components/Table/TablePfProvider.js @@ -0,0 +1,69 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { Table } from './index'; +/** + * TablePfProvider component for Patternfly React + */ +const TablePfProvider = ({ + children, + className, + dataTable, + striped, + bordered, + hover, + condensed, + components, + ...props +}) => { + const headerCell = cellProps => { + return cellProps.children; + }; + const tableCell = cellProps => { + return cellProps.children; + }; + const classes = cx( + { + table: true, + dataTable: dataTable, + 'table-striped': striped, + 'table-bordered': bordered, + 'table-hover': hover, + 'table-condensed': condensed + }, + className + ); + return ( + + {children} + + ); +}; +TablePfProvider.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string, + /** apply dataTable class */ + dataTable: PropTypes.bool, + /** apply Striped class */ + striped: PropTypes.bool, + /** apply Bordered class */ + bordered: PropTypes.bool, + /** apply Hover class */ + hover: PropTypes.bool, + /** apply Condensed class */ + condensed: PropTypes.bool, + /** reactabular components override */ + components: PropTypes.object +}; +export default TablePfProvider; diff --git a/src/components/Table/__mocks__/mockColumns.js b/src/components/Table/__mocks__/mockColumns.js new file mode 100644 index 00000000000..72f9d457000 --- /dev/null +++ b/src/components/Table/__mocks__/mockColumns.js @@ -0,0 +1,140 @@ +import React from 'react'; +import { Table } from '../index'; + +const headerFormat = value => { + return {value}; +}; +const cellFormat = value => { + return {value}; +}; + +export const mockBootstrapColumns = [ + { + header: { + label: '#', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'id' + }, + { + header: { + label: 'First Name', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'first_name' + }, + { + header: { + label: 'Last Name', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'last_name' + }, + { + header: { + label: 'Username', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'username' + } +]; + +const headerFormatRightAlign = value => { + return {value}; +}; +const cellFormatRightAlign = value => { + return {value}; +}; + +export const mockPatternflyColumns = [ + { + header: { + label: 'First Name', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'first_name' + }, + { + header: { + label: 'Last Name', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'last_name' + }, + { + header: { + label: 'Username', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'username' + }, + { + header: { + label: 'Commits', + formatters: [headerFormatRightAlign] + }, + cell: { + formatters: [cellFormatRightAlign] + }, + property: 'commits' + }, + { + header: { + label: 'Additions', + formatters: [headerFormatRightAlign] + }, + cell: { + formatters: [cellFormatRightAlign] + }, + property: 'additions' + }, + { + header: { + label: 'Location', + formatters: [ + value => { + return {value}; + } + ] + }, + cell: { + formatters: [ + value => { + return {value}; + } + ] + }, + property: 'location' + }, + { + header: { + label: 'Gender', + formatters: [headerFormat] + }, + cell: { + formatters: [cellFormat] + }, + property: 'gender' + } +]; diff --git a/src/components/Table/__mocks__/mockRows.js b/src/components/Table/__mocks__/mockRows.js new file mode 100644 index 00000000000..a4f18deffa1 --- /dev/null +++ b/src/components/Table/__mocks__/mockRows.js @@ -0,0 +1,72 @@ +export const mockRows = [ + { + id: 0, + first_name: 'Dan', + last_name: 'Abramov', + username: 'gaearon', + commits: 711, + additions: 272635, + location: 'London, UK', + gender: 'male' + }, + { + id: 1, + first_name: 'Sebastian', + last_name: 'Markbåge', + username: 'sebmarkbage', + commits: 476, + additions: 203610, + location: 'San Francisco, CA', + gender: 'male' + }, + { + id: 2, + first_name: 'Sophie', + last_name: 'Alpert', + username: 'sophiebits', + commits: 828, + additions: 114467, + location: 'California', + gender: 'female' + }, + { + id: 3, + first_name: 'Paul', + last_name: 'O’Shannessy', + username: 'zpao', + commits: 820, + additions: 87324, + location: 'Seattle, WA', + gender: 'male' + }, + { + id: 4, + first_name: 'Pete', + last_name: 'Hunt', + username: 'petehunt', + commits: 205, + additions: 86685, + location: 'San Francisco, CA', + gender: 'male' + }, + { + id: 5, + first_name: 'Andrew', + last_name: 'Clark', + username: 'acdlite', + commits: 320, + additions: 74162, + location: 'Redwood City, CA', + gender: 'male' + }, + { + id: 6, + first_name: 'Nathan', + last_name: 'Hunzaker', + username: 'nhunzaker', + commits: 77, + additions: 34504, + location: 'Durham, NC', + gender: 'male' + } +]; diff --git a/src/components/Table/__snapshots__/Table.test.js.snap b/src/components/Table/__snapshots__/Table.test.js.snap new file mode 100644 index 00000000000..ee79a0c264d --- /dev/null +++ b/src/components/Table/__snapshots__/Table.test.js.snap @@ -0,0 +1,1104 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Bootstrap basic table renders properly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Optional table caption. +
+ # + + First Name + + Last Name + + Username +
+ 0 + + Dan + + Abramov + + gaearon +
+ 1 + + Sebastian + + Markbåge + + sebmarkbage +
+ 2 + + Sophie + + Alpert + + sophiebits +
+`; + +exports[`Bootstrap bordered table renders properly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + First Name + + Last Name + + Username +
+ 0 + + Dan + + Abramov + + gaearon +
+ 1 + + Sebastian + + Markbåge + + sebmarkbage +
+ 2 + + Sophie + + Alpert + + sophiebits +
+`; + +exports[`Bootstrap condensed table renders properly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + First Name + + Last Name + + Username +
+ 0 + + Dan + + Abramov + + gaearon +
+ 1 + + Sebastian + + Markbåge + + sebmarkbage +
+ 2 + + Sophie + + Alpert + + sophiebits +
+`; + +exports[`Bootstrap contextual classes table renders properly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + First Name + + Last Name + + Username +
+ 0 + + Dan + + Abramov + + gaearon +
+ 1 + + Sebastian + + Markbåge + + sebmarkbage +
+ 2 + + Sophie + + Alpert + + sophiebits +
+ 3 + + Paul + + O’Shannessy + + zpao +
+ 4 + + Pete + + Hunt + + petehunt +
+ 5 + + Andrew + + Clark + + acdlite +
+ 6 + + Nathan + + Hunzaker + + nhunzaker +
+`; + +exports[`Bootstrap hover table renders properly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + First Name + + Last Name + + Username +
+ 0 + + Dan + + Abramov + + gaearon +
+ 1 + + Sebastian + + Markbåge + + sebmarkbage +
+ 2 + + Sophie + + Alpert + + sophiebits +
+`; + +exports[`Bootstrap responsive table renders properly 1`] = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + First Name + + Last Name + + Username +
+ 0 + + Dan + + Abramov + + gaearon +
+ 1 + + Sebastian + + Markbåge + + sebmarkbage +
+ 2 + + Sophie + + Alpert + + sophiebits +
+
+`; + +exports[`Bootstrap striped table renders properly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + First Name + + Last Name + + Username +
+ 0 + + Dan + + Abramov + + gaearon +
+ 1 + + Sebastian + + Markbåge + + sebmarkbage +
+ 2 + + Sophie + + Alpert + + sophiebits +
+`; + +exports[`Patternfly table renders properly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ First Name + + Last Name + + Username + + Commits + + Additions + + Location + + Gender +
+ Dan + + Abramov + + gaearon + + 711 + + 272635 + + London, UK + + male +
+ Sebastian + + Markbåge + + sebmarkbage + + 476 + + 203610 + + San Francisco, CA + + male +
+ Sophie + + Alpert + + sophiebits + + 828 + + 114467 + + California + + female +
+ Paul + + O’Shannessy + + zpao + + 820 + + 87324 + + Seattle, WA + + male +
+ Pete + + Hunt + + petehunt + + 205 + + 86685 + + San Francisco, CA + + male +
+ Andrew + + Clark + + acdlite + + 320 + + 74162 + + Redwood City, CA + + male +
+ Nathan + + Hunzaker + + nhunzaker + + 77 + + 34504 + + Durham, NC + + male +
+`; diff --git a/src/components/Table/constants.js b/src/components/Table/constants.js new file mode 100644 index 00000000000..8b6dfbdc2b0 --- /dev/null +++ b/src/components/Table/constants.js @@ -0,0 +1,16 @@ +export const TABLE_ALIGN = { + CENTER: 'center', + RIGHT: 'right' +}; + +export const TABLE_ALIGNMENT_TYPES = [TABLE_ALIGN.CENTER, TABLE_ALIGN.RIGHT]; + +export const TABLE_SORT_DIRECTION = { + ASC: 'asc', + DESC: 'desc' +}; + +export const TABLE_SORT_DIRECTIONS = [ + TABLE_SORT_DIRECTION.ASC, + TABLE_SORT_DIRECTION.DESC +]; diff --git a/src/components/Table/index.js b/src/components/Table/index.js new file mode 100644 index 00000000000..affa6b44a76 --- /dev/null +++ b/src/components/Table/index.js @@ -0,0 +1,14 @@ +import { Table } from './Table'; +import TableActions from './TableActions'; +import TableButton from './TableButton'; +import TableCell from './TableCell'; +import TableHeading from './TableHeading'; +import TablePfProvider from './TablePfProvider'; + +Table.Actions = TableActions; +Table.Button = TableButton; +Table.Cell = TableCell; +Table.Heading = TableHeading; +Table.PfProvider = TablePfProvider; + +export { Table, TableCell, TableHeading, TablePfProvider }; diff --git a/src/index.js b/src/index.js index 7bba9ebdff9..25ccbe6a501 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ export * from './components/AboutModal'; export * from './components/Badge'; export * from './components/Breadcrumb'; export * from './components/Button'; +export * from './components/DataTable'; export * from './components/Dropdown'; export * from './components/DropdownKebab'; export * from './components/EmptyState'; @@ -18,11 +19,13 @@ export * from './components/MenuItem'; export * from './components/Modal'; export * from './components/Nav'; export * from './components/OverlayTrigger'; +export * from './components/Pagination'; export * from './components/Popover'; export * from './components/Sort'; export * from './components/Spinner'; export * from './components/Switch'; export * from './components/Tabs'; +export * from './components/Table'; export * from './components/ToastNotification'; export * from './components/Toolbar'; export * from './components/Tooltip'; diff --git a/storybook/.babelrc b/storybook/.babelrc index 4646081cfc2..331759834e9 100644 --- a/storybook/.babelrc +++ b/storybook/.babelrc @@ -2,6 +2,7 @@ "presets": ["env", "react"], "plugins": [ "transform-class-properties", + "transform-export-extensions", "transform-object-rest-spread", "transform-object-assign" ] From 0b5e201bf458f81f1e33b875fe72d5d5499e15d6 Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Wed, 10 Jan 2018 16:15:03 -0500 Subject: [PATCH 02/15] added row selection and row selection components --- .../DataTable/Stories/PaginationTableStory.js | 6 +- .../__mocks__/mockPaginationTable.js | 250 +++++++++++++++--- .../DataTable/__mocks__/mockSortableTable.js | 25 +- src/components/Pagination/paginate.js | 2 +- src/components/Table/TablePfProvider.js | 17 +- src/components/Table/TableSelectionCell.js | 22 ++ src/components/Table/TableSelectionHeading.js | 22 ++ src/components/Table/index.js | 13 +- 8 files changed, 303 insertions(+), 54 deletions(-) create mode 100644 src/components/Table/TableSelectionCell.js create mode 100644 src/components/Table/TableSelectionHeading.js diff --git a/src/components/DataTable/Stories/PaginationTableStory.js b/src/components/DataTable/Stories/PaginationTableStory.js index 02317dbc8b2..9d48cd6225d 100644 --- a/src/components/DataTable/Stories/PaginationTableStory.js +++ b/src/components/DataTable/Stories/PaginationTableStory.js @@ -1,5 +1,6 @@ import React from 'react'; import { withInfo } from '@storybook/addon-info'; +import { decorateAction } from '@storybook/addon-actions'; import { MockPaginationTable } from '../__mocks__/mockPaginationTable'; /** @@ -13,7 +14,10 @@ const paginationTableAddWithInfo = stories => { source: false, propTablesExclude: [MockPaginationTable], text:
- })(() => ) + })(() => { + const logAction = decorateAction([args => args]); + return ; + }) ); }; diff --git a/src/components/DataTable/__mocks__/mockPaginationTable.js b/src/components/DataTable/__mocks__/mockPaginationTable.js index 8ea6426fdf3..589a8d3ab24 100644 --- a/src/components/DataTable/__mocks__/mockPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockPaginationTable.js @@ -1,10 +1,13 @@ import React from 'react'; +import PropTypes from 'prop-types'; import orderBy from 'lodash.orderby'; +import cx from 'classnames'; import * as sort from 'sortabular'; import * as resolve from 'table-resolver'; import { bindMethods } from '../../../common/helpers'; import { defaultSortingOrder } from '../index'; import { Table } from '../../Table'; +import { ControlLabel } from '../../Form'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; @@ -48,11 +51,78 @@ export class MockPaginationTable extends React.Component { strategy: sort.strategies.byProperty }); + // sortable header cell custom formatter + const sortableHeaderCell = ({ column, cellProps }) => { + const { sortingColumns } = this.state; + const sortDirection = + sortingColumns[column.property] && + sortingColumns[column.property].direction; + return ( + + {column.header.label} + + ); + }; + + // selection header cell custom formatter + const selectionHeaderCell = ({ column, cellProps }) => { + const currentRows = this.currentRows().rows; + const unselectedRows = currentRows.filter(r => !r.selected).length > 0; + return ( + + + Select all rows + + + + ); + }; + + // action header cell custom formatter + const actionHeaderCell = ({ column, cellProps }) => { + return ( + + {column.header.label} + + ); + }; + // cell formatter const cellFormat = value => { return {value}; }; + // selection cell formatter + const selectionCellFormat = (value, { rowData, rowIndex }) => { + const id = `select${rowIndex}`; + const label = `Select row ${rowIndex}`; + return ( + + + {label} + + { + this.onSelectRow(e, rowData); + }} + /> + + ); + }; + this.state = { // Sort the first column in an ascending way by default. sortingColumns: { @@ -61,22 +131,43 @@ export class MockPaginationTable extends React.Component { position: 0 } }, + + // column definitions columns: [ + { + property: 'select', + header: { + label: 'Select all rows', + props: { + index: 0, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [selectionHeaderCell] + }, + cell: { + props: { + index: 0 + }, + formatters: [selectionCellFormat] + } + }, { property: 'name', header: { label: 'Name', props: { - index: 0, + index: 1, rowSpan: 1, colSpan: 1 }, transforms: [sortable], - formatters: [sortingFormat] + formatters: [sortingFormat], + customFormatters: [sortableHeaderCell] }, cell: { props: { - index: 0 + index: 1 }, formatters: [cellFormat] } @@ -86,16 +177,17 @@ export class MockPaginationTable extends React.Component { header: { label: 'Height', props: { - index: 1, + index: 2, rowSpan: 1, colSpan: 1 }, transforms: [sortable], - formatters: [sortingFormat] + formatters: [sortingFormat], + customFormatters: [sortableHeaderCell] }, cell: { props: { - index: 1 + index: 2 }, formatters: [cellFormat] } @@ -105,16 +197,17 @@ export class MockPaginationTable extends React.Component { header: { label: 'Eye Color', props: { - index: 2, + index: 3, rowSpan: 1, colSpan: 1 }, transforms: [sortable], - formatters: [sortingFormat] + formatters: [sortingFormat], + customFormatters: [sortableHeaderCell] }, cell: { props: { - index: 2 + index: 3 }, formatters: [cellFormat] } @@ -124,16 +217,17 @@ export class MockPaginationTable extends React.Component { header: { label: 'Gender', props: { - index: 3, + index: 4, rowSpan: 1, colSpan: 1 }, transforms: [sortable], - formatters: [sortingFormat] + formatters: [sortingFormat], + customFormatters: [sortableHeaderCell] }, cell: { props: { - index: 3 + index: 4 }, formatters: [cellFormat] } @@ -143,16 +237,17 @@ export class MockPaginationTable extends React.Component { header: { label: 'Birth Year', props: { - index: 4, + index: 5, rowSpan: 1, colSpan: 1 }, transforms: [sortable], - formatters: [sortingFormat] + formatters: [sortingFormat], + customFormatters: [sortableHeaderCell] }, cell: { props: { - index: 4 + index: 5 }, formatters: [cellFormat] } @@ -162,14 +257,15 @@ export class MockPaginationTable extends React.Component { header: { label: 'Actions', props: { - index: 5, + index: 6, rowSpan: 1, colSpan: 2 - } + }, + customFormatters: [actionHeaderCell] }, cell: { props: { - index: 5 + index: 6 }, formatters: [ (value, { rowData }) => { @@ -196,7 +292,12 @@ export class MockPaginationTable extends React.Component { } } ], + + // rows and row selection state rows: mockRows, + selectedRows: [], + + // pagination default states pagination: { page: 1, perPage: 6, @@ -210,7 +311,10 @@ export class MockPaginationTable extends React.Component { 'onFirstPage', 'onPreviousPage', 'onNextPage', - 'onLastPage' + 'onLastPage', + 'onRow', + 'onSelectAllRows', + 'onSelectRow' ]); } onPageInput(e) { @@ -255,10 +359,71 @@ export class MockPaginationTable extends React.Component { this.setState({ pagination: newPaginationState }); } } - render() { - const { rows, sortingColumns, columns, pagination } = this.state; + onSelectRow(event, row) { + const { onRowsLogger } = this.props; + const { rows, selectedRows } = this.state; + const selectedRowIndex = rows.findIndex(r => r.id === row.id); + if (selectedRowIndex > -1) { + let updatedSelectedRows, updatedRow; + if (row.selected) { + updatedSelectedRows = selectedRows.filter(r => !(r === row.id)); + updatedRow = this.deselectRow(row); + } else { + selectedRows.push(row.id); + updatedSelectedRows = selectedRows; + updatedRow = this.selectRow(row); + } + rows[selectedRowIndex] = updatedRow; + this.setState({ + rows: rows, + selectedRows: updatedSelectedRows + }); + onRowsLogger(rows.filter(r => r.selected)); + } + } + onSelectAllRows(event) { + const { onRowsLogger } = this.props; + const { rows, selectedRows } = this.state; + const checked = event.target.checked; + const currentRows = this.currentRows().rows; - const sortedPaginatedRows = compose( + if (checked) { + const updatedSelections = [ + ...new Set([...currentRows.map(r => r.id), ...selectedRows]) + ]; + const updatedRows = rows.map(r => { + return updatedSelections.indexOf(r.id) > -1 ? this.selectRow(r) : r; + }); + this.setState({ + // important: you must update `rows` to force a re-render and trigger `onRow` hook + rows: updatedRows, + selectedRows: updatedSelections + }); + onRowsLogger(updatedRows.filter(r => r.selected)); + } else { + const ids = currentRows.map(r => r.id); + const updatedSelections = selectedRows.filter(r => { + return !(ids.indexOf(r) > -1); + }); + const updatedRows = rows.map(r => { + return updatedSelections.indexOf(r.id) > -1 ? r : this.deselectRow(r); + }); + this.setState({ + rows: updatedRows, + selectedRows: updatedSelections + }); + onRowsLogger(updatedRows.filter(r => r.selected)); + } + } + selectRow(row) { + return Object.assign({}, row, { selected: true }); + } + deselectRow(row) { + return Object.assign({}, row, { selected: false }); + } + currentRows() { + const { rows, sortingColumns, columns, pagination } = this.state; + return compose( paginate(pagination), sort.sorter({ columns: columns, @@ -267,22 +432,29 @@ export class MockPaginationTable extends React.Component { strategy: sort.strategies.byProperty }) )(rows); + } + onRow(row, { rowIndex }) { + const selected = this.state.selectedRows.indexOf(row.id) > -1; + return { + className: cx({ selected: selected }), + role: 'row' + }; + } + render() { + const { columns, pagination } = this.state; + const sortedPaginatedRows = this.currentRows(); const customHeaderCell = cellProps => { const { index } = cellProps; const column = columns[index]; - const sortDirection = - sortingColumns[column.property] && - sortingColumns[column.property].direction; - return ( - - {column.header.label} - - ); - }; + const customFormatters = column.header.customFormatters; - const customTableCell = cellProps => { - return cellProps.children; + return customFormatters.reduce( + (params, formatter) => ({ + value: formatter(params) + }), + { column, cellProps } + ).value; }; return ( @@ -294,12 +466,15 @@ export class MockPaginationTable extends React.Component { dataTable columns={columns} components={{ - header: { cell: customHeaderCell }, - body: { cell: customTableCell } + header: { cell: customHeaderCell } }} > - + + {column.header.label} ); }; - const customTableCell = cellProps => { - // just return the cell formatters `td` content and remove the default cell wrapper - return cellProps.children; - }; - return (
- + { + return { + role: 'row' + }; + }} + />
); diff --git a/src/components/Pagination/paginate.js b/src/components/Pagination/paginate.js index 212f96e29c6..6ba10f41cfd 100644 --- a/src/components/Pagination/paginate.js +++ b/src/components/Pagination/paginate.js @@ -2,7 +2,7 @@ * Client Side Pagination helper which returns amountOfPages, itemCount, * itemsStart, itemsEnd, and paginated rows */ -export default function paginate({ page, perPage, perPageOptions }) { +export default function paginate({ page, perPage }) { return (rows = []) => { // adapt to zero indexed logic const p = page - 1 || 0; diff --git a/src/components/Table/TablePfProvider.js b/src/components/Table/TablePfProvider.js index ab2a79a9d35..95ce6fd3e02 100644 --- a/src/components/Table/TablePfProvider.js +++ b/src/components/Table/TablePfProvider.js @@ -22,6 +22,10 @@ const TablePfProvider = ({ const tableCell = cellProps => { return cellProps.children; }; + let mergedComponents = Object.assign( + { header: { cell: headerCell }, body: { cell: tableCell } }, + components + ); const classes = cx( { table: true, @@ -33,16 +37,17 @@ const TablePfProvider = ({ }, className ); + let attributes = {}; + if (dataTable) { + attributes.role = 'grid'; + } + return ( {children} diff --git a/src/components/Table/TableSelectionCell.js b/src/components/Table/TableSelectionCell.js new file mode 100644 index 00000000000..5056fe49042 --- /dev/null +++ b/src/components/Table/TableSelectionCell.js @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; + +/** + * TableSelectionCell component for Patternfly React + */ +const TableSelectionCell = ({ children, className, ...props }) => { + const classes = cx('table-view-pf-select', className); + return ( + + {children} + + ); +}; +TableSelectionCell.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string +}; +export default TableSelectionCell; diff --git a/src/components/Table/TableSelectionHeading.js b/src/components/Table/TableSelectionHeading.js new file mode 100644 index 00000000000..f5e294175e2 --- /dev/null +++ b/src/components/Table/TableSelectionHeading.js @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; + +/** + * TableSelectionHeading component for Patternfly React + */ +const TableSelectionHeading = ({ children, className, ...props }) => { + const classes = cx('table-view-pf-select', className); + return ( + + {children} + + ); +}; +TableSelectionHeading.propTypes = { + /** Children nodes */ + children: PropTypes.node, + /** Additional css classes */ + className: PropTypes.string +}; +export default TableSelectionHeading; diff --git a/src/components/Table/index.js b/src/components/Table/index.js index affa6b44a76..5c3948addd4 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -4,11 +4,22 @@ import TableButton from './TableButton'; import TableCell from './TableCell'; import TableHeading from './TableHeading'; import TablePfProvider from './TablePfProvider'; +import TableSelectionCell from './TableSelectionCell'; +import TableSelectionHeading from './TableSelectionHeading'; Table.Actions = TableActions; Table.Button = TableButton; Table.Cell = TableCell; Table.Heading = TableHeading; Table.PfProvider = TablePfProvider; +Table.SelectionCell = TableSelectionCell; +Table.SelectionHeading = TableSelectionHeading; -export { Table, TableCell, TableHeading, TablePfProvider }; +export { + Table, + TableCell, + TableHeading, + TablePfProvider, + TableSelectionCell, + TableSelectionHeading +}; From 9fd7a3ccfc00295860811e151fcdb594bed2189e Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Thu, 11 Jan 2018 23:26:51 -0500 Subject: [PATCH 03/15] * adds pagination and datatable snapshot tests * exposes some common datatable formatters in constants * adds i18n support to paginator --- package-lock.json | 342 ++++++------------ sass/patternfly-react/_dropdown.scss | 3 - sass/patternfly-react/_pagination.scss | 22 +- sass/patternfly-react/_patternfly-react.scss | 1 - src/components/DataTable/DataTable.test.js | 162 +++++++++ .../__mocks__/mockPaginationTable.js | 86 ++--- .../DataTable/__mocks__/mockSortableTable.js | 45 +-- .../__snapshots__/DataTable.test.js.snap | 323 +++++++++++++++++ src/components/DataTable/constants.js | 25 +- src/components/DataTable/index.js | 6 +- src/components/Pagination/Paginate.test.js | 37 ++ src/components/Pagination/Paginator.js | 78 ++-- .../__snapshots__/Paginate.test.js.snap | 227 ++++++++++++ 13 files changed, 1001 insertions(+), 356 deletions(-) delete mode 100644 sass/patternfly-react/_dropdown.scss create mode 100644 src/components/DataTable/DataTable.test.js create mode 100644 src/components/DataTable/__snapshots__/DataTable.test.js.snap create mode 100644 src/components/Pagination/Paginate.test.js create mode 100644 src/components/Pagination/__snapshots__/Paginate.test.js.snap diff --git a/package-lock.json b/package-lock.json index 9189612a3fc..1d8722bef2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5636,15 +5636,13 @@ "dependencies": { "abbrev": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", + "bundled": true, "dev": true, "optional": true }, "ajv": { "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5654,21 +5652,18 @@ }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "bundled": true, "dev": true }, "aproba": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", + "bundled": true, "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5678,49 +5673,42 @@ }, "asn1": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "bundled": true, "dev": true, "optional": true }, "assert-plus": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "bundled": true, "dev": true, "optional": true }, "asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "bundled": true, "dev": true, "optional": true }, "aws-sign2": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "bundled": true, "dev": true, "optional": true }, "aws4": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "bundled": true, "dev": true, "optional": true }, "balanced-match": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "bundled": true, "dev": true }, "bcrypt-pbkdf": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5729,8 +5717,7 @@ }, "block-stream": { "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "bundled": true, "dev": true, "requires": { "inherits": "2.0.3" @@ -5738,8 +5725,7 @@ }, "boom": { "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "bundled": true, "dev": true, "requires": { "hoek": "2.16.3" @@ -5747,8 +5733,7 @@ }, "brace-expansion": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "bundled": true, "dev": true, "requires": { "balanced-match": "0.4.2", @@ -5757,34 +5742,29 @@ }, "buffer-shims": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "bundled": true, "dev": true }, "caseless": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "bundled": true, "dev": true, "optional": true }, "co": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "bundled": true, "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "bundled": true, "dev": true }, "combined-stream": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "bundled": true, "dev": true, "requires": { "delayed-stream": "1.0.0" @@ -5792,26 +5772,22 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "bundled": true, "dev": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "bundled": true, "dev": true }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "bundled": true, "dev": true }, "cryptiles": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "bundled": true, "dev": true, "requires": { "boom": "2.10.1" @@ -5819,8 +5795,7 @@ }, "dashdash": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5829,8 +5804,7 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "bundled": true, "dev": true, "optional": true } @@ -5838,8 +5812,7 @@ }, "debug": { "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5848,35 +5821,30 @@ }, "deep-extend": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "bundled": true, "dev": true, "optional": true }, "delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "bundled": true, "dev": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "bundled": true, "dev": true, "optional": true }, "detect-libc": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.2.tgz", - "integrity": "sha1-ca1dIEvxempsqPRQxhRUBm70YeE=", + "bundled": true, "dev": true, "optional": true }, "ecc-jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5885,28 +5853,24 @@ }, "extend": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "bundled": true, "dev": true, "optional": true }, "extsprintf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "bundled": true, "dev": true }, "forever-agent": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "bundled": true, "dev": true, "optional": true }, "form-data": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5917,14 +5881,12 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "bundled": true, "dev": true }, "fstream": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "bundled": true, "dev": true, "requires": { "graceful-fs": "4.1.11", @@ -5935,8 +5897,7 @@ }, "fstream-ignore": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5947,8 +5908,7 @@ }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5964,8 +5924,7 @@ }, "getpass": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -5974,8 +5933,7 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "bundled": true, "dev": true, "optional": true } @@ -5983,8 +5941,7 @@ }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "bundled": true, "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -5997,21 +5954,18 @@ }, "graceful-fs": { "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "bundled": true, "dev": true }, "har-schema": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "bundled": true, "dev": true, "optional": true }, "har-validator": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6021,15 +5975,13 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "bundled": true, "dev": true, "optional": true }, "hawk": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "bundled": true, "dev": true, "requires": { "boom": "2.10.1", @@ -6040,14 +5992,12 @@ }, "hoek": { "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "bundled": true, "dev": true }, "http-signature": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6058,8 +6008,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "bundled": true, "dev": true, "requires": { "once": "1.4.0", @@ -6068,21 +6017,18 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "bundled": true, "dev": true }, "ini": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "bundled": true, "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "bundled": true, "dev": true, "requires": { "number-is-nan": "1.0.1" @@ -6090,28 +6036,24 @@ }, "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "bundled": true, "dev": true, "optional": true }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "bundled": true, "dev": true }, "isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "bundled": true, "dev": true, "optional": true }, "jodid25519": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6120,22 +6062,19 @@ }, "jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "bundled": true, "dev": true, "optional": true }, "json-schema": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "bundled": true, "dev": true, "optional": true }, "json-stable-stringify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6144,22 +6083,19 @@ }, "json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "bundled": true, "dev": true, "optional": true }, "jsonify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "bundled": true, "dev": true, "optional": true }, "jsprim": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6171,8 +6107,7 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "bundled": true, "dev": true, "optional": true } @@ -6180,14 +6115,12 @@ }, "mime-db": { "version": "1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", + "bundled": true, "dev": true }, "mime-types": { "version": "2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "bundled": true, "dev": true, "requires": { "mime-db": "1.27.0" @@ -6195,8 +6128,7 @@ }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "bundled": true, "dev": true, "requires": { "brace-expansion": "1.1.7" @@ -6204,14 +6136,12 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "bundled": true, "dev": true }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "bundled": true, "dev": true, "requires": { "minimist": "0.0.8" @@ -6219,15 +6149,13 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "bundled": true, "dev": true, "optional": true }, "node-pre-gyp": { "version": "0.6.39", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", - "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6246,8 +6174,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6257,8 +6184,7 @@ }, "npmlog": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", - "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6270,28 +6196,24 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "bundled": true, "dev": true }, "oauth-sign": { "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "bundled": true, "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "bundled": true, "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "bundled": true, "dev": true, "requires": { "wrappy": "1.0.2" @@ -6299,22 +6221,19 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "bundled": true, "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "bundled": true, "dev": true, "optional": true }, "osenv": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6324,41 +6243,35 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "bundled": true, "dev": true }, "performance-now": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "bundled": true, "dev": true, "optional": true }, "process-nextick-args": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "bundled": true, "dev": true }, "punycode": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "bundled": true, "dev": true, "optional": true }, "qs": { "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "bundled": true, "dev": true, "optional": true }, "rc": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6370,8 +6283,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "bundled": true, "dev": true, "optional": true } @@ -6379,8 +6291,7 @@ }, "readable-stream": { "version": "2.2.9", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", + "bundled": true, "dev": true, "requires": { "buffer-shims": "1.0.0", @@ -6394,8 +6305,7 @@ }, "request": { "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6425,8 +6335,7 @@ }, "rimraf": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "bundled": true, "dev": true, "requires": { "glob": "7.1.2" @@ -6434,35 +6343,30 @@ }, "safe-buffer": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", + "bundled": true, "dev": true }, "semver": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "bundled": true, "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "bundled": true, "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "bundled": true, "dev": true, "optional": true }, "sntp": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "bundled": true, "dev": true, "requires": { "hoek": "2.16.3" @@ -6470,8 +6374,7 @@ }, "sshpk": { "version": "1.13.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", - "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6488,8 +6391,7 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "bundled": true, "dev": true, "optional": true } @@ -6497,8 +6399,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, "dev": true, "requires": { "code-point-at": "1.1.0", @@ -6508,8 +6409,7 @@ }, "string_decoder": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", - "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", + "bundled": true, "dev": true, "requires": { "safe-buffer": "5.0.1" @@ -6517,15 +6417,13 @@ }, "stringstream": { "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "bundled": true, "dev": true, "optional": true }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "bundled": true, "dev": true, "requires": { "ansi-regex": "2.1.1" @@ -6533,15 +6431,13 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "bundled": true, "dev": true, "optional": true }, "tar": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "bundled": true, "dev": true, "requires": { "block-stream": "0.0.9", @@ -6551,8 +6447,7 @@ }, "tar-pack": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6568,8 +6463,7 @@ }, "tough-cookie": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6578,8 +6472,7 @@ }, "tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6588,35 +6481,30 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "bundled": true, "dev": true, "optional": true }, "uid-number": { "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "bundled": true, "dev": true, "optional": true }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "bundled": true, "dev": true }, "uuid": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", + "bundled": true, "dev": true, "optional": true }, "verror": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6625,8 +6513,7 @@ }, "wide-align": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6635,8 +6522,7 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "bundled": true, "dev": true } } diff --git a/sass/patternfly-react/_dropdown.scss b/sass/patternfly-react/_dropdown.scss deleted file mode 100644 index d9a9b302cdc..00000000000 --- a/sass/patternfly-react/_dropdown.scss +++ /dev/null @@ -1,3 +0,0 @@ -.dropdown-menu { - min-width: auto; -} diff --git a/sass/patternfly-react/_pagination.scss b/sass/patternfly-react/_pagination.scss index e3d2b7bddf5..e88b44dd1c6 100644 --- a/sass/patternfly-react/_pagination.scss +++ b/sass/patternfly-react/_pagination.scss @@ -1,8 +1,16 @@ -.content-view-pf-pagination .form-group .btn-group { - display: flex; - margin-left: 0; - margin-right: 5px; - margin-bottom: 0; - width: auto; - float: none; +.content-view-pf-pagination { + .form-group { + .btn-group { + display: flex; + float: none; + margin-left: 0; + margin-right: 5px; + margin-bottom: 0; + padding-right: 13px; + width: auto; + } + .dropdown-menu { + min-width: auto; + } + } } diff --git a/sass/patternfly-react/_patternfly-react.scss b/sass/patternfly-react/_patternfly-react.scss index e4b7cb2d309..0c2c7601b67 100644 --- a/sass/patternfly-react/_patternfly-react.scss +++ b/sass/patternfly-react/_patternfly-react.scss @@ -1,5 +1,4 @@ /** Patternfly React Partials */ -@import '_dropdown'; @import '_pagination'; diff --git a/src/components/DataTable/DataTable.test.js b/src/components/DataTable/DataTable.test.js new file mode 100644 index 00000000000..004ff02e510 --- /dev/null +++ b/src/components/DataTable/DataTable.test.js @@ -0,0 +1,162 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import renderer from 'react-test-renderer'; +import * as resolve from 'table-resolver'; +import { mockRows } from './__mocks__/mockDataTableRows'; +import { MenuItem } from '../MenuItem'; +import { DropdownKebab } from '../DropdownKebab'; +import { Table } from '../Table'; +import { ControlLabel } from '../Form'; +import { actionHeaderCellFormatter, tableCellFormatter } from './index'; + +test('DataTable renders', () => { + const sortableHeaderCellFormatter = ({ column, cellProps }) => { + return ( + + {column.header.label} + + ); + }; + sortableHeaderCellFormatter.propTypes = { + column: PropTypes.object, + cellProps: PropTypes.object + }; + + const selectionHeaderCellFormatter = ({ column, cellProps }) => { + return ( + + + Select all rows + + + + ); + }; + selectionHeaderCellFormatter.propTypes = { + column: PropTypes.object, + cellProps: PropTypes.object + }; + + const selectionCellFormatter = (value, { rowData, rowIndex }) => { + const id = `select${rowIndex}`; + const label = `Select row ${rowIndex}`; + return ( + + + {label} + + + + ); + }; + + const columns = [ + { + property: 'select', + header: { + label: 'Select all rows', + props: { + index: 0, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [selectionHeaderCellFormatter] + }, + cell: { + props: { + index: 0 + }, + formatters: [selectionCellFormatter] + } + }, + { + property: 'name', + header: { + label: 'Name', + props: { + index: 1, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 1 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'actions', + header: { + label: 'Actions', + props: { + index: 2, + rowSpan: 1, + colSpan: 2 + }, + customFormatters: [actionHeaderCellFormatter] + }, + cell: { + props: { + index: 2 + }, + formatters: [ + (value, { rowData }) => { + return [ + + Actions + , + + + Action + Another Action + Something else here + + Separated link + + + ]; + } + ] + } + } + ]; + + const customHeaderCell = cellProps => { + const { index } = cellProps; + const column = columns[index]; + const customFormatters = column.header.customFormatters; + + return customFormatters.reduce( + (params, formatter) => ({ + value: formatter(params) + }), + { column, cellProps } + ).value; + }; + + const component = renderer.create( + + + + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/DataTable/__mocks__/mockPaginationTable.js b/src/components/DataTable/__mocks__/mockPaginationTable.js index 589a8d3ab24..c99e2c41b2a 100644 --- a/src/components/DataTable/__mocks__/mockPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockPaginationTable.js @@ -5,7 +5,11 @@ import cx from 'classnames'; import * as sort from 'sortabular'; import * as resolve from 'table-resolver'; import { bindMethods } from '../../../common/helpers'; -import { defaultSortingOrder } from '../index'; +import { + actionHeaderCellFormatter, + defaultSortingOrder, + tableCellFormatter +} from '../index'; import { Table } from '../../Table'; import { ControlLabel } from '../../Form'; import { DropdownKebab } from '../../DropdownKebab'; @@ -28,8 +32,7 @@ export class MockPaginationTable extends React.Component { // but you can use a state manager as well. const getSortingColumns = () => this.state.sortingColumns || {}; - // sortable transform - const sortable = sort.sort({ + const sortableTransform = sort.sort({ getSortingColumns, onSort: selectedColumn => { this.setState({ @@ -44,15 +47,13 @@ export class MockPaginationTable extends React.Component { strategy: sort.strategies.byProperty }); - // sorting fortmatter - const sortingFormat = sort.header({ - sortable, + const sortingFormatter = sort.header({ + sortableTransform, getSortingColumns, strategy: sort.strategies.byProperty }); - // sortable header cell custom formatter - const sortableHeaderCell = ({ column, cellProps }) => { + const sortableTransformHeaderCellFormatter = ({ column, cellProps }) => { const { sortingColumns } = this.state; const sortDirection = sortingColumns[column.property] && @@ -69,8 +70,7 @@ export class MockPaginationTable extends React.Component { ); }; - // selection header cell custom formatter - const selectionHeaderCell = ({ column, cellProps }) => { + const selectionHeaderCellFormatter = ({ column, cellProps }) => { const currentRows = this.currentRows().rows; const unselectedRows = currentRows.filter(r => !r.selected).length > 0; return ( @@ -88,22 +88,7 @@ export class MockPaginationTable extends React.Component { ); }; - // action header cell custom formatter - const actionHeaderCell = ({ column, cellProps }) => { - return ( - - {column.header.label} - - ); - }; - - // cell formatter - const cellFormat = value => { - return {value}; - }; - - // selection cell formatter - const selectionCellFormat = (value, { rowData, rowIndex }) => { + const selectionCellFormatter = (value, { rowData, rowIndex }) => { const id = `select${rowIndex}`; const label = `Select row ${rowIndex}`; return ( @@ -143,13 +128,13 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 1 }, - customFormatters: [selectionHeaderCell] + customFormatters: [selectionHeaderCellFormatter] }, cell: { props: { index: 0 }, - formatters: [selectionCellFormat] + formatters: [selectionCellFormatter] } }, { @@ -161,15 +146,15 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat], - customFormatters: [sortableHeaderCell] + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableTransformHeaderCellFormatter] }, cell: { props: { index: 1 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -181,15 +166,15 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat], - customFormatters: [sortableHeaderCell] + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableTransformHeaderCellFormatter] }, cell: { props: { index: 2 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -201,15 +186,15 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat], - customFormatters: [sortableHeaderCell] + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableTransformHeaderCellFormatter] }, cell: { props: { index: 3 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -221,15 +206,15 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat], - customFormatters: [sortableHeaderCell] + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableTransformHeaderCellFormatter] }, cell: { props: { index: 4 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -241,15 +226,15 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat], - customFormatters: [sortableHeaderCell] + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableTransformHeaderCellFormatter] }, cell: { props: { index: 5 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -261,7 +246,7 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 2 }, - customFormatters: [actionHeaderCell] + customFormatters: [actionHeaderCellFormatter] }, cell: { props: { @@ -434,7 +419,8 @@ export class MockPaginationTable extends React.Component { )(rows); } onRow(row, { rowIndex }) { - const selected = this.state.selectedRows.indexOf(row.id) > -1; + const { selectedRows } = this.state; + const selected = selectedRows.indexOf(row.id) > -1; return { className: cx({ selected: selected }), role: 'row' diff --git a/src/components/DataTable/__mocks__/mockSortableTable.js b/src/components/DataTable/__mocks__/mockSortableTable.js index 2bffa7f66b0..c5cf7d5fa08 100644 --- a/src/components/DataTable/__mocks__/mockSortableTable.js +++ b/src/components/DataTable/__mocks__/mockSortableTable.js @@ -2,7 +2,7 @@ import React from 'react'; import orderBy from 'lodash.orderby'; import * as sort from 'sortabular'; import * as resolve from 'table-resolver'; -import { defaultSortingOrder } from '../index'; +import { defaultSortingOrder, tableCellFormatter } from '../index'; import { Table } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; @@ -22,8 +22,7 @@ export class MockSortableTable extends React.Component { // but you can use a state manager as well. const getSortingColumns = () => this.state.sortingColumns || {}; - // sortable transform - const sortable = sort.sort({ + const sortableTransform = sort.sort({ getSortingColumns, onSort: selectedColumn => { this.setState({ @@ -38,18 +37,12 @@ export class MockSortableTable extends React.Component { strategy: sort.strategies.byProperty }); - // sorting fortmatter - const sortingFormat = sort.header({ - sortable, + const sortingFormatter = sort.header({ + sortableTransform, getSortingColumns, strategy: sort.strategies.byProperty }); - // cell formatter - const cellFormat = value => { - return {value}; - }; - this.state = { // Sort the first column in an ascending way by default. sortingColumns: { @@ -68,14 +61,14 @@ export class MockSortableTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat] + transforms: [sortableTransform], + formatters: [sortingFormatter] }, cell: { props: { index: 0 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -87,14 +80,14 @@ export class MockSortableTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat] + transforms: [sortableTransform], + formatters: [sortingFormatter] }, cell: { props: { index: 1 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -106,14 +99,14 @@ export class MockSortableTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat] + transforms: [sortableTransform], + formatters: [sortingFormatter] }, cell: { props: { index: 2 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -125,14 +118,14 @@ export class MockSortableTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat] + transforms: [sortableTransform], + formatters: [sortingFormatter] }, cell: { props: { index: 3 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { @@ -144,14 +137,14 @@ export class MockSortableTable extends React.Component { rowSpan: 1, colSpan: 1 }, - transforms: [sortable], - formatters: [sortingFormat] + transforms: [sortableTransform], + formatters: [sortingFormatter] }, cell: { props: { index: 4 }, - formatters: [cellFormat] + formatters: [tableCellFormatter] } }, { diff --git a/src/components/DataTable/__snapshots__/DataTable.test.js.snap b/src/components/DataTable/__snapshots__/DataTable.test.js.snap new file mode 100644 index 00000000000..1baf7be11dd --- /dev/null +++ b/src/components/DataTable/__snapshots__/DataTable.test.js.snap @@ -0,0 +1,323 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DataTable renders 1`] = ` + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Name + + Actions +
+ + + + Luke Skywalker + +
+ +
+
+ +
+ + + + C-3PO + +
+ +
+
+ +
+`; diff --git a/src/components/DataTable/constants.js b/src/components/DataTable/constants.js index 8606fed71b4..1646354bba6 100644 --- a/src/components/DataTable/constants.js +++ b/src/components/DataTable/constants.js @@ -1,10 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from '../Table'; + // Reactabular sorting order allows you to specifiy sort asc/desc only and removes // the unsorted state. This is consistent with current PF Data Table but should // be better spelled out in our design docs. -// https://github.com/patternfly/patternfly-design/blob/master/pattern-library/content-views/table-view/design/design.md +// https://github.com/patternfly/patternfly-design/issues/516 // https://reactabular.js.org/#/data/sorting?a=customizing-sorting-order export const defaultSortingOrder = { FIRST: 'asc', asc: 'desc', desc: 'asc' }; + +export const actionHeaderCellFormatter = ({ column, cellProps }) => { + return ( + + {column.header.label} + + ); +}; +actionHeaderCellFormatter.propTypes = { + /** table columns */ + column: PropTypes.object, + /** cell props */ + cellProps: PropTypes.object +}; + +// table cell formatter +export const tableCellFormatter = value => { + return {value}; +}; diff --git a/src/components/DataTable/index.js b/src/components/DataTable/index.js index e6ea5fd0ff7..59ffefc8d0c 100644 --- a/src/components/DataTable/index.js +++ b/src/components/DataTable/index.js @@ -1 +1,5 @@ -export { defaultSortingOrder } from './constants'; +export { + actionHeaderCellFormatter, + defaultSortingOrder, + tableCellFormatter +} from './constants'; diff --git a/src/components/Pagination/Paginate.test.js b/src/components/Pagination/Paginate.test.js new file mode 100644 index 00000000000..1806925336d --- /dev/null +++ b/src/components/Pagination/Paginate.test.js @@ -0,0 +1,37 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; + +import Paginator from './Paginator'; + +test('Paginator renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/Pagination/Paginator.js b/src/components/Pagination/Paginator.js index 5c1186b64e5..dc683e75454 100644 --- a/src/components/Pagination/Paginator.js +++ b/src/components/Pagination/Paginator.js @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Form, FormControl, FormGroup, ControlLabel } from '../Form'; import { DropdownButton } from '../Button'; -import { ListGroup, ListGroupItem } from '../ListGroup'; import { MenuItem } from '../MenuItem'; import { Icon } from '../Icon'; @@ -16,6 +15,7 @@ const Paginator = ({ itemCount, itemsStart, itemsEnd, + messages, onPerPageSelect, onFirstPage, onPreviousPage, @@ -44,25 +44,21 @@ const Paginator = ({ ); })} - per page + {messages.perPage} {itemsStart}-{itemsEnd} -  of  +  {messages.of}  {itemCount} - - + Current Page -  of  +  {messages.of}  {amountOfPages} - - + ); @@ -153,6 +134,15 @@ Paginator.propTypes = { itemsStart: PropTypes.number, /** calculated items end */ itemsEnd: PropTypes.number, + /** message text inputs for i18n */ + messages: PropTypes.shape({ + firstPage: PropTypes.string, + previousPage: PropTypes.string, + nextPage: PropTypes.string, + lastPage: PropTypes.string, + perPage: PropTypes.string, + of: PropTypes.string + }), /** per page selection callback */ onPerPageSelect: PropTypes.func, /** first page callback */ @@ -166,4 +156,14 @@ Paginator.propTypes = { /** last page callback */ onLastPage: PropTypes.func }; +Paginator.defaultProps = { + messages: { + firstPage: 'First Page', + previousPage: 'Previous Page', + nextPage: 'Next Page', + lastPage: 'Last Page', + perPage: 'per page', + of: 'of' + } +}; export default Paginator; diff --git a/src/components/Pagination/__snapshots__/Paginate.test.js.snap b/src/components/Pagination/__snapshots__/Paginate.test.js.snap new file mode 100644 index 00000000000..816890e453c --- /dev/null +++ b/src/components/Pagination/__snapshots__/Paginate.test.js.snap @@ -0,0 +1,227 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Paginator renders 1`] = ` +
+
+
+ + +
+ + + לדף + + +
+
+ + + 1 + - + 15 + +   + שֶׁל +   + + 75 + + + + + + +   + שֶׁל +   + + 5 + + + +
+
+`; From f476842dfa0446dd0ccc7be262f7e80c6bfbda79 Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Sun, 14 Jan 2018 21:00:10 -0500 Subject: [PATCH 04/15] add server side paging example --- sass/patternfly-react/_pagination.scss | 2 +- src/components/DataTable/DataTable.stories.js | 19 +- src/components/DataTable/DataTable.test.js | 164 +- .../Stories/ClientPaginationTableStory.js | 43 + .../Stories/ClientSortableTableStory.js | 39 + .../DataTable/Stories/PaginationTableStory.js | 24 - .../Stories/ServerPaginationTableStory.js | 44 + .../DataTable/Stories/SortableTableStory.js | 20 - src/components/DataTable/Stories/index.js | 11 +- ...nTable.js => mockClientPaginationTable.js} | 153 +- ...bleTable.js => mockClientSortableTable.js} | 82 +- .../DataTable/__mocks__/mockServerApi.js | 83 + .../__mocks__/mockServerPaginationTable.js | 404 ++++ .../__snapshots__/DataTable.test.js.snap | 1779 ++++++++++++++--- src/components/DataTable/constants.js | 112 +- src/components/DataTable/index.js | 4 + src/components/Pagination/Paginate.test.js | 68 +- .../Pagination/Pagination.stories.js | 4 +- .../{Paginator.js => PaginationRow.js} | 143 +- .../Pagination/PaginationRowAmountOfPages.js | 25 + .../Pagination/PaginationRowBack.js | 62 + .../Pagination/PaginationRowButtonGroup.js | 19 + .../Pagination/PaginationRowForward.js | 65 + .../Pagination/PaginationRowItems.js | 34 + .../__snapshots__/Paginate.test.js.snap | 119 +- src/components/Pagination/index.js | 25 +- src/components/Table/TableCheckbox.js | 34 + src/components/Table/index.js | 7 +- 28 files changed, 2859 insertions(+), 729 deletions(-) create mode 100644 src/components/DataTable/Stories/ClientPaginationTableStory.js create mode 100644 src/components/DataTable/Stories/ClientSortableTableStory.js delete mode 100644 src/components/DataTable/Stories/PaginationTableStory.js create mode 100644 src/components/DataTable/Stories/ServerPaginationTableStory.js delete mode 100644 src/components/DataTable/Stories/SortableTableStory.js rename src/components/DataTable/__mocks__/{mockPaginationTable.js => mockClientPaginationTable.js} (78%) rename src/components/DataTable/__mocks__/{mockSortableTable.js => mockClientSortableTable.js} (75%) create mode 100644 src/components/DataTable/__mocks__/mockServerApi.js create mode 100644 src/components/DataTable/__mocks__/mockServerPaginationTable.js rename src/components/Pagination/{Paginator.js => PaginationRow.js} (53%) create mode 100644 src/components/Pagination/PaginationRowAmountOfPages.js create mode 100644 src/components/Pagination/PaginationRowBack.js create mode 100644 src/components/Pagination/PaginationRowButtonGroup.js create mode 100644 src/components/Pagination/PaginationRowForward.js create mode 100644 src/components/Pagination/PaginationRowItems.js create mode 100644 src/components/Table/TableCheckbox.js diff --git a/sass/patternfly-react/_pagination.scss b/sass/patternfly-react/_pagination.scss index e88b44dd1c6..60fd8bb81ee 100644 --- a/sass/patternfly-react/_pagination.scss +++ b/sass/patternfly-react/_pagination.scss @@ -1,6 +1,6 @@ .content-view-pf-pagination { .form-group { - .btn-group { + .pagination-pf-pagesize.btn-group { display: flex; float: none; margin-left: 0; diff --git a/src/components/DataTable/DataTable.stories.js b/src/components/DataTable/DataTable.stories.js index 4fbef9af746..64d866e811c 100644 --- a/src/components/DataTable/DataTable.stories.js +++ b/src/components/DataTable/DataTable.stories.js @@ -1,26 +1,19 @@ import { storiesOf } from '@storybook/react'; import { withKnobs } from '@storybook/addon-knobs'; import { - sortableTableAddWithInfo, - paginationTableAddWithInfo + clientSortableTableAddWithInfo, + clientPaginationTableAddWithInfo, + serverPaginationTableAddWithInfo } from './Stories'; -import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; -import { DOCUMENTATION_URL } from '../../../storybook/constants'; const stories = storiesOf('DataTable', module); stories.addDecorator(withKnobs); -stories.addDecorator( - defaultTemplate({ - title: 'Data Table', - documentationLink: - DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/' - }) -); /** * DataTable stories */ -sortableTableAddWithInfo(stories); -paginationTableAddWithInfo(stories); +clientSortableTableAddWithInfo(stories); +clientPaginationTableAddWithInfo(stories); +serverPaginationTableAddWithInfo(stories); diff --git a/src/components/DataTable/DataTable.test.js b/src/components/DataTable/DataTable.test.js index 004ff02e510..64b8a883ab3 100644 --- a/src/components/DataTable/DataTable.test.js +++ b/src/components/DataTable/DataTable.test.js @@ -1,161 +1,19 @@ import React from 'react'; -import PropTypes from 'prop-types'; import renderer from 'react-test-renderer'; -import * as resolve from 'table-resolver'; -import { mockRows } from './__mocks__/mockDataTableRows'; -import { MenuItem } from '../MenuItem'; -import { DropdownKebab } from '../DropdownKebab'; -import { Table } from '../Table'; -import { ControlLabel } from '../Form'; -import { actionHeaderCellFormatter, tableCellFormatter } from './index'; +import { MockClientPaginationTable } from './__mocks__/mockClientPaginationTable'; +import { MockServerPaginationTable } from './__mocks__/mockServerPaginationTable'; -test('DataTable renders', () => { - const sortableHeaderCellFormatter = ({ column, cellProps }) => { - return ( - - {column.header.label} - - ); - }; - sortableHeaderCellFormatter.propTypes = { - column: PropTypes.object, - cellProps: PropTypes.object - }; - - const selectionHeaderCellFormatter = ({ column, cellProps }) => { - return ( - - - Select all rows - - - - ); - }; - selectionHeaderCellFormatter.propTypes = { - column: PropTypes.object, - cellProps: PropTypes.object - }; - - const selectionCellFormatter = (value, { rowData, rowIndex }) => { - const id = `select${rowIndex}`; - const label = `Select row ${rowIndex}`; - return ( - - - {label} - - - - ); - }; - - const columns = [ - { - property: 'select', - header: { - label: 'Select all rows', - props: { - index: 0, - rowSpan: 1, - colSpan: 1 - }, - customFormatters: [selectionHeaderCellFormatter] - }, - cell: { - props: { - index: 0 - }, - formatters: [selectionCellFormatter] - } - }, - { - property: 'name', - header: { - label: 'Name', - props: { - index: 1, - rowSpan: 1, - colSpan: 1 - }, - customFormatters: [sortableHeaderCellFormatter] - }, - cell: { - props: { - index: 1 - }, - formatters: [tableCellFormatter] - } - }, - { - property: 'actions', - header: { - label: 'Actions', - props: { - index: 2, - rowSpan: 1, - colSpan: 2 - }, - customFormatters: [actionHeaderCellFormatter] - }, - cell: { - props: { - index: 2 - }, - formatters: [ - (value, { rowData }) => { - return [ - - Actions - , - - - Action - Another Action - Something else here - - Separated link - - - ]; - } - ] - } - } - ]; - - const customHeaderCell = cellProps => { - const { index } = cellProps; - const column = columns[index]; - const customFormatters = column.header.customFormatters; - - return customFormatters.reduce( - (params, formatter) => ({ - value: formatter(params) - }), - { column, cellProps } - ).value; - }; +test('Mock Client Pagination DataTable renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); +test('Mock Server Pagination DataTable renders', () => { const component = renderer.create( - - - - + ); let tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/components/DataTable/Stories/ClientPaginationTableStory.js b/src/components/DataTable/Stories/ClientPaginationTableStory.js new file mode 100644 index 00000000000..b22ada04ffb --- /dev/null +++ b/src/components/DataTable/Stories/ClientPaginationTableStory.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { withInfo } from '@storybook/addon-info'; +import { decorateAction } from '@storybook/addon-actions'; +import { MockClientPaginationTable } from '../__mocks__/mockClientPaginationTable'; +import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../../storybook/constants'; + +/** + * Client Pagination Table stories + */ + +const clientPaginationTableAddWithInfo = stories => { + stories.add( + 'Client Paginated Table', + withInfo({ + source: false, + propTablesExclude: [MockClientPaginationTable], + text:
+ })(() => { + const logAction = decorateAction([args => args]); + let story = ( + + ); + let description = ( +
+ Client side pagination for PatternFly React Data Tables is based on + Reactabular pagination. See the following{' '} + API docs{' '} + for more details. +
+ ); + return inlineTemplate({ + title: 'Client Paginated Table', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', + story: story, + description: description + }); + }) + ); +}; + +export default clientPaginationTableAddWithInfo; diff --git a/src/components/DataTable/Stories/ClientSortableTableStory.js b/src/components/DataTable/Stories/ClientSortableTableStory.js new file mode 100644 index 00000000000..63303e158f8 --- /dev/null +++ b/src/components/DataTable/Stories/ClientSortableTableStory.js @@ -0,0 +1,39 @@ +import React from 'react'; +import { withInfo } from '@storybook/addon-info'; +import { MockClientSortableTable } from '../__mocks__/mockClientSortableTable'; +import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../../storybook/constants'; + +/** + * Client Sortable Table stories + */ + +const clientSortableTableAddWithInfo = stories => { + stories.add( + 'Client Sortable Table', + withInfo({ + source: false, + propTablesExclude: [MockClientSortableTable], + text:
+ })(() => { + let story = ; + let description = ( +
+ Client side sorting for PatternFly React Data Tables is based on + Sortabular. See the following{' '} + API docs for + more details. +
+ ); + return inlineTemplate({ + title: 'Client Sortable Table', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', + story: story, + description: description + }); + }) + ); +}; + +export default clientSortableTableAddWithInfo; diff --git a/src/components/DataTable/Stories/PaginationTableStory.js b/src/components/DataTable/Stories/PaginationTableStory.js deleted file mode 100644 index 9d48cd6225d..00000000000 --- a/src/components/DataTable/Stories/PaginationTableStory.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { withInfo } from '@storybook/addon-info'; -import { decorateAction } from '@storybook/addon-actions'; -import { MockPaginationTable } from '../__mocks__/mockPaginationTable'; - -/** - * Pagination Table stories - */ - -const paginationTableAddWithInfo = stories => { - stories.add( - 'Paginated Table', - withInfo({ - source: false, - propTablesExclude: [MockPaginationTable], - text:
- })(() => { - const logAction = decorateAction([args => args]); - return ; - }) - ); -}; - -export default paginationTableAddWithInfo; diff --git a/src/components/DataTable/Stories/ServerPaginationTableStory.js b/src/components/DataTable/Stories/ServerPaginationTableStory.js new file mode 100644 index 00000000000..21043eeae5b --- /dev/null +++ b/src/components/DataTable/Stories/ServerPaginationTableStory.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { withInfo } from '@storybook/addon-info'; +import { decorateAction } from '@storybook/addon-actions'; +import { MockServerPaginationTable } from '../__mocks__/mockServerPaginationTable'; +import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../../storybook/constants'; + +/** + * Server Pagination Table stories + */ + +const serverPaginationTableAddWithInfo = stories => { + stories.add( + 'Server Paginated Table', + withInfo({ + source: false, + propTablesExclude: [MockServerPaginationTable], + text:
+ })(() => { + const logAction = decorateAction([args => args]); + let story = ( + + ); + let description = ( +
+ Server side sorting for PatternFly React Data Tables is based on + Reactabular. See the following{' '} + API docs for more details. +
+ ); + return inlineTemplate({ + title: 'Server Paginated Table', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', + story: story, + description: description + }); + }) + ); +}; + +export default serverPaginationTableAddWithInfo; diff --git a/src/components/DataTable/Stories/SortableTableStory.js b/src/components/DataTable/Stories/SortableTableStory.js deleted file mode 100644 index d2642c1e211..00000000000 --- a/src/components/DataTable/Stories/SortableTableStory.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { withInfo } from '@storybook/addon-info'; -import { MockSortableTable } from '../__mocks__/mockSortableTable'; - -/** - * Sortable Table stories - */ - -const sortableTableAddWithInfo = stories => { - stories.add( - 'Sortable Table', - withInfo({ - source: false, - propTablesExclude: [MockSortableTable], - text:
- })(() => ) - ); -}; - -export default sortableTableAddWithInfo; diff --git a/src/components/DataTable/Stories/index.js b/src/components/DataTable/Stories/index.js index d3bc182c812..b49b06725d9 100644 --- a/src/components/DataTable/Stories/index.js +++ b/src/components/DataTable/Stories/index.js @@ -1,2 +1,9 @@ -export { default as sortableTableAddWithInfo } from './SortableTableStory'; -export { default as paginationTableAddWithInfo } from './PaginationTableStory'; +export { + default as clientSortableTableAddWithInfo +} from './ClientSortableTableStory'; +export { + default as clientPaginationTableAddWithInfo +} from './ClientPaginationTableStory'; +export { + default as serverPaginationTableAddWithInfo +} from './ServerPaginationTableStory.js'; diff --git a/src/components/DataTable/__mocks__/mockPaginationTable.js b/src/components/DataTable/__mocks__/mockClientPaginationTable.js similarity index 78% rename from src/components/DataTable/__mocks__/mockPaginationTable.js rename to src/components/DataTable/__mocks__/mockClientPaginationTable.js index c99e2c41b2a..63b43dc60c1 100644 --- a/src/components/DataTable/__mocks__/mockPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockClientPaginationTable.js @@ -7,15 +7,18 @@ import * as resolve from 'table-resolver'; import { bindMethods } from '../../../common/helpers'; import { actionHeaderCellFormatter, + customHeaderFormattersDefinition, defaultSortingOrder, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, tableCellFormatter } from '../index'; -import { Table } from '../../Table'; -import { ControlLabel } from '../../Form'; +import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; -import { Paginator, paginate } from '../../Pagination'; +import { PaginationRow, paginate } from '../../Pagination'; import { compose } from 'recompose'; import { mockRows } from './mockDataTableRows'; @@ -24,12 +27,10 @@ import { mockRows } from './mockDataTableRows'; * https://reactabular.js.org/#/data/pagination */ -export class MockPaginationTable extends React.Component { +export class MockClientPaginationTable extends React.Component { constructor(props) { super(props); - // Point the transform to your rows. React state can work for this purpose - // but you can use a state manager as well. const getSortingColumns = () => this.state.sortingColumns || {}; const sortableTransform = sort.sort({ @@ -53,66 +54,27 @@ export class MockPaginationTable extends React.Component { strategy: sort.strategies.byProperty }); - const sortableTransformHeaderCellFormatter = ({ column, cellProps }) => { - const { sortingColumns } = this.state; - const sortDirection = - sortingColumns[column.property] && - sortingColumns[column.property].direction; - return ( - - {column.header.label} - - ); - }; - - const selectionHeaderCellFormatter = ({ column, cellProps }) => { - const currentRows = this.currentRows().rows; - const unselectedRows = currentRows.filter(r => !r.selected).length > 0; - return ( - - - Select all rows - - - - ); - }; + // enables our custom header formatters extensions to reactabular + this.customHeaderFormatters = customHeaderFormattersDefinition; - const selectionCellFormatter = (value, { rowData, rowIndex }) => { - const id = `select${rowIndex}`; - const label = `Select row ${rowIndex}`; - return ( - - - {label} - - { - this.onSelectRow(e, rowData); - }} - /> - - ); - }; + bindMethods(this, [ + 'customHeaderFormatters', + 'onPageInput', + 'onPerPageSelect', + 'onFirstPage', + 'onPreviousPage', + 'onNextPage', + 'onLastPage', + 'onRow', + 'onSelectAllRows', + 'onSelectRow' + ]); this.state = { // Sort the first column in an ascending way by default. sortingColumns: { name: { - direction: 'asc', + direction: TABLE_SORT_DIRECTION.ASC, position: 0 } }, @@ -134,7 +96,15 @@ export class MockPaginationTable extends React.Component { props: { index: 0 }, - formatters: [selectionCellFormatter] + formatters: [ + (value, { rowData, rowIndex }) => { + return selectionCellFormatter( + value, + { rowData, rowIndex }, + this.onSelectRow + ); + } + ] } }, { @@ -148,7 +118,7 @@ export class MockPaginationTable extends React.Component { }, transforms: [sortableTransform], formatters: [sortingFormatter], - customFormatters: [sortableTransformHeaderCellFormatter] + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -168,7 +138,7 @@ export class MockPaginationTable extends React.Component { }, transforms: [sortableTransform], formatters: [sortingFormatter], - customFormatters: [sortableTransformHeaderCellFormatter] + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -188,7 +158,7 @@ export class MockPaginationTable extends React.Component { }, transforms: [sortableTransform], formatters: [sortingFormatter], - customFormatters: [sortableTransformHeaderCellFormatter] + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -208,7 +178,7 @@ export class MockPaginationTable extends React.Component { }, transforms: [sortableTransform], formatters: [sortingFormatter], - customFormatters: [sortableTransformHeaderCellFormatter] + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -228,7 +198,7 @@ export class MockPaginationTable extends React.Component { }, transforms: [sortableTransform], formatters: [sortingFormatter], - customFormatters: [sortableTransformHeaderCellFormatter] + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -246,7 +216,7 @@ export class MockPaginationTable extends React.Component { rowSpan: 1, colSpan: 2 }, - customFormatters: [actionHeaderCellFormatter] + formatters: [actionHeaderCellFormatter] }, cell: { props: { @@ -289,18 +259,6 @@ export class MockPaginationTable extends React.Component { perPageOptions: [6, 10, 15] } }; - - bindMethods(this, [ - 'onPageInput', - 'onPerPageSelect', - 'onFirstPage', - 'onPreviousPage', - 'onNextPage', - 'onLastPage', - 'onRow', - 'onSelectAllRows', - 'onSelectRow' - ]); } onPageInput(e) { let newPaginationState = Object.assign({}, this.state.pagination); @@ -319,8 +277,7 @@ export class MockPaginationTable extends React.Component { this.setState({ pagination: newPaginationState }); } onPreviousPage() { - const { page } = this.state.pagination; - if (page > 1) { + if (this.state.pagination.page > 1) { let newPaginationState = Object.assign({}, this.state.pagination); newPaginationState.page--; this.setState({ pagination: newPaginationState }); @@ -427,22 +384,9 @@ export class MockPaginationTable extends React.Component { }; } render() { - const { columns, pagination } = this.state; + const { columns, pagination, sortingColumns } = this.state; const sortedPaginatedRows = this.currentRows(); - const customHeaderCell = cellProps => { - const { index } = cellProps; - const column = columns[index]; - const customFormatters = column.header.customFormatters; - - return customFormatters.reduce( - (params, formatter) => ({ - value: formatter(params) - }), - { column, cellProps } - ).value; - }; - return ( { + return this.customHeaderFormatters({ + cellProps, + columns, + sortingColumns, + rows: sortedPaginatedRows.rows, + onSelectAllRows: this.onSelectAllRows + }); + } + } }} > @@ -462,8 +416,9 @@ export class MockPaginationTable extends React.Component { onRow={this.onRow} /> - this.state.sortingColumns || {}; @@ -43,11 +50,15 @@ export class MockSortableTable extends React.Component { strategy: sort.strategies.byProperty }); + // enables our custom header formatters extensions to reactabular + this.customHeaderFormatters = customHeaderFormattersDefinition; + bindMethods(this, ['customHeaderFormatters']); + this.state = { // Sort the first column in an ascending way by default. sortingColumns: { name: { - direction: 'asc', + direction: TABLE_SORT_DIRECTION.ASC, position: 0 } }, @@ -59,10 +70,12 @@ export class MockSortableTable extends React.Component { props: { index: 0, rowSpan: 1, - colSpan: 1 + colSpan: 1, + sort: true }, transforms: [sortableTransform], - formatters: [sortingFormatter] + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -78,10 +91,12 @@ export class MockSortableTable extends React.Component { props: { index: 1, rowSpan: 1, - colSpan: 1 + colSpan: 1, + sort: true }, transforms: [sortableTransform], - formatters: [sortingFormatter] + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -97,10 +112,12 @@ export class MockSortableTable extends React.Component { props: { index: 2, rowSpan: 1, - colSpan: 1 + colSpan: 1, + sort: true }, transforms: [sortableTransform], - formatters: [sortingFormatter] + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -116,10 +133,12 @@ export class MockSortableTable extends React.Component { props: { index: 3, rowSpan: 1, - colSpan: 1 + colSpan: 1, + sort: true }, transforms: [sortableTransform], - formatters: [sortingFormatter] + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -135,10 +154,12 @@ export class MockSortableTable extends React.Component { props: { index: 4, rowSpan: 1, - colSpan: 1 + colSpan: 1, + sort: true }, transforms: [sortableTransform], - formatters: [sortingFormatter] + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] }, cell: { props: { @@ -155,7 +176,8 @@ export class MockSortableTable extends React.Component { index: 5, rowSpan: 1, colSpan: 2 - } + }, + formatters: [actionHeaderCellFormatter] }, cell: { props: { @@ -201,24 +223,6 @@ export class MockSortableTable extends React.Component { }) )(rows); - const customHeaderCell = cellProps => { - const { index } = cellProps; - const column = columns[index]; - const sortDirection = - sortingColumns[column.property] && - sortingColumns[column.property].direction; - return ( - - {column.header.label} - - ); - }; - return (
{ + return this.customHeaderFormatters({ + cellProps, + columns, + sortingColumns + }); + } + } }} > diff --git a/src/components/DataTable/__mocks__/mockServerApi.js b/src/components/DataTable/__mocks__/mockServerApi.js new file mode 100644 index 00000000000..65a6418b739 --- /dev/null +++ b/src/components/DataTable/__mocks__/mockServerApi.js @@ -0,0 +1,83 @@ +import { mockRows } from './mockDataTableRows'; + +/** + * simple singleton mockServer API for server paginated table story + * + * typically one would connect this api to redux actions/redux store + * and pass data via a redux connected component. + */ +class MockServerApi { + constructor() { + this.mockRows = mockRows; + } + + getPage({ sortingColumns, page, perPage }) { + // server api accepts sort parameters, the current page, and the number perPage + // callServerApi(sortingColumns, page, perPage) + + // mock server logic to update the paginated rows + const p = page - 1 || 0; + const amountOfPages = Math.ceil(this.mockRows.length / perPage); + const startPage = p < amountOfPages ? p : 0; + const endOfPage = startPage * perPage + perPage; + + return new Promise(resolve => { + // server api returns sorted/paged rows, total of amount of pages, total items, + // items start, items end + resolve({ + rows: this.mockRows.slice(startPage * perPage, endOfPage), + amountOfPages: amountOfPages, + itemCount: this.mockRows.length, + itemsStart: startPage * perPage + 1, + itemsEnd: + endOfPage > this.mockRows.length ? this.mockRows.length : endOfPage + }); + }); + } + + selectRow({ row }) { + // call server api to update row + // callServerApi(row) + + // mock server logic to update `mockRows` + const index = this.mockRows.findIndex(r => r.id === row.id); + if (index > -1) { + this.mockRows[index] = this.toggleRow(this.mockRows[index]); + } + + return new Promise(resolve => { + // server api returns updated row + resolve({ + row: row + }); + }); + } + + selectAllRows({ checked, rows }) { + // call server api to update all current rows + // callServerApi(rows) + + // mock server logic to update `mockRows` + rows.map(row => { + const index = this.mockRows.findIndex(r => r.id === row.id); + if (index > -1) { + const updated = Object.assign({}, this.mockRows[index], { + selected: checked + }); + this.mockRows[index] = updated; + } + }); + + return new Promise(resolve => { + // server api returns updated rows + resolve({ + rows: rows + }); + }); + } + + toggleRow(row) { + return Object.assign({}, row, { selected: !row.selected }); + } +} +export default new MockServerApi(); diff --git a/src/components/DataTable/__mocks__/mockServerPaginationTable.js b/src/components/DataTable/__mocks__/mockServerPaginationTable.js new file mode 100644 index 00000000000..752636d1658 --- /dev/null +++ b/src/components/DataTable/__mocks__/mockServerPaginationTable.js @@ -0,0 +1,404 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import * as resolve from 'table-resolver'; +import { bindMethods } from '../../../common/helpers'; +import { + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, + tableCellFormatter +} from '../index'; +import { Table, TABLE_SORT_DIRECTION } from '../../Table'; +import { DropdownKebab } from '../../DropdownKebab'; +import { MenuItem } from '../../MenuItem'; +import { Grid } from '../../Grid'; +import { PaginationRow } from '../../Pagination'; +import MockServerApi from './mockServerApi'; + +export class MockServerPaginationTable extends React.Component { + constructor(props) { + super(props); + + // enables our custom header formatters extensions to reactabular + this.customHeaderFormatters = customHeaderFormattersDefinition; + + bindMethods(this, [ + 'customHeaderFormatters', + 'onPageInput', + 'onPerPageSelect', + 'onFirstPage', + 'onPreviousPage', + 'onNextPage', + 'onLastPage', + 'onRow', + 'onSelectAllRows', + 'onSelectRow', + 'onSort' + ]); + + this.state = { + // Sort the first column in an ascending way by default. + sortingColumns: { + name: { + direction: TABLE_SORT_DIRECTION.ASC, + position: 0 + } + }, + + // column definitions + columns: [ + { + property: 'select', + header: { + label: 'Select all rows', + props: { + index: 0, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [selectionHeaderCellFormatter] + }, + cell: { + props: { + index: 0 + }, + formatters: [ + (value, { rowData, rowIndex }) => { + return selectionCellFormatter( + value, + { rowData, rowIndex }, + this.onSelectRow + ); + } + ] + } + }, + { + property: 'name', + header: { + label: 'Name', + props: { + index: 1, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 1 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'height', + header: { + label: 'Height', + props: { + index: 2, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 2 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'eye_color', + header: { + label: 'Eye Color', + props: { + index: 3, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 3 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'gender', + header: { + label: 'Gender', + props: { + index: 4, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 4 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'birth_year', + header: { + label: 'Birth Year', + props: { + index: 5, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 5 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'actions', + header: { + label: 'Actions', + props: { + index: 6, + rowSpan: 1, + colSpan: 2 + }, + formatters: [actionHeaderCellFormatter] + }, + cell: { + props: { + index: 6 + }, + formatters: [ + (value, { rowData }) => { + return [ + + alert('clicked ' + rowData.name)} + > + Actions + + , + + + Action + Another Action + Something else here + + Separated link + + + ]; + } + ] + } + } + ], + + // rows and row selection state + rows: [], + selectedRows: [], + + // pagination default states + pagination: { + page: 1, + perPage: 6, + perPageOptions: [6, 10, 15] + }, + + // server side pagination values + amountOfPages: 0, + itemCount: 0, + itemsStart: 0, + itemsEnd: 0 + }; + } + + componentWillMount() { + const { sortingColumns, pagination } = this.state; + this.getPage(sortingColumns, pagination); + } + + getPage(sortingColumns, pagination) { + const { onServerPageLogger } = this.props; + + // call our mock server with next sorting/paging arguments + const getPageArgs = { + sortingColumns, + page: pagination.page, + perPage: pagination.perPage + }; + + onServerPageLogger(getPageArgs); + + MockServerApi.getPage(getPageArgs).then(response => { + this.setState({ + sortingColumns: sortingColumns, + pagination: pagination, + rows: response.rows, + amountOfPages: response.amountOfPages, + itemCount: response.itemCount, + itemsStart: response.itemsStart, + itemsEnd: response.itemsEnd + }); + }); + } + + onSort(e, column, sortDirection) { + // Clearing existing `sortingColumns` does simple single column sort. To do multisort, + // set each column based on existing sorts specified and set sort position. + const updatedSortingColumns = { + [column.property]: { + direction: + sortDirection === TABLE_SORT_DIRECTION.ASC + ? TABLE_SORT_DIRECTION.DESC + : TABLE_SORT_DIRECTION.ASC, + position: 0 + } + }; + + alert( + 'Server API called with: sort by ' + + column.property + + ' ' + + updatedSortingColumns[column.property].direction + ); + + this.getPage(updatedSortingColumns, this.state.pagination); + } + + onSelectRow(event, row) { + const { sortingColumns, pagination } = this.state; + MockServerApi.selectRow({ row }).then(response => { + // refresh rows after row is selected + this.getPage(sortingColumns, pagination); + }); + } + onSelectAllRows(event) { + const { sortingColumns, pagination, rows } = this.state; + const checked = event.target.checked; + MockServerApi.selectAllRows({ rows, checked }).then(response => { + // refresh rows after all rows selected + this.getPage(sortingColumns, pagination); + }); + } + + onPageInput(e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = e.target.value; + this.getPage(this.state.sortingColumns, newPaginationState); + } + onPerPageSelect(eventKey, e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.perPage = eventKey; + newPaginationState.page = 1; + this.getPage(this.state.sortingColumns, newPaginationState); + } + onFirstPage() { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = 1; + this.getPage(this.state.sortingColumns, newPaginationState); + } + onPreviousPage() { + if (this.state.pagination.page > 1) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page--; + this.getPage(this.state.sortingColumns, newPaginationState); + } + } + onNextPage() { + const { pagination, amountOfPages } = this.state; + if (pagination.page < amountOfPages) { + let newPaginationState = Object.assign({}, pagination); + newPaginationState.page++; + this.getPage(this.state.sortingColumns, newPaginationState); + } + } + onLastPage() { + const { pagination, amountOfPages } = this.state; + if (pagination.page < amountOfPages) { + let newPaginationState = Object.assign({}, pagination); + newPaginationState.page = amountOfPages; + this.getPage(this.state.sortingColumns, newPaginationState); + } + } + + onRow(row, { rowIndex }) { + return { + className: cx({ selected: row.selected }), + role: 'row' + }; + } + + render() { + const { + columns, + pagination, + sortingColumns, + rows, + amountOfPages, + itemCount, + itemsStart, + itemsEnd + } = this.state; + + return ( + + { + return this.customHeaderFormatters({ + cellProps, + columns, + sortingColumns, + rows: rows, + onSelectAllRows: this.onSelectAllRows, + onSort: this.onSort + }); + } + } + }} + > + + + + + + ); + } +} +MockServerPaginationTable.propTypes = { + onServerPageLogger: PropTypes.func +}; diff --git a/src/components/DataTable/__snapshots__/DataTable.test.js.snap b/src/components/DataTable/__snapshots__/DataTable.test.js.snap index 1baf7be11dd..9e6db92f2cd 100644 --- a/src/components/DataTable/__snapshots__/DataTable.test.js.snap +++ b/src/components/DataTable/__snapshots__/DataTable.test.js.snap @@ -1,323 +1,1528 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DataTable renders 1`] = ` - - - - - - + + + + + + + + + + + + - Actions - - - - - - - + + + + + + + + + - Luke Skywalker - - - + + + + + + + + + -
- -
    -
  • + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + +
- - - - Name - +
+ + + + Name + + Height + + Eye Color + + Gender + + Birth Year + + Actions +
- - - + + + + Anakin Skywalker + + 188 + + blue + + male + + 41.9BBY + +
+ +
+
+ +
-
- -
-
+ + + + Beru Whitesun lars + + 165 + + blue + + female + + 47BBY + +
+ +
+
+ +
+ Biggs Darklighter + + 183 + + brown + + male + + 24BBY + +
+ +
+
+
+ + +
+
+ + + + C-3PO + + 167 + + yellow + + n/a + + 112BBY + +
+
+
+
+ + +
+
+ + + + Chewbacca + + 228 + + blue + + male + + 200BBY + +
+ +
+
+
+ + +
+
+ + + + Darth Vader + + 202 + + yellow + + male + + 41.9BBY + +
+ +
+
+ -
+ Separated link + + + + +
+
+
+
- - - - + 6 + + + + +
+ + + per page + + +
+
+ + + 1 + - + 6 + +   + of +   + + 20 + + + +
+`; + +exports[`Mock Server Pagination DataTable renders 1`] = ` +
+ + + + + + + + + + + + + +
+ + + + Name + + Height + + Eye Color + + Gender + + Birth Year + + Actions +
+
+
+ - - - - + 15 + + + +
+ + + per page + + +
+
+ + + 0 + - + 0 + +   + of +   + + 0 + + + + + + +   + of +   + + 0 + + + +
+ +
`; diff --git a/src/components/DataTable/constants.js b/src/components/DataTable/constants.js index 1646354bba6..7f0e5188a34 100644 --- a/src/components/DataTable/constants.js +++ b/src/components/DataTable/constants.js @@ -13,21 +13,115 @@ export const defaultSortingOrder = { desc: 'asc' }; -export const actionHeaderCellFormatter = ({ column, cellProps }) => { +export const actionHeaderCellFormatter = (value, { column }) => { return ( - + {column.header.label} ); }; -actionHeaderCellFormatter.propTypes = { - /** table columns */ - column: PropTypes.object, - /** cell props */ - cellProps: PropTypes.object -}; -// table cell formatter export const tableCellFormatter = value => { return {value}; }; + +export const sortableHeaderCellFormatter = ({ + cellProps, + column, + sortingColumns, + onSort +}) => { + const sortDirection = + sortingColumns[column.property] && + sortingColumns[column.property].direction; + return ( + { + onSort && onSort(e, column, sortDirection); + }} + sort + sortDirection={sortDirection} + aria-label={column.header.label} + {...cellProps} + > + {column.header.label} + + ); +}; +sortableHeaderCellFormatter.propTypes = { + cellProps: PropTypes.object, + column: PropTypes.object, + sortingColumns: PropTypes.object, + onSort: PropTypes.func +}; + +export const selectionCellFormatter = ( + value, + { rowData, rowIndex }, + onSelectRow +) => { + const id = `select${rowIndex}`; + const label = `Select row ${rowIndex}`; + return ( + + { + onSelectRow && onSelectRow(e, rowData); + }} + /> + + ); +}; + +export const selectionHeaderCellFormatter = ({ + cellProps, + column, + rows, + onSelectAllRows +}) => { + const unselectedRows = rows.filter(r => !r.selected).length > 0; + return ( + + + + ); +}; +selectionHeaderCellFormatter.propTypes = { + cellProps: PropTypes.object, + column: PropTypes.object, + rows: PropTypes.array, + onSelectAllRows: PropTypes.func +}; + +// wraps the default header definitions and adds support for `customFormatters` +export const customHeaderFormattersDefinition = ({ + cellProps, + columns, + sortingColumns, + rows, + onSelectAllRows, + onSort +}) => { + const { index } = cellProps; + const column = columns[index]; + const customFormatters = column.header.customFormatters; + + if (customFormatters) { + return customFormatters.reduce( + (params, formatter) => ({ + value: formatter(params) + }), + { cellProps, column, sortingColumns, rows, onSelectAllRows, onSort } + ).value; + } else { + return cellProps.children; + } +}; diff --git a/src/components/DataTable/index.js b/src/components/DataTable/index.js index 59ffefc8d0c..7c6b545d04b 100644 --- a/src/components/DataTable/index.js +++ b/src/components/DataTable/index.js @@ -1,5 +1,9 @@ export { actionHeaderCellFormatter, + customHeaderFormattersDefinition, defaultSortingOrder, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, tableCellFormatter } from './constants'; diff --git a/src/components/Pagination/Paginate.test.js b/src/components/Pagination/Paginate.test.js index 1806925336d..a5817085a56 100644 --- a/src/components/Pagination/Paginate.test.js +++ b/src/components/Pagination/Paginate.test.js @@ -1,11 +1,15 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import Paginator from './Paginator'; +import { PaginationRow } from './index'; -test('Paginator renders', () => { +test('PaginationRow renders', () => { const component = renderer.create( - { let tree = component.toJSON(); expect(tree).toMatchSnapshot(); }); + +test('PaginationRow.Items renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('PaginationRow.Back renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('PaginationRow.ButtonGroup renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('PaginationRow.Forward renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('PaginationRow.AmountOfPages renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/Pagination/Pagination.stories.js b/src/components/Pagination/Pagination.stories.js index 86f40a3024b..ffd0403bc99 100644 --- a/src/components/Pagination/Pagination.stories.js +++ b/src/components/Pagination/Pagination.stories.js @@ -5,7 +5,7 @@ import { withKnobs, number } from '@storybook/addon-knobs'; import { bindMethods } from '../../common/helpers'; import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../storybook/constants'; -import { Paginator } from './index'; +import { PaginationRow } from './index'; const stories = storiesOf('Pagination', module); stories.addDecorator(withKnobs); @@ -41,7 +41,7 @@ class PaginationStoryWrapper extends React.Component { } render() { return ( - { const { page, perPage, perPageOptions } = pagination; - + const classes = cx( + { + 'content-view-pf-pagination': contentViewPagination, + 'list-view-pf-pagination': listViewPagination, + 'card-view-pf-pagination': cardViewPagination, + 'table-view-pf-pagination': tableViewPagination, + clearfix: true + }, + className + ); return ( -
+ {perPageOptions && perPageOptions.length && @@ -47,39 +67,21 @@ const Paginator = ({ {messages.perPage} - - - {itemsStart}-{itemsEnd} - -  {messages.of}  - {itemCount} - - + + + + Current Page - -  {messages.of}  - {amountOfPages} - - + + + +
); }; -Paginator.propTypes = { +PaginationRow.propTypes = { /** Additional css classes */ className: PropTypes.string, /** user pagination settings */ @@ -143,6 +129,16 @@ Paginator.propTypes = { perPage: PropTypes.string, of: PropTypes.string }), + /** content view pagination row */ + contentViewPagination: PropTypes.bool, + /** table view pagination row */ + tableViewPagination: PropTypes.bool, + /** list view pagination row */ + listViewPagination: PropTypes.bool, + /** card view pagination row */ + cardViewPagination: PropTypes.bool, + /** dropdown button id */ + dropdownButtonId: PropTypes.string, /** per page selection callback */ onPerPageSelect: PropTypes.func, /** first page callback */ @@ -156,7 +152,7 @@ Paginator.propTypes = { /** last page callback */ onLastPage: PropTypes.func }; -Paginator.defaultProps = { +PaginationRow.defaultProps = { messages: { firstPage: 'First Page', previousPage: 'Previous Page', @@ -164,6 +160,7 @@ Paginator.defaultProps = { lastPage: 'Last Page', perPage: 'per page', of: 'of' - } + }, + dropdownButtonId: 'pagination-row-dropdown' }; -export default Paginator; +export default PaginationRow; diff --git a/src/components/Pagination/PaginationRowAmountOfPages.js b/src/components/Pagination/PaginationRowAmountOfPages.js new file mode 100644 index 00000000000..23c4c857cd2 --- /dev/null +++ b/src/components/Pagination/PaginationRowAmountOfPages.js @@ -0,0 +1,25 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +/** + * PaginationRowAmountOfPages component for Patternfly React + */ +const PaginationRowAmountOfPages = ({ + messagesOf, + amountOfPages, + ...props +}) => { + return ( + +  {messagesOf}  + {amountOfPages} + + ); +}; +PaginationRowAmountOfPages.propTypes = { + /** messages of */ + messagesOf: PropTypes.string, + /** calculated amount of pages */ + amountOfPages: PropTypes.number +}; +export default PaginationRowAmountOfPages; diff --git a/src/components/Pagination/PaginationRowBack.js b/src/components/Pagination/PaginationRowBack.js new file mode 100644 index 00000000000..eecc8cdebb8 --- /dev/null +++ b/src/components/Pagination/PaginationRowBack.js @@ -0,0 +1,62 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { Icon } from '../Icon'; + +/** + * PaginationRowBack component for Patternfly React + */ +const PaginationRowBack = ({ + className, + page, + messagesFirstPage, + messagesPreviousPage, + onFirstPage, + onPreviousPage, + ...props +}) => { + const classes = cx('pagination', 'pagination-pf-back', className); + return ( + + ); +}; +PaginationRowBack.propTypes = { + /** additional class names */ + className: PropTypes.string, + /** pagination page */ + page: PropTypes.number, + /** messages firstPage */ + messagesFirstPage: PropTypes.string, + /** messages previousPage */ + messagesPreviousPage: PropTypes.string, + /** first page callback */ + onFirstPage: PropTypes.func, + /** previous page selection callback */ + onPreviousPage: PropTypes.func +}; +export default PaginationRowBack; diff --git a/src/components/Pagination/PaginationRowButtonGroup.js b/src/components/Pagination/PaginationRowButtonGroup.js new file mode 100644 index 00000000000..7f860bd18d9 --- /dev/null +++ b/src/components/Pagination/PaginationRowButtonGroup.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { ButtonGroup } from '../Button'; + +const PaginationRowButtonGroup = ({ className, ...props }) => { + return ( + + ); +}; +PaginationRowButtonGroup.propTypes = { + /** additional classes */ + className: PropTypes.string +}; + +export default PaginationRowButtonGroup; diff --git a/src/components/Pagination/PaginationRowForward.js b/src/components/Pagination/PaginationRowForward.js new file mode 100644 index 00000000000..36e86381db5 --- /dev/null +++ b/src/components/Pagination/PaginationRowForward.js @@ -0,0 +1,65 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { Icon } from '../Icon'; + +/** + * PaginationRowForward component for Patternfly React + */ +const PaginationRowForward = ({ + className, + page, + amountOfPages, + messagesNextPage, + messagesLastPage, + onNextPage, + onLastPage, + ...props +}) => { + const classes = cx('pagination', 'pagination-pf-forward', className); + return ( + + ); +}; +PaginationRowForward.propTypes = { + /** additional class names */ + className: PropTypes.string, + /** pagination page */ + page: PropTypes.number, + /** calculated amount of pages */ + amountOfPages: PropTypes.number, + /** messages next page */ + messagesNextPage: PropTypes.string, + /** messages last page */ + messagesLastPage: PropTypes.string, + /** next page callback */ + onNextPage: PropTypes.func, + /** last page callback */ + onLastPage: PropTypes.func +}; +export default PaginationRowForward; diff --git a/src/components/Pagination/PaginationRowItems.js b/src/components/Pagination/PaginationRowItems.js new file mode 100644 index 00000000000..3c6bb8a6d1c --- /dev/null +++ b/src/components/Pagination/PaginationRowItems.js @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +/** + * PaginationRowItems component for Patternfly React + */ +const PaginationRowItems = ({ + itemCount, + itemsStart, + itemsEnd, + messagesOf, + ...props +}) => { + return ( + + + {itemsStart}-{itemsEnd} + +  {messagesOf}  + {itemCount} + + ); +}; +PaginationRowItems.propTypes = { + /** calculated number of rows */ + itemCount: PropTypes.number, + /** calculated items start */ + itemsStart: PropTypes.number, + /** calculated items end */ + itemsEnd: PropTypes.number, + /** messages Of */ + messagesOf: PropTypes.string +}; +export default PaginationRowItems; diff --git a/src/components/Pagination/__snapshots__/Paginate.test.js.snap b/src/components/Pagination/__snapshots__/Paginate.test.js.snap index 816890e453c..6904eb90bd6 100644 --- a/src/components/Pagination/__snapshots__/Paginate.test.js.snap +++ b/src/components/Pagination/__snapshots__/Paginate.test.js.snap @@ -1,21 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Paginator renders 1`] = ` +exports[`PaginationRow renders 1`] = `
    @@ -225,3 +225,112 @@ exports[`Paginator renders 1`] = `
`; + +exports[`PaginationRow.AmountOfPages renders 1`] = ` + +   + of +   + + 4 + + +`; + +exports[`PaginationRow.Back renders 1`] = ` + +`; + +exports[`PaginationRow.ButtonGroup renders 1`] = ` +
+`; + +exports[`PaginationRow.Forward renders 1`] = ` + +`; + +exports[`PaginationRow.Items renders 1`] = ` + + + 0 + - + 10 + +   + of +   + + 55 + + +`; diff --git a/src/components/Pagination/index.js b/src/components/Pagination/index.js index 76469176b99..8c9e9a9e6e4 100644 --- a/src/components/Pagination/index.js +++ b/src/components/Pagination/index.js @@ -1,2 +1,23 @@ -export { default as Paginator } from './Paginator'; -export { default as paginate } from './paginate'; +import paginate from './paginate'; +import PaginationRow from './PaginationRow'; +import PaginationRowAmountOfPages from './PaginationRowAmountOfPages'; +import PaginationRowBack from './PaginationRowBack'; +import PaginationRowButtonGroup from './PaginationRowButtonGroup'; +import PaginationRowForward from './PaginationRowForward'; +import PaginationRowItems from './PaginationRowItems'; + +PaginationRow.AmountOfPages = PaginationRowAmountOfPages; +PaginationRow.Back = PaginationRowBack; +PaginationRow.ButtonGroup = PaginationRowButtonGroup; +PaginationRow.Forward = PaginationRowForward; +PaginationRow.Items = PaginationRowItems; + +export { + paginate, + PaginationRow, + PaginationRowAmountOfPages, + PaginationRowBack, + PaginationRowButtonGroup, + PaginationRowForward, + PaginationRowItems +}; diff --git a/src/components/Table/TableCheckbox.js b/src/components/Table/TableCheckbox.js new file mode 100644 index 00000000000..3589563bb9d --- /dev/null +++ b/src/components/Table/TableCheckbox.js @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { ControlLabel } from '../Form'; + +/** + * TableCheckbox component for Patternfly React + */ +const TableCheckbox = ({ id, label, checked, onChange, ...props }) => { + return ( + + + {label} + + + + ); +}; +TableCheckbox.propTypes = { + /** checkbox id */ + id: PropTypes.string, + /** checkbox label */ + label: PropTypes.string, + /** checkbox is checked */ + checked: PropTypes.bool, + /** onChange callback */ + onChange: PropTypes.func +}; +export default TableCheckbox; diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 5c3948addd4..26e2f799742 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -1,7 +1,9 @@ import { Table } from './Table'; +import { TABLE_ALIGN, TABLE_SORT_DIRECTION } from './constants'; import TableActions from './TableActions'; import TableButton from './TableButton'; import TableCell from './TableCell'; +import TableCheckbox from './TableCheckbox'; import TableHeading from './TableHeading'; import TablePfProvider from './TablePfProvider'; import TableSelectionCell from './TableSelectionCell'; @@ -10,6 +12,7 @@ import TableSelectionHeading from './TableSelectionHeading'; Table.Actions = TableActions; Table.Button = TableButton; Table.Cell = TableCell; +Table.Checkbox = TableCheckbox; Table.Heading = TableHeading; Table.PfProvider = TablePfProvider; Table.SelectionCell = TableSelectionCell; @@ -21,5 +24,7 @@ export { TableHeading, TablePfProvider, TableSelectionCell, - TableSelectionHeading + TableSelectionHeading, + TABLE_ALIGN, + TABLE_SORT_DIRECTION }; From fa6dcbceaba272aef6f3fcc8715ef079d7bbc158 Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Mon, 15 Jan 2018 10:32:01 -0500 Subject: [PATCH 05/15] adds pagination.less --- less/pagination.less | 16 ++++++++++++++++ less/patternfly-react.less | 1 + 2 files changed, 17 insertions(+) create mode 100644 less/pagination.less diff --git a/less/pagination.less b/less/pagination.less new file mode 100644 index 00000000000..60fd8bb81ee --- /dev/null +++ b/less/pagination.less @@ -0,0 +1,16 @@ +.content-view-pf-pagination { + .form-group { + .pagination-pf-pagesize.btn-group { + display: flex; + float: none; + margin-left: 0; + margin-right: 5px; + margin-bottom: 0; + padding-right: 13px; + width: auto; + } + .dropdown-menu { + min-width: auto; + } + } +} diff --git a/less/patternfly-react.less b/less/patternfly-react.less index bbb5e95e066..4def6398373 100644 --- a/less/patternfly-react.less +++ b/less/patternfly-react.less @@ -1,3 +1,4 @@ /** Patternfly React Specific Extensions */ +@import 'pagination.less'; From 2922579c3560c05c0336182f19b83dbf0b4a7ba6 Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Mon, 15 Jan 2018 13:21:12 -0500 Subject: [PATCH 06/15] added story source for datatable examples --- .../Stories/ClientPaginationTableStory.js | 12 +- .../Stories/ClientSortableTableStory.js | 12 +- .../Stories/ServerPaginationTableStory.js | 12 +- .../__mocks__/mockClientPaginationTable.js | 443 ++++++++++++++++++ .../__mocks__/mockClientSortableTable.js | 262 +++++++++++ .../__mocks__/mockServerPaginationTable.js | 407 ++++++++++++++++ 6 files changed, 1142 insertions(+), 6 deletions(-) diff --git a/src/components/DataTable/Stories/ClientPaginationTableStory.js b/src/components/DataTable/Stories/ClientPaginationTableStory.js index b22ada04ffb..39bc5885958 100644 --- a/src/components/DataTable/Stories/ClientPaginationTableStory.js +++ b/src/components/DataTable/Stories/ClientPaginationTableStory.js @@ -1,7 +1,10 @@ import React from 'react'; import { withInfo } from '@storybook/addon-info'; import { decorateAction } from '@storybook/addon-actions'; -import { MockClientPaginationTable } from '../__mocks__/mockClientPaginationTable'; +import { + MockClientPaginationTable, + mockClientPaginationTableSource +} from '../__mocks__/mockClientPaginationTable'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; @@ -15,7 +18,12 @@ const clientPaginationTableAddWithInfo = stories => { withInfo({ source: false, propTablesExclude: [MockClientPaginationTable], - text:
+ text: ( +
+

Story Source

+
{mockClientPaginationTableSource}
+
+ ) })(() => { const logAction = decorateAction([args => args]); let story = ( diff --git a/src/components/DataTable/Stories/ClientSortableTableStory.js b/src/components/DataTable/Stories/ClientSortableTableStory.js index 63303e158f8..239e6e9a9bb 100644 --- a/src/components/DataTable/Stories/ClientSortableTableStory.js +++ b/src/components/DataTable/Stories/ClientSortableTableStory.js @@ -1,6 +1,9 @@ import React from 'react'; import { withInfo } from '@storybook/addon-info'; -import { MockClientSortableTable } from '../__mocks__/mockClientSortableTable'; +import { + MockClientSortableTable, + mockClientSortableTableSource +} from '../__mocks__/mockClientSortableTable'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; @@ -14,7 +17,12 @@ const clientSortableTableAddWithInfo = stories => { withInfo({ source: false, propTablesExclude: [MockClientSortableTable], - text:
+ text: ( +
+

Story Source

+
{mockClientSortableTableSource}
+
+ ) })(() => { let story = ; let description = ( diff --git a/src/components/DataTable/Stories/ServerPaginationTableStory.js b/src/components/DataTable/Stories/ServerPaginationTableStory.js index 21043eeae5b..79a7875d1ef 100644 --- a/src/components/DataTable/Stories/ServerPaginationTableStory.js +++ b/src/components/DataTable/Stories/ServerPaginationTableStory.js @@ -1,7 +1,10 @@ import React from 'react'; import { withInfo } from '@storybook/addon-info'; import { decorateAction } from '@storybook/addon-actions'; -import { MockServerPaginationTable } from '../__mocks__/mockServerPaginationTable'; +import { + MockServerPaginationTable, + mockServerPaginationTableSource +} from '../__mocks__/mockServerPaginationTable'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; @@ -15,7 +18,12 @@ const serverPaginationTableAddWithInfo = stories => { withInfo({ source: false, propTablesExclude: [MockServerPaginationTable], - text:
+ text: ( +
+

Story Source

+
{mockServerPaginationTableSource}
+
+ ) })(() => { const logAction = decorateAction([args => args]); let story = ( diff --git a/src/components/DataTable/__mocks__/mockClientPaginationTable.js b/src/components/DataTable/__mocks__/mockClientPaginationTable.js index 63b43dc60c1..926e72cfa84 100644 --- a/src/components/DataTable/__mocks__/mockClientPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockClientPaginationTable.js @@ -438,3 +438,446 @@ export class MockClientPaginationTable extends React.Component { MockClientPaginationTable.propTypes = { onRowsLogger: PropTypes.func }; + +export const mockClientPaginationTableSource = ` +import React from 'react'; +import PropTypes from 'prop-types'; +import orderBy from 'lodash.orderby'; +import cx from 'classnames'; +import * as sort from 'sortabular'; +import * as resolve from 'table-resolver'; +import { bindMethods } from '../../../common/helpers'; +import { + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + defaultSortingOrder, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, + tableCellFormatter +} from '../index'; +import { Table, TABLE_SORT_DIRECTION } from '../../Table'; +import { DropdownKebab } from '../../DropdownKebab'; +import { MenuItem } from '../../MenuItem'; +import { Grid } from '../../Grid'; +import { PaginationRow, paginate } from '../../Pagination'; +import { compose } from 'recompose'; +import { mockRows } from './mockDataTableRows'; + +/** + * Reactabular client side paging based on the following api docs: + * https://reactabular.js.org/#/data/pagination + */ + +export class MockClientPaginationTable extends React.Component { + constructor(props) { + super(props); + + const getSortingColumns = () => this.state.sortingColumns || {}; + + const sortableTransform = sort.sort({ + getSortingColumns, + onSort: selectedColumn => { + this.setState({ + sortingColumns: sort.byColumn({ + sortingColumns: this.state.sortingColumns, + sortingOrder: defaultSortingOrder, + selectedColumn + }) + }); + }, + // Use property or index dependening on the sortingColumns structure specified + strategy: sort.strategies.byProperty + }); + + const sortingFormatter = sort.header({ + sortableTransform, + getSortingColumns, + strategy: sort.strategies.byProperty + }); + + // enables our custom header formatters extensions to reactabular + this.customHeaderFormatters = customHeaderFormattersDefinition; + + bindMethods(this, [ + 'customHeaderFormatters', + 'onPageInput', + 'onPerPageSelect', + 'onFirstPage', + 'onPreviousPage', + 'onNextPage', + 'onLastPage', + 'onRow', + 'onSelectAllRows', + 'onSelectRow' + ]); + + this.state = { + // Sort the first column in an ascending way by default. + sortingColumns: { + name: { + direction: TABLE_SORT_DIRECTION.ASC, + position: 0 + } + }, + + // column definitions + columns: [ + { + property: 'select', + header: { + label: 'Select all rows', + props: { + index: 0, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [selectionHeaderCellFormatter] + }, + cell: { + props: { + index: 0 + }, + formatters: [ + (value, { rowData, rowIndex }) => { + return selectionCellFormatter( + value, + { rowData, rowIndex }, + this.onSelectRow + ); + } + ] + } + }, + { + property: 'name', + header: { + label: 'Name', + props: { + index: 1, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 1 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'height', + header: { + label: 'Height', + props: { + index: 2, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 2 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'eye_color', + header: { + label: 'Eye Color', + props: { + index: 3, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 3 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'gender', + header: { + label: 'Gender', + props: { + index: 4, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 4 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'birth_year', + header: { + label: 'Birth Year', + props: { + index: 5, + rowSpan: 1, + colSpan: 1 + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 5 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'actions', + header: { + label: 'Actions', + props: { + index: 6, + rowSpan: 1, + colSpan: 2 + }, + formatters: [actionHeaderCellFormatter] + }, + cell: { + props: { + index: 6 + }, + formatters: [ + (value, { rowData }) => { + return [ + + alert('clicked ' + rowData.name)} + > + Actions + + , + + + Action + Another Action + Something else here + + Separated link + + + ]; + } + ] + } + } + ], + + // rows and row selection state + rows: mockRows, + selectedRows: [], + + // pagination default states + pagination: { + page: 1, + perPage: 6, + perPageOptions: [6, 10, 15] + } + }; + } + onPageInput(e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = e.target.value; + this.setState({ pagination: newPaginationState }); + } + onPerPageSelect(eventKey, e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.perPage = eventKey; + newPaginationState.page = 1; + this.setState({ pagination: newPaginationState }); + } + onFirstPage() { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = 1; + this.setState({ pagination: newPaginationState }); + } + onPreviousPage() { + if (this.state.pagination.page > 1) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page--; + this.setState({ pagination: newPaginationState }); + } + } + onNextPage() { + const { page, perPage } = this.state.pagination; + const totalPages = Math.ceil(mockRows.length / perPage); + if (page < totalPages) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page++; + this.setState({ pagination: newPaginationState }); + } + } + onLastPage() { + const { page, perPage } = this.state.pagination; + const totalPages = Math.ceil(mockRows.length / perPage); + if (page < totalPages) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = totalPages; + this.setState({ pagination: newPaginationState }); + } + } + onSelectRow(event, row) { + const { onRowsLogger } = this.props; + const { rows, selectedRows } = this.state; + const selectedRowIndex = rows.findIndex(r => r.id === row.id); + if (selectedRowIndex > -1) { + let updatedSelectedRows, updatedRow; + if (row.selected) { + updatedSelectedRows = selectedRows.filter(r => !(r === row.id)); + updatedRow = this.deselectRow(row); + } else { + selectedRows.push(row.id); + updatedSelectedRows = selectedRows; + updatedRow = this.selectRow(row); + } + rows[selectedRowIndex] = updatedRow; + this.setState({ + rows: rows, + selectedRows: updatedSelectedRows + }); + onRowsLogger(rows.filter(r => r.selected)); + } + } + onSelectAllRows(event) { + const { onRowsLogger } = this.props; + const { rows, selectedRows } = this.state; + const checked = event.target.checked; + const currentRows = this.currentRows().rows; + + if (checked) { + const updatedSelections = [ + ...new Set([...currentRows.map(r => r.id), ...selectedRows]) + ]; + const updatedRows = rows.map(r => { + return updatedSelections.indexOf(r.id) > -1 ? this.selectRow(r) : r; + }); + this.setState({ + // important: you must update rows to force a re-render and trigger onRow hook + rows: updatedRows, + selectedRows: updatedSelections + }); + onRowsLogger(updatedRows.filter(r => r.selected)); + } else { + const ids = currentRows.map(r => r.id); + const updatedSelections = selectedRows.filter(r => { + return !(ids.indexOf(r) > -1); + }); + const updatedRows = rows.map(r => { + return updatedSelections.indexOf(r.id) > -1 ? r : this.deselectRow(r); + }); + this.setState({ + rows: updatedRows, + selectedRows: updatedSelections + }); + onRowsLogger(updatedRows.filter(r => r.selected)); + } + } + selectRow(row) { + return Object.assign({}, row, { selected: true }); + } + deselectRow(row) { + return Object.assign({}, row, { selected: false }); + } + currentRows() { + const { rows, sortingColumns, columns, pagination } = this.state; + return compose( + paginate(pagination), + sort.sorter({ + columns: columns, + sortingColumns, + sort: orderBy, + strategy: sort.strategies.byProperty + }) + )(rows); + } + onRow(row, { rowIndex }) { + const { selectedRows } = this.state; + const selected = selectedRows.indexOf(row.id) > -1; + return { + className: cx({ selected: selected }), + role: 'row' + }; + } + render() { + const { columns, pagination, sortingColumns } = this.state; + const sortedPaginatedRows = this.currentRows(); + + return ( + + { + return this.customHeaderFormatters({ + cellProps, + columns, + sortingColumns, + rows: sortedPaginatedRows.rows, + onSelectAllRows: this.onSelectAllRows + }); + } + } + }} + > + + + + + + ); + } +} +MockClientPaginationTable.propTypes = { + onRowsLogger: PropTypes.func +}; +`; diff --git a/src/components/DataTable/__mocks__/mockClientSortableTable.js b/src/components/DataTable/__mocks__/mockClientSortableTable.js index 99dad6e6f42..757abf2be8b 100644 --- a/src/components/DataTable/__mocks__/mockClientSortableTable.js +++ b/src/components/DataTable/__mocks__/mockClientSortableTable.js @@ -258,3 +258,265 @@ export class MockClientSortableTable extends React.Component { ); } } + +export const mockClientSortableTableSource = ` +import React from 'react'; +import orderBy from 'lodash.orderby'; +import * as sort from 'sortabular'; +import * as resolve from 'table-resolver'; +import { bindMethods } from '../../../common/helpers'; +import { + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + defaultSortingOrder, + sortableHeaderCellFormatter, + tableCellFormatter +} from '../index'; +import { Table, TABLE_SORT_DIRECTION } from '../../Table'; +import { DropdownKebab } from '../../DropdownKebab'; +import { MenuItem } from '../../MenuItem'; +import { compose } from 'recompose'; +import { mockRows } from './mockDataTableRows'; + +/** + * Reactabular client side data sorting based on the following api docs: + * https://reactabular.js.org/#/data/sorting + */ + +export class MockClientSortableTable extends React.Component { + constructor(props) { + super(props); + + // Point the transform to your sortingColumns. React state can work for this purpose + // but you can use a state manager as well. + const getSortingColumns = () => this.state.sortingColumns || {}; + + const sortableTransform = sort.sort({ + getSortingColumns, + onSort: selectedColumn => { + this.setState({ + sortingColumns: sort.byColumn({ + sortingColumns: this.state.sortingColumns, + sortingOrder: defaultSortingOrder, + selectedColumn + }) + }); + }, + // Use property or index dependening on the sortingColumns structure specified + strategy: sort.strategies.byProperty + }); + + const sortingFormatter = sort.header({ + sortableTransform, + getSortingColumns, + strategy: sort.strategies.byProperty + }); + + // enables our custom header formatters extensions to reactabular + this.customHeaderFormatters = customHeaderFormattersDefinition; + bindMethods(this, ['customHeaderFormatters']); + + this.state = { + // Sort the first column in an ascending way by default. + sortingColumns: { + name: { + direction: TABLE_SORT_DIRECTION.ASC, + position: 0 + } + }, + columns: [ + { + property: 'name', + header: { + label: 'Name', + props: { + index: 0, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 0 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'height', + header: { + label: 'Height', + props: { + index: 1, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 1 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'eye_color', + header: { + label: 'Eye Color', + props: { + index: 2, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 2 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'gender', + header: { + label: 'Gender', + props: { + index: 3, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 3 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'birth_year', + header: { + label: 'Birth Year', + props: { + index: 4, + rowSpan: 1, + colSpan: 1, + sort: true + }, + transforms: [sortableTransform], + formatters: [sortingFormatter], + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 4 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'actions', + header: { + label: 'Actions', + props: { + index: 5, + rowSpan: 1, + colSpan: 2 + }, + formatters: [actionHeaderCellFormatter] + }, + cell: { + props: { + index: 5 + }, + formatters: [ + (value, { rowData }) => { + return [ + + alert('clicked ' + rowData.name)} + > + Actions + + , + + + Action + Another Action + Something else here + + Separated link + + + ]; + } + ] + } + } + ], + rows: mockRows.slice(0, 6) + }; + } + render() { + const { rows, sortingColumns, columns } = this.state; + + const sortedRows = compose( + sort.sorter({ + columns: columns, + sortingColumns, + sort: orderBy, + strategy: sort.strategies.byProperty + }) + )(rows); + + return ( +
+ { + return this.customHeaderFormatters({ + cellProps, + columns, + sortingColumns + }); + } + } + }} + > + + { + return { + role: 'row' + }; + }} + /> + +
+ ); + } +}`; diff --git a/src/components/DataTable/__mocks__/mockServerPaginationTable.js b/src/components/DataTable/__mocks__/mockServerPaginationTable.js index 752636d1658..3a85d05bb7c 100644 --- a/src/components/DataTable/__mocks__/mockServerPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockServerPaginationTable.js @@ -402,3 +402,410 @@ export class MockServerPaginationTable extends React.Component { MockServerPaginationTable.propTypes = { onServerPageLogger: PropTypes.func }; + +export const mockServerPaginationTableSource = ` +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import * as resolve from 'table-resolver'; +import { bindMethods } from '../../../common/helpers'; +import { + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, + tableCellFormatter +} from '../index'; +import { Table, TABLE_SORT_DIRECTION } from '../../Table'; +import { DropdownKebab } from '../../DropdownKebab'; +import { MenuItem } from '../../MenuItem'; +import { Grid } from '../../Grid'; +import { PaginationRow } from '../../Pagination'; +import MockServerApi from './mockServerApi'; + +export class MockServerPaginationTable extends React.Component { + constructor(props) { + super(props); + + // enables our custom header formatters extensions to reactabular + this.customHeaderFormatters = customHeaderFormattersDefinition; + + bindMethods(this, [ + 'customHeaderFormatters', + 'onPageInput', + 'onPerPageSelect', + 'onFirstPage', + 'onPreviousPage', + 'onNextPage', + 'onLastPage', + 'onRow', + 'onSelectAllRows', + 'onSelectRow', + 'onSort' + ]); + + this.state = { + // Sort the first column in an ascending way by default. + sortingColumns: { + name: { + direction: TABLE_SORT_DIRECTION.ASC, + position: 0 + } + }, + + // column definitions + columns: [ + { + property: 'select', + header: { + label: 'Select all rows', + props: { + index: 0, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [selectionHeaderCellFormatter] + }, + cell: { + props: { + index: 0 + }, + formatters: [ + (value, { rowData, rowIndex }) => { + return selectionCellFormatter( + value, + { rowData, rowIndex }, + this.onSelectRow + ); + } + ] + } + }, + { + property: 'name', + header: { + label: 'Name', + props: { + index: 1, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 1 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'height', + header: { + label: 'Height', + props: { + index: 2, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 2 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'eye_color', + header: { + label: 'Eye Color', + props: { + index: 3, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 3 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'gender', + header: { + label: 'Gender', + props: { + index: 4, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 4 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'birth_year', + header: { + label: 'Birth Year', + props: { + index: 5, + rowSpan: 1, + colSpan: 1 + }, + customFormatters: [sortableHeaderCellFormatter] + }, + cell: { + props: { + index: 5 + }, + formatters: [tableCellFormatter] + } + }, + { + property: 'actions', + header: { + label: 'Actions', + props: { + index: 6, + rowSpan: 1, + colSpan: 2 + }, + formatters: [actionHeaderCellFormatter] + }, + cell: { + props: { + index: 6 + }, + formatters: [ + (value, { rowData }) => { + return [ + + alert('clicked ' + rowData.name)} + > + Actions + + , + + + Action + Another Action + Something else here + + Separated link + + + ]; + } + ] + } + } + ], + + // rows and row selection state + rows: [], + selectedRows: [], + + // pagination default states + pagination: { + page: 1, + perPage: 6, + perPageOptions: [6, 10, 15] + }, + + // server side pagination values + amountOfPages: 0, + itemCount: 0, + itemsStart: 0, + itemsEnd: 0 + }; + } + + componentWillMount() { + const { sortingColumns, pagination } = this.state; + this.getPage(sortingColumns, pagination); + } + + getPage(sortingColumns, pagination) { + const { onServerPageLogger } = this.props; + + // call our mock server with next sorting/paging arguments + const getPageArgs = { + sortingColumns, + page: pagination.page, + perPage: pagination.perPage + }; + + onServerPageLogger(getPageArgs); + + MockServerApi.getPage(getPageArgs).then(response => { + this.setState({ + sortingColumns: sortingColumns, + pagination: pagination, + rows: response.rows, + amountOfPages: response.amountOfPages, + itemCount: response.itemCount, + itemsStart: response.itemsStart, + itemsEnd: response.itemsEnd + }); + }); + } + + onSort(e, column, sortDirection) { + // Clearing existing sortingColumns does simple single column sort. To do multisort, + // set each column based on existing sorts specified and set sort position. + const updatedSortingColumns = { + [column.property]: { + direction: + sortDirection === TABLE_SORT_DIRECTION.ASC + ? TABLE_SORT_DIRECTION.DESC + : TABLE_SORT_DIRECTION.ASC, + position: 0 + } + }; + + alert( + 'Server API called with: sort by ' + + column.property + + ' ' + + updatedSortingColumns[column.property].direction + ); + + this.getPage(updatedSortingColumns, this.state.pagination); + } + + onSelectRow(event, row) { + const { sortingColumns, pagination } = this.state; + MockServerApi.selectRow({ row }).then(response => { + // refresh rows after row is selected + this.getPage(sortingColumns, pagination); + }); + } + onSelectAllRows(event) { + const { sortingColumns, pagination, rows } = this.state; + const checked = event.target.checked; + MockServerApi.selectAllRows({ rows, checked }).then(response => { + // refresh rows after all rows selected + this.getPage(sortingColumns, pagination); + }); + } + + onPageInput(e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = e.target.value; + this.getPage(this.state.sortingColumns, newPaginationState); + } + onPerPageSelect(eventKey, e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.perPage = eventKey; + newPaginationState.page = 1; + this.getPage(this.state.sortingColumns, newPaginationState); + } + onFirstPage() { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = 1; + this.getPage(this.state.sortingColumns, newPaginationState); + } + onPreviousPage() { + if (this.state.pagination.page > 1) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page--; + this.getPage(this.state.sortingColumns, newPaginationState); + } + } + onNextPage() { + const { pagination, amountOfPages } = this.state; + if (pagination.page < amountOfPages) { + let newPaginationState = Object.assign({}, pagination); + newPaginationState.page++; + this.getPage(this.state.sortingColumns, newPaginationState); + } + } + onLastPage() { + const { pagination, amountOfPages } = this.state; + if (pagination.page < amountOfPages) { + let newPaginationState = Object.assign({}, pagination); + newPaginationState.page = amountOfPages; + this.getPage(this.state.sortingColumns, newPaginationState); + } + } + + onRow(row, { rowIndex }) { + return { + className: cx({ selected: row.selected }), + role: 'row' + }; + } + + render() { + const { + columns, + pagination, + sortingColumns, + rows, + amountOfPages, + itemCount, + itemsStart, + itemsEnd + } = this.state; + + return ( + + { + return this.customHeaderFormatters({ + cellProps, + columns, + sortingColumns, + rows: rows, + onSelectAllRows: this.onSelectAllRows, + onSort: this.onSort + }); + } + } + }} + > + + + + + + ); + } +} +MockServerPaginationTable.propTypes = { + onServerPageLogger: PropTypes.func +}; +`; From 15c0acbce6b795be8276a9628d481cb0d23a01bf Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Mon, 15 Jan 2018 19:22:25 -0500 Subject: [PATCH 07/15] update story sources once more --- .../Stories/ClientPaginationTableStory.js | 16 + .../Stories/ClientSortableTableStory.js | 12 + .../Stories/ServerPaginationTableStory.js | 16 + .../__mocks__/mockClientPaginationTable.js | 11 +- .../__mocks__/mockServerPaginationTable.js | 12 +- .../DataTable/actionHeaderCellFormatter.js | 18 + src/components/DataTable/constants.js | 117 ----- .../customHeaderFormattersDefinition.js | 41 ++ src/components/DataTable/index.js | 10 +- .../DataTable/selectionCellFormatter.js | 31 ++ .../DataTable/selectionHeaderCellFormatter.js | 33 ++ .../DataTable/sortableHeaderCellFormatter.js | 39 ++ .../DataTable/tableCellFormatter.js | 12 + src/components/Pagination/Paginate.test.js | 15 +- .../Pagination/Pagination.stories.js | 91 ++-- src/components/Pagination/PaginationRow.js | 27 +- .../Pagination/__mocks__/mockPaginationRow.js | 147 ++++++ .../__snapshots__/Paginate.test.js.snap | 456 +++++++++++++++++- src/components/Pagination/constants.js | 11 + src/components/Pagination/index.js | 5 + src/components/Table/index.js | 15 +- 21 files changed, 932 insertions(+), 203 deletions(-) create mode 100644 src/components/DataTable/actionHeaderCellFormatter.js create mode 100644 src/components/DataTable/customHeaderFormattersDefinition.js create mode 100644 src/components/DataTable/selectionCellFormatter.js create mode 100644 src/components/DataTable/selectionHeaderCellFormatter.js create mode 100644 src/components/DataTable/sortableHeaderCellFormatter.js create mode 100644 src/components/DataTable/tableCellFormatter.js create mode 100644 src/components/Pagination/__mocks__/mockPaginationRow.js create mode 100644 src/components/Pagination/constants.js diff --git a/src/components/DataTable/Stories/ClientPaginationTableStory.js b/src/components/DataTable/Stories/ClientPaginationTableStory.js index 39bc5885958..b7f771c681a 100644 --- a/src/components/DataTable/Stories/ClientPaginationTableStory.js +++ b/src/components/DataTable/Stories/ClientPaginationTableStory.js @@ -5,6 +5,14 @@ import { MockClientPaginationTable, mockClientPaginationTableSource } from '../__mocks__/mockClientPaginationTable'; +import { + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, + tableCellFormatter +} from '../index'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; @@ -17,6 +25,14 @@ const clientPaginationTableAddWithInfo = stories => { 'Client Paginated Table', withInfo({ source: false, + propTables: [ + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, + tableCellFormatter + ], propTablesExclude: [MockClientPaginationTable], text: (
diff --git a/src/components/DataTable/Stories/ClientSortableTableStory.js b/src/components/DataTable/Stories/ClientSortableTableStory.js index 239e6e9a9bb..d90db049516 100644 --- a/src/components/DataTable/Stories/ClientSortableTableStory.js +++ b/src/components/DataTable/Stories/ClientSortableTableStory.js @@ -4,6 +4,12 @@ import { MockClientSortableTable, mockClientSortableTableSource } from '../__mocks__/mockClientSortableTable'; +import { + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + sortableHeaderCellFormatter, + tableCellFormatter +} from '../index'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; @@ -17,6 +23,12 @@ const clientSortableTableAddWithInfo = stories => { withInfo({ source: false, propTablesExclude: [MockClientSortableTable], + propTables: [ + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + sortableHeaderCellFormatter, + tableCellFormatter + ], text: (

Story Source

diff --git a/src/components/DataTable/Stories/ServerPaginationTableStory.js b/src/components/DataTable/Stories/ServerPaginationTableStory.js index 79a7875d1ef..7255a7fb823 100644 --- a/src/components/DataTable/Stories/ServerPaginationTableStory.js +++ b/src/components/DataTable/Stories/ServerPaginationTableStory.js @@ -5,6 +5,14 @@ import { MockServerPaginationTable, mockServerPaginationTableSource } from '../__mocks__/mockServerPaginationTable'; +import { + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, + tableCellFormatter +} from '../index'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; @@ -18,6 +26,14 @@ const serverPaginationTableAddWithInfo = stories => { withInfo({ source: false, propTablesExclude: [MockServerPaginationTable], + propTables: [ + actionHeaderCellFormatter, + customHeaderFormattersDefinition, + selectionCellFormatter, + selectionHeaderCellFormatter, + sortableHeaderCellFormatter, + tableCellFormatter + ], text: (

Story Source

diff --git a/src/components/DataTable/__mocks__/mockClientPaginationTable.js b/src/components/DataTable/__mocks__/mockClientPaginationTable.js index 926e72cfa84..ac5dca497be 100644 --- a/src/components/DataTable/__mocks__/mockClientPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockClientPaginationTable.js @@ -18,7 +18,7 @@ import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; -import { PaginationRow, paginate } from '../../Pagination'; +import { PaginationRow, paginate, PAGINATION_VIEW } from '../../Pagination'; import { compose } from 'recompose'; import { mockRows } from './mockDataTableRows'; @@ -417,7 +417,8 @@ export class MockClientPaginationTable extends React.Component { /> { + return ( + + {column.header.label} + + ); +}; +actionHeaderCellFormatter.propTypes = { + /** cell value */ + value: PropTypes.node, + /** column definition */ + column: PropTypes.object +}; +export default actionHeaderCellFormatter; diff --git a/src/components/DataTable/constants.js b/src/components/DataTable/constants.js index 7f0e5188a34..d9ebfa5ba95 100644 --- a/src/components/DataTable/constants.js +++ b/src/components/DataTable/constants.js @@ -1,7 +1,3 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Table } from '../Table'; - // Reactabular sorting order allows you to specifiy sort asc/desc only and removes // the unsorted state. This is consistent with current PF Data Table but should // be better spelled out in our design docs. @@ -12,116 +8,3 @@ export const defaultSortingOrder = { asc: 'desc', desc: 'asc' }; - -export const actionHeaderCellFormatter = (value, { column }) => { - return ( - - {column.header.label} - - ); -}; - -export const tableCellFormatter = value => { - return {value}; -}; - -export const sortableHeaderCellFormatter = ({ - cellProps, - column, - sortingColumns, - onSort -}) => { - const sortDirection = - sortingColumns[column.property] && - sortingColumns[column.property].direction; - return ( - { - onSort && onSort(e, column, sortDirection); - }} - sort - sortDirection={sortDirection} - aria-label={column.header.label} - {...cellProps} - > - {column.header.label} - - ); -}; -sortableHeaderCellFormatter.propTypes = { - cellProps: PropTypes.object, - column: PropTypes.object, - sortingColumns: PropTypes.object, - onSort: PropTypes.func -}; - -export const selectionCellFormatter = ( - value, - { rowData, rowIndex }, - onSelectRow -) => { - const id = `select${rowIndex}`; - const label = `Select row ${rowIndex}`; - return ( - - { - onSelectRow && onSelectRow(e, rowData); - }} - /> - - ); -}; - -export const selectionHeaderCellFormatter = ({ - cellProps, - column, - rows, - onSelectAllRows -}) => { - const unselectedRows = rows.filter(r => !r.selected).length > 0; - return ( - - - - ); -}; -selectionHeaderCellFormatter.propTypes = { - cellProps: PropTypes.object, - column: PropTypes.object, - rows: PropTypes.array, - onSelectAllRows: PropTypes.func -}; - -// wraps the default header definitions and adds support for `customFormatters` -export const customHeaderFormattersDefinition = ({ - cellProps, - columns, - sortingColumns, - rows, - onSelectAllRows, - onSort -}) => { - const { index } = cellProps; - const column = columns[index]; - const customFormatters = column.header.customFormatters; - - if (customFormatters) { - return customFormatters.reduce( - (params, formatter) => ({ - value: formatter(params) - }), - { cellProps, column, sortingColumns, rows, onSelectAllRows, onSort } - ).value; - } else { - return cellProps.children; - } -}; diff --git a/src/components/DataTable/customHeaderFormattersDefinition.js b/src/components/DataTable/customHeaderFormattersDefinition.js new file mode 100644 index 00000000000..cd706f975cb --- /dev/null +++ b/src/components/DataTable/customHeaderFormattersDefinition.js @@ -0,0 +1,41 @@ +import PropTypes from 'prop-types'; + +// wraps the default header definitions and adds support for `customFormatters` +const customHeaderFormattersDefinition = ({ + cellProps, + columns, + sortingColumns, + rows, + onSelectAllRows, + onSort +}) => { + const { index } = cellProps; + const column = columns[index]; + const customFormatters = column.header.customFormatters; + + if (customFormatters) { + return customFormatters.reduce( + (params, formatter) => ({ + value: formatter(params) + }), + { cellProps, column, sortingColumns, rows, onSelectAllRows, onSort } + ).value; + } else { + return cellProps.children; + } +}; +customHeaderFormattersDefinition.propTypes = { + /** column header cell props */ + cellProps: PropTypes.object, + /** column definitions */ + columns: PropTypes.array, + /** sorting object definition */ + sortingColumns: PropTypes.object, + /** current table rows */ + rows: PropTypes.array, + /** on select all rows callback */ + onSelectAllRows: PropTypes.func, + /** onSort callback */ + onSort: PropTypes.func +}; +export default customHeaderFormattersDefinition; diff --git a/src/components/DataTable/index.js b/src/components/DataTable/index.js index 7c6b545d04b..3e61834c7db 100644 --- a/src/components/DataTable/index.js +++ b/src/components/DataTable/index.js @@ -1,3 +1,11 @@ +import actionHeaderCellFormatter from './actionHeaderCellFormatter'; +import customHeaderFormattersDefinition from './customHeaderFormattersDefinition'; +import { defaultSortingOrder } from './constants'; +import selectionCellFormatter from './selectionCellFormatter'; +import selectionHeaderCellFormatter from './selectionHeaderCellFormatter'; +import sortableHeaderCellFormatter from './sortableHeaderCellFormatter'; +import tableCellFormatter from './tableCellFormatter'; + export { actionHeaderCellFormatter, customHeaderFormattersDefinition, @@ -6,4 +14,4 @@ export { selectionHeaderCellFormatter, sortableHeaderCellFormatter, tableCellFormatter -} from './constants'; +}; diff --git a/src/components/DataTable/selectionCellFormatter.js b/src/components/DataTable/selectionCellFormatter.js new file mode 100644 index 00000000000..3bd21b49d14 --- /dev/null +++ b/src/components/DataTable/selectionCellFormatter.js @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from '../Table'; + +const selectionCellFormatter = (value, { rowData, rowIndex }, onSelectRow) => { + const id = `select${rowIndex}`; + const label = `Select row ${rowIndex}`; + return ( + + { + onSelectRow && onSelectRow(e, rowData); + }} + /> + + ); +}; +selectionCellFormatter.propTypes = { + /** cell value */ + value: PropTypes.node, // eslint-disable-line react/no-unused-prop-types + /** rowData for this row */ + rowData: PropTypes.object, // eslint-disable-line react/no-unused-prop-types + /** rowIndex for this row */ + rowIndex: PropTypes.number, // eslint-disable-line react/no-unused-prop-types + /** row selected callback */ + onSelectRow: PropTypes.func // eslint-disable-line react/no-unused-prop-types +}; +export default selectionCellFormatter; diff --git a/src/components/DataTable/selectionHeaderCellFormatter.js b/src/components/DataTable/selectionHeaderCellFormatter.js new file mode 100644 index 00000000000..1491f5bf754 --- /dev/null +++ b/src/components/DataTable/selectionHeaderCellFormatter.js @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from '../Table'; + +const selectionHeaderCellFormatter = ({ + cellProps, + column, + rows, + onSelectAllRows +}) => { + const unselectedRows = rows.filter(r => !r.selected).length > 0; + return ( + + + + ); +}; +selectionHeaderCellFormatter.propTypes = { + /** column header cell props */ + cellProps: PropTypes.object, + /** column definition */ + column: PropTypes.object, + /** current table rows */ + rows: PropTypes.array, + /** on select all rows callback */ + onSelectAllRows: PropTypes.func +}; +export default selectionHeaderCellFormatter; diff --git a/src/components/DataTable/sortableHeaderCellFormatter.js b/src/components/DataTable/sortableHeaderCellFormatter.js new file mode 100644 index 00000000000..1e2bfaa9700 --- /dev/null +++ b/src/components/DataTable/sortableHeaderCellFormatter.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from '../Table'; + +const sortableHeaderCellFormatter = ({ + cellProps, + column, + sortingColumns, + onSort +}) => { + const sortDirection = + sortingColumns[column.property] && + sortingColumns[column.property].direction; + return ( + { + onSort && onSort(e, column, sortDirection); + }} + sort + sortDirection={sortDirection} + aria-label={column.header.label} + {...cellProps} + > + {column.header.label} + + ); +}; +sortableHeaderCellFormatter.propTypes = { + /** column header cell props */ + cellProps: PropTypes.object, + /** column definition */ + column: PropTypes.object, + /** sorting object definition */ + sortingColumns: PropTypes.object, + /** onSort callback */ + onSort: PropTypes.func +}; + +export default sortableHeaderCellFormatter; diff --git a/src/components/DataTable/tableCellFormatter.js b/src/components/DataTable/tableCellFormatter.js new file mode 100644 index 00000000000..4a50c5ffd69 --- /dev/null +++ b/src/components/DataTable/tableCellFormatter.js @@ -0,0 +1,12 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from '../Table'; + +const tableCellFormatter = value => { + return {value}; +}; +tableCellFormatter.propTypes = { + /** cell value */ + value: PropTypes.node // eslint-disable-line react/no-unused-prop-types +}; +export default tableCellFormatter; diff --git a/src/components/Pagination/Paginate.test.js b/src/components/Pagination/Paginate.test.js index a5817085a56..ec8b62abd96 100644 --- a/src/components/Pagination/Paginate.test.js +++ b/src/components/Pagination/Paginate.test.js @@ -1,15 +1,12 @@ import React from 'react'; import renderer from 'react-test-renderer'; +import { PaginationRow, PAGINATION_VIEW_TYPES } from './index'; -import { PaginationRow } from './index'; - -test('PaginationRow renders', () => { +const testPaginationRowSnapshot = viewType => { const component = renderer.create( { ); let tree = component.toJSON(); expect(tree).toMatchSnapshot(); +}; + +PAGINATION_VIEW_TYPES.forEach(viewType => { + test(`PaginationRow ${viewType} renders properly`, () => { + testPaginationRowSnapshot(viewType); + }); }); test('PaginationRow.Items renders', () => { diff --git a/src/components/Pagination/Pagination.stories.js b/src/components/Pagination/Pagination.stories.js index ffd0403bc99..59de9fdffd6 100644 --- a/src/components/Pagination/Pagination.stories.js +++ b/src/components/Pagination/Pagination.stories.js @@ -1,11 +1,15 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; -import { withKnobs, number } from '@storybook/addon-knobs'; -import { bindMethods } from '../../common/helpers'; +import { withInfo } from '@storybook/addon-info'; +import { withKnobs, number, boolean, select } from '@storybook/addon-knobs'; import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../storybook/constants'; -import { PaginationRow } from './index'; +import { PaginationRow, PAGINATION_VIEW_TYPES } from './index'; +import { + MockPaginationRow, + mockPaginationSource +} from './__mocks__/mockPaginationRow'; const stories = storiesOf('Pagination', module); stories.addDecorator(withKnobs); @@ -17,50 +21,41 @@ stories.addDecorator( }) ); -class PaginationStoryWrapper extends React.Component { - constructor(props) { - super(props); - this.state = { - pagination: { - page: 1, - perPage: 6, - perPageOptions: [6, 10, 15, 25, 50] - } - }; - bindMethods(this, ['onPageInput', 'onPerPageSelect']); - } - onPageInput(e) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = e.target.value; - this.setState({ pagination: newPaginationState }); - } - onPerPageSelect(eventKey, e) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.perPage = eventKey; - this.setState({ pagination: newPaginationState }); - } - render() { - return ( - - ); - } -} - -stories.addWithInfo( +stories.add( 'Pagination row', - `Pagination row for card, list, and table views`, - () => + withInfo({ + source: false, + propTables: [ + PaginationRow, + PaginationRow.AmountOfPages, + PaginationRow.Back, + PaginationRow.ButtonGroup, + PaginationRow.Forward, + PaginationRow.Items + ], + propTablesExclude: [MockPaginationRow], + text: ( +
+

Story Source

+
{mockPaginationSource}
+
+ ) + })(() => ( + + )) ); diff --git a/src/components/Pagination/PaginationRow.js b/src/components/Pagination/PaginationRow.js index e0fbf8f1317..48bacaea5cf 100644 --- a/src/components/Pagination/PaginationRow.js +++ b/src/components/Pagination/PaginationRow.js @@ -6,6 +6,7 @@ import PaginationRowButtonGroup from './PaginationRowButtonGroup'; import PaginationRowItems from './PaginationRowItems'; import PaginationRowBack from './PaginationRowBack'; import PaginationRowForward from './PaginationRowForward'; +import { PAGINATION_VIEW_TYPES, PAGINATION_VIEW } from './constants'; import { Form, FormControl, FormGroup, ControlLabel } from '../Form'; import { DropdownButton } from '../Button'; import { MenuItem } from '../MenuItem'; @@ -15,16 +16,14 @@ import { MenuItem } from '../MenuItem'; */ const PaginationRow = ({ className, + contentView, + viewType, pagination, amountOfPages, itemCount, itemsStart, itemsEnd, messages, - contentViewPagination, - tableViewPagination, - listViewPagination, - cardViewPagination, dropdownButtonId, onPerPageSelect, onFirstPage, @@ -36,10 +35,10 @@ const PaginationRow = ({ const { page, perPage, perPageOptions } = pagination; const classes = cx( { - 'content-view-pf-pagination': contentViewPagination, - 'list-view-pf-pagination': listViewPagination, - 'card-view-pf-pagination': cardViewPagination, - 'table-view-pf-pagination': tableViewPagination, + 'content-view-pf-pagination': contentView, + 'list-view-pf-pagination': viewType === PAGINATION_VIEW.LIST, + 'card-view-pf-pagination': viewType === PAGINATION_VIEW.CARD, + 'table-view-pf-pagination': viewType === PAGINATION_VIEW.TABLE, clearfix: true }, className @@ -110,6 +109,10 @@ const PaginationRow = ({ PaginationRow.propTypes = { /** Additional css classes */ className: PropTypes.string, + /** content view pagination row */ + contentView: PropTypes.bool, + /** pagination row view type */ + viewType: PropTypes.oneOf(PAGINATION_VIEW_TYPES), /** user pagination settings */ pagination: PropTypes.object, /** calculated amount of pages */ @@ -129,14 +132,6 @@ PaginationRow.propTypes = { perPage: PropTypes.string, of: PropTypes.string }), - /** content view pagination row */ - contentViewPagination: PropTypes.bool, - /** table view pagination row */ - tableViewPagination: PropTypes.bool, - /** list view pagination row */ - listViewPagination: PropTypes.bool, - /** card view pagination row */ - cardViewPagination: PropTypes.bool, /** dropdown button id */ dropdownButtonId: PropTypes.string, /** per page selection callback */ diff --git a/src/components/Pagination/__mocks__/mockPaginationRow.js b/src/components/Pagination/__mocks__/mockPaginationRow.js new file mode 100644 index 00000000000..8a4453b540d --- /dev/null +++ b/src/components/Pagination/__mocks__/mockPaginationRow.js @@ -0,0 +1,147 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { bindMethods } from '../../../common/helpers'; +import { PaginationRow, PAGINATION_VIEW_TYPES } from '../index'; + +export class MockPaginationRow extends React.Component { + constructor(props) { + super(props); + this.state = { + pagination: { + page: 1, + perPage: 6, + perPageOptions: [6, 10, 15, 25, 50] + } + }; + bindMethods(this, ['onPageInput', 'onPerPageSelect']); + } + onPageInput(e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = e.target.value; + this.setState({ pagination: newPaginationState }); + } + onPerPageSelect(eventKey, e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.perPage = eventKey; + this.setState({ pagination: newPaginationState }); + } + render() { + const { + contentView, + viewType, + amountOfPages, + itemCount, + itemsStart, + itemsEnd, + onFirstPage, + onPreviousPage, + onNextPage, + onLastPage + } = this.props; + + return ( + + ); + } +} +MockPaginationRow.propTypes = { + contentView: PropTypes.bool, + viewType: PropTypes.oneOf(PAGINATION_VIEW_TYPES), + amountOfPages: PropTypes.number, + itemCount: PropTypes.number, + itemsStart: PropTypes.number, + itemsEnd: PropTypes.number, + onFirstPage: PropTypes.func, + onPreviousPage: PropTypes.func, + onNextPage: PropTypes.func, + onLastPage: PropTypes.func +}; + +export const mockPaginationSource = ` +import React from 'react'; +import PropTypes from 'prop-types'; +import { bindMethods } from '../../../common/helpers'; +import { PaginationRow, PAGINATION_VIEW_TYPES } from '../index'; + +export class MockPaginationRow extends React.Component { + constructor(props) { + super(props); + this.state = { + pagination: { + page: 1, + perPage: 6, + perPageOptions: [6, 10, 15, 25, 50] + } + }; + bindMethods(this, ['onPageInput', 'onPerPageSelect']); + } + onPageInput(e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = e.target.value; + this.setState({ pagination: newPaginationState }); + } + onPerPageSelect(eventKey, e) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.perPage = eventKey; + this.setState({ pagination: newPaginationState }); + } + render() { + const { + contentView, + viewType, + amountOfPages, + itemCount, + itemsStart, + itemsEnd, + onFirstPage, + onPreviousPage, + onNextPage, + onLastPage + } = this.props; + + return ( + + ); + } +} +MockPaginationRow.propTypes = { + contentView: PropTypes.bool, + viewType: PropTypes.oneOf(PAGINATION_VIEW_TYPES), + amountOfPages: PropTypes.number, + itemCount: PropTypes.number, + itemsStart: PropTypes.number, + itemsEnd: PropTypes.number, + onFirstPage: PropTypes.func, + onPreviousPage: PropTypes.func, + onNextPage: PropTypes.func, + onLastPage: PropTypes.func +}; +`; diff --git a/src/components/Pagination/__snapshots__/Paginate.test.js.snap b/src/components/Pagination/__snapshots__/Paginate.test.js.snap index 6904eb90bd6..4675166b82e 100644 --- a/src/components/Pagination/__snapshots__/Paginate.test.js.snap +++ b/src/components/Pagination/__snapshots__/Paginate.test.js.snap @@ -1,8 +1,460 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PaginationRow renders 1`] = ` +exports[`PaginationRow card renders properly 1`] = `
+
+
+ + +
+ + + לדף + + +
+
+ + + 1 + - + 15 + +   + שֶׁל +   + + 75 + + + + + + +   + שֶׁל +   + + 5 + + + +
+
+`; + +exports[`PaginationRow list renders properly 1`] = ` +
+
+
+ + +
+ + + לדף + + +
+
+ + + 1 + - + 15 + +   + שֶׁל +   + + 75 + + + + + + +   + שֶׁל +   + + 5 + + + +
+
+`; + +exports[`PaginationRow table renders properly 1`] = ` +
Date: Tue, 16 Jan 2018 21:06:56 -0500 Subject: [PATCH 08/15] added stateful paginator/pagination row --- .../__mocks__/mockClientPaginationTable.js | 2 - .../DataTable/__mocks__/mockServerApi.js | 5 +- .../__mocks__/mockServerPaginationTable.js | 146 +--- .../__snapshots__/DataTable.test.js.snap | 20 +- src/components/Pagination/Paginate.test.js | 52 +- .../Pagination/Pagination.stories.js | 33 +- src/components/Pagination/PaginationRow.js | 43 +- .../Pagination/PaginationRowArrowIcon.js | 16 + .../Pagination/PaginationRowBack.js | 6 +- .../Pagination/PaginationRowForward.js | 6 +- src/components/Pagination/Paginator.js | 144 ++++ .../Pagination/__mocks__/mockPaginationRow.js | 6 - .../__snapshots__/Paginate.test.js.snap | 644 +++++++++++++++++- src/components/Pagination/index.js | 5 + 14 files changed, 924 insertions(+), 204 deletions(-) create mode 100644 src/components/Pagination/PaginationRowArrowIcon.js create mode 100644 src/components/Pagination/Paginator.js diff --git a/src/components/DataTable/__mocks__/mockClientPaginationTable.js b/src/components/DataTable/__mocks__/mockClientPaginationTable.js index ac5dca497be..f74997a8288 100644 --- a/src/components/DataTable/__mocks__/mockClientPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockClientPaginationTable.js @@ -417,7 +417,6 @@ export class MockClientPaginationTable extends React.Component { /> this.mockRows.length ? this.mockRows.length : endOfPage + itemCount: this.mockRows.length }); }); } diff --git a/src/components/DataTable/__mocks__/mockServerPaginationTable.js b/src/components/DataTable/__mocks__/mockServerPaginationTable.js index 6e22e23ad5b..fa0a175d848 100644 --- a/src/components/DataTable/__mocks__/mockServerPaginationTable.js +++ b/src/components/DataTable/__mocks__/mockServerPaginationTable.js @@ -15,7 +15,7 @@ import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; -import { PaginationRow, PAGINATION_VIEW } from '../../Pagination'; +import { Paginator, PAGINATION_VIEW } from '../../Pagination'; import MockServerApi from './mockServerApi'; export class MockServerPaginationTable extends React.Component { @@ -27,12 +27,8 @@ export class MockServerPaginationTable extends React.Component { bindMethods(this, [ 'customHeaderFormatters', - 'onPageInput', 'onPerPageSelect', - 'onFirstPage', - 'onPreviousPage', - 'onNextPage', - 'onLastPage', + 'onPageSet', 'onRow', 'onSelectAllRows', 'onSelectRow', @@ -219,10 +215,7 @@ export class MockServerPaginationTable extends React.Component { }, // server side pagination values - amountOfPages: 0, - itemCount: 0, - itemsStart: 0, - itemsEnd: 0 + itemCount: 0 }; } @@ -248,10 +241,7 @@ export class MockServerPaginationTable extends React.Component { sortingColumns: sortingColumns, pagination: pagination, rows: response.rows, - amountOfPages: response.amountOfPages, - itemCount: response.itemCount, - itemsStart: response.itemsStart, - itemsEnd: response.itemsEnd + itemCount: response.itemCount }); }); } @@ -295,45 +285,17 @@ export class MockServerPaginationTable extends React.Component { }); } - onPageInput(e) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = e.target.value; - this.getPage(this.state.sortingColumns, newPaginationState); - } onPerPageSelect(eventKey, e) { let newPaginationState = Object.assign({}, this.state.pagination); newPaginationState.perPage = eventKey; newPaginationState.page = 1; this.getPage(this.state.sortingColumns, newPaginationState); } - onFirstPage() { + onPageSet(page) { let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = 1; + newPaginationState.page = page; this.getPage(this.state.sortingColumns, newPaginationState); } - onPreviousPage() { - if (this.state.pagination.page > 1) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page--; - this.getPage(this.state.sortingColumns, newPaginationState); - } - } - onNextPage() { - const { pagination, amountOfPages } = this.state; - if (pagination.page < amountOfPages) { - let newPaginationState = Object.assign({}, pagination); - newPaginationState.page++; - this.getPage(this.state.sortingColumns, newPaginationState); - } - } - onLastPage() { - const { pagination, amountOfPages } = this.state; - if (pagination.page < amountOfPages) { - let newPaginationState = Object.assign({}, pagination); - newPaginationState.page = amountOfPages; - this.getPage(this.state.sortingColumns, newPaginationState); - } - } onRow(row, { rowIndex }) { return { @@ -343,16 +305,7 @@ export class MockServerPaginationTable extends React.Component { } render() { - const { - columns, - pagination, - sortingColumns, - rows, - amountOfPages, - itemCount, - itemsStart, - itemsEnd - } = this.state; + const { columns, pagination, sortingColumns, rows, itemCount } = this.state; return ( @@ -380,20 +333,12 @@ export class MockServerPaginationTable extends React.Component { - ); @@ -421,7 +366,7 @@ import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; -import { PaginationRow, PAGINATION_VIEW } from '../../Pagination'; +import { Paginator, PAGINATION_VIEW } from '../../Pagination'; import MockServerApi from './mockServerApi'; export class MockServerPaginationTable extends React.Component { @@ -433,12 +378,8 @@ export class MockServerPaginationTable extends React.Component { bindMethods(this, [ 'customHeaderFormatters', - 'onPageInput', 'onPerPageSelect', - 'onFirstPage', - 'onPreviousPage', - 'onNextPage', - 'onLastPage', + 'onPageSet', 'onRow', 'onSelectAllRows', 'onSelectRow', @@ -625,10 +566,7 @@ export class MockServerPaginationTable extends React.Component { }, // server side pagination values - amountOfPages: 0, - itemCount: 0, - itemsStart: 0, - itemsEnd: 0 + itemCount: 0 }; } @@ -654,10 +592,7 @@ export class MockServerPaginationTable extends React.Component { sortingColumns: sortingColumns, pagination: pagination, rows: response.rows, - amountOfPages: response.amountOfPages, - itemCount: response.itemCount, - itemsStart: response.itemsStart, - itemsEnd: response.itemsEnd + itemCount: response.itemCount }); }); } @@ -701,45 +636,17 @@ export class MockServerPaginationTable extends React.Component { }); } - onPageInput(e) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = e.target.value; - this.getPage(this.state.sortingColumns, newPaginationState); - } onPerPageSelect(eventKey, e) { let newPaginationState = Object.assign({}, this.state.pagination); newPaginationState.perPage = eventKey; newPaginationState.page = 1; this.getPage(this.state.sortingColumns, newPaginationState); } - onFirstPage() { + onPageSet(page) { let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = 1; + newPaginationState.page = page; this.getPage(this.state.sortingColumns, newPaginationState); } - onPreviousPage() { - if (this.state.pagination.page > 1) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page--; - this.getPage(this.state.sortingColumns, newPaginationState); - } - } - onNextPage() { - const { pagination, amountOfPages } = this.state; - if (pagination.page < amountOfPages) { - let newPaginationState = Object.assign({}, pagination); - newPaginationState.page++; - this.getPage(this.state.sortingColumns, newPaginationState); - } - } - onLastPage() { - const { pagination, amountOfPages } = this.state; - if (pagination.page < amountOfPages) { - let newPaginationState = Object.assign({}, pagination); - newPaginationState.page = amountOfPages; - this.getPage(this.state.sortingColumns, newPaginationState); - } - } onRow(row, { rowIndex }) { return { @@ -749,16 +656,7 @@ export class MockServerPaginationTable extends React.Component { } render() { - const { - columns, - pagination, - sortingColumns, - rows, - amountOfPages, - itemCount, - itemsStart, - itemsEnd - } = this.state; + const { columns, pagination, sortingColumns, rows, itemCount } = this.state; return ( @@ -786,20 +684,12 @@ export class MockServerPaginationTable extends React.Component { - ); diff --git a/src/components/DataTable/__snapshots__/DataTable.test.js.snap b/src/components/DataTable/__snapshots__/DataTable.test.js.snap index 9e6db92f2cd..6f4f8ffb785 100644 --- a/src/components/DataTable/__snapshots__/DataTable.test.js.snap +++ b/src/components/DataTable/__snapshots__/DataTable.test.js.snap @@ -1044,6 +1044,7 @@ exports[`Mock Client Pagination DataTable renders 1`] = `
@@ -1173,7 +1174,7 @@ exports[`Mock Client Pagination DataTable renders 1`] = ` > @@ -1214,7 +1215,7 @@ exports[`Mock Client Pagination DataTable renders 1`] = ` > @@ -1228,7 +1229,7 @@ exports[`Mock Client Pagination DataTable renders 1`] = ` > @@ -1333,6 +1334,7 @@ exports[`Mock Server Pagination DataTable renders 1`] = `
- 0 + 1 - 0 @@ -1448,7 +1450,7 @@ exports[`Mock Server Pagination DataTable renders 1`] = ` > @@ -1462,7 +1464,7 @@ exports[`Mock Server Pagination DataTable renders 1`] = ` > @@ -1503,7 +1505,7 @@ exports[`Mock Server Pagination DataTable renders 1`] = ` > @@ -1517,7 +1519,7 @@ exports[`Mock Server Pagination DataTable renders 1`] = ` > diff --git a/src/components/Pagination/Paginate.test.js b/src/components/Pagination/Paginate.test.js index ec8b62abd96..033649f91e7 100644 --- a/src/components/Pagination/Paginate.test.js +++ b/src/components/Pagination/Paginate.test.js @@ -1,11 +1,10 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import { PaginationRow, PAGINATION_VIEW_TYPES } from './index'; +import { Paginator, PaginationRow, PAGINATION_VIEW_TYPES } from './index'; const testPaginationRowSnapshot = viewType => { const component = renderer.create( { perPage: 'לדף', of: 'שֶׁל' }} + onSubmit={jest.fn()} onPerPageSelect={jest.fn()} onFirstPage={jest.fn()} onPreviousPage={jest.fn()} @@ -100,3 +100,51 @@ test('PaginationRow.AmountOfPages renders', () => { let tree = component.toJSON(); expect(tree).toMatchSnapshot(); }); + +test('Paginator renders properly the first page', () => { + const component = renderer.create( + + ); + + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Paginator renders properly a middle page', () => { + const component = renderer.create( + + ); + + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Paginator renders properly the last page', () => { + const component = renderer.create( + + ); + + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/Pagination/Pagination.stories.js b/src/components/Pagination/Pagination.stories.js index 59de9fdffd6..1b5857c04c9 100644 --- a/src/components/Pagination/Pagination.stories.js +++ b/src/components/Pagination/Pagination.stories.js @@ -2,10 +2,10 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { withInfo } from '@storybook/addon-info'; -import { withKnobs, number, boolean, select } from '@storybook/addon-knobs'; +import { withKnobs, text, number, select } from '@storybook/addon-knobs'; import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../storybook/constants'; -import { PaginationRow, PAGINATION_VIEW_TYPES } from './index'; +import { PaginationRow, Paginator, PAGINATION_VIEW_TYPES } from './index'; import { MockPaginationRow, mockPaginationSource @@ -42,7 +42,6 @@ stories.add( ) })(() => ( )) ); + +stories.addWithInfo('Pagination row w/ state manager', '', () => { + const page = select('Page', ['1', '3', '8'], '1'); + const totalCount = select('Total items', ['75', '80', '81'], '75'); + var messages = {}; + for (var key in PaginationRow.defaultProps.messages) { + messages[key] = text(key, PaginationRow.defaultProps.messages[key]); + } + + return ( + + ); +}); diff --git a/src/components/Pagination/PaginationRow.js b/src/components/Pagination/PaginationRow.js index 48bacaea5cf..cedb3eed8c3 100644 --- a/src/components/Pagination/PaginationRow.js +++ b/src/components/Pagination/PaginationRow.js @@ -16,7 +16,6 @@ import { MenuItem } from '../MenuItem'; */ const PaginationRow = ({ className, - contentView, viewType, pagination, amountOfPages, @@ -25,6 +24,7 @@ const PaginationRow = ({ itemsEnd, messages, dropdownButtonId, + onSubmit, onPerPageSelect, onFirstPage, onPreviousPage, @@ -33,18 +33,20 @@ const PaginationRow = ({ onLastPage }) => { const { page, perPage, perPageOptions } = pagination; - const classes = cx( - { - 'content-view-pf-pagination': contentView, - 'list-view-pf-pagination': viewType === PAGINATION_VIEW.LIST, - 'card-view-pf-pagination': viewType === PAGINATION_VIEW.CARD, - 'table-view-pf-pagination': viewType === PAGINATION_VIEW.TABLE, - clearfix: true - }, - className - ); + const classes = cx(className, { + 'list-view-pf-pagination': viewType === PAGINATION_VIEW.LIST, + 'card-view-pf-pagination': viewType === PAGINATION_VIEW.CARD, + 'table-view-pf-pagination': viewType === PAGINATION_VIEW.TABLE, + clearfix: true + }); return ( - + { + e.preventDefault(); + onSubmit && onSubmit(e); + }} + > - Current Page + {messages.currentPage} { + const iconName = `angle-${name}`; + return ; +}; +PaginationRowArrowIcon.propTypes = { + /** icon name */ + name: PropTypes.oneOf(['left', 'double-left', 'right', 'double-right']) +}; +export default PaginationRowArrowIcon; diff --git a/src/components/Pagination/PaginationRowBack.js b/src/components/Pagination/PaginationRowBack.js index eecc8cdebb8..fe811f53010 100644 --- a/src/components/Pagination/PaginationRowBack.js +++ b/src/components/Pagination/PaginationRowBack.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { Icon } from '../Icon'; +import PaginationRowArrowIcon from './PaginationRowArrowIcon'; /** * PaginationRowBack component for Patternfly React @@ -27,7 +27,7 @@ const PaginationRowBack = ({ onFirstPage(e); }} > - +
  • @@ -39,7 +39,7 @@ const PaginationRowBack = ({ onPreviousPage(e); }} > - +
  • diff --git a/src/components/Pagination/PaginationRowForward.js b/src/components/Pagination/PaginationRowForward.js index 36e86381db5..72099b2792b 100644 --- a/src/components/Pagination/PaginationRowForward.js +++ b/src/components/Pagination/PaginationRowForward.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { Icon } from '../Icon'; +import PaginationRowArrowIcon from './PaginationRowArrowIcon'; /** * PaginationRowForward component for Patternfly React @@ -28,7 +28,7 @@ const PaginationRowForward = ({ onNextPage(e); }} > - +
  • @@ -40,7 +40,7 @@ const PaginationRowForward = ({ onLastPage(e); }} > - +
  • diff --git a/src/components/Pagination/Paginator.js b/src/components/Pagination/Paginator.js new file mode 100644 index 00000000000..826e1b7498f --- /dev/null +++ b/src/components/Pagination/Paginator.js @@ -0,0 +1,144 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import PaginationRow from './PaginationRow'; +import { PAGINATION_VIEW_TYPES } from './constants'; + +class Paginator extends React.Component { + constructor(props) { + super(props); + + this.initPagination(props); + + this.state = { + pagination: props.pagination + }; + } + + componentWillReceiveProps(nextProps) { + const { pagination } = nextProps; + if ( + this.props.pagination.page !== pagination.page || + this.props.pagination.perPage !== pagination.perPage + ) { + this.setState({ pagination: pagination }); + } + + this.initPagination(nextProps); + } + + initPagination(props) { + const { pagination } = props; + this.perPage = Number(pagination.perPage); + this.currentPage = Number(pagination.page); + this.itemCount = Number(props.itemCount); + } + + totalPages() { + return Math.ceil(this.props.itemCount / this.perPage); + } + + setPageRelative(diff) { + const { pagination } = this.props; + const page = Number(pagination.page) + diff; + if (page > 0 && page <= this.totalPages()) { + this.setPage(page); + } + } + + setPage(page) { + if (page !== '') { + this.props.onPageSet(Number(page)); + } else { + console.error("Page can't be blank"); + } + } + + handlePageChange(e) { + const value = Number(e.target.value); + if (value === parseInt(value, 10)) { + const newPagination = Object.assign({}, this.state.pagination); + newPagination.page = value; + this.setState({ pagination: newPagination }); + if (value <= this.totalPages() && value > 0) { + this.setPage(value); + } + } + } + + handleFormSubmit(e) { + this.setPage(this.state.pagination.page); + } + + render() { + const { pagination } = this.state; + + const { + className, + viewType, + itemCount, + messages, + dropdownButtonId, + onPerPageSelect + } = this.props; + + const itemsStart = (this.currentPage - 1) * this.perPage + 1; + const itemsEnd = Math.min(itemsStart + this.perPage - 1, this.itemCount); + const totalPages = this.totalPages(); + + return ( + this.setPage(1)} + onPreviousPage={() => this.setPageRelative(-1)} + onPageInput={e => this.handlePageChange(e)} + onNextPage={() => this.setPageRelative(1)} + onLastPage={() => this.setPage(totalPages)} + /> + ); + } +} + +Paginator.propTypes = { + /** Additional css classes */ + className: PropTypes.string, + /** pagination row view type */ + viewType: PropTypes.oneOf(PAGINATION_VIEW_TYPES), + /** user pagination settings */ + pagination: PropTypes.shape({ + /** the current page */ + page: PropTypes.number.isRequired, + /** the current per page setting */ + perPage: PropTypes.number.isRequired, + /** per page options */ + perPageOptions: PropTypes.array + }), + /** calculated number of rows */ + itemCount: PropTypes.number.isRequired, + /** message text inputs for i18n */ + messages: PropTypes.shape({ + firstPage: PropTypes.string, + previousPage: PropTypes.string, + nextPage: PropTypes.string, + lastPage: PropTypes.string, + perPage: PropTypes.string, + of: PropTypes.string + }), + /** dropdown button id */ + dropdownButtonId: PropTypes.string, + /** A callback triggered when a page is switched */ + onPageSet: PropTypes.func, + /** per page selection callback */ + onPerPageSelect: PropTypes.func +}; + +export default Paginator; diff --git a/src/components/Pagination/__mocks__/mockPaginationRow.js b/src/components/Pagination/__mocks__/mockPaginationRow.js index 8a4453b540d..6490ac71ba7 100644 --- a/src/components/Pagination/__mocks__/mockPaginationRow.js +++ b/src/components/Pagination/__mocks__/mockPaginationRow.js @@ -27,7 +27,6 @@ export class MockPaginationRow extends React.Component { } render() { const { - contentView, viewType, amountOfPages, itemCount, @@ -41,7 +40,6 @@ export class MockPaginationRow extends React.Component { return (
    @@ -162,7 +163,7 @@ exports[`PaginationRow card renders properly 1`] = ` > @@ -170,9 +171,7 @@ exports[`PaginationRow card renders properly 1`] = ` + /> @@ -217,7 +216,7 @@ exports[`PaginationRow card renders properly 1`] = ` > @@ -228,7 +227,8 @@ exports[`PaginationRow card renders properly 1`] = ` exports[`PaginationRow list renders properly 1`] = `
    @@ -388,7 +388,7 @@ exports[`PaginationRow list renders properly 1`] = ` > @@ -396,9 +396,7 @@ exports[`PaginationRow list renders properly 1`] = ` + /> @@ -443,7 +441,7 @@ exports[`PaginationRow list renders properly 1`] = ` > @@ -454,7 +452,8 @@ exports[`PaginationRow list renders properly 1`] = ` exports[`PaginationRow table renders properly 1`] = `
    @@ -614,7 +613,7 @@ exports[`PaginationRow table renders properly 1`] = ` > @@ -622,9 +621,7 @@ exports[`PaginationRow table renders properly 1`] = ` + /> @@ -669,7 +666,7 @@ exports[`PaginationRow table renders properly 1`] = ` > @@ -705,7 +702,7 @@ exports[`PaginationRow.Back renders 1`] = ` > @@ -719,7 +716,7 @@ exports[`PaginationRow.Back renders 1`] = ` > @@ -746,7 +743,7 @@ exports[`PaginationRow.Forward renders 1`] = ` > @@ -760,7 +757,7 @@ exports[`PaginationRow.Forward renders 1`] = ` > @@ -786,3 +783,594 @@ exports[`PaginationRow.Items renders 1`] = ` `; + +exports[`Paginator renders properly a middle page 1`] = ` + +
    +
    + + +
    + + + per page + + +
    +
    + + + 31 + - + 40 + +   + of +   + + 75 + + + + + + +   + of +   + + 8 + + + +
    + +`; + +exports[`Paginator renders properly the first page 1`] = ` +
    +
    +
    + + +
    + + + per page + + +
    +
    + + + 1 + - + 10 + +   + of +   + + 75 + + + + + + +   + of +   + + 8 + + + +
    +
    +`; + +exports[`Paginator renders properly the last page 1`] = ` +
    +
    +
    + + +
    + + + per page + + +
    +
    + + + 71 + - + 75 + +   + of +   + + 75 + + + + + + +   + of +   + + 8 + + + +
    +
    +`; diff --git a/src/components/Pagination/index.js b/src/components/Pagination/index.js index 741b3a679a7..a01045aa944 100644 --- a/src/components/Pagination/index.js +++ b/src/components/Pagination/index.js @@ -1,13 +1,16 @@ import paginate from './paginate'; import { PAGINATION_VIEW, PAGINATION_VIEW_TYPES } from './constants'; +import Paginator from './Paginator'; import PaginationRow from './PaginationRow'; import PaginationRowAmountOfPages from './PaginationRowAmountOfPages'; +import PaginationRowArrowIcon from './PaginationRowArrowIcon'; import PaginationRowBack from './PaginationRowBack'; import PaginationRowButtonGroup from './PaginationRowButtonGroup'; import PaginationRowForward from './PaginationRowForward'; import PaginationRowItems from './PaginationRowItems'; PaginationRow.AmountOfPages = PaginationRowAmountOfPages; +PaginationRow.ArrowIcon = PaginationRowArrowIcon; PaginationRow.Back = PaginationRowBack; PaginationRow.ButtonGroup = PaginationRowButtonGroup; PaginationRow.Forward = PaginationRowForward; @@ -17,10 +20,12 @@ PaginationRow.PAGINATION_VIEW_TYPES = PAGINATION_VIEW_TYPES; export { paginate, + Paginator, PAGINATION_VIEW, PAGINATION_VIEW_TYPES, PaginationRow, PaginationRowAmountOfPages, + PaginationRowArrowIcon, PaginationRowBack, PaginationRowButtonGroup, PaginationRowForward, From 4cc28c9f9c92e3166b8a17d0578b4655b561297c Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Wed, 17 Jan 2018 19:27:40 -0500 Subject: [PATCH 09/15] scope all reactabular components from Table instead --- less/pagination.less | 16 - less/patternfly-react.less | 1 - package-lock.json | 126 +- sass/patternfly-react/_pagination.scss | 16 - sass/patternfly-react/_patternfly-react.scss | 1 - src/components/DataTable/DataTable.stories.js | 19 - src/components/DataTable/DataTable.test.js | 20 - src/components/DataTable/Stories/index.js | 9 - .../DataTable/__mocks__/mockDataTableRows.js | 225 --- .../__snapshots__/DataTable.test.js.snap | 1530 ----------------- src/components/DataTable/constants.js | 10 - src/components/DataTable/index.js | 17 - .../Formatters}/actionHeaderCellFormatter.js | 2 +- .../customHeaderFormattersDefinition.js | 0 .../Formatters}/selectionCellFormatter.js | 2 +- .../selectionHeaderCellFormatter.js | 2 +- .../sortableHeaderCellFormatter.js | 2 +- .../Formatters}/tableCellFormatter.js | 2 +- .../Table/Stories/BootstrapTableStory.js | 128 +- .../Stories/ClientPaginationTableStory.js | 0 .../Stories/ClientSortableTableStory.js | 0 .../Table/Stories/PatternflyTableStory.js | 44 +- .../Stories/ServerPaginationTableStory.js | 0 src/components/Table/Stories/index.js | 9 + src/components/Table/Table.stories.js | 17 +- src/components/Table/Table.test.js | 39 +- ...mockColumns.js => mockBootstrapColumns.js} | 0 .../Table/__mocks__/mockBootstrapRows.js | 72 + .../__mocks__/mockClientPaginationTable.js | 14 +- .../__mocks__/mockClientSortableTable.js | 14 +- src/components/Table/__mocks__/mockRows.js | 243 ++- .../__mocks__/mockServerApi.js | 2 +- .../__mocks__/mockServerPaginationTable.js | 10 +- .../Table/__snapshots__/Table.test.js.snap | 1529 ++++++++++++++++ src/components/Table/constants.js | 11 + src/components/Table/index.js | 23 + src/index.js | 1 - 37 files changed, 2069 insertions(+), 2087 deletions(-) delete mode 100644 less/pagination.less delete mode 100644 sass/patternfly-react/_pagination.scss delete mode 100644 src/components/DataTable/DataTable.stories.js delete mode 100644 src/components/DataTable/DataTable.test.js delete mode 100644 src/components/DataTable/Stories/index.js delete mode 100644 src/components/DataTable/__mocks__/mockDataTableRows.js delete mode 100644 src/components/DataTable/__snapshots__/DataTable.test.js.snap delete mode 100644 src/components/DataTable/constants.js delete mode 100644 src/components/DataTable/index.js rename src/components/{DataTable => Table/Formatters}/actionHeaderCellFormatter.js (93%) rename src/components/{DataTable => Table/Formatters}/customHeaderFormattersDefinition.js (100%) rename src/components/{DataTable => Table/Formatters}/selectionCellFormatter.js (96%) rename src/components/{DataTable => Table/Formatters}/selectionHeaderCellFormatter.js (96%) rename src/components/{DataTable => Table/Formatters}/sortableHeaderCellFormatter.js (96%) rename src/components/{DataTable => Table/Formatters}/tableCellFormatter.js (90%) rename src/components/{DataTable => Table}/Stories/ClientPaginationTableStory.js (100%) rename src/components/{DataTable => Table}/Stories/ClientSortableTableStory.js (100%) rename src/components/{DataTable => Table}/Stories/ServerPaginationTableStory.js (100%) rename src/components/Table/__mocks__/{mockColumns.js => mockBootstrapColumns.js} (100%) create mode 100644 src/components/Table/__mocks__/mockBootstrapRows.js rename src/components/{DataTable => Table}/__mocks__/mockClientPaginationTable.js (99%) rename src/components/{DataTable => Table}/__mocks__/mockClientSortableTable.js (98%) rename src/components/{DataTable => Table}/__mocks__/mockServerApi.js (97%) rename src/components/{DataTable => Table}/__mocks__/mockServerPaginationTable.js (99%) diff --git a/less/pagination.less b/less/pagination.less deleted file mode 100644 index 60fd8bb81ee..00000000000 --- a/less/pagination.less +++ /dev/null @@ -1,16 +0,0 @@ -.content-view-pf-pagination { - .form-group { - .pagination-pf-pagesize.btn-group { - display: flex; - float: none; - margin-left: 0; - margin-right: 5px; - margin-bottom: 0; - padding-right: 13px; - width: auto; - } - .dropdown-menu { - min-width: auto; - } - } -} diff --git a/less/patternfly-react.less b/less/patternfly-react.less index 4def6398373..bbb5e95e066 100644 --- a/less/patternfly-react.less +++ b/less/patternfly-react.less @@ -1,4 +1,3 @@ /** Patternfly React Specific Extensions */ -@import 'pagination.less'; diff --git a/package-lock.json b/package-lock.json index 1d8722bef2f..80ce89724c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "@babel/types": "7.0.0-beta.36", "babylon": "7.0.0-beta.36", "debug": "3.1.0", - "globals": "11.1.0", + "globals": "11.2.0", "invariant": "2.2.2", "lodash": "4.17.4" }, @@ -87,9 +87,9 @@ } }, "globals": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.1.0.tgz", - "integrity": "sha512-uEuWt9mqTlPDwSqi+sHjD4nWU/1N+q0fiWI9T1mZpD2UENqX20CFD5T/ziLZvztPaBKl7ZylUi1q6Qfm7E2CiQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.2.0.tgz", + "integrity": "sha512-RDC7Tj17I/56wpVvCVLSXtnn2Fo6CQZ9vaj+ARn+qlzm/ozbKQZe+j9fvHZCbSq+4JSGjTpKEt7p/AA1IKXRFA==", "dev": true } } @@ -255,7 +255,7 @@ "npm-conf": "1.1.3", "npm-registry-client": "8.5.0", "read-pkg": "3.0.0", - "registry-auth-token": "3.3.1" + "registry-auth-token": "3.3.2" }, "dependencies": { "debug": { @@ -384,9 +384,9 @@ } }, "@semantic-release/release-notes-generator": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-6.0.3.tgz", - "integrity": "sha512-qvO8B6kUm5cVHdBx0uAla/clAitUR3Pyl+/6a7wSp6RqExgWfa1twq1ekVRBgCHmxX1LFFmLtL3knnSXjpXj1Q==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-6.0.4.tgz", + "integrity": "sha512-/TlcuMFh6hHpbaTiD6aAm2OegYhnCqCf6JowkFB4uHkTaHi2Zc7l/Dej7TYEd9N+auxKA6l518IYvF8K1crxfA==", "dev": true, "requires": { "conventional-changelog-angular": "1.6.0", @@ -394,7 +394,7 @@ "conventional-commits-parser": "2.1.0", "debug": "3.1.0", "get-stream": "3.0.0", - "git-url-parse": "7.0.2", + "git-url-parse": "8.0.0", "import-from": "2.1.0", "into-stream": "3.1.0", "lodash": "4.17.4" @@ -410,9 +410,9 @@ } }, "git-url-parse": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-7.0.2.tgz", - "integrity": "sha512-OQVonLdJfnGUz7Umyh9NmkZ4j9QmTB+r8ARqqSeZWlMgepn6oTrrHn7wSM5ptJQ1AcXj04xHFacpZVcXvKQAqg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-8.0.0.tgz", + "integrity": "sha512-7BqaSBLGji3qnJXDPqZZHHvzaIKZWVLFjtCyu2IItX9qDlEpB4LrbZqUVSBE63upZbmzj3EuHemdSvyx2ODvyA==", "dev": true, "requires": { "git-up": "2.0.10" @@ -543,7 +543,7 @@ "@storybook/addons": "3.3.10", "@storybook/channel-postmessage": "3.3.10", "@storybook/ui": "3.3.10", - "airbnb-js-shims": "1.4.0", + "airbnb-js-shims": "1.4.1", "autoprefixer": "7.2.5", "babel-core": "6.26.0", "babel-loader": "7.1.2", @@ -812,9 +812,9 @@ } }, "airbnb-js-shims": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/airbnb-js-shims/-/airbnb-js-shims-1.4.0.tgz", - "integrity": "sha512-KIlW3epMtB1x3PtJr2L7ltkoYvuKzjqrgZPq6mzJUL6Gz+3Y2Oc9FS9ZRzRWym2/jk1r+JsDOOyS2Vavc0E3Pw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/airbnb-js-shims/-/airbnb-js-shims-1.4.1.tgz", + "integrity": "sha512-b7S3d+DPRMwaDAs0cgKQTMLO/JG/iSehIlzEGvt2FpxIztRDDABEjWI73AfTxkSiK3/OsraPRYxVNAX3yhSNLw==", "dev": true, "requires": { "array-includes": "3.0.3", @@ -2988,7 +2988,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000794", + "caniuse-db": "1.0.30000795", "lodash.memoize": "4.1.2", "lodash.uniq": "4.5.0" }, @@ -2999,16 +2999,16 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000794", + "caniuse-db": "1.0.30000795", "electron-to-chromium": "1.3.31" } } } }, "caniuse-db": { - "version": "1.0.30000794", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000794.tgz", - "integrity": "sha1-u+cRBPonfOSzYjh9VJBei4jlLzU=", + "version": "1.0.30000795", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000795.tgz", + "integrity": "sha1-ZE8D+rAN2L0Wk+Xh5w2Gsxxc/s4=", "dev": true }, "caniuse-lite": { @@ -3986,7 +3986,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000794", + "caniuse-db": "1.0.30000795", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "5.2.18", @@ -3999,7 +3999,7 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000794", + "caniuse-db": "1.0.30000795", "electron-to-chromium": "1.3.31" } }, @@ -4391,9 +4391,9 @@ "dev": true }, "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, "domexception": { @@ -4743,7 +4743,7 @@ "file-entry-cache": "2.0.0", "functional-red-black-tree": "1.0.1", "glob": "7.1.2", - "globals": "11.1.0", + "globals": "11.2.0", "ignore": "3.3.7", "imurmurhash": "0.1.4", "inquirer": "3.3.0", @@ -4818,9 +4818,9 @@ } }, "globals": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.1.0.tgz", - "integrity": "sha512-uEuWt9mqTlPDwSqi+sHjD4nWU/1N+q0fiWI9T1mZpD2UENqX20CFD5T/ziLZvztPaBKl7ZylUi1q6Qfm7E2CiQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.2.0.tgz", + "integrity": "sha512-RDC7Tj17I/56wpVvCVLSXtnn2Fo6CQZ9vaj+ARn+qlzm/ozbKQZe+j9fvHZCbSq+4JSGjTpKEt7p/AA1IKXRFA==", "dev": true }, "inquirer": { @@ -5053,9 +5053,9 @@ "dev": true }, "eslint-plugin-react": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.5.1.tgz", - "integrity": "sha512-YGSjB9Qu6QbVTroUZi66pYky3DfoIPLdHQ/wmrBGyBRnwxQsBXAov9j2rpXt/55i8nyMv6IRWJv2s4d4YnduzQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.6.0.tgz", + "integrity": "sha512-5rTLxuZg8nJnjAVjd6aySU4NrThUNf7spX+eA179B1UJHzcIAvdqLv8Hnv/3OhtfQbtvjvE2DntPrxkSaSLPug==", "dev": true, "requires": { "doctrine": "2.1.0", @@ -8066,7 +8066,7 @@ "requires": { "jest-mock": "22.1.0", "jest-util": "22.1.4", - "jsdom": "11.6.0" + "jsdom": "11.6.1" } }, "jest-environment-node": { @@ -8126,7 +8126,7 @@ "jest-matcher-utils": "22.1.0", "jest-message-util": "22.1.0", "jest-snapshot": "22.1.2", - "source-map-support": "0.5.2" + "source-map-support": "0.5.3" }, "dependencies": { "callsites": { @@ -8136,9 +8136,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.2.tgz", - "integrity": "sha512-9zHceZbQwERaMK1MiFguvx1dL9GQPLXInr2D/wUxAsuV6ZKc9F0DHYWeloMcalkYRbtanwqUakoDjvj55cL/4A==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", + "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", "dev": true, "requires": { "source-map": "0.6.1" @@ -8425,9 +8425,9 @@ "optional": true }, "jsdom": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.6.0.tgz", - "integrity": "sha512-4lMxDCiQYK7qfVi9fKhDf2PpvXXeH/KAmcH6o0Ga7fApi8+lTBxRqGHWZ9B11SsK/pxQKOtsw413utw0M+hUrg==", + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.6.1.tgz", + "integrity": "sha512-x1vDo5CQuwsuP0w3kuU04vQdem9Q8apRV2PXp8GeSFQpgtYvSwbcypIbNgRrXu82O4TMroGYSAbu9wyVZHcehw==", "dev": true, "requires": { "abab": "1.0.4", @@ -9410,7 +9410,7 @@ "console-browserify": "1.1.0", "constants-browserify": "1.0.0", "crypto-browserify": "3.12.0", - "domain-browser": "1.1.7", + "domain-browser": "1.2.0", "events": "1.1.1", "https-browserify": "1.0.0", "os-browserify": "0.3.0", @@ -9422,7 +9422,7 @@ "stream-browserify": "2.0.1", "stream-http": "2.8.0", "string_decoder": "1.0.3", - "timers-browserify": "2.0.5", + "timers-browserify": "2.0.6", "tty-browserify": "0.0.0", "url": "0.11.0", "util": "0.10.3", @@ -10030,7 +10030,7 @@ "dev": true, "requires": { "got": "6.7.1", - "registry-auth-token": "3.3.1", + "registry-auth-token": "3.3.2", "registry-url": "3.1.0", "semver": "5.5.0" }, @@ -10208,9 +10208,9 @@ } }, "patternfly": { - "version": "3.37.7", - "resolved": "https://registry.npmjs.org/patternfly/-/patternfly-3.37.7.tgz", - "integrity": "sha1-5DHyjcfVmKqo0KLQYHXVMu/SXjc=", + "version": "3.37.8", + "resolved": "https://registry.npmjs.org/patternfly/-/patternfly-3.37.8.tgz", + "integrity": "sha1-i+A14cKFNuyuk6e1C2qlIeNTIQ4=", "requires": { "bootstrap": "3.3.7", "bootstrap-datepicker": "1.7.1", @@ -11380,7 +11380,7 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000794", + "caniuse-db": "1.0.30000795", "electron-to-chromium": "1.3.31" } }, @@ -13151,7 +13151,7 @@ "change-emitter": "0.1.6", "fbjs": "0.8.16", "hoist-non-react-statics": "2.3.1", - "symbol-observable": "1.1.0" + "symbol-observable": "1.2.0" } }, "redent": { @@ -13226,7 +13226,7 @@ "lodash": "4.17.4", "lodash-es": "4.17.4", "loose-envify": "1.3.1", - "symbol-observable": "1.1.0" + "symbol-observable": "1.2.0" } }, "regenerate": { @@ -13272,9 +13272,9 @@ } }, "registry-auth-token": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", - "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "dev": true, "requires": { "rc": "1.2.4", @@ -13795,16 +13795,16 @@ } }, "semantic-release": { - "version": "12.2.4", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-12.2.4.tgz", - "integrity": "sha512-+CAi6HN74cY2bY9Rkwb3Buti876NJ9e393RpIPb6spvXeW/Zc0mwHqHFPZBYmkuXPmr70g9B/NSka4D3w6OR4Q==", + "version": "12.2.5", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-12.2.5.tgz", + "integrity": "sha512-Wd6MShvE0qScw3zb1ZNOekKBuXue3eTBKhZf6FPUd5RwL8dh9wa7C9m+wrF56SEEX5cM1c5+m8z/3z7WWxHR2A==", "dev": true, "requires": { "@semantic-release/commit-analyzer": "5.0.1", "@semantic-release/error": "2.1.0", "@semantic-release/github": "3.0.3", "@semantic-release/npm": "2.6.4", - "@semantic-release/release-notes-generator": "6.0.3", + "@semantic-release/release-notes-generator": "6.0.4", "chalk": "2.3.0", "commander": "2.13.0", "cosmiconfig": "4.0.0", @@ -14520,9 +14520,9 @@ } }, "symbol-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz", - "integrity": "sha512-dQoid9tqQ+uotGhuTKEY11X4xhyYePVnqGSoSm3OGKh2E8LZ6RPULp1uXTctk33IeERlrRJYoVSBglsL05F5Uw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "symbol-tree": { "version": "3.2.2", @@ -14717,9 +14717,9 @@ "dev": true }, "timers-browserify": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.5.tgz", - "integrity": "sha512-BMeI1W6E2/mSaPVLUnH9rjEY1Ys2FEC/GKmE/101wusU3byZO5g68BJ5hpJEP8iD1qAJ6SzYAShGA+urHMxOzQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz", + "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", "dev": true, "requires": { "setimmediate": "1.0.5" diff --git a/sass/patternfly-react/_pagination.scss b/sass/patternfly-react/_pagination.scss deleted file mode 100644 index 60fd8bb81ee..00000000000 --- a/sass/patternfly-react/_pagination.scss +++ /dev/null @@ -1,16 +0,0 @@ -.content-view-pf-pagination { - .form-group { - .pagination-pf-pagesize.btn-group { - display: flex; - float: none; - margin-left: 0; - margin-right: 5px; - margin-bottom: 0; - padding-right: 13px; - width: auto; - } - .dropdown-menu { - min-width: auto; - } - } -} diff --git a/sass/patternfly-react/_patternfly-react.scss b/sass/patternfly-react/_patternfly-react.scss index 0c2c7601b67..c8735a3a46f 100644 --- a/sass/patternfly-react/_patternfly-react.scss +++ b/sass/patternfly-react/_patternfly-react.scss @@ -1,4 +1,3 @@ /** Patternfly React Partials */ -@import '_pagination'; diff --git a/src/components/DataTable/DataTable.stories.js b/src/components/DataTable/DataTable.stories.js deleted file mode 100644 index 64d866e811c..00000000000 --- a/src/components/DataTable/DataTable.stories.js +++ /dev/null @@ -1,19 +0,0 @@ -import { storiesOf } from '@storybook/react'; -import { withKnobs } from '@storybook/addon-knobs'; -import { - clientSortableTableAddWithInfo, - clientPaginationTableAddWithInfo, - serverPaginationTableAddWithInfo -} from './Stories'; - -const stories = storiesOf('DataTable', module); - -stories.addDecorator(withKnobs); - -/** - * DataTable stories - */ - -clientSortableTableAddWithInfo(stories); -clientPaginationTableAddWithInfo(stories); -serverPaginationTableAddWithInfo(stories); diff --git a/src/components/DataTable/DataTable.test.js b/src/components/DataTable/DataTable.test.js deleted file mode 100644 index 64b8a883ab3..00000000000 --- a/src/components/DataTable/DataTable.test.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; -import { MockClientPaginationTable } from './__mocks__/mockClientPaginationTable'; -import { MockServerPaginationTable } from './__mocks__/mockServerPaginationTable'; - -test('Mock Client Pagination DataTable renders', () => { - const component = renderer.create( - - ); - let tree = component.toJSON(); - expect(tree).toMatchSnapshot(); -}); - -test('Mock Server Pagination DataTable renders', () => { - const component = renderer.create( - - ); - let tree = component.toJSON(); - expect(tree).toMatchSnapshot(); -}); diff --git a/src/components/DataTable/Stories/index.js b/src/components/DataTable/Stories/index.js deleted file mode 100644 index b49b06725d9..00000000000 --- a/src/components/DataTable/Stories/index.js +++ /dev/null @@ -1,9 +0,0 @@ -export { - default as clientSortableTableAddWithInfo -} from './ClientSortableTableStory'; -export { - default as clientPaginationTableAddWithInfo -} from './ClientPaginationTableStory'; -export { - default as serverPaginationTableAddWithInfo -} from './ServerPaginationTableStory.js'; diff --git a/src/components/DataTable/__mocks__/mockDataTableRows.js b/src/components/DataTable/__mocks__/mockDataTableRows.js deleted file mode 100644 index e9f7af8c324..00000000000 --- a/src/components/DataTable/__mocks__/mockDataTableRows.js +++ /dev/null @@ -1,225 +0,0 @@ -/** - * mockRows made courtesy of https://swapi.co/ - */ -export const mockRows = [ - { - id: 0, - name: 'Luke Skywalker', - height: 172, - mass: 77, - hair_color: 'blond', - skin_color: 'fair', - eye_color: 'blue', - birth_year: '19BBY', - gender: 'male' - }, - { - id: 1, - name: 'C-3PO', - height: 167, - mass: 75, - hair_color: 'n/a', - skin_color: 'gold', - eye_color: 'yellow', - birth_year: '112BBY', - gender: 'n/a' - }, - { - id: 2, - name: 'R2-D2', - height: 96, - mass: 32, - hair_color: 'n/a', - skin_color: 'white, blue', - eye_color: 'red', - birth_year: '33BBY', - gender: 'n/a' - }, - { - id: 3, - name: 'Darth Vader', - height: 202, - mass: 136, - hair_color: 'none', - skin_color: 'white', - eye_color: 'yellow', - birth_year: '41.9BBY', - gender: 'male' - }, - { - id: 4, - name: 'Leia Organa', - height: 150, - mass: 49, - hair_color: 'brown', - skin_color: 'light', - eye_color: 'brown', - birth_year: '19BBY', - gender: 'female' - }, - { - id: 5, - name: 'Owen Lars', - height: 178, - mass: 120, - hair_color: 'brown, grey', - skin_color: 'light', - eye_color: 'blue', - birth_year: '52BBY', - gender: 'male' - }, - { - id: 6, - name: 'Beru Whitesun lars', - height: 165, - mass: 75, - hair_color: 'brown', - skin_color: 'light', - eye_color: 'blue', - birth_year: '47BBY', - gender: 'female' - }, - { - id: 7, - name: 'R5-D4', - height: 97, - mass: 32, - hair_color: 'n/a', - skin_color: 'white, red', - eye_color: 'red', - birth_year: 'unknown', - gender: 'n/a' - }, - { - id: 8, - name: 'Biggs Darklighter', - height: 183, - mass: 84, - hair_color: 'black', - skin_color: 'light', - eye_color: 'brown', - birth_year: '24BBY', - gender: 'male' - }, - { - id: 9, - name: 'Obi-Wan Kenobi', - height: 182, - mass: 77, - hair_color: 'auburn, white', - skin_color: 'fair', - eye_color: 'blue-gray', - birth_year: '57BBY', - gender: 'male' - }, - { - id: 10, - name: 'Anakin Skywalker', - height: 188, - mass: 84, - hair_color: 'blond', - skin_color: 'fair', - eye_color: 'blue', - birth_year: '41.9BBY', - gender: 'male' - }, - { - id: 11, - name: 'Wilhuff Tarkin', - height: 180, - mass: 'unknown', - hair_color: 'auburn, grey', - skin_color: 'fair', - eye_color: 'blue', - birth_year: '64BBY', - gender: 'male' - }, - { - id: 12, - name: 'Chewbacca', - height: 228, - mass: 112, - hair_color: 'brown', - skin_color: 'unknown', - eye_color: 'blue', - birth_year: '200BBY', - gender: 'male' - }, - { - id: 13, - name: 'Han Solo', - height: 180, - mass: 80, - hair_color: 'brown', - skin_color: 'fair', - eye_color: 'brown', - birth_year: '29BBY', - gender: 'male' - }, - { - id: 14, - name: 'Greedo', - height: 173, - mass: 74, - hair_color: 'n/a', - skin_color: 'green', - eye_color: 'black', - birth_year: '44BBY', - gender: 'male' - }, - { - id: 15, - name: 'Jabba Desilijic Tiure', - height: 175, - mass: 1358, - hair_color: 'n/a', - skin_color: 'green-tan, brown', - eye_color: 'orange', - birth_year: '600BBY', - gender: 'hermaphrodite' - }, - { - id: 16, - name: 'Wedge Antilles', - height: 170, - mass: 77, - hair_color: 'brown', - skin_color: 'fair', - eye_color: 'hazel', - birth_year: '21BBY', - gender: 'male' - }, - { - id: 17, - name: 'Jek Tono Porkins', - height: 180, - mass: 110, - hair_color: 'brown', - skin_color: 'fair', - eye_color: 'blue', - birth_year: 'unknown', - gender: 'male' - }, - { - id: 18, - name: 'Yoda', - height: 66, - mass: 17, - hair_color: 'white', - skin_color: 'green', - eye_color: 'brown', - birth_year: '896BBY', - gender: 'male' - }, - { - id: 19, - name: 'Palpatine', - height: 170, - mass: 75, - hair_color: 'grey', - skin_color: 'pale', - eye_color: 'yellow', - birth_year: '82BBY', - gender: 'male' - } -]; diff --git a/src/components/DataTable/__snapshots__/DataTable.test.js.snap b/src/components/DataTable/__snapshots__/DataTable.test.js.snap deleted file mode 100644 index 6f4f8ffb785..00000000000 --- a/src/components/DataTable/__snapshots__/DataTable.test.js.snap +++ /dev/null @@ -1,1530 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Mock Client Pagination DataTable renders 1`] = ` -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - - Name - - Height - - Eye Color - - Gender - - Birth Year - - Actions -
    - - - - Anakin Skywalker - - 188 - - blue - - male - - 41.9BBY - -
    - -
    -
    - -
    - - - - Beru Whitesun lars - - 165 - - blue - - female - - 47BBY - -
    - -
    -
    - -
    - - - - Biggs Darklighter - - 183 - - brown - - male - - 24BBY - -
    - -
    -
    - -
    - - - - C-3PO - - 167 - - yellow - - n/a - - 112BBY - -
    - -
    -
    - -
    - - - - Chewbacca - - 228 - - blue - - male - - 200BBY - -
    - -
    -
    - -
    - - - - Darth Vader - - 202 - - yellow - - male - - 41.9BBY - -
    - -
    -
    - -
    -
    -
    -
    - - -
    - - - per page - - -
    -
    - - - 1 - - - 6 - -   - of -   - - 20 - - - - - - -   - of -   - - 4 - - - -
    -
    -
    -`; - -exports[`Mock Server Pagination DataTable renders 1`] = ` -
    - - - - - - - - - - - - - -
    - - - - Name - - Height - - Eye Color - - Gender - - Birth Year - - Actions -
    -
    -
    -
    - - -
    - - - per page - - -
    -
    - - - 1 - - - 0 - -   - of -   - - 0 - - - - - - -   - of -   - - 0 - - - -
    -
    -
    -`; diff --git a/src/components/DataTable/constants.js b/src/components/DataTable/constants.js deleted file mode 100644 index d9ebfa5ba95..00000000000 --- a/src/components/DataTable/constants.js +++ /dev/null @@ -1,10 +0,0 @@ -// Reactabular sorting order allows you to specifiy sort asc/desc only and removes -// the unsorted state. This is consistent with current PF Data Table but should -// be better spelled out in our design docs. -// https://github.com/patternfly/patternfly-design/issues/516 -// https://reactabular.js.org/#/data/sorting?a=customizing-sorting-order -export const defaultSortingOrder = { - FIRST: 'asc', - asc: 'desc', - desc: 'asc' -}; diff --git a/src/components/DataTable/index.js b/src/components/DataTable/index.js deleted file mode 100644 index 3e61834c7db..00000000000 --- a/src/components/DataTable/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import actionHeaderCellFormatter from './actionHeaderCellFormatter'; -import customHeaderFormattersDefinition from './customHeaderFormattersDefinition'; -import { defaultSortingOrder } from './constants'; -import selectionCellFormatter from './selectionCellFormatter'; -import selectionHeaderCellFormatter from './selectionHeaderCellFormatter'; -import sortableHeaderCellFormatter from './sortableHeaderCellFormatter'; -import tableCellFormatter from './tableCellFormatter'; - -export { - actionHeaderCellFormatter, - customHeaderFormattersDefinition, - defaultSortingOrder, - selectionCellFormatter, - selectionHeaderCellFormatter, - sortableHeaderCellFormatter, - tableCellFormatter -}; diff --git a/src/components/DataTable/actionHeaderCellFormatter.js b/src/components/Table/Formatters/actionHeaderCellFormatter.js similarity index 93% rename from src/components/DataTable/actionHeaderCellFormatter.js rename to src/components/Table/Formatters/actionHeaderCellFormatter.js index e59c86d6faf..b38027d50c9 100644 --- a/src/components/DataTable/actionHeaderCellFormatter.js +++ b/src/components/Table/Formatters/actionHeaderCellFormatter.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Table } from '../Table'; +import { Table } from '../index'; const actionHeaderCellFormatter = (value, { column }) => { return ( diff --git a/src/components/DataTable/customHeaderFormattersDefinition.js b/src/components/Table/Formatters/customHeaderFormattersDefinition.js similarity index 100% rename from src/components/DataTable/customHeaderFormattersDefinition.js rename to src/components/Table/Formatters/customHeaderFormattersDefinition.js diff --git a/src/components/DataTable/selectionCellFormatter.js b/src/components/Table/Formatters/selectionCellFormatter.js similarity index 96% rename from src/components/DataTable/selectionCellFormatter.js rename to src/components/Table/Formatters/selectionCellFormatter.js index 3bd21b49d14..a83212ad7c9 100644 --- a/src/components/DataTable/selectionCellFormatter.js +++ b/src/components/Table/Formatters/selectionCellFormatter.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Table } from '../Table'; +import { Table } from '../index'; const selectionCellFormatter = (value, { rowData, rowIndex }, onSelectRow) => { const id = `select${rowIndex}`; diff --git a/src/components/DataTable/selectionHeaderCellFormatter.js b/src/components/Table/Formatters/selectionHeaderCellFormatter.js similarity index 96% rename from src/components/DataTable/selectionHeaderCellFormatter.js rename to src/components/Table/Formatters/selectionHeaderCellFormatter.js index 1491f5bf754..e32080938eb 100644 --- a/src/components/DataTable/selectionHeaderCellFormatter.js +++ b/src/components/Table/Formatters/selectionHeaderCellFormatter.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Table } from '../Table'; +import { Table } from '../index'; const selectionHeaderCellFormatter = ({ cellProps, diff --git a/src/components/DataTable/sortableHeaderCellFormatter.js b/src/components/Table/Formatters/sortableHeaderCellFormatter.js similarity index 96% rename from src/components/DataTable/sortableHeaderCellFormatter.js rename to src/components/Table/Formatters/sortableHeaderCellFormatter.js index 1e2bfaa9700..ea5725ec00b 100644 --- a/src/components/DataTable/sortableHeaderCellFormatter.js +++ b/src/components/Table/Formatters/sortableHeaderCellFormatter.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Table } from '../Table'; +import { Table } from '../index'; const sortableHeaderCellFormatter = ({ cellProps, diff --git a/src/components/DataTable/tableCellFormatter.js b/src/components/Table/Formatters/tableCellFormatter.js similarity index 90% rename from src/components/DataTable/tableCellFormatter.js rename to src/components/Table/Formatters/tableCellFormatter.js index 4a50c5ffd69..f869af507cf 100644 --- a/src/components/DataTable/tableCellFormatter.js +++ b/src/components/Table/Formatters/tableCellFormatter.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Table } from '../Table'; +import { Table } from '../index'; const tableCellFormatter = value => { return {value}; diff --git a/src/components/Table/Stories/BootstrapTableStory.js b/src/components/Table/Stories/BootstrapTableStory.js index 3d0b74ce19a..5560da5bb3e 100644 --- a/src/components/Table/Stories/BootstrapTableStory.js +++ b/src/components/Table/Stories/BootstrapTableStory.js @@ -1,77 +1,89 @@ import React from 'react'; import { Table } from '../index'; -import { mockRows } from '../__mocks__/mockRows'; -import { mockBootstrapColumns } from '../__mocks__/mockColumns'; +import { mockBootstrapRows } from '../__mocks__/mockBootstrapRows'; +import { mockBootstrapColumns } from '../__mocks__/mockBootstrapColumns'; + +import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../../storybook/constants'; /** * Bootstrap Table stories */ const bootstrapTableAddWithInfo = stories => { - stories.addWithInfo('Bootstrap Table Styles', '', () => ( -
    -

    Boostrap examples

    -

    Basic example

    - - Optional table caption. - - - - -

    Striped Rows

    - - - - + stories.addWithInfo('Bootstrap Table Styles', '', () => { + let story = ( +
    +

    Boostrap examples

    +

    Basic example

    + + Optional table caption. + + + -

    Bordered Table

    - - - - +

    Striped Rows

    + + + + -

    Hover Rows

    - - - - +

    Bordered Table

    + + + + -

    Condensed table

    - - - - +

    Hover Rows

    + + + + -

    Contextual Classes

    - - - { - switch (rowIndex) { - case 0: - return { className: 'active' }; - case 2: - return { className: 'success' }; - case 4: - return { className: 'warning' }; - case 6: - return { className: 'danger' }; - } - }} - /> - +

    Condensed table

    + + + + -

    Responsive Tables

    -
    - +

    Contextual Classes

    + - + { + switch (rowIndex) { + case 0: + return { className: 'active' }; + case 2: + return { className: 'success' }; + case 4: + return { className: 'warning' }; + case 6: + return { className: 'danger' }; + } + }} + /> + +

    Responsive Tables

    +
    + + + + +
    -
    - )); + ); + + return inlineTemplate({ + title: 'Table', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', + story: story + }); + }); }; export default bootstrapTableAddWithInfo; diff --git a/src/components/DataTable/Stories/ClientPaginationTableStory.js b/src/components/Table/Stories/ClientPaginationTableStory.js similarity index 100% rename from src/components/DataTable/Stories/ClientPaginationTableStory.js rename to src/components/Table/Stories/ClientPaginationTableStory.js diff --git a/src/components/DataTable/Stories/ClientSortableTableStory.js b/src/components/Table/Stories/ClientSortableTableStory.js similarity index 100% rename from src/components/DataTable/Stories/ClientSortableTableStory.js rename to src/components/Table/Stories/ClientSortableTableStory.js diff --git a/src/components/Table/Stories/PatternflyTableStory.js b/src/components/Table/Stories/PatternflyTableStory.js index 2fb27aef24d..ade0c88ba8f 100644 --- a/src/components/Table/Stories/PatternflyTableStory.js +++ b/src/components/Table/Stories/PatternflyTableStory.js @@ -1,25 +1,41 @@ import React from 'react'; import { Table } from '../index'; -import { mockRows } from '../__mocks__/mockRows'; -import { mockPatternflyColumns } from '../__mocks__/mockColumns'; +import { mockBootstrapRows } from '../__mocks__/mockBootstrapRows'; +import { mockPatternflyColumns } from '../__mocks__/mockBootstrapColumns'; + +import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; +import { DOCUMENTATION_URL } from '../../../../storybook/constants'; /** * Patternfly Table stories */ const patternflyTableAddWithInfo = stories => { - stories.addWithInfo('PatternFly Table Styles', '', () => ( -
    -

    - PatternFly recommendation: Bootstrap striped, bordered, hover, and - responsive -

    - - - - -
    - )); + stories.addWithInfo('PatternFly Table Styles', '', () => { + let story = ( +
    +

    + PatternFly recommendation: Bootstrap striped, bordered, hover, and + responsive +

    + + + + +
    + ); + return inlineTemplate({ + title: 'Table', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', + story: story + }); + }); }; export default patternflyTableAddWithInfo; diff --git a/src/components/DataTable/Stories/ServerPaginationTableStory.js b/src/components/Table/Stories/ServerPaginationTableStory.js similarity index 100% rename from src/components/DataTable/Stories/ServerPaginationTableStory.js rename to src/components/Table/Stories/ServerPaginationTableStory.js diff --git a/src/components/Table/Stories/index.js b/src/components/Table/Stories/index.js index f2041a98bba..224d16049ef 100644 --- a/src/components/Table/Stories/index.js +++ b/src/components/Table/Stories/index.js @@ -1,2 +1,11 @@ export { default as bootstrapTableAddWithInfo } from './BootstrapTableStory'; export { default as patternflyTableAddWithInfo } from './PatternflyTableStory'; +export { + default as clientSortableTableAddWithInfo +} from './ClientSortableTableStory'; +export { + default as clientPaginationTableAddWithInfo +} from './ClientPaginationTableStory'; +export { + default as serverPaginationTableAddWithInfo +} from './ServerPaginationTableStory.js'; diff --git a/src/components/Table/Table.stories.js b/src/components/Table/Table.stories.js index 5bf5795be23..69b8f2946d2 100644 --- a/src/components/Table/Table.stories.js +++ b/src/components/Table/Table.stories.js @@ -2,20 +2,14 @@ import { storiesOf } from '@storybook/react'; import { withKnobs } from '@storybook/addon-knobs'; import { bootstrapTableAddWithInfo, - patternflyTableAddWithInfo + clientSortableTableAddWithInfo, + clientPaginationTableAddWithInfo, + patternflyTableAddWithInfo, + serverPaginationTableAddWithInfo } from './Stories'; -import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; -import { DOCUMENTATION_URL } from '../../../storybook/constants'; const stories = storiesOf('Table', module); - stories.addDecorator(withKnobs); -stories.addDecorator( - defaultTemplate({ - title: 'Table', - documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_WIDGETS + '#tables' - }) -); /** * Table stories @@ -23,3 +17,6 @@ stories.addDecorator( bootstrapTableAddWithInfo(stories); patternflyTableAddWithInfo(stories); +clientSortableTableAddWithInfo(stories); +clientPaginationTableAddWithInfo(stories); +serverPaginationTableAddWithInfo(stories); diff --git a/src/components/Table/Table.test.js b/src/components/Table/Table.test.js index c85b6657433..49202c6a253 100644 --- a/src/components/Table/Table.test.js +++ b/src/components/Table/Table.test.js @@ -2,18 +2,37 @@ import React from 'react'; import renderer from 'react-test-renderer'; import { Table } from './index'; -import { mockRows } from './__mocks__/mockRows'; +import { mockBootstrapRows } from './__mocks__/mockBootstrapRows'; import { mockBootstrapColumns, mockPatternflyColumns -} from './__mocks__/mockColumns'; +} from './__mocks__/mockBootstrapColumns'; + +import { MockClientPaginationTable } from './__mocks__/mockClientPaginationTable'; +import { MockServerPaginationTable } from './__mocks__/mockServerPaginationTable'; + +test('Mock Client Pagination table renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Mock Server Pagination table renders', () => { + const component = renderer.create( + + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); test('Bootstrap basic table renders properly', () => { const component = renderer.create( Optional table caption. - + ); @@ -25,7 +44,7 @@ test('Bootstrap striped table renders properly', () => { const component = renderer.create( - + ); @@ -37,7 +56,7 @@ test('Bootstrap bordered table renders properly', () => { const component = renderer.create( - + ); @@ -49,7 +68,7 @@ test('Bootstrap hover table renders properly', () => { const component = renderer.create( - + ); @@ -61,7 +80,7 @@ test('Bootstrap condensed table renders properly', () => { const component = renderer.create( - + ); @@ -74,7 +93,7 @@ test('Bootstrap contextual classes table renders properly', () => { { switch (rowIndex) { @@ -101,7 +120,7 @@ test('Bootstrap responsive table renders properly', () => {
    - +
    ); @@ -114,7 +133,7 @@ test('Patternfly table renders properly', () => { const component = renderer.create( - + ); diff --git a/src/components/Table/__mocks__/mockColumns.js b/src/components/Table/__mocks__/mockBootstrapColumns.js similarity index 100% rename from src/components/Table/__mocks__/mockColumns.js rename to src/components/Table/__mocks__/mockBootstrapColumns.js diff --git a/src/components/Table/__mocks__/mockBootstrapRows.js b/src/components/Table/__mocks__/mockBootstrapRows.js new file mode 100644 index 00000000000..c345d073d09 --- /dev/null +++ b/src/components/Table/__mocks__/mockBootstrapRows.js @@ -0,0 +1,72 @@ +export const mockBootstrapRows = [ + { + id: 0, + first_name: 'Dan', + last_name: 'Abramov', + username: 'gaearon', + commits: 711, + additions: 272635, + location: 'London, UK', + gender: 'male' + }, + { + id: 1, + first_name: 'Sebastian', + last_name: 'Markbåge', + username: 'sebmarkbage', + commits: 476, + additions: 203610, + location: 'San Francisco, CA', + gender: 'male' + }, + { + id: 2, + first_name: 'Sophie', + last_name: 'Alpert', + username: 'sophiebits', + commits: 828, + additions: 114467, + location: 'California', + gender: 'female' + }, + { + id: 3, + first_name: 'Paul', + last_name: 'O’Shannessy', + username: 'zpao', + commits: 820, + additions: 87324, + location: 'Seattle, WA', + gender: 'male' + }, + { + id: 4, + first_name: 'Pete', + last_name: 'Hunt', + username: 'petehunt', + commits: 205, + additions: 86685, + location: 'San Francisco, CA', + gender: 'male' + }, + { + id: 5, + first_name: 'Andrew', + last_name: 'Clark', + username: 'acdlite', + commits: 320, + additions: 74162, + location: 'Redwood City, CA', + gender: 'male' + }, + { + id: 6, + first_name: 'Nathan', + last_name: 'Hunzaker', + username: 'nhunzaker', + commits: 77, + additions: 34504, + location: 'Durham, NC', + gender: 'male' + } +]; diff --git a/src/components/DataTable/__mocks__/mockClientPaginationTable.js b/src/components/Table/__mocks__/mockClientPaginationTable.js similarity index 99% rename from src/components/DataTable/__mocks__/mockClientPaginationTable.js rename to src/components/Table/__mocks__/mockClientPaginationTable.js index f74997a8288..095d25cd87a 100644 --- a/src/components/DataTable/__mocks__/mockClientPaginationTable.js +++ b/src/components/Table/__mocks__/mockClientPaginationTable.js @@ -12,15 +12,16 @@ import { selectionCellFormatter, selectionHeaderCellFormatter, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table, + TABLE_SORT_DIRECTION } from '../index'; -import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; import { PaginationRow, paginate, PAGINATION_VIEW } from '../../Pagination'; import { compose } from 'recompose'; -import { mockRows } from './mockDataTableRows'; +import { mockRows } from './mockRows'; /** * Reactabular client side paging based on the following api docs: @@ -454,15 +455,16 @@ import { selectionCellFormatter, selectionHeaderCellFormatter, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table, + TABLE_SORT_DIRECTION } from '../index'; -import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; import { PaginationRow, paginate, PAGINATION_VIEW } from '../../Pagination'; import { compose } from 'recompose'; -import { mockRows } from './mockDataTableRows'; +import { mockRows } from './mockRows'; /** * Reactabular client side paging based on the following api docs: diff --git a/src/components/DataTable/__mocks__/mockClientSortableTable.js b/src/components/Table/__mocks__/mockClientSortableTable.js similarity index 98% rename from src/components/DataTable/__mocks__/mockClientSortableTable.js rename to src/components/Table/__mocks__/mockClientSortableTable.js index 757abf2be8b..679a5b9b324 100644 --- a/src/components/DataTable/__mocks__/mockClientSortableTable.js +++ b/src/components/Table/__mocks__/mockClientSortableTable.js @@ -8,13 +8,14 @@ import { customHeaderFormattersDefinition, defaultSortingOrder, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table, + TABLE_SORT_DIRECTION } from '../index'; -import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { compose } from 'recompose'; -import { mockRows } from './mockDataTableRows'; +import { mockRows } from './mockRows'; /** * Reactabular client side data sorting based on the following api docs: @@ -270,13 +271,14 @@ import { customHeaderFormattersDefinition, defaultSortingOrder, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table, + TABLE_SORT_DIRECTION } from '../index'; -import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { compose } from 'recompose'; -import { mockRows } from './mockDataTableRows'; +import { mockRows } from './mockRows'; /** * Reactabular client side data sorting based on the following api docs: diff --git a/src/components/Table/__mocks__/mockRows.js b/src/components/Table/__mocks__/mockRows.js index a4f18deffa1..e9f7af8c324 100644 --- a/src/components/Table/__mocks__/mockRows.js +++ b/src/components/Table/__mocks__/mockRows.js @@ -1,72 +1,225 @@ +/** + * mockRows made courtesy of https://swapi.co/ + */ export const mockRows = [ { id: 0, - first_name: 'Dan', - last_name: 'Abramov', - username: 'gaearon', - commits: 711, - additions: 272635, - location: 'London, UK', + name: 'Luke Skywalker', + height: 172, + mass: 77, + hair_color: 'blond', + skin_color: 'fair', + eye_color: 'blue', + birth_year: '19BBY', gender: 'male' }, { id: 1, - first_name: 'Sebastian', - last_name: 'Markbåge', - username: 'sebmarkbage', - commits: 476, - additions: 203610, - location: 'San Francisco, CA', - gender: 'male' + name: 'C-3PO', + height: 167, + mass: 75, + hair_color: 'n/a', + skin_color: 'gold', + eye_color: 'yellow', + birth_year: '112BBY', + gender: 'n/a' }, { id: 2, - first_name: 'Sophie', - last_name: 'Alpert', - username: 'sophiebits', - commits: 828, - additions: 114467, - location: 'California', - gender: 'female' + name: 'R2-D2', + height: 96, + mass: 32, + hair_color: 'n/a', + skin_color: 'white, blue', + eye_color: 'red', + birth_year: '33BBY', + gender: 'n/a' }, { id: 3, - first_name: 'Paul', - last_name: 'O’Shannessy', - username: 'zpao', - commits: 820, - additions: 87324, - location: 'Seattle, WA', + name: 'Darth Vader', + height: 202, + mass: 136, + hair_color: 'none', + skin_color: 'white', + eye_color: 'yellow', + birth_year: '41.9BBY', gender: 'male' }, { id: 4, - first_name: 'Pete', - last_name: 'Hunt', - username: 'petehunt', - commits: 205, - additions: 86685, - location: 'San Francisco, CA', - gender: 'male' + name: 'Leia Organa', + height: 150, + mass: 49, + hair_color: 'brown', + skin_color: 'light', + eye_color: 'brown', + birth_year: '19BBY', + gender: 'female' }, { id: 5, - first_name: 'Andrew', - last_name: 'Clark', - username: 'acdlite', - commits: 320, - additions: 74162, - location: 'Redwood City, CA', + name: 'Owen Lars', + height: 178, + mass: 120, + hair_color: 'brown, grey', + skin_color: 'light', + eye_color: 'blue', + birth_year: '52BBY', gender: 'male' }, { id: 6, - first_name: 'Nathan', - last_name: 'Hunzaker', - username: 'nhunzaker', - commits: 77, - additions: 34504, - location: 'Durham, NC', + name: 'Beru Whitesun lars', + height: 165, + mass: 75, + hair_color: 'brown', + skin_color: 'light', + eye_color: 'blue', + birth_year: '47BBY', + gender: 'female' + }, + { + id: 7, + name: 'R5-D4', + height: 97, + mass: 32, + hair_color: 'n/a', + skin_color: 'white, red', + eye_color: 'red', + birth_year: 'unknown', + gender: 'n/a' + }, + { + id: 8, + name: 'Biggs Darklighter', + height: 183, + mass: 84, + hair_color: 'black', + skin_color: 'light', + eye_color: 'brown', + birth_year: '24BBY', + gender: 'male' + }, + { + id: 9, + name: 'Obi-Wan Kenobi', + height: 182, + mass: 77, + hair_color: 'auburn, white', + skin_color: 'fair', + eye_color: 'blue-gray', + birth_year: '57BBY', + gender: 'male' + }, + { + id: 10, + name: 'Anakin Skywalker', + height: 188, + mass: 84, + hair_color: 'blond', + skin_color: 'fair', + eye_color: 'blue', + birth_year: '41.9BBY', + gender: 'male' + }, + { + id: 11, + name: 'Wilhuff Tarkin', + height: 180, + mass: 'unknown', + hair_color: 'auburn, grey', + skin_color: 'fair', + eye_color: 'blue', + birth_year: '64BBY', + gender: 'male' + }, + { + id: 12, + name: 'Chewbacca', + height: 228, + mass: 112, + hair_color: 'brown', + skin_color: 'unknown', + eye_color: 'blue', + birth_year: '200BBY', + gender: 'male' + }, + { + id: 13, + name: 'Han Solo', + height: 180, + mass: 80, + hair_color: 'brown', + skin_color: 'fair', + eye_color: 'brown', + birth_year: '29BBY', + gender: 'male' + }, + { + id: 14, + name: 'Greedo', + height: 173, + mass: 74, + hair_color: 'n/a', + skin_color: 'green', + eye_color: 'black', + birth_year: '44BBY', + gender: 'male' + }, + { + id: 15, + name: 'Jabba Desilijic Tiure', + height: 175, + mass: 1358, + hair_color: 'n/a', + skin_color: 'green-tan, brown', + eye_color: 'orange', + birth_year: '600BBY', + gender: 'hermaphrodite' + }, + { + id: 16, + name: 'Wedge Antilles', + height: 170, + mass: 77, + hair_color: 'brown', + skin_color: 'fair', + eye_color: 'hazel', + birth_year: '21BBY', + gender: 'male' + }, + { + id: 17, + name: 'Jek Tono Porkins', + height: 180, + mass: 110, + hair_color: 'brown', + skin_color: 'fair', + eye_color: 'blue', + birth_year: 'unknown', + gender: 'male' + }, + { + id: 18, + name: 'Yoda', + height: 66, + mass: 17, + hair_color: 'white', + skin_color: 'green', + eye_color: 'brown', + birth_year: '896BBY', + gender: 'male' + }, + { + id: 19, + name: 'Palpatine', + height: 170, + mass: 75, + hair_color: 'grey', + skin_color: 'pale', + eye_color: 'yellow', + birth_year: '82BBY', gender: 'male' } ]; diff --git a/src/components/DataTable/__mocks__/mockServerApi.js b/src/components/Table/__mocks__/mockServerApi.js similarity index 97% rename from src/components/DataTable/__mocks__/mockServerApi.js rename to src/components/Table/__mocks__/mockServerApi.js index f68914e7d68..cf33f500202 100644 --- a/src/components/DataTable/__mocks__/mockServerApi.js +++ b/src/components/Table/__mocks__/mockServerApi.js @@ -1,4 +1,4 @@ -import { mockRows } from './mockDataTableRows'; +import { mockRows } from './mockRows'; /** * simple singleton mockServer API for server paginated table story diff --git a/src/components/DataTable/__mocks__/mockServerPaginationTable.js b/src/components/Table/__mocks__/mockServerPaginationTable.js similarity index 99% rename from src/components/DataTable/__mocks__/mockServerPaginationTable.js rename to src/components/Table/__mocks__/mockServerPaginationTable.js index fa0a175d848..dabcb09ff18 100644 --- a/src/components/DataTable/__mocks__/mockServerPaginationTable.js +++ b/src/components/Table/__mocks__/mockServerPaginationTable.js @@ -9,9 +9,10 @@ import { selectionCellFormatter, selectionHeaderCellFormatter, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table, + TABLE_SORT_DIRECTION } from '../index'; -import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; @@ -360,9 +361,10 @@ import { selectionCellFormatter, selectionHeaderCellFormatter, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table, + TABLE_SORT_DIRECTION } from '../index'; -import { Table, TABLE_SORT_DIRECTION } from '../../Table'; import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; diff --git a/src/components/Table/__snapshots__/Table.test.js.snap b/src/components/Table/__snapshots__/Table.test.js.snap index ee79a0c264d..841be978595 100644 --- a/src/components/Table/__snapshots__/Table.test.js.snap +++ b/src/components/Table/__snapshots__/Table.test.js.snap @@ -796,6 +796,1535 @@ exports[`Bootstrap striped table renders properly 1`] = ` `; +exports[`Mock Client Pagination table renders 1`] = ` +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + Name + + Height + + Eye Color + + Gender + + Birth Year + + Actions +
    + + + + Anakin Skywalker + + 188 + + blue + + male + + 41.9BBY + +
    + +
    +
    + +
    + + + + Beru Whitesun lars + + 165 + + blue + + female + + 47BBY + +
    + +
    +
    + +
    + + + + Biggs Darklighter + + 183 + + brown + + male + + 24BBY + +
    + +
    +
    + +
    + + + + C-3PO + + 167 + + yellow + + n/a + + 112BBY + +
    + +
    +
    + +
    + + + + Chewbacca + + 228 + + blue + + male + + 200BBY + +
    + +
    +
    + +
    + + + + Darth Vader + + 202 + + yellow + + male + + 41.9BBY + +
    + +
    +
    + +
    +
    +
    +
    + + +
    + + + per page + + +
    +
    + + + 1 + - + 6 + +   + of +   + + 20 + + + + + + +   + of +   + + 4 + + + +
    +
    +
    +`; + +exports[`Mock Server Pagination table renders 1`] = ` +
    + + + + + + + + + + + + + +
    + + + + Name + + Height + + Eye Color + + Gender + + Birth Year + + Actions +
    +
    +
    +
    + + +
    + + + per page + + +
    +
    + + + 1 + - + 0 + +   + of +   + + 0 + + + + + + +   + of +   + + 0 + + + +
    +
    +
    +`; + exports[`Patternfly table renders properly 1`] = ` Date: Thu, 18 Jan 2018 23:44:55 -0500 Subject: [PATCH 10/15] adds TableDropdownKebab, story descriptions, i18n fixes --- src/components/DropdownKebab/DropdownKebab.js | 29 +++- .../Pagination/Pagination.stories.js | 77 ++++++--- src/components/Pagination/PaginationRow.js | 13 +- src/components/Pagination/Paginator.js | 48 +++--- .../__snapshots__/Paginate.test.js.snap | 6 +- .../Formatters/selectionCellFormatter.js | 23 ++- .../selectionHeaderCellFormatter.js | 5 +- .../Table/Stories/BootstrapTableStory.js | 7 +- .../Stories/ClientPaginationTableStory.js | 23 +-- .../Table/Stories/ClientSortableTableStory.js | 20 +-- .../Table/Stories/PatternflyTableStory.js | 6 +- .../Stories/ServerPaginationTableStory.js | 22 ++- .../Table/Stories/tableStoryDescriptions.js | 9 + src/components/Table/TableDropdownKebab.js | 29 ++++ .../__mocks__/mockClientPaginationTable.js | 160 +++++++++++------- .../__mocks__/mockClientSortableTable.js | 12 +- .../__mocks__/mockServerPaginationTable.js | 14 +- .../Table/__snapshots__/Table.test.js.snap | 69 ++++---- src/components/Table/index.js | 6 + 19 files changed, 362 insertions(+), 216 deletions(-) create mode 100644 src/components/Table/Stories/tableStoryDescriptions.js create mode 100644 src/components/Table/TableDropdownKebab.js diff --git a/src/components/DropdownKebab/DropdownKebab.js b/src/components/DropdownKebab/DropdownKebab.js index 562f1fb96f9..3c2cd23639d 100644 --- a/src/components/DropdownKebab/DropdownKebab.js +++ b/src/components/DropdownKebab/DropdownKebab.js @@ -3,15 +3,28 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Icon } from '../Icon'; import { Dropdown } from '../Dropdown'; +import { ButtonGroup } from '../Button'; /** * DropdownKebab Component for Patternfly React */ -const DropdownKebab = ({ className, children, id, pullRight }) => { +const DropdownKebab = ({ + className, + children, + id, + pullRight, + componentClass, + toggleStyle +}) => { const kebabClass = ClassNames('dropdown-kebab-pf', className); return ( - - + + {children} @@ -26,6 +39,14 @@ DropdownKebab.propTypes = { /** kebab dropdown id */ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, /** menu right aligned */ - pullRight: PropTypes.bool + pullRight: PropTypes.bool, + /** dropdown component class */ + componentClass: PropTypes.func, + /** toggle style */ + toggleStyle: PropTypes.string +}; +DropdownKebab.defaultProps = { + componentClass: ButtonGroup, + toggleStyle: 'link' }; export default DropdownKebab; diff --git a/src/components/Pagination/Pagination.stories.js b/src/components/Pagination/Pagination.stories.js index 1b5857c04c9..b2f73c9ef10 100644 --- a/src/components/Pagination/Pagination.stories.js +++ b/src/components/Pagination/Pagination.stories.js @@ -3,8 +3,9 @@ import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { withInfo } from '@storybook/addon-info'; import { withKnobs, text, number, select } from '@storybook/addon-knobs'; -import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; +import { inlineTemplate } from '../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../storybook/constants'; + import { PaginationRow, Paginator, PAGINATION_VIEW_TYPES } from './index'; import { MockPaginationRow, @@ -13,13 +14,6 @@ import { const stories = storiesOf('Pagination', module); stories.addDecorator(withKnobs); -stories.addDecorator( - defaultTemplate({ - title: 'Pagination', - documentationLink: - DOCUMENTATION_URL.PATTERNFLY_ORG_NAVIGATION + 'pagination/' - }) -); stories.add( 'Pagination row', @@ -40,34 +34,50 @@ stories.add(
    {mockPaginationSource}
    ) - })(() => ( - - )) + })(() => { + let story = ( + + ); + return inlineTemplate({ + title: 'Pagination Row', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_NAVIGATION + 'pagination/', + story: story, + description: ( +
    + Pagination Row is a stateless functional component which exposes all + pagination callbacks (i.e.:{' '} + onFirstPage, onPreviousPage, onNextPage, onLastPage). See + Action Logger for details. +
    + ) + }); + }) ); stories.addWithInfo('Pagination row w/ state manager', '', () => { const page = select('Page', ['1', '3', '8'], '1'); const totalCount = select('Total items', ['75', '80', '81'], '75'); var messages = {}; - for (var key in PaginationRow.defaultProps.messages) { + for (let key of Object.keys(PaginationRow.defaultProps.messages)) { messages[key] = text(key, PaginationRow.defaultProps.messages[key]); } - return ( + let story = ( { messages={messages} /> ); + return inlineTemplate({ + title: 'Pagination Row with State Manager, a.k.a. Paginator', + documentationLink: + DOCUMENTATION_URL.PATTERNFLY_ORG_NAVIGATION + 'pagination/', + story: story, + description: ( +
    + Paginator is a stateful component which manages pagination state for you + and exposes a single onPageSet callback. See Action Logger for + details. +
    + ) + }); }); diff --git a/src/components/Pagination/PaginationRow.js b/src/components/Pagination/PaginationRow.js index cedb3eed8c3..b23652e6c43 100644 --- a/src/components/Pagination/PaginationRow.js +++ b/src/components/Pagination/PaginationRow.js @@ -15,9 +15,11 @@ import { MenuItem } from '../MenuItem'; * PaginationRow component for Patternfly React */ const PaginationRow = ({ + baseClassName, className, viewType, pagination, + pageInputValue, amountOfPages, itemCount, itemsStart, @@ -33,12 +35,13 @@ const PaginationRow = ({ onLastPage }) => { const { page, perPage, perPageOptions } = pagination; - const classes = cx(className, { + const classes = cx(baseClassName, className, { 'list-view-pf-pagination': viewType === PAGINATION_VIEW.LIST, 'card-view-pf-pagination': viewType === PAGINATION_VIEW.CARD, 'table-view-pf-pagination': viewType === PAGINATION_VIEW.TABLE, clearfix: true }); + const pageValue = pageInputValue !== undefined ? pageInputValue : page; return (
    @@ -109,6 +112,8 @@ const PaginationRow = ({ ); }; PaginationRow.propTypes = { + /** Base css class */ + baseClassName: PropTypes.string, /** Additional css classes */ className: PropTypes.string, /** pagination row view type */ @@ -122,6 +127,8 @@ PaginationRow.propTypes = { /** per page options */ perPageOptions: PropTypes.array }), + /** page input (optional override for page input) */ + pageInputValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), /** calculated amount of pages */ amountOfPages: PropTypes.number, /** calculated number of rows */ @@ -166,7 +173,7 @@ PaginationRow.defaultProps = { perPage: 'per page', of: 'of' }, - className: 'content-view-pf-pagination', + baseClassName: 'content-view-pf-pagination', dropdownButtonId: 'pagination-row-dropdown' }; export default PaginationRow; diff --git a/src/components/Pagination/Paginator.js b/src/components/Pagination/Paginator.js index 826e1b7498f..4e0195c6e54 100644 --- a/src/components/Pagination/Paginator.js +++ b/src/components/Pagination/Paginator.js @@ -1,16 +1,20 @@ import React from 'react'; import PropTypes from 'prop-types'; import PaginationRow from './PaginationRow'; +import { bindMethods } from '../../common/helpers'; import { PAGINATION_VIEW_TYPES } from './constants'; class Paginator extends React.Component { constructor(props) { super(props); + bindMethods(this, ['handleFormSubmit']); + this.initPagination(props); this.state = { - pagination: props.pagination + pagination: props.pagination, + pageChangeValue: props.pagination.page }; } @@ -18,9 +22,14 @@ class Paginator extends React.Component { const { pagination } = nextProps; if ( this.props.pagination.page !== pagination.page || - this.props.pagination.perPage !== pagination.perPage + this.props.pagination.perPage !== pagination.perPage || + this.props.pagination.perPageOptions.toString() !== + pagination.perPageOptions.toString() ) { - this.setState({ pagination: pagination }); + this.setState({ + pagination: pagination, + pageChangeValue: pagination.page + }); } this.initPagination(nextProps); @@ -40,37 +49,31 @@ class Paginator extends React.Component { setPageRelative(diff) { const { pagination } = this.props; const page = Number(pagination.page) + diff; - if (page > 0 && page <= this.totalPages()) { - this.setPage(page); - } + this.setPage(page); } - setPage(page) { - if (page !== '') { - this.props.onPageSet(Number(page)); - } else { - console.error("Page can't be blank"); + setPage(value) { + const page = Number(value); + if ( + !isNaN(value) && + value !== '' && + page > 0 && + page <= this.totalPages() + ) { + this.props.onPageSet(page); } } handlePageChange(e) { - const value = Number(e.target.value); - if (value === parseInt(value, 10)) { - const newPagination = Object.assign({}, this.state.pagination); - newPagination.page = value; - this.setState({ pagination: newPagination }); - if (value <= this.totalPages() && value > 0) { - this.setPage(value); - } - } + this.setState({ pageChangeValue: e.target.value }); } handleFormSubmit(e) { - this.setPage(this.state.pagination.page); + this.setPage(this.state.pageChangeValue); } render() { - const { pagination } = this.state; + const { pagination, pageChangeValue } = this.state; const { className, @@ -91,6 +94,7 @@ class Paginator extends React.Component { onSubmit={this.handleFormSubmit} viewType={viewType} pagination={pagination} + pageInputValue={pageChangeValue} amountOfPages={this.totalPages()} itemCount={itemCount} itemsStart={itemsStart} diff --git a/src/components/Pagination/__snapshots__/Paginate.test.js.snap b/src/components/Pagination/__snapshots__/Paginate.test.js.snap index 540dcd78cd1..d1a1ca5578f 100644 --- a/src/components/Pagination/__snapshots__/Paginate.test.js.snap +++ b/src/components/Pagination/__snapshots__/Paginate.test.js.snap @@ -2,7 +2,7 @@ exports[`PaginationRow card renders properly 1`] = `
    { - const id = `select${rowIndex}`; - const label = `Select row ${rowIndex}`; +const selectionCellFormatter = ( + { rowData, rowIndex }, + onSelectRow, + id, + label +) => { + const checkboxId = id || `select${rowIndex}`; + const checkboxLabel = label || `Select row ${rowIndex}`; return ( { onSelectRow && onSelectRow(e, rowData); @@ -19,13 +24,15 @@ const selectionCellFormatter = (value, { rowData, rowIndex }, onSelectRow) => { ); }; selectionCellFormatter.propTypes = { - /** cell value */ - value: PropTypes.node, // eslint-disable-line react/no-unused-prop-types /** rowData for this row */ rowData: PropTypes.object, // eslint-disable-line react/no-unused-prop-types /** rowIndex for this row */ rowIndex: PropTypes.number, // eslint-disable-line react/no-unused-prop-types /** row selected callback */ - onSelectRow: PropTypes.func // eslint-disable-line react/no-unused-prop-types + onSelectRow: PropTypes.func, // eslint-disable-line react/no-unused-prop-types + /** checkbox id override */ + id: PropTypes.string, // eslint-disable-line react/no-unused-prop-types + /** checkbox label override */ + label: PropTypes.string // eslint-disable-line react/no-unused-prop-types }; export default selectionCellFormatter; diff --git a/src/components/Table/Formatters/selectionHeaderCellFormatter.js b/src/components/Table/Formatters/selectionHeaderCellFormatter.js index e32080938eb..7af494163d7 100644 --- a/src/components/Table/Formatters/selectionHeaderCellFormatter.js +++ b/src/components/Table/Formatters/selectionHeaderCellFormatter.js @@ -9,11 +9,12 @@ const selectionHeaderCellFormatter = ({ onSelectAllRows }) => { const unselectedRows = rows.filter(r => !r.selected).length > 0; + const id = cellProps.id || 'selectAll'; return ( diff --git a/src/components/Table/Stories/BootstrapTableStory.js b/src/components/Table/Stories/BootstrapTableStory.js index 5560da5bb3e..70d087899d6 100644 --- a/src/components/Table/Stories/BootstrapTableStory.js +++ b/src/components/Table/Stories/BootstrapTableStory.js @@ -5,6 +5,7 @@ import { mockBootstrapColumns } from '../__mocks__/mockBootstrapColumns'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; +import { reactabularDescription } from './tableStoryDescriptions'; /** * Bootstrap Table stories @@ -14,7 +15,6 @@ const bootstrapTableAddWithInfo = stories => { stories.addWithInfo('Bootstrap Table Styles', '', () => { let story = (
    -

    Boostrap examples

    Basic example

    @@ -78,10 +78,11 @@ const bootstrapTableAddWithInfo = stories => { ); return inlineTemplate({ - title: 'Table', + title: 'Bootstrap Table Styles', documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', - story: story + story: story, + description: reactabularDescription }); }); }; diff --git a/src/components/Table/Stories/ClientPaginationTableStory.js b/src/components/Table/Stories/ClientPaginationTableStory.js index b7f771c681a..8c7bb5e9e0b 100644 --- a/src/components/Table/Stories/ClientPaginationTableStory.js +++ b/src/components/Table/Stories/ClientPaginationTableStory.js @@ -11,10 +11,12 @@ import { selectionCellFormatter, selectionHeaderCellFormatter, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table } from '../index'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; +import { reactabularDescription } from './tableStoryDescriptions'; /** * Client Pagination Table stories @@ -26,6 +28,15 @@ const clientPaginationTableAddWithInfo = stories => { withInfo({ source: false, propTables: [ + Table.Actions, + Table.Button, + Table.Cell, + Table.Checkbox, + Table.DropdownKebab, + Table.Heading, + Table.PfProvider, + Table.SelectionCell, + Table.SelectionHeading, actionHeaderCellFormatter, customHeaderFormattersDefinition, selectionCellFormatter, @@ -45,20 +56,12 @@ const clientPaginationTableAddWithInfo = stories => { let story = ( ); - let description = ( -
    - Client side pagination for PatternFly React Data Tables is based on - Reactabular pagination. See the following{' '} - API docs{' '} - for more details. -
    - ); return inlineTemplate({ title: 'Client Paginated Table', documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', story: story, - description: description + description: reactabularDescription }); }) ); diff --git a/src/components/Table/Stories/ClientSortableTableStory.js b/src/components/Table/Stories/ClientSortableTableStory.js index d90db049516..a94cdd52d27 100644 --- a/src/components/Table/Stories/ClientSortableTableStory.js +++ b/src/components/Table/Stories/ClientSortableTableStory.js @@ -8,10 +8,12 @@ import { actionHeaderCellFormatter, customHeaderFormattersDefinition, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table } from '../index'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; +import { reactabularDescription } from './tableStoryDescriptions'; /** * Client Sortable Table stories @@ -24,6 +26,12 @@ const clientSortableTableAddWithInfo = stories => { source: false, propTablesExclude: [MockClientSortableTable], propTables: [ + Table.Actions, + Table.Button, + Table.Cell, + Table.DropdownKebab, + Table.Heading, + Table.PfProvider, actionHeaderCellFormatter, customHeaderFormattersDefinition, sortableHeaderCellFormatter, @@ -37,20 +45,12 @@ const clientSortableTableAddWithInfo = stories => { ) })(() => { let story = ; - let description = ( -
    - Client side sorting for PatternFly React Data Tables is based on - Sortabular. See the following{' '} - API docs for - more details. -
    - ); return inlineTemplate({ title: 'Client Sortable Table', documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', story: story, - description: description + description: reactabularDescription }); }) ); diff --git a/src/components/Table/Stories/PatternflyTableStory.js b/src/components/Table/Stories/PatternflyTableStory.js index ade0c88ba8f..73f3791a55a 100644 --- a/src/components/Table/Stories/PatternflyTableStory.js +++ b/src/components/Table/Stories/PatternflyTableStory.js @@ -5,6 +5,7 @@ import { mockPatternflyColumns } from '../__mocks__/mockBootstrapColumns'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; +import { reactabularDescription } from './tableStoryDescriptions'; /** * Patternfly Table stories @@ -30,10 +31,11 @@ const patternflyTableAddWithInfo = stories => { ); return inlineTemplate({ - title: 'Table', + title: 'PatternFly Table Styles', documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', - story: story + story: story, + description: reactabularDescription }); }); }; diff --git a/src/components/Table/Stories/ServerPaginationTableStory.js b/src/components/Table/Stories/ServerPaginationTableStory.js index 7255a7fb823..cc20efb0d54 100644 --- a/src/components/Table/Stories/ServerPaginationTableStory.js +++ b/src/components/Table/Stories/ServerPaginationTableStory.js @@ -11,10 +11,12 @@ import { selectionCellFormatter, selectionHeaderCellFormatter, sortableHeaderCellFormatter, - tableCellFormatter + tableCellFormatter, + Table } from '../index'; import { inlineTemplate } from '../../../../storybook/decorators/storyTemplates'; import { DOCUMENTATION_URL } from '../../../../storybook/constants'; +import { reactabularDescription } from './tableStoryDescriptions'; /** * Server Pagination Table stories @@ -27,6 +29,15 @@ const serverPaginationTableAddWithInfo = stories => { source: false, propTablesExclude: [MockServerPaginationTable], propTables: [ + Table.Actions, + Table.Button, + Table.Cell, + Table.Checkbox, + Table.DropdownKebab, + Table.Heading, + Table.PfProvider, + Table.SelectionCell, + Table.SelectionHeading, actionHeaderCellFormatter, customHeaderFormattersDefinition, selectionCellFormatter, @@ -47,19 +58,12 @@ const serverPaginationTableAddWithInfo = stories => { onServerPageLogger={logAction('Server page requested')} /> ); - let description = ( -
    - Server side sorting for PatternFly React Data Tables is based on - Reactabular. See the following{' '} - API docs for more details. -
    - ); return inlineTemplate({ title: 'Server Paginated Table', documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_CONTENT_VIEWS + 'table-view/', story: story, - description: description + description: reactabularDescription }); }) ); diff --git a/src/components/Table/Stories/tableStoryDescriptions.js b/src/components/Table/Stories/tableStoryDescriptions.js new file mode 100644 index 00000000000..b038b670538 --- /dev/null +++ b/src/components/Table/Stories/tableStoryDescriptions.js @@ -0,0 +1,9 @@ +import React from 'react'; + +export const reactabularDescription = ( +
    + PatternFly React Tables are based on Reactabular. See{' '} + Reactabular Docs for complete + Reactabular documentation. +
    +); diff --git a/src/components/Table/TableDropdownKebab.js b/src/components/Table/TableDropdownKebab.js new file mode 100644 index 00000000000..2586990cfee --- /dev/null +++ b/src/components/Table/TableDropdownKebab.js @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { ButtonGroup } from '../Button'; +import { DropdownKebab } from '../DropdownKebab'; + +/** + * TableDropdownKebab component for Patternfly React + */ +const TableDropdownKebab = ({ children, ...props }) => { + const CustomButtonGroup = props => { + return ; + }; + + return ( + + {children} + + ); +}; +TableDropdownKebab.propTypes = { + /** children nodes */ + children: PropTypes.node +}; + +export default TableDropdownKebab; diff --git a/src/components/Table/__mocks__/mockClientPaginationTable.js b/src/components/Table/__mocks__/mockClientPaginationTable.js index 095d25cd87a..efd2c466325 100644 --- a/src/components/Table/__mocks__/mockClientPaginationTable.js +++ b/src/components/Table/__mocks__/mockClientPaginationTable.js @@ -16,7 +16,6 @@ import { Table, TABLE_SORT_DIRECTION } from '../index'; -import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; import { PaginationRow, paginate, PAGINATION_VIEW } from '../../Pagination'; @@ -45,7 +44,7 @@ export class MockClientPaginationTable extends React.Component { }) }); }, - // Use property or index dependening on the `sortingColumns` structure specified + // Use property or index dependening on the sortingColumns structure specified strategy: sort.strategies.byProperty }); @@ -61,6 +60,7 @@ export class MockClientPaginationTable extends React.Component { bindMethods(this, [ 'customHeaderFormatters', 'onPageInput', + 'onSubmit', 'onPerPageSelect', 'onFirstPage', 'onPreviousPage', @@ -68,7 +68,9 @@ export class MockClientPaginationTable extends React.Component { 'onLastPage', 'onRow', 'onSelectAllRows', - 'onSelectRow' + 'onSelectRow', + 'setPage', + 'totalPages' ]); this.state = { @@ -85,11 +87,12 @@ export class MockClientPaginationTable extends React.Component { { property: 'select', header: { - label: 'Select all rows', + label: 'Vyberte všechny řádky', props: { index: 0, rowSpan: 1, - colSpan: 1 + colSpan: 1, + id: 'vybrat vše' }, customFormatters: [selectionHeaderCellFormatter] }, @@ -100,9 +103,10 @@ export class MockClientPaginationTable extends React.Component { formatters: [ (value, { rowData, rowIndex }) => { return selectionCellFormatter( - value, { rowData, rowIndex }, - this.onSelectRow + this.onSelectRow, + `vybrat${rowIndex}`, + `vyberte řádek ${rowIndex}` ); } ] @@ -234,13 +238,13 @@ export class MockClientPaginationTable extends React.Component { , - + Action Another Action Something else here Separated link - + ]; } @@ -258,13 +262,34 @@ export class MockClientPaginationTable extends React.Component { page: 1, perPage: 6, perPageOptions: [6, 10, 15] - } + }, + + // page input value + pageChangeValue: 1 }; } + totalPages() { + const { perPage } = this.state.pagination; + return Math.ceil(mockRows.length / perPage); + } onPageInput(e) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = e.target.value; - this.setState({ pagination: newPaginationState }); + this.setState({ pageChangeValue: e.target.value }); + } + onSubmit() { + this.setPage(this.state.pageChangeValue); + } + setPage(value) { + const page = Number(value); + if ( + !isNaN(value) && + value !== '' && + page > 0 && + page <= this.totalPages() + ) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = page; + this.setState({ pagination: newPaginationState }); + } } onPerPageSelect(eventKey, e) { let newPaginationState = Object.assign({}, this.state.pagination); @@ -273,33 +298,24 @@ export class MockClientPaginationTable extends React.Component { this.setState({ pagination: newPaginationState }); } onFirstPage() { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = 1; - this.setState({ pagination: newPaginationState }); + this.setPage(1); } onPreviousPage() { if (this.state.pagination.page > 1) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page--; - this.setState({ pagination: newPaginationState }); + this.setPage(this.state.pagination.page - 1); } } onNextPage() { - const { page, perPage } = this.state.pagination; - const totalPages = Math.ceil(mockRows.length / perPage); - if (page < totalPages) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page++; - this.setState({ pagination: newPaginationState }); + const { page } = this.state.pagination; + if (page < this.totalPages()) { + this.setPage(this.state.pagination.page + 1); } } onLastPage() { - const { page, perPage } = this.state.pagination; - const totalPages = Math.ceil(mockRows.length / perPage); + const { page } = this.state.pagination; + const totalPages = this.totalPages(); if (page < totalPages) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = totalPages; - this.setState({ pagination: newPaginationState }); + this.setPage(totalPages); } } onSelectRow(event, row) { @@ -338,7 +354,7 @@ export class MockClientPaginationTable extends React.Component { return updatedSelections.indexOf(r.id) > -1 ? this.selectRow(r) : r; }); this.setState({ - // important: you must update `rows` to force a re-render and trigger `onRow` hook + // important: you must update rows to force a re-render and trigger onRow hook rows: updatedRows, selectedRows: updatedSelections }); @@ -385,7 +401,7 @@ export class MockClientPaginationTable extends React.Component { }; } render() { - const { columns, pagination, sortingColumns } = this.state; + const { columns, pagination, sortingColumns, pageChangeValue } = this.state; const sortedPaginatedRows = this.currentRows(); return ( @@ -419,8 +435,8 @@ export class MockClientPaginationTable extends React.Component { ); @@ -459,7 +476,6 @@ import { Table, TABLE_SORT_DIRECTION } from '../index'; -import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; import { PaginationRow, paginate, PAGINATION_VIEW } from '../../Pagination'; @@ -504,6 +520,7 @@ export class MockClientPaginationTable extends React.Component { bindMethods(this, [ 'customHeaderFormatters', 'onPageInput', + 'onSubmit', 'onPerPageSelect', 'onFirstPage', 'onPreviousPage', @@ -511,7 +528,9 @@ export class MockClientPaginationTable extends React.Component { 'onLastPage', 'onRow', 'onSelectAllRows', - 'onSelectRow' + 'onSelectRow', + 'setPage', + 'totalPages' ]); this.state = { @@ -528,11 +547,12 @@ export class MockClientPaginationTable extends React.Component { { property: 'select', header: { - label: 'Select all rows', + label: 'Vyberte všechny řádky', props: { index: 0, rowSpan: 1, - colSpan: 1 + colSpan: 1, + id: 'vybrat vše' }, customFormatters: [selectionHeaderCellFormatter] }, @@ -543,9 +563,9 @@ export class MockClientPaginationTable extends React.Component { formatters: [ (value, { rowData, rowIndex }) => { return selectionCellFormatter( - value, { rowData, rowIndex }, - this.onSelectRow + this.onSelectRow, + \`vybrat \${rowIndex}\`, \`vyberte řádek \${rowIndex}\` ); } ] @@ -677,13 +697,13 @@ export class MockClientPaginationTable extends React.Component { , - + Action Another Action Something else here Separated link - + ]; } @@ -701,13 +721,34 @@ export class MockClientPaginationTable extends React.Component { page: 1, perPage: 6, perPageOptions: [6, 10, 15] - } + }, + + // page input value + pageChangeValue: 1 }; } + totalPages() { + const { perPage } = this.state.pagination; + return Math.ceil(mockRows.length / perPage); + } onPageInput(e) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = e.target.value; - this.setState({ pagination: newPaginationState }); + this.setState({ pageChangeValue: e.target.value }); + } + onSubmit() { + this.setPage(this.state.pageChangeValue); + } + setPage(value) { + const page = Number(value); + if ( + !isNaN(value) && + value !== '' && + page > 0 && + page <= this.totalPages() + ) { + let newPaginationState = Object.assign({}, this.state.pagination); + newPaginationState.page = page; + this.setState({ pagination: newPaginationState }); + } } onPerPageSelect(eventKey, e) { let newPaginationState = Object.assign({}, this.state.pagination); @@ -716,33 +757,24 @@ export class MockClientPaginationTable extends React.Component { this.setState({ pagination: newPaginationState }); } onFirstPage() { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = 1; - this.setState({ pagination: newPaginationState }); + this.setPage(1); } onPreviousPage() { if (this.state.pagination.page > 1) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page--; - this.setState({ pagination: newPaginationState }); + this.setPage(this.state.pagination.page - 1); } } onNextPage() { - const { page, perPage } = this.state.pagination; - const totalPages = Math.ceil(mockRows.length / perPage); - if (page < totalPages) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page++; - this.setState({ pagination: newPaginationState }); + const { page } = this.state.pagination; + if (page < this.totalPages()) { + this.setPage(this.state.pagination.page + 1); } } onLastPage() { - const { page, perPage } = this.state.pagination; - const totalPages = Math.ceil(mockRows.length / perPage); + const { page } = this.state.pagination; + const totalPages = this.totalPages(); if (page < totalPages) { - let newPaginationState = Object.assign({}, this.state.pagination); - newPaginationState.page = totalPages; - this.setState({ pagination: newPaginationState }); + this.setPage(totalPages); } } onSelectRow(event, row) { @@ -828,7 +860,7 @@ export class MockClientPaginationTable extends React.Component { }; } render() { - const { columns, pagination, sortingColumns } = this.state; + const { columns, pagination, sortingColumns, pageChangeValue } = this.state; const sortedPaginatedRows = this.currentRows(); return ( @@ -863,6 +895,7 @@ export class MockClientPaginationTable extends React.Component { ); diff --git a/src/components/Table/__mocks__/mockClientSortableTable.js b/src/components/Table/__mocks__/mockClientSortableTable.js index 679a5b9b324..ec1afbe10d0 100644 --- a/src/components/Table/__mocks__/mockClientSortableTable.js +++ b/src/components/Table/__mocks__/mockClientSortableTable.js @@ -12,7 +12,6 @@ import { Table, TABLE_SORT_DIRECTION } from '../index'; -import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { compose } from 'recompose'; import { mockRows } from './mockRows'; @@ -41,7 +40,7 @@ export class MockClientSortableTable extends React.Component { }) }); }, - // Use property or index dependening on the `sortingColumns` structure specified + // Use property or index dependening on the sortingColumns structure specified strategy: sort.strategies.byProperty }); @@ -195,13 +194,13 @@ export class MockClientSortableTable extends React.Component { , - + Action Another Action Something else here Separated link - + ]; } @@ -275,7 +274,6 @@ import { Table, TABLE_SORT_DIRECTION } from '../index'; -import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { compose } from 'recompose'; import { mockRows } from './mockRows'; @@ -458,13 +456,13 @@ export class MockClientSortableTable extends React.Component { , - + Action Another Action Something else here Separated link - + ]; } diff --git a/src/components/Table/__mocks__/mockServerPaginationTable.js b/src/components/Table/__mocks__/mockServerPaginationTable.js index dabcb09ff18..b13787e4e7b 100644 --- a/src/components/Table/__mocks__/mockServerPaginationTable.js +++ b/src/components/Table/__mocks__/mockServerPaginationTable.js @@ -13,7 +13,6 @@ import { Table, TABLE_SORT_DIRECTION } from '../index'; -import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; import { Paginator, PAGINATION_VIEW } from '../../Pagination'; @@ -65,7 +64,6 @@ export class MockServerPaginationTable extends React.Component { formatters: [ (value, { rowData, rowIndex }) => { return selectionCellFormatter( - value, { rowData, rowIndex }, this.onSelectRow ); @@ -189,13 +187,13 @@ export class MockServerPaginationTable extends React.Component { , - + Action Another Action Something else here Separated link - + ]; } @@ -248,7 +246,7 @@ export class MockServerPaginationTable extends React.Component { } onSort(e, column, sortDirection) { - // Clearing existing `sortingColumns` does simple single column sort. To do multisort, + // Clearing existing sortingColumns does simple single column sort. To do multisort, // set each column based on existing sorts specified and set sort position. const updatedSortingColumns = { [column.property]: { @@ -365,7 +363,6 @@ import { Table, TABLE_SORT_DIRECTION } from '../index'; -import { DropdownKebab } from '../../DropdownKebab'; import { MenuItem } from '../../MenuItem'; import { Grid } from '../../Grid'; import { Paginator, PAGINATION_VIEW } from '../../Pagination'; @@ -417,7 +414,6 @@ export class MockServerPaginationTable extends React.Component { formatters: [ (value, { rowData, rowIndex }) => { return selectionCellFormatter( - value, { rowData, rowIndex }, this.onSelectRow ); @@ -541,13 +537,13 @@ export class MockServerPaginationTable extends React.Component { , - + Action Another Action Something else here Separated link - + ]; } diff --git a/src/components/Table/__snapshots__/Table.test.js.snap b/src/components/Table/__snapshots__/Table.test.js.snap index 841be978595..dd3882b912f 100644 --- a/src/components/Table/__snapshots__/Table.test.js.snap +++ b/src/components/Table/__snapshots__/Table.test.js.snap @@ -807,21 +807,22 @@ exports[`Mock Client Pagination table renders 1`] = `
    Optional table caption.
    @@ -897,13 +898,13 @@ exports[`Mock Client Pagination table renders 1`] = ` > @@ -953,12 +954,12 @@ exports[`Mock Client Pagination table renders 1`] = ` className="table-view-pf-actions" >
    +   - לדף -
    +   - לדף -
    +   - לדף -
    +   - per page -
    +   - per page -
    +   - per page -
    +   - per page -
    +   - per page -
    Date: Wed, 24 Jan 2018 08:20:03 -0500 Subject: [PATCH 14/15] removed state from paginator --- src/components/Pagination/Paginator.js | 16 +++++----------- .../Table/__mocks__/mockClientPaginationTable.js | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/components/Pagination/Paginator.js b/src/components/Pagination/Paginator.js index 790b1b4f247..77f6f5a4023 100644 --- a/src/components/Pagination/Paginator.js +++ b/src/components/Pagination/Paginator.js @@ -13,22 +13,15 @@ class Paginator extends React.Component { this.initPagination(props); this.state = { - pagination: props.pagination, pageChangeValue: props.pagination.page }; } componentWillReceiveProps(nextProps) { const { pagination } = nextProps; - if ( - this.props.pagination.page !== pagination.page || - this.props.pagination.perPage !== pagination.perPage || - this.props.pagination.perPageOptions.toString() !== - pagination.perPageOptions.toString() - ) { + if (this.state.pageChangeValue !== pagination.page) { this.setState({ - pagination: pagination, - pageChangeValue: pagination.page + pageChangeValue: Number(pagination.page) }); } @@ -73,7 +66,7 @@ class Paginator extends React.Component { } render() { - const { pagination, pageChangeValue } = this.state; + const { pageChangeValue } = this.state; const { className, @@ -81,7 +74,8 @@ class Paginator extends React.Component { itemCount, messages, dropdownButtonId, - onPerPageSelect + onPerPageSelect, + pagination } = this.props; const itemsStart = (this.currentPage - 1) * this.perPage + 1; diff --git a/src/components/Table/__mocks__/mockClientPaginationTable.js b/src/components/Table/__mocks__/mockClientPaginationTable.js index 3bf60dd036e..6e173bda2b6 100644 --- a/src/components/Table/__mocks__/mockClientPaginationTable.js +++ b/src/components/Table/__mocks__/mockClientPaginationTable.js @@ -747,7 +747,7 @@ export class MockClientPaginationTable extends React.Component { ) { let newPaginationState = Object.assign({}, this.state.pagination); newPaginationState.page = page; - this.setState({ pagination: newPaginationState }); + this.setState({ pagination: newPaginationState, pageChangeValue: page }); } } onPerPageSelect(eventKey, e) { From 08ddd9fb5c96bc18b1b243a70e20932a4eb39576 Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Fri, 26 Jan 2018 14:26:22 -0500 Subject: [PATCH 15/15] remove per page non breaking space --- src/components/Pagination/PaginationRow.js | 1 - .../Pagination/__snapshots__/Paginate.test.js.snap | 6 ------ src/components/Table/__snapshots__/Table.test.js.snap | 2 -- 3 files changed, 9 deletions(-) diff --git a/src/components/Pagination/PaginationRow.js b/src/components/Pagination/PaginationRow.js index bedcd8ab50b..96f23441a1b 100644 --- a/src/components/Pagination/PaginationRow.js +++ b/src/components/Pagination/PaginationRow.js @@ -67,7 +67,6 @@ const PaginationRow = ({ ); })} -   {messages.perPage} diff --git a/src/components/Pagination/__snapshots__/Paginate.test.js.snap b/src/components/Pagination/__snapshots__/Paginate.test.js.snap index 7ebe3afec9c..e1e332d3f21 100644 --- a/src/components/Pagination/__snapshots__/Paginate.test.js.snap +++ b/src/components/Pagination/__snapshots__/Paginate.test.js.snap @@ -110,7 +110,6 @@ exports[`PaginationRow card renders properly 1`] = `
    -   לדף @@ -334,7 +333,6 @@ exports[`PaginationRow list renders properly 1`] = ` -   לדף @@ -558,7 +556,6 @@ exports[`PaginationRow table renders properly 1`] = ` -   לדף @@ -861,7 +858,6 @@ exports[`Paginator renders properly a middle page 1`] = ` -   per page @@ -1057,7 +1053,6 @@ exports[`Paginator renders properly the first page 1`] = ` -   per page @@ -1253,7 +1248,6 @@ exports[`Paginator renders properly the last page 1`] = ` -   per page diff --git a/src/components/Table/__snapshots__/Table.test.js.snap b/src/components/Table/__snapshots__/Table.test.js.snap index 64e0df959fd..5e1a6f7031a 100644 --- a/src/components/Table/__snapshots__/Table.test.js.snap +++ b/src/components/Table/__snapshots__/Table.test.js.snap @@ -1918,7 +1918,6 @@ exports[`Mock Client Pagination table renders 1`] = ` -   per page @@ -2207,7 +2206,6 @@ exports[`Mock Server Pagination table renders 1`] = ` -   per page