From e8c984e788b23938a280f3ca0fade1254ad71baf Mon Sep 17 00:00:00 2001 From: Stephen James Date: Wed, 23 May 2018 14:11:49 -0600 Subject: [PATCH 1/2] Move form components, deprecate InlineEdit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * The audit showed that checkbox and inline edit's `onChange` are wrong (along with deprecated lookup's onChange, onBlur, onFocus, but we aren't updating Lookup). DataTable's `onChange` is also `(selectedArrayOfItems, event)`. I'm going think about that one a little more. * SLDS has moved their form components out from under the form folder, so a breaking change in event parameters was a good time to move the `checkbox` component out of the forms folder. That way, consumers can upgrade at their leisure--unless they are using the CommonJS named imports. * Right now, CommonJS and `components/forms/checkbox` users (that is everyone) will get the warning, but nothing will break. * Went ahead and converted checkbox to a ES6 class and removed a lodash function. * There was a comment in all the site-stories that mentioned `forms-checkbox` and that has been removed. * The first commit "Align event prop parameters" just adds from empty objects to a few components as the second parameter `data` object to existing event callbacks, just to guard from being `undefined` if accessed. This should be standardized. * If oldEventParameterOrder is true, a warning will fire. This will tell folks to change their path—if they are consuming the source code. The CommonJS named imports will just have to break at the next breaking change due to the parameter change, since you can’t change the path. --- .../__docs__/storybook-stories.jsx | 2 +- .../app-launcher/__examples__/default.jsx | 2 +- .../__tests__/app-launcher.browser-test.jsx | 2 +- components/app-launcher/index.jsx | 6 +- components/app-launcher/section.jsx | 2 +- .../__examples__/checkbox-error.jsx | 2 +- .../button-group/__examples__/checkbox.jsx | 2 +- components/button/index.jsx | 2 +- components/card/filter.jsx | 2 +- .../checkbox/__docs__/site-stories.js | 0 .../checkbox/__docs__/storybook-stories.jsx | 102 ++--- components/checkbox/__examples__/default.jsx | 74 ++++ .../checkbox/__examples__/error.jsx | 18 +- .../checkbox/__examples__/snapshot-base.jsx | 18 +- .../checkbox/__examples__/snapshot-toggle.jsx | 17 +- .../checkbox/__examples__/toggle.jsx | 16 +- .../checkbox.snapshot-test.jsx.snap | 0 .../__tests__/checkbox.snapshot-test.jsx | 2 +- components/checkbox/check-props.js | 36 ++ components/checkbox/index.jsx | 391 +++++++++++++++++ components/combobox/combobox.jsx | 22 +- components/data-table/index.jsx | 11 +- components/data-table/private/head.jsx | 6 +- components/data-table/private/row.jsx | 10 +- .../date-picker/__examples__/custom-input.jsx | 2 +- .../__tests__/date-picker.browser-test.jsx | 2 +- components/date-picker/date-picker.jsx | 4 +- .../date-picker/private/calendar-wrapper.jsx | 6 +- components/date-picker/private/calendar.jsx | 4 +- components/date-picker/private/day.jsx | 4 - components/date-picker/private/week.jsx | 5 - .../forms/checkbox/__examples__/default.jsx | 35 -- components/forms/checkbox/check-props.js | 25 -- components/forms/checkbox/index.jsx | 388 +---------------- .../input/__docs__/inline/site-stories.js | 10 - .../__docs__/inline/storybook-stories.jsx | 2 +- components/forms/input/index.jsx | 411 +----------------- components/forms/input/inline-check-props.js | 19 + components/forms/input/inline.jsx | 7 + components/forms/input/search.jsx | 91 +--- components/forms/radio/index.jsx | 111 +---- components/forms/textarea/index.jsx | 332 +------------- components/index.js | 18 +- .../__docs__/search/storybook-stories.jsx | 4 +- .../input/__docs__/site-stories.js | 8 +- .../input/__docs__/storybook-stories.jsx | 8 +- .../input/__examples__/default.jsx | 2 +- .../{forms => }/input/__examples__/error.jsx | 2 +- .../{forms => }/input/__examples__/icons.jsx | 2 +- .../input/__examples__/inactiveInputs.jsx | 2 +- .../input/__tests__/input.browser-test.jsx | 8 +- components/{forms => }/input/check-props.js | 6 +- components/input/index.jsx | 410 +++++++++++++++++ .../{forms => }/input/private/inner-input.jsx | 2 +- components/input/search.jsx | 94 ++++ components/lookup/lookup.jsx | 2 +- .../radio/__examples__/default.jsx | 2 +- .../radio/__examples__/disabled.jsx | 2 +- components/radio/index.jsx | 114 +++++ components/site-stories.js | 3 +- .../slider/__tests__/slider.browser-test.jsx | 2 - components/story-based-tests.js | 9 +- components/storybook-stories.js | 8 +- .../tabs/__docs__/storybook-stories.jsx | 4 +- .../textarea/__docs__/site-stories.js | 0 .../textarea/__docs__/storybook-stories.jsx | 4 +- .../textarea/__examples__/default.jsx | 2 +- .../textarea/__examples__/disabled.jsx | 2 +- .../textarea/__examples__/error.jsx | 2 +- .../__tests__/textarea.browser-test.jsx | 0 .../{forms => }/textarea/check-props.js | 2 +- components/textarea/index.jsx | 333 ++++++++++++++ .../time-picker/private/dropdown-trigger.jsx | 2 +- .../tree/__docs__/storybook-stories.jsx | 2 +- .../tree/__tests__/tree.browser-test.jsx | 2 +- components/utilities/label/index.jsx | 68 +++ package.json | 68 ++- .../warning/deprecated-event-parameter.js | 45 ++ utilities/warning/low-priority-warning.js | 2 +- 79 files changed, 1805 insertions(+), 1644 deletions(-) rename components/{forms => }/checkbox/__docs__/site-stories.js (100%) rename components/{forms => }/checkbox/__docs__/storybook-stories.jsx (78%) create mode 100644 components/checkbox/__examples__/default.jsx rename components/{forms => }/checkbox/__examples__/error.jsx (77%) rename components/{forms => }/checkbox/__examples__/snapshot-base.jsx (87%) rename components/{forms => }/checkbox/__examples__/snapshot-toggle.jsx (87%) rename components/{forms => }/checkbox/__examples__/toggle.jsx (91%) rename components/{forms => }/checkbox/__tests__/__snapshots__/checkbox.snapshot-test.jsx.snap (100%) rename components/{forms => }/checkbox/__tests__/checkbox.snapshot-test.jsx (91%) create mode 100644 components/checkbox/check-props.js create mode 100644 components/checkbox/index.jsx delete mode 100644 components/forms/checkbox/__examples__/default.jsx delete mode 100644 components/forms/checkbox/check-props.js delete mode 100644 components/forms/input/__docs__/inline/site-stories.js create mode 100644 components/forms/input/inline-check-props.js rename components/{forms => }/input/__docs__/search/storybook-stories.jsx (80%) rename components/{forms => }/input/__docs__/site-stories.js (84%) rename components/{forms => }/input/__docs__/storybook-stories.jsx (96%) rename components/{forms => }/input/__examples__/default.jsx (93%) rename components/{forms => }/input/__examples__/error.jsx (92%) rename components/{forms => }/input/__examples__/icons.jsx (96%) rename components/{forms => }/input/__examples__/inactiveInputs.jsx (93%) rename components/{forms => }/input/__tests__/input.browser-test.jsx (98%) rename components/{forms => }/input/check-props.js (90%) create mode 100644 components/input/index.jsx rename components/{forms => }/input/private/inner-input.jsx (99%) create mode 100644 components/input/search.jsx rename components/{forms => }/radio/__examples__/default.jsx (76%) rename components/{forms => }/radio/__examples__/disabled.jsx (76%) create mode 100644 components/radio/index.jsx rename components/{forms => }/textarea/__docs__/site-stories.js (100%) rename components/{forms => }/textarea/__docs__/storybook-stories.jsx (89%) rename components/{forms => }/textarea/__examples__/default.jsx (81%) rename components/{forms => }/textarea/__examples__/disabled.jsx (83%) rename components/{forms => }/textarea/__examples__/error.jsx (84%) rename components/{forms => }/textarea/__tests__/textarea.browser-test.jsx (100%) rename components/{forms => }/textarea/check-props.js (85%) create mode 100644 components/textarea/index.jsx create mode 100644 components/utilities/label/index.jsx create mode 100644 utilities/warning/deprecated-event-parameter.js diff --git a/components/app-launcher/__docs__/storybook-stories.jsx b/components/app-launcher/__docs__/storybook-stories.jsx index a151b23988..1e16ffb22c 100644 --- a/components/app-launcher/__docs__/storybook-stories.jsx +++ b/components/app-launcher/__docs__/storybook-stories.jsx @@ -10,7 +10,7 @@ import AppLauncherTile from '../../app-launcher/tile'; import AppLauncherSection from '../../app-launcher/section'; import Icon from '../../icon'; import Button from '../../button'; -import Search from '../../forms/input/search'; +import Search from '../../input/search'; import GlobalNavigationBar from '../../global-navigation-bar'; import GlobalNavigationBarRegion from '../../global-navigation-bar/region'; diff --git a/components/app-launcher/__examples__/default.jsx b/components/app-launcher/__examples__/default.jsx index 0d778528b4..20fb9bb150 100644 --- a/components/app-launcher/__examples__/default.jsx +++ b/components/app-launcher/__examples__/default.jsx @@ -9,7 +9,7 @@ import GlobalNavigationBarRegion from '~/components/global-navigation-bar/region import Icon from '~/components/icon'; import Button from '~/components/button'; -import Search from '~/components/forms/input/search'; +import Search from '~/components/input/search'; import IconSettings from '~/components/icon-settings'; const Example = createReactClass({ diff --git a/components/app-launcher/__tests__/app-launcher.browser-test.jsx b/components/app-launcher/__tests__/app-launcher.browser-test.jsx index efcd70a90d..175925aafb 100644 --- a/components/app-launcher/__tests__/app-launcher.browser-test.jsx +++ b/components/app-launcher/__tests__/app-launcher.browser-test.jsx @@ -10,7 +10,7 @@ import IconSettings from '../../icon-settings'; import AppLauncher from '../../app-launcher'; import AppLauncherTile from '../../app-launcher/tile'; import AppLauncherSection from '../../app-launcher/section'; -import Search from '../../forms/input/search'; +import Search from '../../input/search'; import Button from '../../button'; /* eslint-disable react/no-find-dom-node */ diff --git a/components/app-launcher/index.jsx b/components/app-launcher/index.jsx index d33920ff1f..de40d80dc3 100644 --- a/components/app-launcher/index.jsx +++ b/components/app-launcher/index.jsx @@ -97,7 +97,7 @@ const AppLauncher = createReactClass({ */ onClose: PropTypes.func, /** - * Search bar for the Modal's header. Will typically be an instance of `design-system-react/forms/input/search` + * Search bar for the Modal's header. Will typically be an instance of `design-system-react/input/search` */ search: PropTypes.node, /** @@ -140,7 +140,7 @@ const AppLauncher = createReactClass({ this.setState({ isOpen: true }); if (isFunction(this.props.triggerOnClick)) { - this.props.triggerOnClick(event); + this.props.triggerOnClick(event, {}); } }, @@ -148,7 +148,7 @@ const AppLauncher = createReactClass({ this.setState({ isOpen: false }); if (isFunction(this.props.onClose)) { - this.props.onClose(event); + this.props.onClose(event, {}); } }, diff --git a/components/app-launcher/section.jsx b/components/app-launcher/section.jsx index 5e64772d37..61243dfcea 100644 --- a/components/app-launcher/section.jsx +++ b/components/app-launcher/section.jsx @@ -77,7 +77,7 @@ const AppLauncherSection = createReactClass({ this.setState({ isOpen: !this.state.isOpen }); if (isFunction(this.props.onToggleClick)) { - this.props.onToggleClick(event); + this.props.onToggleClick(event, {}); } }, diff --git a/components/button-group/__examples__/checkbox-error.jsx b/components/button-group/__examples__/checkbox-error.jsx index c852c7eff0..09f99923b3 100644 --- a/components/button-group/__examples__/checkbox-error.jsx +++ b/components/button-group/__examples__/checkbox-error.jsx @@ -1,7 +1,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import ButtonGroup from '~/components/button-group'; -import Checkbox from '~/components/forms/checkbox'; +import Checkbox from '~/components/checkbox'; const Example = createReactClass({ displayName: 'ButtonGroupExample', diff --git a/components/button-group/__examples__/checkbox.jsx b/components/button-group/__examples__/checkbox.jsx index f0b7dfe246..7a4d4b6a01 100644 --- a/components/button-group/__examples__/checkbox.jsx +++ b/components/button-group/__examples__/checkbox.jsx @@ -1,7 +1,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import ButtonGroup from '~/components/button-group'; -import Checkbox from '~/components/forms/checkbox'; +import Checkbox from '~/components/checkbox'; const Example = createReactClass({ displayName: 'ButtonGroupExample', diff --git a/components/button/index.jsx b/components/button/index.jsx index 19f0405ae2..8d4d8ac190 100644 --- a/components/button/index.jsx +++ b/components/button/index.jsx @@ -251,7 +251,7 @@ const Button = createReactClass({ handleClick (event) { if (this.props.onClick) { - this.props.onClick(event); + this.props.onClick(event, {}); } }, diff --git a/components/card/filter.jsx b/components/card/filter.jsx index 70f3c1491e..32a066f855 100644 --- a/components/card/filter.jsx +++ b/components/card/filter.jsx @@ -5,7 +5,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Input from '../forms/input'; +import Input from '../input'; import InputIcon from '../icon/input-icon'; import { CARD_FILTER } from '../../utilities/constants'; diff --git a/components/forms/checkbox/__docs__/site-stories.js b/components/checkbox/__docs__/site-stories.js similarity index 100% rename from components/forms/checkbox/__docs__/site-stories.js rename to components/checkbox/__docs__/site-stories.js diff --git a/components/forms/checkbox/__docs__/storybook-stories.jsx b/components/checkbox/__docs__/storybook-stories.jsx similarity index 78% rename from components/forms/checkbox/__docs__/storybook-stories.jsx rename to components/checkbox/__docs__/storybook-stories.jsx index 54b74b30be..917ca7013b 100644 --- a/components/forms/checkbox/__docs__/storybook-stories.jsx +++ b/components/checkbox/__docs__/storybook-stories.jsx @@ -1,24 +1,24 @@ import React from 'react'; -import createReactClass from 'create-react-class'; -import { storiesOf, action } from '@storybook/react'; -import IconSettings from '../../../icon-settings'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import IconSettings from '../../icon-settings'; -import { FORMS_CHECKBOX } from '../../../../utilities/constants'; +import { FORMS_CHECKBOX } from '../../../utilities/constants'; import Checkbox from '../'; -import Button from '../../../button'; +import Button from '../../button'; -const CheckboxIndeterminate = createReactClass({ - displayName: `${FORMS_CHECKBOX}_INDETERMINATE`, +import DefaultCheckbox from '../__examples__/default'; - getInitialState () { - return { - indeterminate: true, - checked: true, - currentStateHelper: 'Indeterminate', - }; - }, +class CheckboxIndeterminate extends React.Component { + static displayName = `${FORMS_CHECKBOX}_INDETERMINATE`; + + state = { + indeterminate: true, + checked: true, + currentStateHelper: 'Indeterminate', + }; - handleChange (checked, event, data) { + handleChange = (checked, event, data) => { const checkedLabel = data.checked ? 'Checked' : 'Unchecked'; this.setState({ checked: data.checked, @@ -32,9 +32,9 @@ const CheckboxIndeterminate = createReactClass({ `checked: ${data.checked}, indeterminate: ${data.indeterminate}` ); - }, + }; - changeToIndeterminate (event) { + changeToIndeterminate = (event) => { this.setState({ currentStateHelper: 'Indeterminate', checked: true, @@ -44,25 +44,25 @@ const CheckboxIndeterminate = createReactClass({ event, 'checked: true, indeterminate: true' ); - }, + }; - changeToCheck (event) { + changeToCheck = (event) => { this.setState({ currentStateHelper: 'Checked', checked: true, indeterminate: false, }); action('changeToCheck')(event, 'checked: true, indeterminate: false'); - }, + }; - changeToUnChecked (event) { + changeToUnChecked = (event) => { this.setState({ currentStateHelper: 'Unchecked', checked: false, indeterminate: false, }); action('changeToUnChecked')(event, 'checked: false, indeterminate: false'); - }, + }; render () { return ( @@ -74,7 +74,9 @@ const CheckboxIndeterminate = createReactClass({ Current State: {this.state.currentStateHelper}

); - }, -}); + } +} storiesOf(FORMS_CHECKBOX, module) .addDecorator((getStory) => ( @@ -110,54 +112,22 @@ storiesOf(FORMS_CHECKBOX, module) {getStory()} )) - .add('Checkbox', () => ( - { - console.log('bluring ', e.target); - }} - /> - )) - .add('Checkbox (with error)', () => ( - { - console.log('bluring ', e.target); - }} - /> - )) - .add('Checkbox (required)', () => ( - - )) - .add('Checkbox (disabled)', () => ( - + .add('Checkbox (default, indeterminate, required, disabled', () => ( + )) .add('Checkbox (assistive text)', () => (
(
+
+
+ { + console.log('onChange ', e.target); + }} + /> +
+
+ { + console.log('onChange ', e.target); + }} + /> +
+
+ { + console.log('onChange ', e.target); + }} + /> +
+
+ { + console.log('onChange ', e.target); + }} + /> +
+
+ + ); + } +} + +export default Example; // export is replaced with `ReactDOM.render(, mountNode);` at runtime diff --git a/components/forms/checkbox/__examples__/error.jsx b/components/checkbox/__examples__/error.jsx similarity index 77% rename from components/forms/checkbox/__examples__/error.jsx rename to components/checkbox/__examples__/error.jsx index 7240092fd5..937933cebc 100644 --- a/components/forms/checkbox/__examples__/error.jsx +++ b/components/checkbox/__examples__/error.jsx @@ -1,11 +1,9 @@ import React from 'react'; -import createReactClass from 'create-react-class'; +// `~` is replaced with design-system-react at runtime import IconSettings from '~/components/icon-settings'; -import Checkbox from '~/components/forms/checkbox'; // `~` is replaced with design-system-react at runtime - -const Example = createReactClass({ - displayName: 'CheckboxExample', +import Checkbox from '~/components/checkbox'; +class Example extends React.Component { render () { return ( @@ -19,7 +17,9 @@ const Example = createReactClass({
@@ -33,7 +33,9 @@ const Example = createReactClass({
); - }, -}); + } +} + +Example.displayName = 'CheckboxExample'; export default Example; // export is replaced with `ReactDOM.render(, mountNode);` at runtime diff --git a/components/forms/checkbox/__examples__/snapshot-base.jsx b/components/checkbox/__examples__/snapshot-base.jsx similarity index 87% rename from components/forms/checkbox/__examples__/snapshot-base.jsx rename to components/checkbox/__examples__/snapshot-base.jsx index be6aeb34cd..c4dc002092 100644 --- a/components/forms/checkbox/__examples__/snapshot-base.jsx +++ b/components/checkbox/__examples__/snapshot-base.jsx @@ -1,12 +1,8 @@ import React from 'react'; -import createReactClass from 'create-react-class'; - // `~` is replaced with design-system-react at runtime -import Checkbox from '~/components/forms/checkbox'; - -const Example = createReactClass({ - displayName: 'CheckboxExample', +import Checkbox from '~/components/checkbox'; +class Example extends React.Component { render () { return (
@@ -36,7 +32,9 @@ const Example = createReactClass({
@@ -64,7 +62,9 @@ const Example = createReactClass({
); - }, -}); + } +} + +Example.displayName = 'CheckboxExample'; export default Example; // export is replaced with `ReactDOM.render(, mountNode);` at runtime diff --git a/components/forms/checkbox/__examples__/snapshot-toggle.jsx b/components/checkbox/__examples__/snapshot-toggle.jsx similarity index 87% rename from components/forms/checkbox/__examples__/snapshot-toggle.jsx rename to components/checkbox/__examples__/snapshot-toggle.jsx index b38937eb25..1db2e01859 100644 --- a/components/forms/checkbox/__examples__/snapshot-toggle.jsx +++ b/components/checkbox/__examples__/snapshot-toggle.jsx @@ -1,12 +1,9 @@ import React from 'react'; -import createReactClass from 'create-react-class'; // `~` is replaced with design-system-react at runtime -import Checkbox from '~/components/forms/checkbox'; - -const Example = createReactClass({ - displayName: 'CheckboxExample', +import Checkbox from '~/components/checkbox'; +class Example extends React.Component { render () { return (
@@ -43,7 +40,9 @@ const Example = createReactClass({
@@ -67,7 +66,9 @@ const Example = createReactClass({
); - }, -}); + } +} + +Example.displayName = 'CheckboxExample'; export default Example; // export is replaced with `ReactDOM.render(, mountNode);` at runtime diff --git a/components/forms/checkbox/__examples__/toggle.jsx b/components/checkbox/__examples__/toggle.jsx similarity index 91% rename from components/forms/checkbox/__examples__/toggle.jsx rename to components/checkbox/__examples__/toggle.jsx index 1bf66f8ac4..afcb7bd425 100644 --- a/components/forms/checkbox/__examples__/toggle.jsx +++ b/components/checkbox/__examples__/toggle.jsx @@ -1,13 +1,9 @@ import React from 'react'; -import createReactClass from 'create-react-class'; -import IconSettings from '~/components/icon-settings'; - // `~` is replaced with design-system-react at runtime -import Checkbox from '~/components/forms/checkbox'; - -const Example = createReactClass({ - displayName: 'CheckboxExample', +import IconSettings from '~/components/icon-settings'; +import Checkbox from '~/components/checkbox'; +class Example extends React.Component { render () { return ( @@ -74,7 +70,9 @@ const Example = createReactClass({ ); - }, -}); + } +} + +Example.displayName = 'CheckboxExample'; export default Example; // export is replaced with `ReactDOM.render(, mountNode);` at runtime diff --git a/components/forms/checkbox/__tests__/__snapshots__/checkbox.snapshot-test.jsx.snap b/components/checkbox/__tests__/__snapshots__/checkbox.snapshot-test.jsx.snap similarity index 100% rename from components/forms/checkbox/__tests__/__snapshots__/checkbox.snapshot-test.jsx.snap rename to components/checkbox/__tests__/__snapshots__/checkbox.snapshot-test.jsx.snap diff --git a/components/forms/checkbox/__tests__/checkbox.snapshot-test.jsx b/components/checkbox/__tests__/checkbox.snapshot-test.jsx similarity index 91% rename from components/forms/checkbox/__tests__/checkbox.snapshot-test.jsx rename to components/checkbox/__tests__/checkbox.snapshot-test.jsx index fbdf6af13a..aec92c08c4 100644 --- a/components/forms/checkbox/__tests__/checkbox.snapshot-test.jsx +++ b/components/checkbox/__tests__/checkbox.snapshot-test.jsx @@ -1,6 +1,6 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import { renderMarkup } from '../../../../tests/snapshot-helpers'; +import { renderMarkup } from '../../../tests/snapshot-helpers'; import CheckboxBase from '../__examples__/snapshot-base'; import CheckboxToggle from '../__examples__/snapshot-toggle'; diff --git a/components/checkbox/check-props.js b/components/checkbox/check-props.js new file mode 100644 index 0000000000..16a20db239 --- /dev/null +++ b/components/checkbox/check-props.js @@ -0,0 +1,36 @@ +/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ +/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ +/* eslint-disable import/no-mutable-exports */ +/* eslint-disable max-len */ + +import deprecatedEventParameter from '../../utilities/warning/deprecated-event-parameter'; +import onlyOneOfProperties from '../../utilities/warning/only-one-of-properties'; + +let checkProps = function () {}; + +if (process.env.NODE_ENV !== 'production') { + checkProps = function (COMPONENT, props) { + deprecatedEventParameter( + COMPONENT, + { + oldEventParameterOrder: props.oldEventParameterOrder, + propAsString: 'onChange', + propAsValue: props.onChange, + }, + '`components/forms/checkbox` is deprecated. `components/checkbox` should be used. When this path update is made `onChange` event parameters will change from `onChange(value, event, { value } to `onChange(event, { value }). Please update your event parameters when you change paths.` If you are using the CommonJS named import, `Checkbox` events will break at v1.0 and this warning will be present until then. Please review https://github.com/salesforce/design-system-react/releases when you upgrade.' + ); + + if (props.variant === 'toggle' && props.indeterminate === true) { + onlyOneOfProperties( + COMPONENT, + { + variant: props.variant, + indeterminate: props.indeterminate, + }, + 'Currently SLDS does not support the `indeterminate` state in Checkbox Toggle. See SLDS documentation about [Checkbox Toggle](https://lightningdesignsystem.com/components/forms/#flavor-checkbox-toggle-checkbox-toggle) for more information.' + ); + } + }; +} + +export default checkProps; diff --git a/components/checkbox/index.jsx b/components/checkbox/index.jsx new file mode 100644 index 0000000000..857ac0537c --- /dev/null +++ b/components/checkbox/index.jsx @@ -0,0 +1,391 @@ +/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ +/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ + +// # Checkbox Component + +// Implements the [Checkbox design pattern](https://www.lightningdesignsystem.com/components/forms/#checkbox) in React. + +import React from 'react'; +import PropTypes from 'prop-types'; + +import classNames from 'classnames'; + +// ### shortid +// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid) +// shortid is a short, non-sequential, url-friendly, unique id generator +import shortid from 'shortid'; + +// ### Event Helpers +import KEYS from '../../utilities/key-code'; +import EventUtil from '../../utilities/event'; + +// This component's `checkProps` which issues warnings to developers about properties when in development mode (similar to React's built in development tools) +import checkProps from './check-props'; + +import { FORMS_CHECKBOX } from '../../utilities/constants'; + +const propTypes = { + /** + * An HTML ID that is shared with ARIA-supported devices with the + * `aria-controls` attribute in order to relate the input with + * another region of the page. An example would be a select box + * that shows or hides a panel. + */ + 'aria-controls': PropTypes.string, + /** + * The `aria-describedby` attribute is used to indicate the IDs of the elements that describe the object. It is used to establish a relationship between widgets or groups and text that described them. This is very similar to aria-labelledby: a label describes the essence of an object, while a description provides more information that the user might need. + */ + 'aria-describedby': PropTypes.string, + /** + * `aria-owns` indicate that an element depends on the current one when the relation can't be determined by the hierarchy structure. + */ + 'aria-owns': PropTypes.string, + /** + * The `aria-required` attribute is used to indicate that user input is required on an element before a form can be submitted. + */ + 'aria-required': PropTypes.bool, + /** + * **Assistive text for accessibility** + * This object is merged with the default props object on every render. + * * `label`: This is used as a visually hidden label if, no `labels.label` is provided. + */ + assistiveText: PropTypes.shape({ + label: PropTypes.string, + }), + /** + * The Checkbox should be a controlled component, and will always be in the state specified. If checked is not defined, the state of the uncontrolled native `input` component will be used. + */ + checked: PropTypes.bool, + /** + * This is the initial value of an uncontrolled form element and is present only + * to provide compatibility with hybrid framework applications that are not + * entirely React. It should only be used in an application without centralized + * state (Redux, Flux). "Controlled components" with centralized state is highly recommended. See [Code Overview](https://github.com/salesforce/design-system-react/blob/master/docs/codebase-overview.md#controlled-and-uncontrolled-components) for more information. + */ + defaultChecked: PropTypes.bool, + /** + * Class names to be added to the outer container of the Checkbox. + */ + className: PropTypes.oneOfType([ + PropTypes.array, + PropTypes.object, + PropTypes.string, + ]), + /** + * Disables the Checkbox and prevents clicking it. + */ + disabled: PropTypes.bool, + /** + * Message to display when the Checkbox is in an error state. When this is present, also visually highlights the component as in error. + */ + errorText: PropTypes.string, + /** + * A unique ID is needed in order to support keyboard navigation and ARIA support. This ID is added to the `input` element + */ + id: PropTypes.string, + /** + * The Checkbox will be indeterminate if its state can not be figured out or is partially checked. Once a checkbox is indeterminate, a click should cause it to be checked. Since a user cannot put a checkbox into an indeterminate state, it is assumed you are controlling the value of `checked` with the parent, also, and that this is a controlled component. **Note:** `indeterminate` proptype does nothing in the `toggle` variant, as [SLDS does not support it](https://lightningdesignsystem.com/components/forms/#flavor-checkbox-toggle-checkbox-toggle). + */ + indeterminate: PropTypes.bool, + /** + * **Text labels for internationalization** + * This object is merged with the default props object on every render. + * * `label`: Label for the _enabled_ state of the Toggle variant. Defaults to "Enabled". + * * `toggleDisabled`: Label for the _disabled_ state of the Toggle variant. Defaults to "Disabled". Note that this uses SLDS language, and meaning, of "Enabled" and "Disabled"; referring to the state of whatever the checkbox is _toggling_, not whether the checkbox itself is enabled or disabled. + * * `toggleEnabled`: Label for the _enabled_ state of the Toggle variant. Defaults to "Enabled". + */ + labels: PropTypes.shape({ + label: PropTypes.string, + toggleDisabled: PropTypes.string, + toggleEnabled: PropTypes.string, + }), + /** + * Name of the submitted form parameter. + */ + name: PropTypes.string, + /** + * This event fires when the Checkbox looses focus. It passes in `{ event }`. + */ + onBlur: PropTypes.func, + /** + * This event fires when the Checkbox changes. Passes in `event, { checked }`. This used to be `checked, event, { checked }`. + */ + onChange: PropTypes.func, + /** + * This event fires when the Checkbox is focused. It passes in `{ event }`. + */ + onFocus: PropTypes.func, + /** + * This event fires when a key is pressed down. It passes in `{ event }`. + */ + onKeyDown: PropTypes.func, + /** + * This event fires when a character is typed. See [this article](http://www.bloggingdeveloper.com/post/KeyPress-KeyDown-KeyUp-The-Difference-Between-Javascript-Key-Events.aspx) for more information. It passes in `{ event }`. + */ + onKeyPress: PropTypes.func, + /** + * This event fires when a pressed key is released. It passes in `{ event }`. + */ + onKeyUp: PropTypes.func, + /** + * Displays the value of the input, but does not allow changes. + */ + readOnly: PropTypes.bool, + /** + * Highlights the Checkbox as a required field (does not perform any validation). + */ + required: PropTypes.bool, + /** + * The aria-role of the checkbox. + */ + role: PropTypes.string, + /** + * Which UX pattern of checkbox? The default is `base` while other option is `toggle`. (**Note:** `toggle` variant does not support the `indeterminate` feature, because [SLDS does not support it](https://lightningdesignsystem.com/components/forms/#flavor-checkbox-toggle-checkbox-toggle).) + */ + variant: PropTypes.oneOf(['base', 'toggle', 'button-group']), +}; + +const defaultProps = { + assistiveText: {}, + labels: { + toggleDisabled: 'Disabled', + toggleEnabled: 'Enabled', + }, + variant: 'base', +}; + +/** + * The ability to style checkboxes with CSS varies across browsers. Using this component ensures checkboxes look the same everywhere. + */ +class Checkbox extends React.Component { + componentWillMount () { + checkProps(FORMS_CHECKBOX, this.props); + this.generatedId = shortid.generate(); + } + + getId = () => this.props.id || this.generatedId; + + handleChange = (event) => { + const { checked, indeterminate, onChange } = this.props; + + if (typeof onChange === 'function') { + // `target.checked` is present twice to maintain backwards compatibility. Please remove first parameter `value` on the next breaking change or when `forms/checkbox` is removed. + if (this.props.oldEventParameterOrder) { + onChange(event.target.checked, event, { + checked: indeterminate ? true : !checked, + indeterminate: false, + }); + } else { + // NEW API + onChange(event, { + checked: indeterminate ? true : !checked, + indeterminate: false, + }); + } + } + }; + + handleKeyDown = (event) => { + if (event.keyCode) { + if (event.keyCode === KEYS.ENTER || event.keyCode === KEYS.SPACE) { + EventUtil.trapImmediate(event); + this.handleChange(event); + } + } + }; + + renderButtonGroupVariant = (props, assistiveText, labels) => ( + + { + this.input = component; + }} + role={props.role} + required={props.required} + type="checkbox" + /> + + + ); + + renderBaseVariant = (props, assistiveText, labels) => ( +
+
+ + {props.required ? ( + + * + + ) : null} + { + if (component) { + component.indeterminate = props.indeterminate; + } + this.input = component; + }} + role={props.role} + required={props.required} + type="checkbox" + /> + + +
+ {props.errorText ? ( +
{props.errorText}
+ ) : null} +
+ ); + + renderToggleVariant = (props, assistiveText, labels) => ( +
+ + {props.errorText ? ( +
{props.errorText}
+ ) : null} +
+ ); + + render () { + const assistiveText = { + ...defaultProps.assistiveText, + /* Remove backward compatibility at next breaking change */ + ...(typeof this.props.assistiveText === 'string' + ? { label: this.props.assistiveText } + : {}), + ...(typeof this.props.assistiveText === 'object' + ? this.props.assistiveText + : {}), + }; + const labels = { + ...defaultProps.labels, + /* Remove backward compatibility at next breaking change */ + ...(this.props.label ? { label: this.props.label } : {}), + ...this.props.labels, + }; + + const subRenders = { + base: this.renderBaseVariant, + 'button-group': this.renderButtonGroupVariant, + toggle: this.renderToggleVariant, + }; + const variantExists = subRenders[this.props.variant]; + + return variantExists + ? subRenders[this.props.variant](this.props, assistiveText, labels) + : subRenders.base(this.props, assistiveText, labels); + } +} + +Checkbox.displayName = FORMS_CHECKBOX; +Checkbox.propTypes = propTypes; +Checkbox.defaultProps = defaultProps; + +export default Checkbox; diff --git a/components/combobox/combobox.jsx b/components/combobox/combobox.jsx index 3669c099c8..581f9e52b5 100644 --- a/components/combobox/combobox.jsx +++ b/components/combobox/combobox.jsx @@ -20,7 +20,7 @@ import classNames from 'classnames'; import shortid from 'shortid'; import Dialog from '../utilities/dialog'; -import InnerInput from '../../components/forms/input/private/inner-input'; +import InnerInput from '../../components/input/private/inner-input'; import InputIcon from '../icon/input-icon'; import Menu from './private/menu'; import Label from '../forms/private/label'; @@ -387,11 +387,11 @@ class Combobox extends React.Component { * Menu open/close and sub-render methods */ - handleClickOutside = () => { - this.handleRequestClose(); + handleClickOutside = (event) => { + this.handleRequestClose(event, {}); }; - handleClose = () => { + handleClose = (event) => { const isOpen = this.getIsOpen(); if (isOpen) { @@ -406,7 +406,7 @@ class Combobox extends React.Component { }); if (this.props.events.onClose) { - this.props.events.onClose(); + this.props.events.onClose(event, {}); } } }; @@ -414,7 +414,7 @@ class Combobox extends React.Component { handleInputBlur = (event) => { // If menu is open when the input's onBlur event fires, it will close before the onClick of the menu item can fire. setTimeout(() => { - this.handleClose(); + this.handleClose(event); }, 200); if (this.props.events.onBlur) { @@ -429,7 +429,7 @@ class Combobox extends React.Component { handleInputFocus = (event) => { if (this.props.events.onFocus) { - this.props.events.onFocus(event); + this.props.events.onFocus(event, {}); } }; @@ -542,7 +542,7 @@ class Combobox extends React.Component { }); }; - handleOpen = () => { + handleOpen = (event, data) => { const isOpen = this.getIsOpen(); if (!isOpen) { @@ -557,7 +557,7 @@ class Combobox extends React.Component { }); if (this.props.events.onOpen) { - this.props.events.onOpen(); + this.props.events.onOpen(event, data); } } }; @@ -612,9 +612,9 @@ class Combobox extends React.Component { } }; - handleRequestClose = () => { + handleRequestClose = (event, data) => { if (this.props.events.onRequestClose) { - this.props.events.onRequestClose(); + this.props.events.onRequestClose(event, data); } if (this.getIsOpen()) { diff --git a/components/data-table/index.jsx b/components/data-table/index.jsx index 29ca3c2c7f..eecac9d825 100644 --- a/components/data-table/index.jsx +++ b/components/data-table/index.jsx @@ -23,9 +23,6 @@ import classNames from 'classnames'; // ### assign import assign from 'lodash.assign'; -// ### isFunction -import isFunction from 'lodash.isfunction'; - // ### reject import reject from 'lodash.reject'; @@ -190,16 +187,16 @@ const DataTable = createReactClass({ checkProps(DATA_TABLE, this.props); }, - handleToggleAll (selected, e) { - if (isFunction(this.props.onChange)) { - const selection = selected ? [...this.props.items] : []; + handleToggleAll (e, { checked }) { + if (typeof this.props.onChange === 'function') { + const selection = checked ? [...this.props.items] : []; this.props.onChange(selection, e); } }, handleRowToggle (item, selected, e) { - if (isFunction(this.props.onChange)) { + if (typeof this.props.onChange === 'function') { let selection; if (selected) { diff --git a/components/data-table/private/head.jsx b/components/data-table/private/head.jsx index 21ee552bb6..381a8ced9f 100644 --- a/components/data-table/private/head.jsx +++ b/components/data-table/private/head.jsx @@ -7,7 +7,7 @@ import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; // ## Children -import Checkbox from '../../forms/checkbox'; +import Checkbox from '../../checkbox'; import HeaderCell from './header-cell'; // ## Constants @@ -65,7 +65,9 @@ const DataTableHead = createReactClass({ >
{ if (event.keyCode === KEYS.ESCAPE) { EventUtil.trapEvent(event); - this.props.onRequestClose(event); + this.props.onRequestClose(event, {}); } }; @@ -160,9 +160,9 @@ class DatepickerCalendarWrapper extends React.Component { } }; - handleRequestClose = () => { + handleRequestClose = (event) => { if (this.props.onRequestClose) { - this.props.onRequestClose(); + this.props.onRequestClose(event, {}); } }; diff --git a/components/date-picker/private/calendar.jsx b/components/date-picker/private/calendar.jsx index ec93db2e06..8c8b3c1f11 100644 --- a/components/date-picker/private/calendar.jsx +++ b/components/date-picker/private/calendar.jsx @@ -112,9 +112,9 @@ const DatepickerCalendar = createReactClass({ } }, - handleRequestClose () { + handleRequestClose (event) { if (this.props.onRequestClose) { - this.props.onRequestClose(); + this.props.onRequestClose(event, {}); } }, diff --git a/components/date-picker/private/day.jsx b/components/date-picker/private/day.jsx index 348b1bffa5..7cf9229a73 100644 --- a/components/date-picker/private/day.jsx +++ b/components/date-picker/private/day.jsx @@ -173,10 +173,6 @@ DatepickerCalendarDay.propTypes = { * For keyboard navigation. Changes the focus to the same day in the previous week on the calendar. Triggered when up arrow button is pressed. */ onKeyboardNavigateToPreviousWeek: PropTypes.func.isRequired, - /** - * Triggered when the calendar is cancelled. - */ - onRequestClose: PropTypes.func.isRequired, /** * Triggered when a date on the calendar is clicked. */ diff --git a/components/date-picker/private/week.jsx b/components/date-picker/private/week.jsx index ee64af0776..b85d6f9c01 100644 --- a/components/date-picker/private/week.jsx +++ b/components/date-picker/private/week.jsx @@ -28,7 +28,6 @@ const DatepickerWeek = (props) => { props.onKeyboardNavigateToPreviousWeek } onCalendarBlur={props.onCalendarBlur} - onRequestClose={props.onRequestClose} onRequestInternalFocusDate={props.onRequestInternalFocusDate} onSelectDate={props.onSelectDate} selectedDate={props.selectedDate} @@ -87,10 +86,6 @@ DatepickerWeek.propTypes = { * For keyboard navigation. Changes the focus to the same day in the previous week on the calendar. Triggered when up arrow button is pressed. */ onKeyboardNavigateToPreviousWeek: PropTypes.func.isRequired, - /** - * Triggered when the calendar is cancelled. - */ - onRequestClose: PropTypes.func.isRequired, /** * Triggered when the user wants to focus on a new day witht he keyboard. It returns the keyboard event a data object with the shape: `{date: [Date object]}`. Keyboard event is ommited if a new month is rendered. _Tested with Mocha framework._ */ diff --git a/components/forms/checkbox/__examples__/default.jsx b/components/forms/checkbox/__examples__/default.jsx deleted file mode 100644 index f158ce33da..0000000000 --- a/components/forms/checkbox/__examples__/default.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import createReactClass from 'create-react-class'; -import IconSettings from '~/components/icon-settings'; -import Checkbox from '~/components/forms/checkbox'; // `~` is replaced with design-system-react at runtime - -const Example = createReactClass({ - displayName: 'CheckboxExample', - - render () { - return ( - -
-
- -
-
- -
-
- -
-
- -
-
-
- ); - }, -}); - -export default Example; // export is replaced with `ReactDOM.render(, mountNode);` at runtime diff --git a/components/forms/checkbox/check-props.js b/components/forms/checkbox/check-props.js deleted file mode 100644 index d3e38e7b3d..0000000000 --- a/components/forms/checkbox/check-props.js +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ -/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ -/* eslint-disable import/no-mutable-exports */ -/* eslint-disable max-len */ - -import onlyOneOfProperties from '../../../utilities/warning/only-one-of-properties'; - -let checkProps = function () {}; - -if (process.env.NODE_ENV !== 'production') { - checkProps = function (COMPONENT, props) { - if (props.variant === 'toggle' && props.indeterminate === true) { - onlyOneOfProperties( - COMPONENT, - { - variant: props.variant, - indeterminate: props.indeterminate, - }, - 'Currently SLDS does not support the `indeterminate` state in Checkbox Toggle. See SLDS documentation about [Checkbox Toggle](https://lightningdesignsystem.com/components/forms/#flavor-checkbox-toggle-checkbox-toggle) for more information.' - ); - } - }; -} - -export default checkProps; diff --git a/components/forms/checkbox/index.jsx b/components/forms/checkbox/index.jsx index 3b2fc57302..60b1013e48 100644 --- a/components/forms/checkbox/index.jsx +++ b/components/forms/checkbox/index.jsx @@ -1,390 +1,10 @@ /* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ /* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ - -// # Checkbox Component - -// Implements the [Checkbox design pattern](https://www.lightningdesignsystem.com/components/forms/#checkbox) in React. - -// ### React import React from 'react'; -import createReactClass from 'create-react-class'; -import PropTypes from 'prop-types'; - -// ### isFunction -import isFunction from 'lodash.isfunction'; - -// ### classNames -import classNames from 'classnames'; - -// ### shortid -// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid) -// shortid is a short, non-sequential, url-friendly, unique id generator -import shortid from 'shortid'; - -// ### Event Helpers -import KEYS from '../../../utilities/key-code'; -import EventUtil from '../../../utilities/event'; - -// This component's `checkProps` which issues warnings to developers about properties when in development mode (similar to React's built in development tools) -import checkProps from './check-props'; - -import { FORMS_CHECKBOX } from '../../../utilities/constants'; - -/** - * The ability to style checkboxes with CSS varies across browsers. Using this component ensures checkboxes look the same everywhere. - */ -const Checkbox = createReactClass({ - // ### Display Name - // Always use the canonical component name as the React display name. - displayName: FORMS_CHECKBOX, - - // ### Prop Types - propTypes: { - /** - * An HTML ID that is shared with ARIA-supported devices with the - * `aria-controls` attribute in order to relate the input with - * another region of the page. An example would be a select box - * that shows or hides a panel. - */ - 'aria-controls': PropTypes.string, - /** - * The `aria-describedby` attribute is used to indicate the IDs of the elements that describe the object. It is used to establish a relationship between widgets or groups and text that described them. This is very similar to aria-labelledby: a label describes the essence of an object, while a description provides more information that the user might need. - */ - 'aria-describedby': PropTypes.string, - /** - * `aria-owns` indicate that an element depends on the current one when the relation can't be determined by the hierarchy structure. - */ - 'aria-owns': PropTypes.string, - /** - * The `aria-required` attribute is used to indicate that user input is required on an element before a form can be submitted. - */ - 'aria-required': PropTypes.bool, - /** - * Text that is visually hidden but read aloud by screenreaders to tell the user what the Checkbox is for. - * If the Checkbox has a visible label, you can omit the assistiveText prop and use the label prop. - */ - assistiveText: PropTypes.string, - /** - * The Checkbox is a controlled component, and will always be in this state. If checked is not defined, the state of the uncontrolled native `input` component will be used. - */ - checked: PropTypes.bool, - /** - * This is the initial value of an uncontrolled form element and is present only to provide compatibility with - * hybrid framework applications that are not entirely React. It should only be used in an application without - * centralized state (Redux, Flux). "Controlled components" with centralized state is highly recommended. - * See [Code Overview](https://github.com/salesforce/design-system-react/blob/master/docs/codebase-overview.md#controlled-and-uncontrolled-components) for more information. - */ - defaultChecked: PropTypes.bool, - /** - * Class names to be added to the outer container of the Checkbox. - */ - className: PropTypes.oneOfType([ - PropTypes.array, - PropTypes.object, - PropTypes.string, - ]), - /** - * Disables the Checkbox and prevents clicking it. - */ - disabled: PropTypes.bool, - /** - * Message to display when the Checkbox is in an error state. When this is present, also visually highlights the component as in error. - */ - errorText: PropTypes.string, - /** - * A unique ID is needed in order to support keyboard navigation and ARIA support. This ID is added to the `input` element - */ - id: PropTypes.string, - /** - * The Checkbox will be indeterminate if its state can not be figured out or is partially checked. Once a checkbox is indeterminate, a click should cause it to be checked. Since a user cannot put a checkbox into an indeterminate state, it is assumed you are controlling the value of `checked` with the parent, also, and that this is a controlled component. **Note:** `indeterminate` proptype does nothing in the `toggle` variant, as [SLDS does not support it](https://lightningdesignsystem.com/components/forms/#flavor-checkbox-toggle-checkbox-toggle). - */ - indeterminate: PropTypes.bool, - /** - * An optional label for the Checkbox. - */ - label: PropTypes.string, - /** - * Label for the _enabled_ state of the Toggle variant. Defaults to "Enabled". - */ - labelToggleEnabled: PropTypes.string, - /** - * Label for the _disabled_ state of the Toggle variant. Defaults to "Disabled". Note that this uses SLDS language, and meaning, of "Enabled" and "Disabled"; referring to the state of whatever the checkbox is _toggling_, not whether the checkbox itself is enabled or disabled. - */ - labelToggleDisabled: PropTypes.string, - /** - * Name of the submitted form parameter. - */ - name: PropTypes.string, - /** - * This event fires when the Checkbox focused is blurred. - */ - onBlur: PropTypes.func, - /** - * This event fires when the Checkbox changes. - */ - onChange: PropTypes.func, - /** - * This event fires when the Checkbox is focused. - */ - onFocus: PropTypes.func, - /** - * This event fires when a key is pressed down. - */ - onKeyDown: PropTypes.func, - /** - * This event fires when a character is typed. Probably. 👀 See [this article](http://www.bloggingdeveloper.com/post/KeyPress-KeyDown-KeyUp-The-Difference-Between-Javascript-Key-Events.aspx) for more information. - */ - onKeyPress: PropTypes.func, - /** - * This event fires when a pressed key is released. - */ - onKeyUp: PropTypes.func, - /** - * Displays the value of the input, but does not allow changes. - */ - readOnly: PropTypes.bool, - /** - * Highlights the Checkbox as a required field (does not perform any validation). - */ - required: PropTypes.bool, - /** - * The aria-role of the checkbox. - */ - role: PropTypes.string, - /** - * Which flavor of checkbox? Default is `base` while other option is `toggle`. (**Note:** `toggle` variant does not support the `indeterminate` feature, because [SLDS does not support it](https://lightningdesignsystem.com/components/forms/#flavor-checkbox-toggle-checkbox-toggle).) - */ - variant: PropTypes.oneOf(['base', 'toggle', 'button-group']), - }, - - getDefaultProps () { - return { - variant: 'base', - labelToggleEnabled: 'Enabled', - labelToggleDisabled: 'Disabled', - }; - }, - - componentWillMount () { - checkProps(FORMS_CHECKBOX, this.props); - this.generatedId = shortid.generate(); - }, - - getId () { - return this.props.id || this.generatedId; - }, - - handleChange (event) { - const value = event.target.checked; - const { checked, indeterminate, onChange } = this.props; - - if (isFunction(onChange)) { - // `checked` is present twice to maintain backwards compatibility. Please remove first parameter `value` on the next breaking change. - onChange(value, event, { - checked: indeterminate ? true : !checked, - indeterminate: false, - }); - } - }, - - handleKeyDown (event) { - if (event.keyCode) { - if (event.keyCode === KEYS.ENTER || event.keyCode === KEYS.SPACE) { - EventUtil.trapImmediate(event); - this.handleChange(event); - } - } - }, - - renderButtonGroupVariant (props) { - return ( - - { - this.input = component; - }} - role={props.role} - required={props.required} - type="checkbox" - /> - - - ); - }, - - renderBaseVariant (props) { - return ( -
-
- - {props.required ? ( - - * - - ) : null} - { - if (component) { - component.indeterminate = props.indeterminate; - } - this.input = component; - }} - role={props.role} - required={props.required} - type="checkbox" - /> - - -
- {props.errorText ? ( -
{props.errorText}
- ) : null} -
- ); - }, - renderToggleVariant (props) { - return ( -
- - {props.errorText ? ( -
{props.errorText}
- ) : null} -
- ); - }, +// Alias +import Checkbox from '../../checkbox'; - // ### Render - render () { - let renderer; - switch (this.props.variant) { - case 'toggle': - renderer = this.renderToggleVariant(this.props); - break; - case 'button-group': - renderer = this.renderButtonGroupVariant(this.props); - break; - default: - renderer = this.renderBaseVariant(this.props); - } - return renderer; - }, -}); +const OldCheckbox = (props) => ; -export default Checkbox; +export default OldCheckbox; diff --git a/components/forms/input/__docs__/inline/site-stories.js b/components/forms/input/__docs__/inline/site-stories.js deleted file mode 100644 index 0fbb864752..0000000000 --- a/components/forms/input/__docs__/inline/site-stories.js +++ /dev/null @@ -1,10 +0,0 @@ -// This object is imported into the documentation site. An example for the documentation site should be part of the pull request for the component. The object key is the kabob case of the "URL folder". In the case of `http://localhost:8080/components/app-launcher/`, `app-launcher` is the `key`. The folder name is created by `components.component` value in `package.json`. The following uses webpack's raw-loader plugin to get "text files" that will be eval()'d by CodeMirror within the documentation site on page load. - -/* eslint-env node */ -/* eslint-disable global-require */ - -const siteStories = [ - require('raw-loader!@salesforce/design-system-react/components/forms/input/__examples__/inline-default.jsx'), -]; - -module.exports = siteStories; diff --git a/components/forms/input/__docs__/inline/storybook-stories.jsx b/components/forms/input/__docs__/inline/storybook-stories.jsx index cce6ab5b65..e4d65351e8 100644 --- a/components/forms/input/__docs__/inline/storybook-stories.jsx +++ b/components/forms/input/__docs__/inline/storybook-stories.jsx @@ -6,7 +6,7 @@ import { storiesOf, action } from '@storybook/react'; import IconSettings from '../../../../../components/icon-settings'; import { FORMS_INLINE_EDIT } from '../../../../../utilities/constants'; -import InlineEdit from '../../../input/inline'; +import InlineEdit from '../../inline'; const DemoInlineEdit = createReactClass({ displayName: 'DemoInlineEdit', diff --git a/components/forms/input/index.jsx b/components/forms/input/index.jsx index 3283130dfe..cb89bfc627 100644 --- a/components/forms/input/index.jsx +++ b/components/forms/input/index.jsx @@ -1,414 +1,7 @@ /* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ /* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ -// # Input Component - -// Implements the [Input design pattern](https://lightningdesignsystem.com/components/forms/#flavor-input) in React. Does not yet implement [fixed text](https://lightningdesignsystem.com/components/forms/#flavor-input-input-fixed-text). -// Based on SLDS v2.2.1 -// - -// ### React -import React from 'react'; -import createReactClass from 'create-react-class'; -import PropTypes from 'prop-types'; - -// ### classNames -// [github.com/JedWatson/classnames](https://github.com/JedWatson/classnames) -// This project uses `classnames`, "a simple javascript utility for conditionally -// joining classNames together." -import classNames from 'classnames'; - -// ### shortid -// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid) -// shortid is a short, non-sequential, url-friendly, unique id generator -import shortid from 'shortid'; - -// ## Children -import InputIcon from '../../icon/input-icon'; -import InnerInput from './private/inner-input'; -import Label from '../private/label'; - -// This component's `checkProps` which issues warnings to developers about properties when in development mode (similar to React's built in development tools) -import checkProps from './check-props'; - -import { FORMS_INPUT } from '../../../utilities/constants'; - -/** - * The HTML `input` with a label and error messaging. - */ -const Input = createReactClass({ - displayName: FORMS_INPUT, - - propTypes: { - /** - * The aria-activedescendant attribute contains the ID of the currently active child object that is part of a composite widget within the Document Object Model. It makes do with the overhead of having all or more than one child focusable. As the name specifies, it helps in managing the current active child of the composite widget. - */ - 'aria-activedescendant': PropTypes.string, - /** - * Indicates if the suggestions in a composite widget are values that complete the current textbox input. - */ - 'aria-autocomplete': PropTypes.string, - /** - * An HTML ID that is shared with ARIA-supported devices with the - * `aria-controls` attribute in order to relate the input with - * another region of the page. An example would be a select box - * that shows or hides a panel. - */ - 'aria-controls': PropTypes.string, - /** - * The `aria-describedby` attribute is used to indicate the IDs of the elements that describe the object. It is used to establish a relationship between widgets or groups and text that described them. This is very similar to aria-labelledby: a label describes the essence of an object, while a description provides more information that the user might need. - */ - 'aria-describedby': PropTypes.string, - /** - * Use the `aria-expanded` state to indicate whether regions of the content are collapsible, and to expose whether a region is currently expanded or collapsed. - */ - 'aria-expanded': PropTypes.bool, - /** - * Indicates that the element has a popup context menu or sub-level menu. - */ - 'aria-haspopup': PropTypes.bool, - /** - * The aria-labelledby attribute contains the element IDs of labels in objects such as input elements, widgets, and groups. The attribute establishes relationships between objects and their labels. Assistive technology, such as screen readers, use this attribute to catalog the objects in a document so that users can navigate between them. Without an element ID, the assistive technology cannot catalog the object. - */ - 'aria-labelledby': PropTypes.string, - /** - * An HTML ID that is shared with ARIA-supported devices with the - * `aria-controls` attribute in order to relate the input with - * another region of the page. An example would be a search field - * that shows search results. - */ - 'aria-owns': PropTypes.string, - /** - * The `aria-required` attribute is used to indicate that user input is required on an element before a form can be submitted. - */ - 'aria-required': PropTypes.bool, - /** - * **Assistive text for accessibility** - * * `label`: Visually hidden label but read out loud by screen readers. - * * `spinner`: Text for loading spinner icon. - */ - assistiveText: PropTypes.shape({ - label: PropTypes.string, - spinner: PropTypes.string, - }), - /** - * Elements are added after the `input`. - */ - children: PropTypes.node, - /** - * Class names to be added to the outer container of the input. - */ - className: PropTypes.oneOfType([ - PropTypes.array, - PropTypes.object, - PropTypes.string, - ]), - /** - * Disables the input and prevents editing the contents. - */ - disabled: PropTypes.bool, - /** - * Message to display when the input is in an error state. When this is present, also visually highlights the component as in error. - */ - errorText: PropTypes.string, - /** - * Displays text or node to the left of the input. This follows the fixed text input UX pattern. - */ - fixedTextLeft: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), - /** - * Displays text or node to the right of the input. This follows the fixed text input UX pattern. - */ - fixedTextRight: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), - /** - * If true, loading spinner appears inside input on right hand side. - */ - hasSpinner: PropTypes.bool, - /** - * Left aligned icon, must be instace of `design-system-react/components/icon/input-icon` - */ - iconLeft: PropTypes.node, - /** - * [DEPRECATED] Please use `iconLeft` and `iconRight`. - */ - iconPosition: PropTypes.string, - /** - * Right aligned icon, must be instace of `design-system-react/components/icon/input-icon` - */ - iconRight: PropTypes.node, - /** - * Triggered when an `InlineEdit` becomes editable. - */ - inlineEditTrigger: PropTypes.node, - /** - * Every input must have a unique ID in order to support keyboard navigation and ARIA support. - */ - id: PropTypes.string, - /** - * This callback exposes the input reference / DOM node to parent components. ` this.input = inputComponent} /> - */ - inputRef: PropTypes.func, - /** - * Displays the value of the input statically. This follows the static input UX pattern. - */ - isStatic: PropTypes.bool, - /** - * This label appears above the input. - */ - label: PropTypes.string, - /** - * Triggered when focus is removed. - */ - onBlur: PropTypes.func, - /** - * This callback fires when the input changes. The synthetic React event will be the first parameter to the callback. You will probably want to reference `event.target.value` in your callback. No custom data object is provided. - */ - onChange: PropTypes.func, - /** - * This event fires when the input is clicked. - */ - onClick: PropTypes.func, - /** - * Triggered when component is focused. - */ - onFocus: PropTypes.func, - /** - * Similar to `onchange`. Triggered when an element gets user input. - */ - onInput: PropTypes.func, - /** - * Triggered when a submittable `` element is invalid. - */ - onInvalid: PropTypes.func, - /** - * Triggered when a key is pressed down - */ - onKeyDown: PropTypes.func, - /** - * Triggered when a key is pressed and released - */ - onKeyPress: PropTypes.func, - /** - * Triggered when a key is released - */ - onKeyUp: PropTypes.func, - /** - * Triggered after some text has been selected in an element. - */ - onSelect: PropTypes.func, - /** - * Fires when a form is submitted. - */ - onSubmit: PropTypes.func, - /** - * Text that will appear in an empty input. - */ - placeholder: PropTypes.string, - /** - * Sets the minimum number of characters that an `` can accept. - */ - minLength: PropTypes.string, - /** - * Sets the maximum number of characters that an `` can accept. - */ - maxLength: PropTypes.string, - /** - * Name of the submitted form parameter. - */ - name: PropTypes.string, - /** - * Displays the value of the input as readOnly. - */ - readOnly: PropTypes.bool, - /** - * Highlights the input as a required field (does not perform any validation). - */ - required: PropTypes.bool, - /** - * The `` element includes support for all HTML5 types. - */ - type: PropTypes.oneOf([ - 'text', - 'password', - 'datetime', - 'datetime-local', - 'date', - 'month', - 'time', - 'week', - 'number', - 'email', - 'url', - 'search', - 'tel', - 'color', - ]), - /** - * The input is a controlled component, and will always display this value. - */ - value: PropTypes.string, - /** - * This is the initial value of an uncontrolled form element and is present only to provide compatibility - * with hybrid framework applications that are not entirely React. It should only be used in an application - * without centralized state (Redux, Flux). "Controlled components" with centralized state is highly recommended. - * See [Code Overview](https://github.com/salesforce/design-system-react/blob/master/docs/codebase-overview.md#controlled-and-uncontrolled-components) for more information. - */ - defaultValue: PropTypes.string, - /** - * ARIA role - */ - role: PropTypes.string, - }, - - getDefaultProps () { - return { - type: 'text', - }; - }, - - componentWillMount () { - // `checkProps` issues warnings to developers about properties (similar to React's built in development tools) - checkProps(FORMS_INPUT, this.props); - - this.generatedId = shortid.generate(); - if (this.props.errorText) { - this.generatedErrorId = shortid.generate(); - } - }, - - getId () { - return this.props.id || this.generatedId; - }, - - getErrorId () { - return this.props['aria-describedby'] || this.generatedErrorId; - }, - - // This is convuluted to maintain backwards compatibility. Please remove deprecatedProps on next breaking change. - getIconRender (position, iconPositionProp) { - let icon; - - /* eslint-disable react/prop-types */ - const deprecatedProps = { - assistiveText: - (this.props[iconPositionProp] && - this.props[iconPositionProp].props.assistiveText) || - this.props.iconAssistiveText, - category: - (this.props[iconPositionProp] && - this.props[iconPositionProp].props.category) || - this.props.iconCategory, - name: - (this.props[iconPositionProp] && - this.props[iconPositionProp].props.name) || - this.props.iconName, - onClick: - (this.props[iconPositionProp] && - this.props[iconPositionProp].props.onClick) || - this.props.onIconClick, - }; - /* eslint-enable react/prop-types */ - - if ( - this.props[iconPositionProp] && - position && - this.props[iconPositionProp] - ) { - icon = React.cloneElement(this.props[iconPositionProp], { - iconPosition: `${position}`, - }); - } else if (deprecatedProps.name) { - icon = ; - } - - return icon; - }, - - render () { - // this is a hack to make left the default prop unless overwritten by `iconPosition="right"` - const hasLeftIcon = - !!this.props.iconLeft || - ((this.props.iconPosition === 'left' || - this.props.iconPosition === undefined) && - !!this.props.iconName); - const hasRightIcon = - !!this.props.iconRight || - (this.props.iconPosition === 'right' && !!this.props.iconName); - - return ( -
-
- ); - }, -}); +// Alias +import Input from '../../input'; export default Input; diff --git a/components/forms/input/inline-check-props.js b/components/forms/input/inline-check-props.js new file mode 100644 index 0000000000..0cecdaa305 --- /dev/null +++ b/components/forms/input/inline-check-props.js @@ -0,0 +1,19 @@ +/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ +/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ +/* eslint-disable import/no-mutable-exports */ +/* eslint-disable max-len */ + +import componentIsDeprecated from '../../../utilities/warning/component-is-deprecated'; + +let checkProps = function () {}; + +if (process.env.NODE_ENV !== 'production') { + checkProps = function (COMPONENT) { + componentIsDeprecated( + COMPONENT, + 'For a multiple input form, please use the pattern located at https://www.lightningdesignsystem.com/components/form-element/#Record-Detail that swaps out a read-only `Input` with a base `Input`. For a single input, please use a `Popover` paired with `