From 02e67019dd60aa59f54ac44f6a1bb89cadb46dd9 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 25 Mar 2019 18:21:18 -0700 Subject: [PATCH 1/8] fix(list): remove tabblableOnListItemFocus and added back listItemClassNames and listItemAttr --- package-lock.json | 265 ++++++++++++++---------- packages/list/ListItem.tsx | 8 +- packages/list/ListItemGraphic.tsx | 4 +- packages/list/ListItemMeta.tsx | 6 +- packages/list/ListItemText.tsx | 6 +- packages/list/README.md | 3 - packages/list/index.tsx | 112 +++++++--- test/screenshot/list/index.tsx | 189 +++++++++-------- test/unit/list/ListItemGraphic.test.tsx | 14 -- test/unit/list/ListItemMeta.test.tsx | 14 -- test/unit/list/ListItemText.test.tsx | 14 -- 11 files changed, 357 insertions(+), 278 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a0a1000a..839118093 100644 --- a/package-lock.json +++ b/package-lock.json @@ -692,21 +692,6 @@ "tslib": "^1.9.3" } }, - "@material/list": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/list/-/list-0.41.0.tgz", - "integrity": "sha512-HhYN0I02CTT8j91c1eeeI+L2KXVKdfzj0Zuapp2SdeCmQZLJO2tu2NYj0W6REBDTVBWBccr12Sn8o71CodEScQ==", - "dev": true, - "requires": { - "@material/base": "^0.41.0", - "@material/dom": "^0.41.0", - "@material/ripple": "^0.41.0", - "@material/rtl": "^0.40.1", - "@material/shape": "^0.41.0", - "@material/theme": "^0.41.0", - "@material/typography": "^0.41.0" - } - }, "@material/ripple": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-1.0.1.tgz", @@ -809,23 +794,51 @@ "dev": true }, "@material/floating-label": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-0.41.0.tgz", - "integrity": "sha512-qI6f1nZU3crXxWAI9fw3U5fHw2qOzEor49EvskbcaV5KSRW5qO+jtfUQ3ib/Vhki7lqhgwNHB/0n7KYhvhjRHQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-1.0.0.tgz", + "integrity": "sha512-yESFi8HEUO0PmPWvaU3VW4X8+xaoFHhj2xGxLPu2hGye4ZBjTpmjOX6y1vwYqZLD5KWXj91k695UAT6J2wtWFQ==", "dev": true, "requires": { - "@material/animation": "^0.41.0", - "@material/base": "^0.41.0", - "@material/rtl": "^0.40.1", - "@material/theme": "^0.41.0", - "@material/typography": "^0.41.0" + "@material/animation": "^1.0.0", + "@material/base": "^1.0.0", + "@material/rtl": "^0.42.0", + "@material/theme": "^1.0.0", + "@material/typography": "^1.0.0", + "tslib": "^1.9.3" }, "dependencies": { - "@material/base": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/base/-/base-0.41.0.tgz", - "integrity": "sha512-tEyzwBRu3d1H120SfKsDVYZHcqT5lKohh/7cWKR93aAaPDkSvjpKJIjyu2yuSkjpDduVZGzVocYbOvhUKhhzXQ==", + "@material/animation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-1.0.0.tgz", + "integrity": "sha512-Ed5/vggn6ZhSJ87yn3ZS1d826VJNFz73jHF2bSsgRtHDoB8KCuOwQMfdgAgDa4lKDF6CDIPCKBZPKrs2ubehdw==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, + "@material/rtl": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-0.42.0.tgz", + "integrity": "sha512-VrnrKJzhmspsN8WXHuxxBZ69yM5IwhCUqWr1t1eNfw3ZEvEj7i1g3P31HGowKThIN1dc1Wh4LE14rCISWCtv5w==", "dev": true + }, + "@material/theme": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-1.0.0.tgz", + "integrity": "sha512-Bg/BQLU5MmCwtQ3DHcSs9DodZB8PTvuItv1wXrP54S/wBVwryIB5uMDmERhnItbNnAFbkKhlAuhn1asMmMzfkQ==", + "dev": true, + "requires": { + "@material/feature-targeting": "^0.44.1" + } + }, + "@material/typography": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-1.0.0.tgz", + "integrity": "sha512-Oeqbjci1cC7jTE8/n3dwnkqKe9ZeWiaE+rgMtRYtRFw1HvAw14SpGA5EEAS/Li2Hu2KZ50FYCe3HYqShfxtChA==", + "dev": true, + "requires": { + "@material/feature-targeting": "^0.44.1" + } } } }, @@ -942,6 +955,15 @@ "tslib": "^1.9.3" }, "dependencies": { + "@material/animation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-1.0.0.tgz", + "integrity": "sha512-Ed5/vggn6ZhSJ87yn3ZS1d826VJNFz73jHF2bSsgRtHDoB8KCuOwQMfdgAgDa4lKDF6CDIPCKBZPKrs2ubehdw==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, "@material/dom": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@material/dom/-/dom-1.0.1.tgz", @@ -951,6 +973,20 @@ "tslib": "^1.9.3" } }, + "@material/ripple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-1.0.1.tgz", + "integrity": "sha512-aBigRoVMjIU2lLDq7TMocI2H2YFbO1hICs5FTdSRp4Yis/QFTrgaW32q8yuHdZI56j+b2BWIWapqA2xpSmCMXQ==", + "dev": true, + "requires": { + "@material/animation": "^1.0.0", + "@material/base": "^1.0.0", + "@material/dom": "^1.0.1", + "@material/feature-targeting": "^0.44.1", + "@material/theme": "^1.0.0", + "tslib": "^1.9.3" + } + }, "@material/rtl": { "version": "0.42.0", "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-0.42.0.tgz", @@ -1684,6 +1720,19 @@ "integrity": "sha512-tEyzwBRu3d1H120SfKsDVYZHcqT5lKohh/7cWKR93aAaPDkSvjpKJIjyu2yuSkjpDduVZGzVocYbOvhUKhhzXQ==", "dev": true }, + "@material/floating-label": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-0.41.0.tgz", + "integrity": "sha512-qI6f1nZU3crXxWAI9fw3U5fHw2qOzEor49EvskbcaV5KSRW5qO+jtfUQ3ib/Vhki7lqhgwNHB/0n7KYhvhjRHQ==", + "dev": true, + "requires": { + "@material/animation": "^0.41.0", + "@material/base": "^0.41.0", + "@material/rtl": "^0.40.1", + "@material/theme": "^0.41.0", + "@material/typography": "^0.41.0" + } + }, "@material/line-ripple": { "version": "0.41.0", "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-0.41.0.tgz", @@ -17682,7 +17731,7 @@ }, "ts-loader": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-3.5.0.tgz", + "resolved": "http://registry.npmjs.org/ts-loader/-/ts-loader-3.5.0.tgz", "integrity": "sha512-JTia3kObhTk36wPFgy0RnkZReiusYx7Le9IhcUWRrCTcFcr6Dy1zGsFd3x8DG4gevlbN65knI8W50FfoykXcng==", "dev": true, "requires": { @@ -18706,28 +18755,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "resolved": "", + "resolved": false, "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, @@ -18738,14 +18787,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -18756,28 +18805,28 @@ }, "chownr": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true @@ -18791,7 +18840,7 @@ }, "debug": { "version": "2.6.9", - "resolved": "", + "resolved": false, "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, @@ -18801,28 +18850,28 @@ }, "deep-extend": { "version": "0.5.1", - "resolved": "", + "resolved": false, "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -18832,14 +18881,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -18856,7 +18905,7 @@ }, "glob": { "version": "7.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "optional": true, @@ -18871,14 +18920,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "resolved": "", + "resolved": false, "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, "optional": true, @@ -18888,7 +18937,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -18898,7 +18947,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -18909,21 +18958,21 @@ }, "inherits": { "version": "2.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -18940,7 +18989,7 @@ }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -18950,14 +18999,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": "", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "optional": true, @@ -18968,7 +19017,7 @@ }, "minizlib": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, "optional": true, @@ -18978,7 +19027,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -18988,14 +19037,14 @@ }, "ms": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "dev": true, "optional": true, @@ -19007,7 +19056,7 @@ }, "node-pre-gyp": { "version": "0.10.0", - "resolved": "", + "resolved": false, "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, @@ -19026,7 +19075,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -19037,14 +19086,14 @@ }, "npm-bundled": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "resolved": "", + "resolved": false, "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, @@ -19055,7 +19104,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -19068,21 +19117,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -19092,21 +19141,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -19117,7 +19166,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true @@ -19131,7 +19180,7 @@ }, "rc": { "version": "1.2.7", - "resolved": "", + "resolved": false, "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, @@ -19144,7 +19193,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -19169,7 +19218,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": "", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, @@ -19179,49 +19228,49 @@ }, "safe-buffer": { "version": "5.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.5.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -19243,7 +19292,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -19253,14 +19302,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.1", - "resolved": "", + "resolved": false, "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, "optional": true, @@ -19283,7 +19332,7 @@ }, "wide-align": { "version": "1.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, @@ -19293,14 +19342,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "dev": true, "optional": true @@ -19763,21 +19812,21 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true @@ -19795,14 +19844,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -19813,35 +19862,35 @@ }, "chownr": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true @@ -19858,21 +19907,21 @@ }, "deep-extend": { "version": "0.5.1", - "resolved": "", + "resolved": false, "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true @@ -19889,7 +19938,7 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true @@ -19928,7 +19977,7 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true @@ -19945,7 +19994,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -19966,14 +20015,14 @@ }, "inherits": { "version": "2.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true @@ -19990,14 +20039,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, diff --git a/packages/list/ListItem.tsx b/packages/list/ListItem.tsx index b0f75abea..6ba6f5052 100644 --- a/packages/list/ListItem.tsx +++ b/packages/list/ListItem.tsx @@ -34,6 +34,7 @@ export interface ListItemProps extends React.HTMLProps { tag?: string; activated?: boolean; selected?: boolean; + onDestroy?: () => void; }; // TODO: convert to functional component @@ -53,9 +54,14 @@ export default class ListItem extends React onClick: () => {}, onFocus: () => {}, onBlur: () => {}, + onDestroy: () => {}, tag: 'li', }; + componentWillUnmount() { + this.props.onDestroy!(); + } + get classes() { const {className, activated, selected} = this.props; return classnames('mdc-list-item', className, { @@ -84,9 +90,9 @@ export default class ListItem extends React role, checkboxList, radioList, + onDestroy, /* eslint-enable no-unused-vars */ tag: Tag, - ...otherProps } = this.props; return ( diff --git a/packages/list/ListItemGraphic.tsx b/packages/list/ListItemGraphic.tsx index 45e986d5a..cdbf2bae5 100644 --- a/packages/list/ListItemGraphic.tsx +++ b/packages/list/ListItemGraphic.tsx @@ -24,7 +24,6 @@ import * as React from 'react'; import classnames from 'classnames'; export interface ListItemGraphicProps { - tabbableOnListItemFocus?: boolean; className?: string; tabIndex?: number; graphic: React.ReactElement; @@ -34,13 +33,12 @@ export interface ListItemGraphicProps { const ListItemGraphic:React.FunctionComponent = ({ tabIndex = -1, // eslint-disable-line no-unused-vars graphic, - tabbableOnListItemFocus = false, className = '', ...otherProps }) => { const graphicProps = { className: classnames('mdc-list-item__graphic', className), - tabIndex: tabbableOnListItemFocus ? tabIndex : -1, + tabIndex: tabIndex !== undefined ? tabIndex : -1, ...otherProps, }; return React.cloneElement(graphic, graphicProps); diff --git a/packages/list/ListItemMeta.tsx b/packages/list/ListItemMeta.tsx index c7ba881e5..6f137b1ab 100644 --- a/packages/list/ListItemMeta.tsx +++ b/packages/list/ListItemMeta.tsx @@ -24,7 +24,6 @@ import * as React from 'react'; import classnames from 'classnames'; export interface ListItemMetaProps { - tabbableOnListItemFocus?: boolean; className?: string; tabIndex?: number; meta: string | React.ReactElement; @@ -32,10 +31,9 @@ export interface ListItemMetaProps { }; const ListItemMeta:React.FunctionComponent = ({ - tabIndex = -1, // eslint-disable-line no-unused-vars + tabIndex, // eslint-disable-line no-unused-vars meta, className = '', - tabbableOnListItemFocus = false, ...otherProps }) => { let metaElement: React.ReactElement; @@ -46,7 +44,7 @@ const ListItemMeta:React.FunctionComponent = ({ } const metaProps = { className: classnames('mdc-list-item__meta', className, metaElement.props.className), - tabIndex: tabbableOnListItemFocus ? tabIndex : -1, + tabIndex: tabIndex !== undefined ? tabIndex : -1, ...otherProps, }; return React.cloneElement(metaElement, metaProps); diff --git a/packages/list/ListItemText.tsx b/packages/list/ListItemText.tsx index 13efa4978..a811bc29b 100644 --- a/packages/list/ListItemText.tsx +++ b/packages/list/ListItemText.tsx @@ -24,7 +24,6 @@ import * as React from 'react'; import classnames from 'classnames'; export interface ListItemTextProps { - tabbableOnListItemFocus?: boolean; tabIndex?: number; className?: string; primaryText?: React.ReactNode; @@ -41,7 +40,6 @@ const ListItemText: React.FunctionComponent = ({ /* eslint-disable react/prop-types */ primaryText = '', secondaryText = '', - tabbableOnListItemFocus = false, tabIndex = -1, className = '', /* eslint-enable react/prop-types */ @@ -53,7 +51,7 @@ const ListItemText: React.FunctionComponent = ({ return ( {text} @@ -78,7 +76,7 @@ const ListItemText: React.FunctionComponent = ({ return ( {renderText(primaryText, 'mdc-list-item__primary-text')} diff --git a/packages/list/README.md b/packages/list/README.md index 1cbb1d67f..cf882259f 100644 --- a/packages/list/README.md +++ b/packages/list/README.md @@ -312,7 +312,6 @@ class MyApp extends Component { | ----------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- | | className | String | Classes to be applied to the list item text element | | tabIndex | Number | Tab index of the list item text | -| tabbableOnListItemFocus | Boolean | Whether focusing list item will toggle tab index of the list item text. If false, the tab index will always be -1 | | primaryText | String | Primary text for the list item | | secondaryText | String | Secondary text for the list item | @@ -322,7 +321,6 @@ class MyApp extends Component { | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- | | className | String | Classes to be applied to the list item graphic element | | tabIndex | Number | Tab index of the list item graphic | -| tabbableOnListItemFocus | Boolean | Whether focusing list item will toggle tab index of the list item graphic. If false, the tab index will always be -1 | | graphic | Element | The graphic element to be displayed in front of list item text | ### ListItemMeta @@ -331,7 +329,6 @@ class MyApp extends Component { | ----------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------- | | className | String | Classes to be applied to the list item meta element | | tabIndex | Number | Tab index of the list item meta | -| tabbableOnListItemFocus | Boolean | Whether focusing list item will toggle tab index of the list item meta. If false, the tab index will always be -1 | | meta | Element or String | The meta element or string to be displayed behind list item text | ### ListDivider diff --git a/packages/list/index.tsx b/packages/list/index.tsx index 3b598b7f0..d83a2af85 100644 --- a/packages/list/index.tsx +++ b/packages/list/index.tsx @@ -34,6 +34,7 @@ import ListGroup from './ListGroup'; import ListGroupSubheader from './ListGroupSubheader'; const ARIA_ORIENTATION = 'aria-orientation'; const VERTICAL = 'vertical'; +// const CLASS_SEPARATOR = ' '; export interface ListProps extends React.HTMLProps { className?: string; @@ -51,6 +52,12 @@ export interface ListProps extends React.HTMLProps | ListItem[] | React.ReactNode; }; + +interface ListState { + listItemClassNames: {[N: number]: string[]}, + listItemAttributes: {[N: number]: string[]}, +} + function isReactText(element: any): element is React.ReactText { return typeof element === 'string' || typeof element === 'number'; } @@ -63,13 +70,18 @@ function isSelectedIndexType(selectedIndex: unknown): selectedIndex is MDCListIn return typeof selectedIndex === 'number' && !isNaN(selectedIndex) || Array.isArray(selectedIndex); } -export default class List extends React.Component, {}> { +export default class List extends React.Component, ListState> { listItemCount = 0; foundation!: MDCListFoundation; hasInitializedListItem = false; private listElement = React.createRef(); + state: ListState = { + listItemClassNames: {}, + listItemAttributes: {}, + }; + static defaultProps: Partial> = { 'className': '', 'checkboxList': false, @@ -186,15 +198,22 @@ export default class List extends React.Com } }, addClassForElementIndex: (index, className) => { - const listItem = this.listElements[index]; - if (listItem) { - listItem.classList.add(className); + const {listItemClassNames} = this.state; + if (listItemClassNames[index] && listItemClassNames[index].indexOf(className) === -1) { + listItemClassNames[index].push(className); + } else { + listItemClassNames[index] = [className]; } + this.setState({listItemClassNames}); }, removeClassForElementIndex: (index, className) => { - const listItem = this.listElements[index]; - if (listItem) { - listItem.classList.remove(className); + const {listItemClassNames} = this.state; + if (listItemClassNames[index]) { + const removalIndex = listItemClassNames[index].indexOf(className); + if (removalIndex !== -1) { + listItemClassNames[index].splice(removalIndex, 1); + this.setState({listItemClassNames}); + } } }, setTabIndexForListItemChildren: (listItemIndex, tabIndexValue) => { @@ -251,6 +270,46 @@ export default class List extends React.Com return null; } + getListItemInitialTabIndex = (index: number) => { + const {checkboxList, selectedIndex} = this.props; + let tabIndex = {}; + if (!this.hasInitializedListItem) { + const isSelectedIndexArray = Array.isArray(selectedIndex) && selectedIndex.length > 0; + // if selectedIndex is populated then check if its a checkbox/radioList + if (selectedIndex && (isSelectedIndexArray || selectedIndex > -1)) { + const isCheckboxListSelected + = checkboxList && Array.isArray(selectedIndex) && selectedIndex.indexOf(index) > -1; + const isNonCheckboxListSelected = selectedIndex === index; + if (isCheckboxListSelected || isNonCheckboxListSelected) { + tabIndex = {tabIndex: 0}; + this.hasInitializedListItem = true; + } + // set tabIndex=0 to first listItem if selectedIndex is not populated + } else { + tabIndex = {tabIndex: 0}; + this.hasInitializedListItem = true; + } + } + + return tabIndex; + } + + getListItemClassNames = (index: number, listItem: React.ReactElement>) => { + let {className = ''} = listItem.props; + const {listItemClassNames} = this.state; + if (listItemClassNames[index]) { + listItemClassNames[index]; + className = classnames(className, listItemClassNames[index]); + } + + return className; + } + + getListItemAttributes = (index: number, listItem: React.ReactElement>) => { + console.log(index, listItem) + return {}; + } + handleKeyDown = (e: React.KeyboardEvent, index: number) => { e.persist(); // Persist the synthetic event to access its `key` this.foundation.handleKeydown( @@ -323,40 +382,27 @@ export default class List extends React.Com }; renderListItem = (listItem: React.ReactElement>, index: number) => { - const {checkboxList, radioList, selectedIndex} = this.props; - let tabIndex = {}; + const {checkboxList, radioList} = this.props; + const tabIndex = this.getListItemInitialTabIndex(index); + const attributes = this.getListItemAttributes(index, listItem); + const className = this.getListItemClassNames(index, listItem); + const { onKeyDown, onClick, onFocus, onBlur, + className: _classNames, ...otherProps } = listItem.props; - if (!this.hasInitializedListItem) { - const isSelectedIndexArray = Array.isArray(selectedIndex) && selectedIndex.length > 0; - // if selectedIndex is populated then check if its a checkbox/radioList - if (selectedIndex && (isSelectedIndexArray || selectedIndex > -1)) { - const isCheckboxListSelected - = checkboxList && Array.isArray(selectedIndex) && selectedIndex.indexOf(index) > -1; - const isNonCheckboxListSelected = selectedIndex === index; - if (isCheckboxListSelected || isNonCheckboxListSelected) { - tabIndex = {tabIndex: 0}; - this.hasInitializedListItem = true; - } - // set tabIndex=0 to first listItem if selectedIndex is not populated - } else { - tabIndex = {tabIndex: 0}; - this.hasInitializedListItem = true; - } - } - - const props = { - // otherProps must come first + // attributes and otherProps must appear in this order + ...attributes, ...otherProps, checkboxList, radioList, + className, onKeyDown: (e: React.KeyboardEvent) => { onKeyDown!(e); this.handleKeyDown(e, index); @@ -373,6 +419,14 @@ export default class List extends React.Com onBlur!(e); this.handleBlur(e, index); }, + onDestroy: () => { + const {listItemAttributes, listItemClassNames} = this.state; + delete listItemAttributes[index]; + delete listItemClassNames[index]; + this.setState({ + listItemAttributes, listItemClassNames, + }); + }, ...tabIndex, }; return React.cloneElement(listItem, props); diff --git a/test/screenshot/list/index.tsx b/test/screenshot/list/index.tsx index 3c1e6e0ee..e0dca3d78 100644 --- a/test/screenshot/list/index.tsx +++ b/test/screenshot/list/index.tsx @@ -7,35 +7,42 @@ import List, { ListItemGraphic, ListItemText, ListItemMeta, - ListDivider, - ListGroup, - ListGroupSubheader, + // ListDivider, + // ListGroup, + // ListGroupSubheader, } from '../../../packages/list/index'; -import Checkbox from '../../../packages/checkbox/index'; -import Radio, {NativeRadioControl} from '../../../packages/radio/index'; +// import Checkbox from '../../../packages/checkbox/index'; +// import Radio, {NativeRadioControl} from '../../../packages/radio/index'; import {ListItemTextProps} from '../../../packages/list/ListItemText'; // eslint-disable-line no-unused-vars -import {MDCListIndex} from '@material/list/types'; +// import {MDCListIndex} from '@material/list/types'; // @ts-ignore no .d.ts file import * as uuidv4 from 'uuid/v4'; -const groceryItems = ['Milk', 'Eggs', 'Barley']; +// const groceryItems = ['Milk', 'Eggs', 'Barley']; interface SelectionListTestState { selectedIndex: number; listItems: string[]; }; -function renderListItem(options: ListItemTextProps) { - const {primaryText, secondaryText} = options; +interface ListItemOptions extends ListItemTextProps { + icon?: string; + onMetaClick?: (e: React.MouseEvent) => void; +} + +function renderListItem(options: ListItemOptions) { + const {primaryText, secondaryText, icon = 'info', onMetaClick = () => {}} = options; const key = uuidv4(); return ( } /> } + meta={} /> ); @@ -53,6 +60,16 @@ class SelectionListTest extends React.Component<{}, SelectionListTestState> { this.setState({listItems, selectedIndex: selectedIndex + 1}); }; + removeListItem = (index: number, e: React.MouseEvent) => { + const {listItems, selectedIndex} = this.state; + listItems.splice(index, 1); + if (this.state.selectedIndex > index) { + this.setState({selectedIndex: selectedIndex - 1}); + } + this.setState({listItems}); + e.stopPropagation(); + }; + render() { return ( @@ -62,83 +79,87 @@ class SelectionListTest extends React.Component<{}, SelectionListTestState> { selectedIndex={this.state.selectedIndex} handleSelect={(selectedIndex) => this.setState({selectedIndex})} > - {this.state.listItems.map((text) => renderListItem({primaryText: text}))} + {this.state.listItems.map((text, index) => renderListItem({ + primaryText: text, + icon: 'delete', + onMetaClick: (e) => this.removeListItem(index, e) + }))} ); } } -class CheckboxList extends React.Component<{}, {selectedIndex: MDCListIndex}> { - state = { - selectedIndex: [1], - }; - - handleSelect = (_selectedIndex: number, selected: MDCListIndex) => { - this.setState({selectedIndex: selected}); - } - - render() { - return ( - -
- Selected index: {this.state.selectedIndex} -
- - {groceryItems.map((item, index) => ( - - - - - ))} - -
- ); - } -} - -class RadioList extends React.Component<{}, {selectedItem: string}> { - state = { - selectedItem: 'Milk', - }; - - handleChange = (e: React.ChangeEvent) => { - this.setState({selectedItem: e.target.value}); - } - - render() { - return ( - -
- Selected index: {this.state.selectedItem} -
- - {groceryItems.map((item, index) => ( - - - - - - - ))} - -
- ); - } -} +// class CheckboxList extends React.Component<{}, {selectedIndex: MDCListIndex}> { +// state = { +// selectedIndex: [1], +// }; + +// handleSelect = (_selectedIndex: number, selected: MDCListIndex) => { +// this.setState({selectedIndex: selected}); +// } + +// render() { +// return ( +// +//
+// Selected index: {this.state.selectedIndex} +//
+// +// {groceryItems.map((item, index) => ( +// +// +// +// +// ))} +// +//
+// ); +// } +// } + +// class RadioList extends React.Component<{}, {selectedItem: string}> { +// state = { +// selectedItem: 'Milk', +// }; + +// handleChange = (e: React.ChangeEvent) => { +// this.setState({selectedItem: e.target.value}); +// } + +// render() { +// return ( +// +//
+// Selected index: {this.state.selectedItem} +//
+// +// {groceryItems.map((item, index) => ( +// +// +// +// +// +// +// ))} +// +//
+// ); +// } +// } const ListScreenshotTest = () => { return ( @@ -146,7 +167,7 @@ const ListScreenshotTest = () => {

One-line Selection List

-

Two-line List

+ {/*

Two-line List

{renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'})} {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'})} @@ -174,7 +195,7 @@ const ListScreenshotTest = () => {

Checkbox List

Radio List

- + */} ); }; diff --git a/test/unit/list/ListItemGraphic.test.tsx b/test/unit/list/ListItemGraphic.test.tsx index a31c3052e..748e51b64 100644 --- a/test/unit/list/ListItemGraphic.test.tsx +++ b/test/unit/list/ListItemGraphic.test.tsx @@ -16,17 +16,3 @@ test('has mdc-list-item__graphic class', () => { const wrapper = shallow(} />); assert.isTrue(wrapper.hasClass('mdc-list-item__graphic')); }); - -test('has tabIndex of props.tabIndex if specified and tabbableOnListItemFocus is true', () => { - const wrapper = shallow( - } tabIndex={3} tabbableOnListItemFocus /> - ); - assert.equal(wrapper.find('.mdc-list-item__graphic').props().tabIndex, 3); -}); - -test('has tabIndex of -1 if tabbableOnListItemFocus is false', () => { - const wrapper = shallow( - } childrenTabIndex={3} /> - ); - assert.equal(wrapper.find('.mdc-list-item__graphic').props().tabIndex, -1); -}); diff --git a/test/unit/list/ListItemMeta.test.tsx b/test/unit/list/ListItemMeta.test.tsx index 376fde1e0..b67b3533f 100644 --- a/test/unit/list/ListItemMeta.test.tsx +++ b/test/unit/list/ListItemMeta.test.tsx @@ -38,17 +38,3 @@ test('renders element if meta is an element', () => { const wrapper = shallow(} />); assert.equal(wrapper.find('.mdc-list-item__meta').type(), 'button'); }); - -test('has tabIndex of props.tabIndex if specified and tabbableOnListItemFocus is true', () => { - const wrapper = shallow( - } tabIndex={3} tabbableOnListItemFocus /> - ); - assert.equal(wrapper.find('.mdc-list-item__meta').props().tabIndex, 3); -}); - -test('has tabIndex of -1 if tabbableOnListItemFocus is false', () => { - const wrapper = shallow( - } childrenTabIndex={3} /> - ); - assert.equal(wrapper.find('.mdc-list-item__meta').props().tabIndex, -1); -}); diff --git a/test/unit/list/ListItemText.test.tsx b/test/unit/list/ListItemText.test.tsx index db3eb3472..dde391581 100644 --- a/test/unit/list/ListItemText.test.tsx +++ b/test/unit/list/ListItemText.test.tsx @@ -40,17 +40,3 @@ test('renders primary and secondary text if text are elements', () => { assert.exists(wrapper.find('.mdc-list-item__primary-text')); assert.exists(wrapper.find('.mdc-list-item__secondary-text')); }); - -test('has tabIndex of props.tabIndex if specified and tabbableOnListItemFocus is true', () => { - const wrapper = shallow( - - ); - assert.equal(wrapper.find('.mdc-list-item__text').props().tabIndex, 3); -}); - -test('has tabIndex of -1 if tabbableOnListItemFocus is false', () => { - const wrapper = shallow( - - ); - assert.equal(wrapper.find('.mdc-list-item__text').props().tabIndex, -1); -}); From 244b124af27b1ff1700a0c02c95ff01aff5badf2 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Tue, 26 Mar 2019 15:04:47 -0700 Subject: [PATCH 2/8] fix: onDestroy --- packages/list/ListItem.tsx | 4 +- packages/list/index.tsx | 17 +-- test/screenshot/list/index.tsx | 200 +++++++++++++++++---------------- 3 files changed, 108 insertions(+), 113 deletions(-) diff --git a/packages/list/ListItem.tsx b/packages/list/ListItem.tsx index 6ba6f5052..9b1f70931 100644 --- a/packages/list/ListItem.tsx +++ b/packages/list/ListItem.tsx @@ -35,10 +35,8 @@ export interface ListItemProps extends React.HTMLProps { activated?: boolean; selected?: boolean; onDestroy?: () => void; -}; +} -// TODO: convert to functional component -// https://github.com/material-components/material-components-web-react/issues/729 export default class ListItem extends React.Component< ListItemProps, {} diff --git a/packages/list/index.tsx b/packages/list/index.tsx index d83a2af85..a2eea1403 100644 --- a/packages/list/index.tsx +++ b/packages/list/index.tsx @@ -55,7 +55,6 @@ export interface ListProps extends React.HTMLProps extends React.Com state: ListState = { listItemClassNames: {}, - listItemAttributes: {}, }; static defaultProps: Partial> = { @@ -305,11 +303,6 @@ export default class List extends React.Com return className; } - getListItemAttributes = (index: number, listItem: React.ReactElement>) => { - console.log(index, listItem) - return {}; - } - handleKeyDown = (e: React.KeyboardEvent, index: number) => { e.persist(); // Persist the synthetic event to access its `key` this.foundation.handleKeydown( @@ -384,7 +377,6 @@ export default class List extends React.Com renderListItem = (listItem: React.ReactElement>, index: number) => { const {checkboxList, radioList} = this.props; const tabIndex = this.getListItemInitialTabIndex(index); - const attributes = this.getListItemAttributes(index, listItem); const className = this.getListItemClassNames(index, listItem); const { @@ -398,7 +390,6 @@ export default class List extends React.Com const props = { // attributes and otherProps must appear in this order - ...attributes, ...otherProps, checkboxList, radioList, @@ -419,13 +410,11 @@ export default class List extends React.Com onBlur!(e); this.handleBlur(e, index); }, + // https://github.com/material-components/material-components-web-react/issues/775 onDestroy: () => { - const {listItemAttributes, listItemClassNames} = this.state; - delete listItemAttributes[index]; + const {listItemClassNames} = this.state; delete listItemClassNames[index]; - this.setState({ - listItemAttributes, listItemClassNames, - }); + this.setState({listItemClassNames}); }, ...tabIndex, }; diff --git a/test/screenshot/list/index.tsx b/test/screenshot/list/index.tsx index e0dca3d78..71d4f273e 100644 --- a/test/screenshot/list/index.tsx +++ b/test/screenshot/list/index.tsx @@ -7,19 +7,19 @@ import List, { ListItemGraphic, ListItemText, ListItemMeta, - // ListDivider, - // ListGroup, - // ListGroupSubheader, + ListDivider, + ListGroup, + ListGroupSubheader, } from '../../../packages/list/index'; -// import Checkbox from '../../../packages/checkbox/index'; -// import Radio, {NativeRadioControl} from '../../../packages/radio/index'; +import Checkbox from '../../../packages/checkbox/index'; +import Radio, {NativeRadioControl} from '../../../packages/radio/index'; import {ListItemTextProps} from '../../../packages/list/ListItemText'; // eslint-disable-line no-unused-vars -// import {MDCListIndex} from '@material/list/types'; +import {MDCListIndex} from '@material/list/types'; // @ts-ignore no .d.ts file import * as uuidv4 from 'uuid/v4'; -// const groceryItems = ['Milk', 'Eggs', 'Barley']; +const groceryItems = ['Milk', 'Eggs', 'Barley']; interface SelectionListTestState { selectedIndex: number; @@ -31,9 +31,8 @@ interface ListItemOptions extends ListItemTextProps { onMetaClick?: (e: React.MouseEvent) => void; } -function renderListItem(options: ListItemOptions) { +function renderListItem(options: ListItemOptions, key: number) { const {primaryText, secondaryText, icon = 'info', onMetaClick = () => {}} = options; - const key = uuidv4(); return ( } /> @@ -60,10 +59,19 @@ class SelectionListTest extends React.Component<{}, SelectionListTestState> { this.setState({listItems, selectedIndex: selectedIndex + 1}); }; - removeListItem = (index: number, e: React.MouseEvent) => { + removeListItem = (e: React.MouseEvent) => { + const listItemElement = e.currentTarget!.parentElement; + const listItemElements = listItemElement!.parentElement!.querySelectorAll('.mdc-list-item'); + let interactionIndex = -1; + listItemElements.forEach((item, index) => { + if (item === listItemElement) { + interactionIndex = index; + // should break from this forEach + } + }); const {listItems, selectedIndex} = this.state; - listItems.splice(index, 1); - if (this.state.selectedIndex > index) { + listItems.splice(interactionIndex, 1); + if (this.state.selectedIndex > interactionIndex) { this.setState({selectedIndex: selectedIndex - 1}); } this.setState({listItems}); @@ -82,84 +90,84 @@ class SelectionListTest extends React.Component<{}, SelectionListTestState> { {this.state.listItems.map((text, index) => renderListItem({ primaryText: text, icon: 'delete', - onMetaClick: (e) => this.removeListItem(index, e) - }))} + onMetaClick: this.removeListItem + }, index))}
); } } -// class CheckboxList extends React.Component<{}, {selectedIndex: MDCListIndex}> { -// state = { -// selectedIndex: [1], -// }; - -// handleSelect = (_selectedIndex: number, selected: MDCListIndex) => { -// this.setState({selectedIndex: selected}); -// } - -// render() { -// return ( -// -//
-// Selected index: {this.state.selectedIndex} -//
-// -// {groceryItems.map((item, index) => ( -// -// -// -// -// ))} -// -//
-// ); -// } -// } - -// class RadioList extends React.Component<{}, {selectedItem: string}> { -// state = { -// selectedItem: 'Milk', -// }; - -// handleChange = (e: React.ChangeEvent) => { -// this.setState({selectedItem: e.target.value}); -// } - -// render() { -// return ( -// -//
-// Selected index: {this.state.selectedItem} -//
-// -// {groceryItems.map((item, index) => ( -// -// -// -// -// -// -// ))} -// -//
-// ); -// } -// } +class CheckboxList extends React.Component<{}, {selectedIndex: MDCListIndex}> { + state = { + selectedIndex: [1], + }; + + handleSelect = (_selectedIndex: number, selected: MDCListIndex) => { + this.setState({selectedIndex: selected}); + } + + render() { + return ( + +
+ Selected index: {this.state.selectedIndex} +
+ + {groceryItems.map((item, index) => ( + + + + + ))} + +
+ ); + } +} + +class RadioList extends React.Component<{}, {selectedItem: string}> { + state = { + selectedItem: 'Milk', + }; + + handleChange = (e: React.ChangeEvent) => { + this.setState({selectedItem: e.target.value}); + } + + render() { + return ( + +
+ Selected index: {this.state.selectedItem} +
+ + {groceryItems.map((item, index) => ( + + + + + + + ))} + +
+ ); + } +} const ListScreenshotTest = () => { return ( @@ -167,35 +175,35 @@ const ListScreenshotTest = () => {

One-line Selection List

- {/*

Two-line List

+

Two-line List

- {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'})} - {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'})} - {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'})} + {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 1)} + {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 2)} + {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 3)} - {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'})} - {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'})} + {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 4)} + {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 5)}

List group

Folders - {renderListItem({primaryText: 'Photos'})} - {renderListItem({primaryText: 'Recipes'})} - {renderListItem({primaryText: 'Work'})} + {renderListItem({primaryText: 'Photos'}, 1)} + {renderListItem({primaryText: 'Recipes'}, 2)} + {renderListItem({primaryText: 'Work'}, 3)} Recent Files - {renderListItem({primaryText: 'Vacation itinerary'})} - {renderListItem({primaryText: 'Kitchen remodel'})} + {renderListItem({primaryText: 'Vacation itinerary'}, 1)} + {renderListItem({primaryText: 'Kitchen remodel'}, 2)}

Checkbox List

Radio List

- */} + ); }; From 3a90d9441982eb3c2f706528c2ab33d07b15a379 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Tue, 26 Mar 2019 17:30:41 -0700 Subject: [PATCH 3/8] WIP: tabindex --- packages/list/index.tsx | 31 ++++++++++++++++++++++++++----- test/unit/list/index.test.tsx | 12 ++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/list/index.tsx b/packages/list/index.tsx index a2eea1403..7cf8ee2fb 100644 --- a/packages/list/index.tsx +++ b/packages/list/index.tsx @@ -34,7 +34,6 @@ import ListGroup from './ListGroup'; import ListGroupSubheader from './ListGroupSubheader'; const ARIA_ORIENTATION = 'aria-orientation'; const VERTICAL = 'vertical'; -// const CLASS_SEPARATOR = ' '; export interface ListProps extends React.HTMLProps { className?: string; @@ -54,7 +53,7 @@ export interface ListProps extends React.HTMLProps extends React.Com listItem.setAttribute(attr, value); } }, + /** + * Pushes class name to state.listItemClassNames[listItemIndex] if it doesn't yet exist. + */ addClassForElementIndex: (index, className) => { const {listItemClassNames} = this.state; if (listItemClassNames[index] && listItemClassNames[index].indexOf(className) === -1) { @@ -204,6 +206,10 @@ export default class List extends React.Com } this.setState({listItemClassNames}); }, + /** + * Finds the className within state.listItemClassNames[listItemIndex], and removes it + * from the array. + */ removeClassForElementIndex: (index, className) => { const {listItemClassNames} = this.state; if (listItemClassNames[index]) { @@ -268,7 +274,14 @@ export default class List extends React.Com return null; } - getListItemInitialTabIndex = (index: number) => { + /** + * Initializes the tabIndex prop for the listItems. tabIndex is determined by: + * 1. If checkboxList and props.selectedIndex is set + * 2. If radioList and props.selectedIndex is set + * 3. If neither and props.selectedIndex is set, set tabIndex on selectedIndex + * 4. If neither type of list nor props.selectedIndex is set, then set tabIndex on first element + */ + private getListItemInitialTabIndex = (index: number) => { const {checkboxList, selectedIndex} = this.props; let tabIndex = {}; if (!this.hasInitializedListItem) { @@ -292,7 +305,12 @@ export default class List extends React.Com return tabIndex; } - getListItemClassNames = (index: number, listItem: React.ReactElement>) => { + /** + * Method checks if the list item at `index` contains classes. If true, + * method merges state.listItemClassNames[index] with listItem.props.className. + * The return value is used as the listItem's className. + */ + private getListItemClassNames = (index: number, listItem: React.ReactElement>) => { let {className = ''} = listItem.props; const {listItemClassNames} = this.state; if (listItemClassNames[index]) { @@ -376,14 +394,17 @@ export default class List extends React.Com renderListItem = (listItem: React.ReactElement>, index: number) => { const {checkboxList, radioList} = this.props; - const tabIndex = this.getListItemInitialTabIndex(index); + const tabIndex = this.hasInitializedListItem ? {tabIndex: listItem.props.tabIndex} : this.getListItemInitialTabIndex(index); const className = this.getListItemClassNames(index, listItem); + console.log(tabIndex) + const { onKeyDown, onClick, onFocus, onBlur, + tabIndex: _tabIndex, className: _classNames, ...otherProps } = listItem.props; diff --git a/test/unit/list/index.test.tsx b/test/unit/list/index.test.tsx index 5209798af..88270bb6e 100644 --- a/test/unit/list/index.test.tsx +++ b/test/unit/list/index.test.tsx @@ -153,7 +153,7 @@ test('#adapter.setAttributeForElementIndex call nothing when no children exist', assert.doesNotThrow(() => wrapper.instance().adapter.setAttributeForElementIndex(0, 'role', 'menu')); }); -test('#adapter.addClassForElementIndex adds class to classList', () => { +test('#adapter.addClassForElementIndex adds classes to listitem', () => { const wrapper = mount({children()}); wrapper.instance().adapter.addClassForElementIndex = coerceForTesting<(index: number, className: string) => void>(td.func()); @@ -176,10 +176,13 @@ test('#adapter.addClassForElementIndex adds class to classList and keeps props.c assert.isTrue(listItem.html().includes('mdc-list-item')); }); -test('#adapter.removeClassForElementIndex adds class to classList and keeps props.className', () => { +test('#adapter.removeClassForElementIndex removes classname from state.listItemClassNames', () => { const wrapper = mount( -
meow
+
meow
); + wrapper.setState({listItemClassNames: { + 0: ['class987', 'class4321'], + }}); wrapper.instance().adapter.removeClassForElementIndex = coerceForTesting<(index: number, className: string) => void>(td.func()); wrapper.instance().adapter.removeClassForElementIndex(0, 'class4321'); @@ -417,13 +420,14 @@ test('renders list items with tabindex=-1 and first with tabindex=0', () => { assert.equal(list.childAt(2).props().tabIndex, -1); }); -test('renders list items with tabindex=-1 and child at props.selectedIndex tabindex=0', () => { +test.only('renders list items with tabindex=-1 and child at props.selectedIndex tabindex=0', () => { const wrapper = mount( {threeChildren()} ); const list = wrapper.childAt(0); + debugger assert.equal(list.childAt(0).props().tabIndex, -1); assert.equal(list.childAt(1).props().tabIndex, 0); assert.equal(list.childAt(2).props().tabIndex, -1); From 272d7790e1831c016f316f391ce616bd906d1ee2 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Tue, 26 Mar 2019 18:16:11 -0700 Subject: [PATCH 4/8] fix: tests --- packages/list/index.tsx | 26 +++++++++++++++----------- test/screenshot/list/index.tsx | 15 ++++++--------- test/unit/list/ListItem.test.tsx | 8 ++++++++ test/unit/list/index.test.tsx | 11 +++++++++-- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/packages/list/index.tsx b/packages/list/index.tsx index 7cf8ee2fb..faf8212d0 100644 --- a/packages/list/index.tsx +++ b/packages/list/index.tsx @@ -71,7 +71,8 @@ function isSelectedIndexType(selectedIndex: unknown): selectedIndex is MDCListIn export default class List extends React.Component, ListState> { listItemCount = 0; foundation!: MDCListFoundation; - hasInitializedListItem = false; + hasInitializedListItemTabIndex = false; + hasInitializedList = false; private listElement = React.createRef(); @@ -109,6 +110,11 @@ export default class List extends React.Com this.props[ARIA_ORIENTATION] === VERTICAL ); this.initializeListType(); + + // tabIndex for the list items can only be initialized after + // the above logic has executed. Once this is true, we need to call forceUpdate. + this.hasInitializedList = true; + this.forceUpdate(); } componentDidUpdate(prevProps: ListProps) { @@ -195,7 +201,7 @@ export default class List extends React.Com } }, /** - * Pushes class name to state.listItemClassNames[listItemIndex] if it doesn't yet exist. + * Pushes class name to state.listItemClassNames[listItemIndex] if it doesn't yet exist. */ addClassForElementIndex: (index, className) => { const {listItemClassNames} = this.state; @@ -284,7 +290,7 @@ export default class List extends React.Com private getListItemInitialTabIndex = (index: number) => { const {checkboxList, selectedIndex} = this.props; let tabIndex = {}; - if (!this.hasInitializedListItem) { + if (this.hasInitializedList && !this.hasInitializedListItemTabIndex) { const isSelectedIndexArray = Array.isArray(selectedIndex) && selectedIndex.length > 0; // if selectedIndex is populated then check if its a checkbox/radioList if (selectedIndex && (isSelectedIndexArray || selectedIndex > -1)) { @@ -293,12 +299,12 @@ export default class List extends React.Com const isNonCheckboxListSelected = selectedIndex === index; if (isCheckboxListSelected || isNonCheckboxListSelected) { tabIndex = {tabIndex: 0}; - this.hasInitializedListItem = true; + this.hasInitializedListItemTabIndex = true; } // set tabIndex=0 to first listItem if selectedIndex is not populated } else { tabIndex = {tabIndex: 0}; - this.hasInitializedListItem = true; + this.hasInitializedListItemTabIndex = true; } } @@ -394,23 +400,22 @@ export default class List extends React.Com renderListItem = (listItem: React.ReactElement>, index: number) => { const {checkboxList, radioList} = this.props; - const tabIndex = this.hasInitializedListItem ? {tabIndex: listItem.props.tabIndex} : this.getListItemInitialTabIndex(index); + const tabIndex = this.getListItemInitialTabIndex(index); const className = this.getListItemClassNames(index, listItem); - console.log(tabIndex) - const { onKeyDown, onClick, onFocus, onBlur, - tabIndex: _tabIndex, + /* eslint-disable no-unused-vars */ className: _classNames, + /* eslint-enable no-unused-vars */ ...otherProps } = listItem.props; const props = { - // attributes and otherProps must appear in this order + // otherProps must be first ...otherProps, checkboxList, radioList, @@ -431,7 +436,6 @@ export default class List extends React.Com onBlur!(e); this.handleBlur(e, index); }, - // https://github.com/material-components/material-components-web-react/issues/775 onDestroy: () => { const {listItemClassNames} = this.state; delete listItemClassNames[index]; diff --git a/test/screenshot/list/index.tsx b/test/screenshot/list/index.tsx index 71d4f273e..c12e2bde9 100644 --- a/test/screenshot/list/index.tsx +++ b/test/screenshot/list/index.tsx @@ -16,9 +16,6 @@ import Radio, {NativeRadioControl} from '../../../packages/radio/index'; import {ListItemTextProps} from '../../../packages/list/ListItemText'; // eslint-disable-line no-unused-vars import {MDCListIndex} from '@material/list/types'; -// @ts-ignore no .d.ts file -import * as uuidv4 from 'uuid/v4'; - const groceryItems = ['Milk', 'Eggs', 'Barley']; interface SelectionListTestState { @@ -31,14 +28,14 @@ interface ListItemOptions extends ListItemTextProps { onMetaClick?: (e: React.MouseEvent) => void; } -function renderListItem(options: ListItemOptions, key: number) { +function renderListItem(options: ListItemOptions, key: string | number) { const {primaryText, secondaryText, icon = 'info', onMetaClick = () => {}} = options; return ( } /> } @@ -55,7 +52,7 @@ class SelectionListTest extends React.Component<{}, SelectionListTestState> { insertListItem = () => { const {listItems, selectedIndex} = this.state; - listItems.splice(0, 0, 'New list item'); + listItems.splice(0, 0, `New list item ${(new Date()).getTime()}`); this.setState({listItems, selectedIndex: selectedIndex + 1}); }; @@ -87,11 +84,11 @@ class SelectionListTest extends React.Component<{}, SelectionListTestState> { selectedIndex={this.state.selectedIndex} handleSelect={(selectedIndex) => this.setState({selectedIndex})} > - {this.state.listItems.map((text, index) => renderListItem({ + {this.state.listItems.map((text) => renderListItem({ primaryText: text, icon: 'delete', - onMetaClick: this.removeListItem - }, index))} + onMetaClick: this.removeListItem, + }, text))}
); diff --git a/test/unit/list/ListItem.test.tsx b/test/unit/list/ListItem.test.tsx index 9e4292049..1e0e8fe48 100644 --- a/test/unit/list/ListItem.test.tsx +++ b/test/unit/list/ListItem.test.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import {assert} from 'chai'; +import * as td from 'testdouble'; import {mount, shallow} from 'enzyme'; import {ListItem} from '../../../packages/list/index'; @@ -69,3 +70,10 @@ test('renders a list item with an anchor tag', () => { const wrapper = shallow(
Test
); assert.equal(wrapper.type(), 'a'); }); + +test('componentWillUnmount calls props.onDestroy()', () => { + const onDestroy = td.func<() => void>(); + const wrapper = shallow(
Test
); + wrapper.unmount(); + td.verify(onDestroy(), {times: 1}); +}); diff --git a/test/unit/list/index.test.tsx b/test/unit/list/index.test.tsx index 88270bb6e..df0ebe922 100644 --- a/test/unit/list/index.test.tsx +++ b/test/unit/list/index.test.tsx @@ -357,6 +357,14 @@ test('renders with role=menu if props.role=menu and props.checkboxList', () => { assert.equal(wrapper.props().role, 'menu'); }); +test('#listItem.props.onDestroy removes class name from state.listItemClassNames', () => { + const wrapper = mount( +
meow
+
); + wrapper.setState({listItemClassNames: {0: ['test']}}); + wrapper.childAt(0).childAt(0).props().onDestroy(); + console.log(wrapper.state().listItemClassNames[0]); +}); test('#handleKeyDown calls #foudation.handleKeydown', () => { const wrapper = shallow({children()}); @@ -420,14 +428,13 @@ test('renders list items with tabindex=-1 and first with tabindex=0', () => { assert.equal(list.childAt(2).props().tabIndex, -1); }); -test.only('renders list items with tabindex=-1 and child at props.selectedIndex tabindex=0', () => { +test('renders list items with tabindex=-1 and child at props.selectedIndex tabindex=0', () => { const wrapper = mount( {threeChildren()} ); const list = wrapper.childAt(0); - debugger assert.equal(list.childAt(0).props().tabIndex, -1); assert.equal(list.childAt(1).props().tabIndex, 0); assert.equal(list.childAt(2).props().tabIndex, -1); From 54eb6ea3b0f463a9045996badda0304c15fed4c4 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Tue, 26 Mar 2019 18:20:55 -0700 Subject: [PATCH 5/8] fix: update golden --- test/screenshot/golden.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/screenshot/golden.json b/test/screenshot/golden.json index 6d7f1e41c..848d7755a 100644 --- a/test/screenshot/golden.json +++ b/test/screenshot/golden.json @@ -9,7 +9,7 @@ "layout-grid": "fe40f7a34853bc2a1d9a836e812599d4d47b5b26b839a8eaed7f98ea91946790", "line-ripple": "56b136db2dc7e09260849447e6bde9b55a837af332a05d9f52506ab1c95e2e57", "linear-progress": "f7a23058842b37875a02b3562e0b9c82f7fb24f9b88e9727d10b921b001d115e", - "list": "4dcdfeb03d781eca5367f6cbf4c7ebd13d1fe8932ca5758520b994d541f49095", + "list": "11f637cfb29bb0d9013eacb60946911cfc37aa24aa7a92770a8025d3e9a8263a", "material-icon": "442b39fb22d2c7a74efb23ca098429b471501ce21df8662327bbf9871fe0bcb0", "menu-surface": "f5face1a24fe166e86e8a3dc35ea85b2d4431469a3d06bf6fc1a30fbdc175aff", "notched-outline": "7770dd381c27608a1f43b6f83da92507fe53963f5e4409bd73184b86275538fe", From b14a9a9524bf6e9fbc88c1ce299110f43dc837b9 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 1 Apr 2019 16:21:05 -0700 Subject: [PATCH 6/8] fix: added test case for disabled --- packages/list/ListItem.tsx | 3 ++- packages/list/index.tsx | 2 +- test/screenshot/list/index.tsx | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/list/ListItem.tsx b/packages/list/ListItem.tsx index 9b1f70931..4c52a3cd4 100644 --- a/packages/list/ListItem.tsx +++ b/packages/list/ListItem.tsx @@ -61,10 +61,11 @@ export default class ListItem extends React } get classes() { - const {className, activated, selected} = this.props; + const {className, activated, disabled, selected} = this.props; return classnames('mdc-list-item', className, { [MDCListFoundation.cssClasses.LIST_ITEM_ACTIVATED_CLASS]: activated, [MDCListFoundation.cssClasses.LIST_ITEM_SELECTED_CLASS]: selected, + 'mdc-list-item--disabled': disabled, }); } diff --git a/packages/list/index.tsx b/packages/list/index.tsx index faf8212d0..8a7df3b96 100644 --- a/packages/list/index.tsx +++ b/packages/list/index.tsx @@ -393,7 +393,7 @@ export default class List extends React.Com renderChild = (child: React.ReactElement> | React.ReactChild) => { if (!isReactText(child) && isListItem(child)) { - return this.renderListItem(child, this.listItemCount++); + return this.renderListItem(child, child.props.disabled ? -1 : this.listItemCount++); } return child; }; diff --git a/test/screenshot/list/index.tsx b/test/screenshot/list/index.tsx index c12e2bde9..908c52618 100644 --- a/test/screenshot/list/index.tsx +++ b/test/screenshot/list/index.tsx @@ -26,12 +26,13 @@ interface SelectionListTestState { interface ListItemOptions extends ListItemTextProps { icon?: string; onMetaClick?: (e: React.MouseEvent) => void; + disabled?: boolean; } function renderListItem(options: ListItemOptions, key: string | number) { - const {primaryText, secondaryText, icon = 'info', onMetaClick = () => {}} = options; + const {primaryText, secondaryText, icon = 'info', onMetaClick = () => {}, disabled = false} = options; return ( - + } /> {

Two-line List

{renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 1)} - {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 2)} + {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text', disabled: true}, 2)} {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 3)} {renderListItem({primaryText: 'List item', secondaryText: 'Secondary text'}, 4)} From 6d1166fbc1c2d58e7e9c1686e8fca630e15af542 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 1 Apr 2019 16:35:26 -0700 Subject: [PATCH 7/8] fix: added tests --- test/screenshot/golden.json | 2 +- test/unit/list/ListItem.test.tsx | 5 +++++ test/unit/list/index.test.tsx | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/test/screenshot/golden.json b/test/screenshot/golden.json index 848d7755a..5dfd98a5a 100644 --- a/test/screenshot/golden.json +++ b/test/screenshot/golden.json @@ -9,7 +9,7 @@ "layout-grid": "fe40f7a34853bc2a1d9a836e812599d4d47b5b26b839a8eaed7f98ea91946790", "line-ripple": "56b136db2dc7e09260849447e6bde9b55a837af332a05d9f52506ab1c95e2e57", "linear-progress": "f7a23058842b37875a02b3562e0b9c82f7fb24f9b88e9727d10b921b001d115e", - "list": "11f637cfb29bb0d9013eacb60946911cfc37aa24aa7a92770a8025d3e9a8263a", + "list": "e633e9536eeadfe7bbb46cdf3d42c5ebf40a8974420f6183057b6f7ddcbeca21", "material-icon": "442b39fb22d2c7a74efb23ca098429b471501ce21df8662327bbf9871fe0bcb0", "menu-surface": "f5face1a24fe166e86e8a3dc35ea85b2d4431469a3d06bf6fc1a30fbdc175aff", "notched-outline": "7770dd381c27608a1f43b6f83da92507fe53963f5e4409bd73184b86275538fe", diff --git a/test/unit/list/ListItem.test.tsx b/test/unit/list/ListItem.test.tsx index 1e0e8fe48..becb70a2f 100644 --- a/test/unit/list/ListItem.test.tsx +++ b/test/unit/list/ListItem.test.tsx @@ -21,6 +21,11 @@ test('has activated class if props.activated = true', () => { assert.isTrue(wrapper.hasClass('mdc-list-item--activated')); }); +test('has activated class if props.disabled = true', () => { + const wrapper = shallow(
meow
); + assert.isTrue(wrapper.hasClass('mdc-list-item--disabled')); +}); + test('has selected class if props.selected = true', () => { const wrapper = shallow(
meow
); assert.isTrue(wrapper.hasClass('mdc-list-item--selected')); diff --git a/test/unit/list/index.test.tsx b/test/unit/list/index.test.tsx index df0ebe922..ede98b575 100644 --- a/test/unit/list/index.test.tsx +++ b/test/unit/list/index.test.tsx @@ -475,3 +475,31 @@ test('renders a list with children which is not DOM', () => {
); assert.isTrue(wrapper.children().length === 1); }); + +test('renders listItemCount of 2 when 1 disabled and 2 enabled list items', () => { + const wrapper = mount( + + + + + + ); + assert.equal(coerceForTesting(wrapper.instance()).listItemCount, 2); +}); + +test('renders listItemCount of 3 when 1 of the list items changes from disabled to enabled', () => { + const TestComponent = (props: {disabled?: boolean}) => { + const {disabled = true} = props; + return ( + + + + + + ); + }; + const wrapper = mount(); + assert.equal(coerceForTesting(wrapper.childAt(0).instance()).listItemCount, 2); + wrapper.setProps({disabled: false}); + assert.equal(coerceForTesting(wrapper.childAt(0).instance()).listItemCount, 3); +}); From 3cef521e309c3a9282299d4eb3c00fb945dd6982 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Tue, 2 Apr 2019 16:01:46 -0700 Subject: [PATCH 8/8] fix: clean logic for getListItemInitialTabIndex --- packages/list/index.tsx | 25 ++++++++----------------- test/unit/list/index.test.tsx | 2 +- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/list/index.tsx b/packages/list/index.tsx index 8a7df3b96..8ea2ee7a8 100644 --- a/packages/list/index.tsx +++ b/packages/list/index.tsx @@ -282,27 +282,18 @@ export default class List extends React.Com /** * Initializes the tabIndex prop for the listItems. tabIndex is determined by: - * 1. If checkboxList and props.selectedIndex is set - * 2. If radioList and props.selectedIndex is set - * 3. If neither and props.selectedIndex is set, set tabIndex on selectedIndex - * 4. If neither type of list nor props.selectedIndex is set, then set tabIndex on first element + * 1. if selectedIndex is an array, and the index === selectedIndex[0] + * 2. if selectedIndex is a number, and the the index === selectedIndex + * 3. if there is no selectedIndex */ private getListItemInitialTabIndex = (index: number) => { - const {checkboxList, selectedIndex} = this.props; + const {selectedIndex} = this.props; let tabIndex = {}; if (this.hasInitializedList && !this.hasInitializedListItemTabIndex) { - const isSelectedIndexArray = Array.isArray(selectedIndex) && selectedIndex.length > 0; - // if selectedIndex is populated then check if its a checkbox/radioList - if (selectedIndex && (isSelectedIndexArray || selectedIndex > -1)) { - const isCheckboxListSelected - = checkboxList && Array.isArray(selectedIndex) && selectedIndex.indexOf(index) > -1; - const isNonCheckboxListSelected = selectedIndex === index; - if (isCheckboxListSelected || isNonCheckboxListSelected) { - tabIndex = {tabIndex: 0}; - this.hasInitializedListItemTabIndex = true; - } - // set tabIndex=0 to first listItem if selectedIndex is not populated - } else { + const isSelectedIndexArray + = Array.isArray(selectedIndex) && selectedIndex.length > 0 && index === selectedIndex[0]; + const isSelected = selectedIndex === index; + if (isSelectedIndexArray || isSelected || selectedIndex === -1) { tabIndex = {tabIndex: 0}; this.hasInitializedListItemTabIndex = true; } diff --git a/test/unit/list/index.test.tsx b/test/unit/list/index.test.tsx index ede98b575..95c0348e9 100644 --- a/test/unit/list/index.test.tsx +++ b/test/unit/list/index.test.tsx @@ -416,7 +416,7 @@ test('renders 3 list items', () => { assert.equal(wrapper.childAt(0).children().length, 3); }); -test('renders list items with tabindex=-1 and first with tabindex=0', () => { +test.only('renders list items with tabindex=-1 and first with tabindex=0', () => { const wrapper = mount( {threeChildren()}