From 5ef9290b6bcdc0e8425d60d65ea4a2bd6da8a192 Mon Sep 17 00:00:00 2001 From: JuanMa Date: Tue, 10 Jul 2018 18:42:38 +0200 Subject: [PATCH 01/81] chore(release): adding 6.3.0 (#1124) --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13eeae3e8..0d77a5b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ + +# [6.3.0](https://github.com/reactstrap/reactstrap/compare/6.1.0...6.3.0) (2018-07-10) + +### Features + +* **CustomInput:** add innerRef to CustomInput ([#1123](https://github.com/reactstrap/reactstrap/issues/1123)) ([418fdf8](https://github.com/reactstrap/reactstrap/commit/418fdf8)) + +* **Tooltip:** allow additional arrow classNames ([#1119](https://github.com/reactstrap/reactstrap/issues/1119)) ([9ffa55f](https://github.com/reactstrap/reactstrap/commit/9ffa55f)), closes [#1117](https://github.com/reactstrap/reactstrap/issues/1117) + # [6.2.0](https://github.com/reactstrap/reactstrap/compare/6.1.0...6.2.0) (2018-06-28) diff --git a/package.json b/package.json index b608985cf..dc5976a3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reactstrap", - "version": "6.2.0", + "version": "6.3.0", "description": "React Bootstrap 4 components", "main": "dist/reactstrap.cjs.js", "jsnext:main": "dist/reactstrap.es.js", From 1f1c0a89b022dfaaeaba970f113b3990374f478c Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Tue, 10 Jul 2018 12:54:08 -0400 Subject: [PATCH 02/81] chore(*): fix travis deploy and changelog --- .travis.yml | 2 +- CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e3f4b8710..abb328fd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ before_deploy: - npm run build deploy: provider: npm - email: "dumbdrun@gmail.com" + email: "dumbdrum@gmail.com" api_key: secure: P+6SEhs1BQ8sUygZ3L+Mqe2lxrhA9CB35DOds4d8Lp1LneRMm3jDt/DDBhIxF3N1xhd5f9BaHifyXcOfm+EiNIczkTP2G8wJCaxoA7bVKLNx27//kXBUVq7J4vnM3nZltFO7YHd3l46XkTeapQuXVXmbNbT6enQISkwNKMGd/hJk/hNqtS/faJAk84wqjcSGh/wY1YQ7po7FaAUWN2V6y8M8GfW/8s9BEAlY0sY4QYZr90CnSmOsvFtY//0Mx0NevUbo7UcEa+8cO7YOsrgdIMxQBq3UlN9L2XkQyLBobwShk+GeQhFWNVw5aByluWg8DoY5GOkgRSMhTLUyQ3fKnV8B2Mo1SqsqdSD9830TWY+Br47O1Mx20WrbzkpMdGa/Msmvrk0wMFNY0+TuMyn6ItMpGnijbKLoM23deiq69TwgUlVLOHGRcuE7P9iyP+bCNgHxPtI3d3akO2ZWO+8wiVw8sJ29Ywr4x+omFeuyLalfNCGVYF2vg07hT8oWPXNPcNA7Lktqb6PWmaUMGAaLv6Awe8lBu/RT/Tx5rVzdygxKj+xti95vduKaSZp0KfNCkA1txemQCbiuURM5S7KhAhWsDrFJOlYdP5tSf53HhCNrpniAEUXPuNW0aPjgFYYcOVFL/7U4eWLUaXaFL9mBF4wT6K9ekjHaEOSCjZeKDCw= skip_cleanup: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d77a5b86..e23f108f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -# [6.3.0](https://github.com/reactstrap/reactstrap/compare/6.1.0...6.3.0) (2018-07-10) +# [6.3.0](https://github.com/reactstrap/reactstrap/compare/6.2.0...6.3.0) (2018-07-10) ### Features From 36437a5e9d4af762262e4fa20fd6f8f48d70934b Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Thu, 12 Jul 2018 15:17:15 -0400 Subject: [PATCH 03/81] chore(travis): update npm token after eslist-scope incident --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index abb328fd8..056751915 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,15 +21,15 @@ env: branches: only: - master - - /^\d+\.\d+(\.\d+)?(-\S*)?$/ -before_deploy: - - "echo 'Preparing to publish'" - - npm run build + - "/^\\d+\\.\\d+(\\.\\d+)?(-\\S*)?$/" +before_deploy: +- echo 'Preparing to publish' +- npm run build deploy: provider: npm - email: "dumbdrum@gmail.com" + email: dumbdrum@gmail.com api_key: - secure: P+6SEhs1BQ8sUygZ3L+Mqe2lxrhA9CB35DOds4d8Lp1LneRMm3jDt/DDBhIxF3N1xhd5f9BaHifyXcOfm+EiNIczkTP2G8wJCaxoA7bVKLNx27//kXBUVq7J4vnM3nZltFO7YHd3l46XkTeapQuXVXmbNbT6enQISkwNKMGd/hJk/hNqtS/faJAk84wqjcSGh/wY1YQ7po7FaAUWN2V6y8M8GfW/8s9BEAlY0sY4QYZr90CnSmOsvFtY//0Mx0NevUbo7UcEa+8cO7YOsrgdIMxQBq3UlN9L2XkQyLBobwShk+GeQhFWNVw5aByluWg8DoY5GOkgRSMhTLUyQ3fKnV8B2Mo1SqsqdSD9830TWY+Br47O1Mx20WrbzkpMdGa/Msmvrk0wMFNY0+TuMyn6ItMpGnijbKLoM23deiq69TwgUlVLOHGRcuE7P9iyP+bCNgHxPtI3d3akO2ZWO+8wiVw8sJ29Ywr4x+omFeuyLalfNCGVYF2vg07hT8oWPXNPcNA7Lktqb6PWmaUMGAaLv6Awe8lBu/RT/Tx5rVzdygxKj+xti95vduKaSZp0KfNCkA1txemQCbiuURM5S7KhAhWsDrFJOlYdP5tSf53HhCNrpniAEUXPuNW0aPjgFYYcOVFL/7U4eWLUaXaFL9mBF4wT6K9ekjHaEOSCjZeKDCw= + secure: Fth8EnmhP+/TV7Uj5Czz3nwuek40GOnIlWRppqGs2IA+Q/le7V3R4xvgTlA/p5xN17IH1EhT/UVZBoNd1lOi6wcbgqrkeqPTarqNbkhzaEFJokNBkOJR7FmGC41Y76XbqPl2zeU//p+7dtrCEUacXCDWEfoCjA34GHaD0snd2yuPXYH0FOy1YkydauqGjLg8Y65g3f94xxAnY2Wm2TJzC5rnCjKPMXKEKAX6LpI5UIbq8UDPP8sFv+JpAkgI1/ygY8daBOeIurJEUUoVNazZWtvgdMFcBlL90QbP62HTU1vGTMK7cNT64COTlOrP4BdvIgWh1KaYXiboEwFrooA3fp/1KDNcCC1sXmXa2UoFOAUh9vr4SFBA8vE7tjWEvYtI2zCVD8b5ekLGfPandezKolh8V3sbeJrDFhY0G9lap8ftHWQejohvG/6xjI+/pe+UDpHH1USIqf0DtAnDn5mNWWav/5xCp8HlsIsmh5EKCM3//a8aWY6H+eMfeEFPzU/X3jC4ZUGZ+D5ZGQQ/NX9tvyeoH0pnRnlZil8MAjf/e2/IWu66ruAUde3eMpX4h+w0q8bVGvH4ObR7QKiJdTntwJSsp9iO4uBHTSpoo9yYO9ycepMAc5af0Np7K1xOv7ja5BvMesSVzyZsbDK3Q8iOOWBDk54bqV7PrFtRPn+VIjk= skip_cleanup: true on: tags: true From f380b412c53662936c827da1cbaed3afe44aadf1 Mon Sep 17 00:00:00 2001 From: illiteratewriter Date: Mon, 16 Jul 2018 19:12:43 +0530 Subject: [PATCH 04/81] fix(Collapse): add function and string to innerRef propType (#1129) #1054 --- src/Collapse.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Collapse.js b/src/Collapse.js index edd517280..399c3004d 100644 --- a/src/Collapse.js +++ b/src/Collapse.js @@ -15,7 +15,11 @@ const propTypes = { className: PropTypes.node, navbar: PropTypes.bool, cssModule: PropTypes.object, - innerRef: PropTypes.object, + innerRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.string, + PropTypes.object + ]), }; const defaultProps = { From e3a736c5ae35b2edf42a95e12941e58e62aa3c8a Mon Sep 17 00:00:00 2001 From: Sercan Date: Tue, 17 Jul 2018 17:10:58 +0300 Subject: [PATCH 05/81] docs(Carousel): fix typo in prop types configuration (#1132) --- docs/lib/Components/CarouselPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/lib/Components/CarouselPage.js b/docs/lib/Components/CarouselPage.js index a5af1499e..1c23f3f6f 100644 --- a/docs/lib/Components/CarouselPage.js +++ b/docs/lib/Components/CarouselPage.js @@ -130,7 +130,7 @@ export default class CarouselPage extends React.Component {
           
 {`UncontrolledCarousel.propTypes = {
-  items: PropTypes.array,isRequired,
+  items: PropTypes.array.isRequired,
   indicators: PropTypes.bool, // default: true
   controls: PropTypes.bool, // default: true
   autoPlay: PropTypes.bool, // default: true

From 0005b928b1557e5c0653b4306777a63506847365 Mon Sep 17 00:00:00 2001
From: illiteratewriter 
Date: Tue, 17 Jul 2018 19:42:03 +0530
Subject: [PATCH 06/81] feat(Tooltip) : add trigger for tooltip (#1128)

Closes #1022
---
 docs/lib/Components/TooltipsPage.js |  3 ++-
 src/Tooltip.js                      | 39 +++++++++++++++++++++--------
 2 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/docs/lib/Components/TooltipsPage.js b/docs/lib/Components/TooltipsPage.js
index 47d86a4d8..846cb9931 100644
--- a/docs/lib/Components/TooltipsPage.js
+++ b/docs/lib/Components/TooltipsPage.js
@@ -81,10 +81,11 @@ export default class TooltipsPage extends React.Component {
   ]),
   // Custom ref handler that will be assigned to the "ref" of the 
wrapping the tooltip elements innerRef: PropTypes.oneOfType([ - PropTypes.func, + PropTypes.func, PropTypes.string, PropTypes.object ]), + trigger: PropTypes.string, }`}
diff --git a/src/Tooltip.js b/src/Tooltip.js index f5095456e..06981b512 100644 --- a/src/Tooltip.js +++ b/src/Tooltip.js @@ -40,6 +40,7 @@ const propTypes = { PropTypes.string, PropTypes.object ]), + trigger: PropTypes.string, }; const DEFAULT_DELAYS = { @@ -173,16 +174,34 @@ class Tooltip extends React.Component { } addTargetEvents() { - this._target.addEventListener('mouseover', this.onMouseOverTooltip, true); - this._target.addEventListener('mouseout', this.onMouseLeaveTooltip, true); - this._target.addEventListener('keydown', this.onEscKeyDown, true); - this._target.addEventListener('focusin', this.show, true); - this._target.addEventListener('focusout', this.hide, true); - - - ['click', 'touchstart'].forEach(event => - document.addEventListener(event, this.handleDocumentClick, true) - ); + if (this.props.trigger) { + let triggers = this.props.trigger.split(' '); + if (triggers.indexOf('manual') === -1) { + if (triggers.indexOf('click') > -1) { + ['click', 'touchstart'].forEach(event => + document.addEventListener(event, this.handleDocumentClick, true) + ); + } + if (triggers.indexOf('hover') > -1) { + this._target.addEventListener('mouseover', this.onMouseOverTooltip, true); + this._target.addEventListener('mouseout', this.onMouseLeaveTooltip, true); + } + if (triggers.indexOf('focus') > -1) { + this._target.addEventListener('focusin', this.show, true); + this._target.addEventListener('focusout', this.hide, true); + } + this._target.addEventListener('keydown', this.onEscKeyDown, true); + } + } else { + this._target.addEventListener('mouseover', this.onMouseOverTooltip, true); + this._target.addEventListener('mouseout', this.onMouseLeaveTooltip, true); + this._target.addEventListener('keydown', this.onEscKeyDown, true); + this._target.addEventListener('focusin', this.show, true); + this._target.addEventListener('focusout', this.hide, true); + ['click', 'touchstart'].forEach(event => + document.addEventListener(event, this.handleDocumentClick, true) + ); + } } removeTargetEvents() { From c1374b492f27bf1e7e90d28b9f7ff813e50e79ba Mon Sep 17 00:00:00 2001 From: Konrad Jarosinski Date: Tue, 17 Jul 2018 18:48:39 +0200 Subject: [PATCH 07/81] fix(CustomInput): allow any node for label (#1095) --- src/CustomInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CustomInput.js b/src/CustomInput.js index 6ec6a9a1b..6e614e7d7 100644 --- a/src/CustomInput.js +++ b/src/CustomInput.js @@ -7,7 +7,7 @@ const propTypes = { className: PropTypes.string, id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, type: PropTypes.string.isRequired, - label: PropTypes.string, + label: PropTypes.node, inline: PropTypes.bool, valid: PropTypes.bool, invalid: PropTypes.bool, From f0f4495cbbe4b66193d4af98e6c3be1c95f1984d Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Tue, 17 Jul 2018 12:55:58 -0400 Subject: [PATCH 08/81] chore(GitHub): update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 000000000..206c92516 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +- components: `name` +- reactstrap version `#x.x.x` +- import method `umd/csj/es` +- react version `#x.x.x` +- bootstrap version `#x.x.x` + +### What is happening? + + + +### What should be happening? + + + +### Steps to reproduce issue + +1. ... +2. ... + +### Error message in console + +``` +paste error message with stacktrack here +``` + +### Code + + From 423d10d8b0212133679e8a39813d8351c10c4f06 Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Tue, 17 Jul 2018 13:20:22 -0400 Subject: [PATCH 09/81] chore(GitHub): add PR template --- PULL_REQUEST_TEMPLATE.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..9a367031d --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ + +- [ ] Bug fix +- [ ] New feature +- [ ] Chore +- [ ] Breaking change +- [ ] There is an open issue which this change addresses +- [ ] I have read the **[CONTRIBUTING](./CONTRIBUTING.md)** document. +- [ ] My commits follow the [Git Commit Guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) +- [ ] My code follows the code style of this project. +- [ ] My change requires a change to the documentation. +- - [ ] I have updated the documentation accordingly. +- [ ] I have added tests to cover my changes. +- [ ] All new and existing tests passed. + + + + + From 0aba60ff449dc891147ff033e1b80991241a5a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gianfranc=C3=B8=20Palumbo?= Date: Mon, 23 Jul 2018 20:15:31 +0300 Subject: [PATCH 10/81] docs(readme): update link to create-react-app guide (#1140) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9700ac6ec..c3e1e8384 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Stateless React Components for Bootstrap 4. ## Getting Started -Follow the [create-react-app instructions](https://github.com/facebookincubator/create-react-app#getting-started) up to the `Adding Bootstrap` section and instead follow the reactstrap version of [adding bootstrap](#adding-bootstrap). +Follow the [create-react-app instructions](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md) **up to** the `Adding Bootstrap` section and instead follow the reactstrap version of [adding bootstrap](#adding-bootstrap). ### tl;dr From f16728f97db0f263079a0d479099a55329fbfb38 Mon Sep 17 00:00:00 2001 From: Evan Sharp Date: Fri, 27 Jul 2018 13:21:53 -0400 Subject: [PATCH 11/81] chore(release): adding 6.3.1 (#1146) --- CHANGELOG.md | 11 +++++++++++ package.json | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e23f108f5..a6c401616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +## [6.3.1](https://github.com/reactstrap/reactstrap/compare/6.3.0...6.3.1) (2018-07-27) + + +### Bug Fixes + +* **Collapse:** add function and string to innerRef propType ([#1129](https://github.com/reactstrap/reactstrap/issues/1129)) ([f380b41](https://github.com/reactstrap/reactstrap/commit/f380b41)), closes [#1054](https://github.com/reactstrap/reactstrap/issues/1054) +* **CustomInput:** allow any node for label ([#1095](https://github.com/reactstrap/reactstrap/issues/1095)) ([c1374b4](https://github.com/reactstrap/reactstrap/commit/c1374b4)) + + + # [6.3.0](https://github.com/reactstrap/reactstrap/compare/6.2.0...6.3.0) (2018-07-10) diff --git a/package.json b/package.json index dc5976a3b..6f6446cb5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reactstrap", - "version": "6.3.0", + "version": "6.3.1", "description": "React Bootstrap 4 components", "main": "dist/reactstrap.cjs.js", "jsnext:main": "dist/reactstrap.es.js", @@ -19,8 +19,8 @@ "build": "rollup -c", "prebuild": "cross-env BABEL_ENV=lib-dir babel src --out-dir lib --ignore src/__tests__/", "postbuild": "node ./scripts/postbuild.js", - "create-release": "npm test && sh ./scripts/release", - "publish-release": "npm test && sh ./scripts/publish", + "create-release": "npm run cover && sh ./scripts/release", + "publish-release": "npm run cover && sh ./scripts/publish", "lint": "eslint src" }, "repository": { From e941dfdc1a58de5b652908a335c3e6bc261aa484 Mon Sep 17 00:00:00 2001 From: DrianHillman Date: Sat, 28 Jul 2018 21:56:50 -0400 Subject: [PATCH 12/81] Grammatical edit for Button Docs I noticed a grammatical error & thought I'd submit this quick edit! --- docs/lib/Components/ButtonsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/lib/Components/ButtonsPage.js b/docs/lib/Components/ButtonsPage.js index 065c2c6ff..fad133df7 100644 --- a/docs/lib/Components/ButtonsPage.js +++ b/docs/lib/Components/ButtonsPage.js @@ -113,7 +113,7 @@ export default class ButtonsPage extends React.Component { Checkbox and Radio Buttons (Stateful Buttons)

- In order to have checkbox and radio buttons, your component needs to manage the state of which button(s) are active/select. It is not in the opinion of this library to manage state within it's components so it is left up to you. Below is a simple example showcasing how this could be done uses the components which already exist in this library. + In order to have checkbox and radio buttons, your component needs to manage the state of which button(s) are active/select. It is not in the opinion of this library to manage state within it's components so it is left up to you. Below is a simple example showcasing how this could be done using the components which already exist in this library.

From 02b455581e53cae27215b3dc7ee5ef5c8f278a19 Mon Sep 17 00:00:00 2001 From: Graham Monteith Date: Wed, 1 Aug 2018 10:25:59 -0500 Subject: [PATCH 13/81] feat(Popover/Tooltip): add boundariesElement prop (#1149) Closes #1118 --- docs/lib/Components/PopoversPage.js | 2 ++ docs/lib/Components/TooltipsPage.js | 2 ++ src/Popover.js | 2 ++ src/PopperContent.js | 4 ++++ src/Tooltip.js | 2 ++ src/__tests__/Uncontrolled.spec.js | 5 +++++ 6 files changed, 17 insertions(+) diff --git a/docs/lib/Components/PopoversPage.js b/docs/lib/Components/PopoversPage.js index 21b6360fd..e79f17b5c 100644 --- a/docs/lib/Components/PopoversPage.js +++ b/docs/lib/Components/PopoversPage.js @@ -30,6 +30,8 @@ export default class PopoversPage extends React.Component { isOpen: PropTypes.bool, // callback for toggling isOpen in the controlling component toggle: PropTypes.func, + // boundaries for popper, can be scrollParent, window, viewport, or any DOM element + boundariesElement: PropTypes.string, target: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, diff --git a/docs/lib/Components/TooltipsPage.js b/docs/lib/Components/TooltipsPage.js index 846cb9931..266016086 100644 --- a/docs/lib/Components/TooltipsPage.js +++ b/docs/lib/Components/TooltipsPage.js @@ -30,6 +30,8 @@ export default class TooltipsPage extends React.Component {
           
 {`Tooltip.propTypes = {
+  // boundaries for popper, can be scrollParent, window, viewport, or any DOM element
+  boundariesElement: PropTypes.string,
   // boolean to control the state of the tooltip
   isOpen: PropTypes.bool,
   hideArrow: PropTypes.bool,
diff --git a/src/Popover.js b/src/Popover.js
index 5da44ecad..07f163487 100644
--- a/src/Popover.js
+++ b/src/Popover.js
@@ -16,6 +16,7 @@ const propTypes = {
     PropTypes.func,
     DOMElement,
   ]),
+  boundariesElement: PropTypes.string,
   isOpen: PropTypes.bool,
   disabled: PropTypes.bool,
   hideArrow: PropTypes.bool,
@@ -182,6 +183,7 @@ class Popover extends React.Component {
         container={this.props.container}
         modifiers={this.props.modifiers}
         offset={this.props.offset}
+        boundariesElement={this.props.boundariesElement}
       >
         
diff --git a/src/PopperContent.js b/src/PopperContent.js index 5e34a76c4..046149c9e 100644 --- a/src/PopperContent.js +++ b/src/PopperContent.js @@ -21,9 +21,11 @@ const propTypes = { container: PropTypes.oneOfType([PropTypes.string, PropTypes.func, DOMElement]), target: PropTypes.oneOfType([PropTypes.string, PropTypes.func, DOMElement]).isRequired, modifiers: PropTypes.object, + boundariesElement: PropTypes.string, }; const defaultProps = { + boundariesElement: 'scrollParent', placement: 'auto', hideArrow: false, isOpen: false, @@ -144,6 +146,7 @@ class PopperContent extends React.Component { tag, container, modifiers, + boundariesElement, ...attrs } = this.props; const arrowClassName = mapToCssModules(classNames( @@ -159,6 +162,7 @@ class PopperContent extends React.Component { const extendedModifiers = { offset: { offset }, flip: { enabled: flip, behavior: fallbackPlacement }, + preventOverflow: { boundariesElement }, update: { enabled: true, order: 950, diff --git a/src/Tooltip.js b/src/Tooltip.js index 06981b512..c7c9a9e2f 100644 --- a/src/Tooltip.js +++ b/src/Tooltip.js @@ -19,6 +19,7 @@ const propTypes = { isOpen: PropTypes.bool, disabled: PropTypes.bool, hideArrow: PropTypes.bool, + boundariesElement: PropTypes.string, className: PropTypes.string, innerClassName: PropTypes.string, arrowClassName: PropTypes.string, @@ -247,6 +248,7 @@ class Tooltip extends React.Component { target={this.props.target} isOpen={this.props.isOpen} hideArrow={this.props.hideArrow} + boundariesElement={this.props.boundariesElement} placement={this.props.placement} placementPrefix={this.props.placementPrefix} arrowClassName={this.props.arrowClassName} diff --git a/src/__tests__/Uncontrolled.spec.js b/src/__tests__/Uncontrolled.spec.js index c37badff3..e883e5ec8 100644 --- a/src/__tests__/Uncontrolled.spec.js +++ b/src/__tests__/Uncontrolled.spec.js @@ -109,4 +109,9 @@ describe('UncontrolledTooltip', () => { tooltip.update(); expect(tooltip.prop('isOpen')).toBe(true); }); + + it('should have boundary set to string', () => { + const tooltip = shallow(Yo!); + expect(tooltip.prop('boundariesElement')).toBe('window'); + }); }); From 2a9ac210be8d84aa1d4f6f4a85c6f65164b43fa1 Mon Sep 17 00:00:00 2001 From: illiteratewriter Date: Thu, 2 Aug 2018 01:07:12 +0530 Subject: [PATCH 14/81] docs(Tooltip): change tooltip links to span (#1144) (#1154) --- docs/lib/examples/Tooltip.js | 2 +- docs/lib/examples/TooltipAutoHide.js | 2 +- docs/lib/examples/TooltipUncontrolled.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/lib/examples/Tooltip.js b/docs/lib/examples/Tooltip.js index d7e408b0c..090f33c58 100644 --- a/docs/lib/examples/Tooltip.js +++ b/docs/lib/examples/Tooltip.js @@ -21,7 +21,7 @@ export default class Example extends React.Component { render() { return (
-

Somewhere in here is a tooltip.

+

Somewhere in here is a tooltip.

Hello world! diff --git a/docs/lib/examples/TooltipAutoHide.js b/docs/lib/examples/TooltipAutoHide.js index 8aa73cb17..90e8237c4 100644 --- a/docs/lib/examples/TooltipAutoHide.js +++ b/docs/lib/examples/TooltipAutoHide.js @@ -21,7 +21,7 @@ export default class Example extends React.Component { render() { return (
-

Sometimes you need to allow users to select text within a tooltip.

+

Sometimes you need to allow users to select text within a tooltip.

Try to select this text! diff --git a/docs/lib/examples/TooltipUncontrolled.js b/docs/lib/examples/TooltipUncontrolled.js index a86c2f903..fc4afdb26 100644 --- a/docs/lib/examples/TooltipUncontrolled.js +++ b/docs/lib/examples/TooltipUncontrolled.js @@ -5,7 +5,7 @@ import { UncontrolledTooltip } from 'reactstrap'; export default function Example() { return (
-

Somewhere in here is a tooltip.

+

Somewhere in here is a tooltip.

Hello world! From 4d19b09acd933af17113cef4cf85c01a3e409e81 Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 6 Aug 2018 09:21:14 -0500 Subject: [PATCH 15/81] feat(Modal): add charCode prop for custom icon (#1162) Closes #1155 --- docs/lib/Components/ModalsPage.js | 17 ++++++++++ docs/lib/examples/ModalCustomCloseIcon.js | 41 +++++++++++++++++++++++ src/ModalHeader.js | 6 +++- src/__tests__/ModalHeader.spec.js | 15 +++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 docs/lib/examples/ModalCustomCloseIcon.js diff --git a/docs/lib/Components/ModalsPage.js b/docs/lib/Components/ModalsPage.js index 931033bd0..e1db04a11 100644 --- a/docs/lib/Components/ModalsPage.js +++ b/docs/lib/Components/ModalsPage.js @@ -21,6 +21,9 @@ const ModalFadelessExampleSource = require('!!raw!../examples/ModalFadeless'); import ModalExternalExample from '../examples/ModalExternal'; const ModalExternalExampleSource = require('!!raw!../examples/ModalExternal'); +import ModalCustomCloseIconExample from '../examples/ModalCustomCloseIcon'; +const ModalCustomCloseIconExampleSource = require('!!raw!../examples/ModalCutomCloseIcon'); + export default class ModalsPage extends React.Component { render() { return ( @@ -171,6 +174,20 @@ export default class ModalsPage extends React.Component { {ModalExternalExampleSource}
+ +

Modals with custom close icon

+
+
+
+ +
+
+
+
+          
+            {ModalCustomCloseIconExampleSource}
+          
+        
); } diff --git a/docs/lib/examples/ModalCustomCloseIcon.js b/docs/lib/examples/ModalCustomCloseIcon.js new file mode 100644 index 000000000..6aa63bd74 --- /dev/null +++ b/docs/lib/examples/ModalCustomCloseIcon.js @@ -0,0 +1,41 @@ +/* eslint react/no-multi-comp: 0, react/prop-types: 0 */ + +import React from 'react'; +import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; + +class ModalExample extends React.Component { + constructor(props) { + super(props); + this.state = { + modal: false + }; + + this.toggle = this.toggle.bind(this); + } + + toggle() { + this.setState({ + modal: !this.state.modal + }); + } + + render() { + return ( +
+ + + Modal title + + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + + + {' '} + + + +
+ ); + } +} + +export default ModalExample; diff --git a/src/ModalHeader.js b/src/ModalHeader.js index 54a222a46..1e55b0b1a 100644 --- a/src/ModalHeader.js +++ b/src/ModalHeader.js @@ -11,12 +11,14 @@ const propTypes = { cssModule: PropTypes.object, children: PropTypes.node, closeAriaLabel: PropTypes.string, + charCode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) }; const defaultProps = { tag: 'h5', wrapTag: 'div', closeAriaLabel: 'Close', + charCode: 215, }; const ModalHeader = (props) => { @@ -29,6 +31,7 @@ const ModalHeader = (props) => { tag: Tag, wrapTag: WrapTag, closeAriaLabel, + charCode, ...attributes } = props; const classes = mapToCssModules(classNames( @@ -37,9 +40,10 @@ const ModalHeader = (props) => { ), cssModule); if (toggle) { + const closeIcon = typeof charCode === 'number' ? String.fromCharCode(charCode) : charCode; closeButton = ( ); } diff --git a/src/__tests__/ModalHeader.spec.js b/src/__tests__/ModalHeader.spec.js index 0eb448ca0..2aed6b72c 100644 --- a/src/__tests__/ModalHeader.spec.js +++ b/src/__tests__/ModalHeader.spec.js @@ -44,4 +44,19 @@ describe('ModalHeader', () => { const closeButton = wrapper.find('button.close').first(); expect(closeButton.prop('aria-label')).toBe('oseclay'); }); + + it('should render close button with default icon', () => { + const wrapper = shallow( {}}>Yo!); + + const closeButtonIcon = wrapper.find('button.close span'); + const defaultIcon = String.fromCharCode(215); + expect(closeButtonIcon.text()).toEqual(defaultIcon); + }); + + it('should render close button with custom icon', () => { + const wrapper = shallow( {}} charCode={'X'}>Yo!); + + const closeButtonIcon = wrapper.find('button.close span'); + expect(closeButtonIcon.text()).toEqual('X'); + }); }); From 9d7948f623ff2e159b79e861a53f32602e999490 Mon Sep 17 00:00:00 2001 From: makepost Date: Mon, 6 Aug 2018 17:30:10 +0300 Subject: [PATCH 16/81] fix(Modal): prevent scrollbar from closing (#1165) Closes #1097 --- src/Modal.js | 14 ++++++++------ src/__tests__/Modal.spec.js | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Modal.js b/src/Modal.js index b9c3425e5..89e5595c7 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -83,8 +83,8 @@ class Modal extends React.Component { this._element = null; this._originalBodyPadding = null; + this.handleBackdropClick = this.handleBackdropClick.bind(this); this.handleBackdropMouseDown = this.handleBackdropMouseDown.bind(this); - this.handleBackdropMouseUp = this.handleBackdropMouseUp.bind(this); this.handleEscape = this.handleEscape.bind(this); this.onOpened = this.onOpened.bind(this); this.onClosed = this.onClosed.bind(this); @@ -166,10 +166,8 @@ class Modal extends React.Component { } } - handleBackdropMouseDown(e) { - this._mouseDownElement = e.target; - } - handleBackdropMouseUp(e) { + // not mouseUp because scrollbar fires it, shouldn't close when user scrolls + handleBackdropClick(e) { if (e.target === this._mouseDownElement) { e.stopPropagation(); if (!this.props.isOpen || this.props.backdrop !== true) return; @@ -182,6 +180,10 @@ class Modal extends React.Component { } } + handleBackdropMouseDown(e) { + this._mouseDownElement = e.target; + } + handleEscape(e) { if (this.props.isOpen && this.props.keyboard && e.keyCode === 27 && this.props.toggle) { this.props.toggle(e); @@ -269,8 +271,8 @@ class Modal extends React.Component { } = this.props; const modalAttributes = { + onClick: this.handleBackdropClick, onMouseDown: this.handleBackdropMouseDown, - onMouseUp: this.handleBackdropMouseUp, onKeyUp: this.handleEscape, style: { display: 'block' }, 'aria-labelledby': labelledBy, diff --git a/src/__tests__/Modal.spec.js b/src/__tests__/Modal.spec.js index 22585b044..bd70d5058 100644 --- a/src/__tests__/Modal.spec.js +++ b/src/__tests__/Modal.spec.js @@ -563,9 +563,9 @@ describe('Modal', () => { mouseDownEvent.initEvent('mousedown', true, true); modal.dispatchEvent(mouseDownEvent); - const mouseUpEvent = document.createEvent('MouseEvents'); - mouseUpEvent.initEvent('mouseup', true, true); - modal.dispatchEvent(mouseUpEvent); + const clickEvent = document.createEvent('MouseEvents'); + clickEvent.initEvent('click', true, true); + modal.dispatchEvent(clickEvent); jest.runTimersToTime(300); From abbac56c18ba4aeb7552fff7a23976bee28d99c0 Mon Sep 17 00:00:00 2001 From: Graham Monteith Date: Mon, 6 Aug 2018 14:16:41 -0500 Subject: [PATCH 17/81] feat(Dropdown): Select first element matching pressed key (#1160) Closes #1156 --- src/Dropdown.js | 10 +++++++--- src/__tests__/Dropdown.spec.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/Dropdown.js b/src/Dropdown.js index 0f7e501ab..e74e8e85b 100644 --- a/src/Dropdown.js +++ b/src/Dropdown.js @@ -106,7 +106,7 @@ class Dropdown extends React.Component { } handleKeyDown(e) { - if ([keyCodes.esc, keyCodes.up, keyCodes.down, keyCodes.space].indexOf(e.which) === -1 || + if (keyCodes.tab === e.which || (/button/i.test(e.target.tagName) && e.which === keyCodes.space) || /input|textarea/i.test(e.target.tagName)) { return; @@ -132,12 +132,15 @@ class Dropdown extends React.Component { const disabledClass = mapToCssModules('disabled', this.props.cssModule); const items = container.querySelectorAll(`.${menuClass} .${itemClass}:not(.${disabledClass})`); - if (!items.length) return; let index = -1; + + const charPressed = String.fromCharCode(e.which).toLowerCase(); + for (let i = 0; i < items.length; i += 1) { - if (items[i] === e.target) { + const firstLetter = items[i].textContent && items[i].textContent[0].toLowerCase(); + if (firstLetter === charPressed || items[i] === e.target) { index = i; break; } @@ -151,6 +154,7 @@ class Dropdown extends React.Component { index += 1; } + if (index < 0) { index = 0; } diff --git a/src/__tests__/Dropdown.spec.js b/src/__tests__/Dropdown.spec.js index b9b2b44d4..0b73f35ce 100644 --- a/src/__tests__/Dropdown.spec.js +++ b/src/__tests__/Dropdown.spec.js @@ -432,6 +432,36 @@ describe('Dropdown', () => { wrapper.detach(); }); + it('should focus the first menu item matching the character pressed when isOpen is true', () => { + isOpen = true; + jest.spyOn(Dropdown.prototype, 'toggle'); + const focus1 = jest.fn(); + const focus2 = jest.fn(); + const focus3 = jest.fn(); + + const wrapper = mount( + + Toggle + + Reactstrap + 4 + + Lyfe + + , { attachTo: element }); + + expect(Dropdown.prototype.toggle.mock.calls.length).toBe(0); + + wrapper.find('#dropdown').hostNodes().simulate('keydown', { which: 52 }); + expect(Dropdown.prototype.toggle.mock.calls.length).toBe(0); + + expect(focus1.mock.calls.length).toBe(0); + expect(focus2.mock.calls.length).toBe(1); + expect(focus3.mock.calls.length).toBe(0); + + wrapper.detach(); + }); + it('should skip non-menu items focus the next menu item on down arrow keydown when it isOpen is true and anther item is focused', () => { isOpen = true; jest.spyOn(Dropdown.prototype, 'toggle'); From e6781d7a81480c0a7c8310bdafa55f6120dc3a1d Mon Sep 17 00:00:00 2001 From: Graham Monteith Date: Mon, 13 Aug 2018 09:00:10 -0500 Subject: [PATCH 18/81] feat(Modal): trap focus in modal (#1161) Closes #310 --- docs/lib/Components/ModalsPage.js | 2 +- src/Modal.js | 46 ++++++++++++++++++++++- src/__tests__/Modal.spec.js | 62 ++++++++++++++++++++++++++++++- src/utils.js | 15 ++++++++ webpack.dev.config.js | 1 + 5 files changed, 123 insertions(+), 3 deletions(-) diff --git a/docs/lib/Components/ModalsPage.js b/docs/lib/Components/ModalsPage.js index e1db04a11..ce62b4109 100644 --- a/docs/lib/Components/ModalsPage.js +++ b/docs/lib/Components/ModalsPage.js @@ -22,7 +22,7 @@ import ModalExternalExample from '../examples/ModalExternal'; const ModalExternalExampleSource = require('!!raw!../examples/ModalExternal'); import ModalCustomCloseIconExample from '../examples/ModalCustomCloseIcon'; -const ModalCustomCloseIconExampleSource = require('!!raw!../examples/ModalCutomCloseIcon'); +const ModalCustomCloseIconExampleSource = require('!!raw!../examples/ModalCustomCloseIcon'); export default class ModalsPage extends React.Component { render() { diff --git a/src/Modal.js b/src/Modal.js index 89e5595c7..a5cd94158 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -9,6 +9,7 @@ import { setScrollbarWidth, mapToCssModules, omit, + focusableElements, TransitionTimeouts } from './utils'; @@ -83,9 +84,11 @@ class Modal extends React.Component { this._element = null; this._originalBodyPadding = null; + this.getFocusableChildren = this.getFocusableChildren.bind(this); this.handleBackdropClick = this.handleBackdropClick.bind(this); this.handleBackdropMouseDown = this.handleBackdropMouseDown.bind(this); this.handleEscape = this.handleEscape.bind(this); + this.handleTab = this.handleTab.bind(this); this.onOpened = this.onOpened.bind(this); this.onClosed = this.onClosed.bind(this); @@ -166,6 +169,22 @@ class Modal extends React.Component { } } + getFocusableChildren() { + return this._element.querySelectorAll(focusableElements.join(', ')); + } + + getFocusedChild() { + let currentFocus; + const focusableChildren = this.getFocusableChildren(); + + try { + currentFocus = document.activeElement; + } catch (err) { + currentFocus = focusableChildren[0]; + } + return currentFocus; + } + // not mouseUp because scrollbar fires it, shouldn't close when user scrolls handleBackdropClick(e) { if (e.target === this._mouseDownElement) { @@ -180,6 +199,31 @@ class Modal extends React.Component { } } + handleTab(e) { + if (e.which !== 9) return; + + const focusableChildren = this.getFocusableChildren(); + const totalFocusable = focusableChildren.length; + const currentFocus = this.getFocusedChild(); + + let focusedIndex = 0; + + for (let i = 0; i < totalFocusable; i += 1) { + if (focusableChildren[i] === currentFocus) { + focusedIndex = i; + break; + } + } + + if (e.shiftKey && focusedIndex === 0) { + e.preventDefault(); + focusableChildren[totalFocusable - 1].focus(); + } else if (!e.shiftKey && focusedIndex === totalFocusable - 1) { + e.preventDefault(); + focusableChildren[0].focus(); + } + } + handleBackdropMouseDown(e) { this._mouseDownElement = e.target; } @@ -200,7 +244,6 @@ class Modal extends React.Component { conditionallyUpdateScrollbar(); document.body.appendChild(this._element); - if (!this.bodyClassAdded) { document.body.className = classNames( document.body.className, @@ -274,6 +317,7 @@ class Modal extends React.Component { onClick: this.handleBackdropClick, onMouseDown: this.handleBackdropMouseDown, onKeyUp: this.handleEscape, + onKeyDown: this.handleTab, style: { display: 'block' }, 'aria-labelledby': labelledBy, role, diff --git a/src/__tests__/Modal.spec.js b/src/__tests__/Modal.spec.js index bd70d5058..21a75fce9 100644 --- a/src/__tests__/Modal.spec.js +++ b/src/__tests__/Modal.spec.js @@ -1,6 +1,7 @@ +/* eslint-disable jsx-a11y/no-noninteractive-tabindex */ import React from 'react'; import { mount, shallow } from 'enzyme'; -import { Modal, ModalBody } from '../'; +import { Modal, ModalBody, ModalHeader, ModalFooter, Button } from '../'; describe('Modal', () => { let isOpen; @@ -757,4 +758,63 @@ describe('Modal', () => { wrapper.setProps({ zIndex: 1 }); expect(wrapper.instance()._element.style.zIndex).toBe('1'); }); + + it('should allow focus on only focusable elements', () => { + isOpen = true; + + const wrapper = mount( + + Modal title + + Test + + test + + + + + + +