diff options
author | Israel Lavi <israel.lavi@intl.att.com> | 2018-05-21 17:42:00 +0300 |
---|---|---|
committer | Israel Lavi <il0695@att.com> | 2018-05-21 17:52:01 +0300 |
commit | 1994c98063c27a41797dec01f2ca9fcbe33ceab0 (patch) | |
tree | f30beeaf15a8358f6da78fdd74bcbda74bd334f8 /src/react | |
parent | 4749f4631426fcbe29ed98cef8f24cab18b501d0 (diff) |
init commit onap ui
Change-Id: I1dace78817dbba752c550c182dfea118b4a38646
Issue-ID: SDC-1350
Signed-off-by: Israel Lavi <il0695@att.com>
Diffstat (limited to 'src/react')
-rw-r--r-- | src/react/Accordion.js | 40 | ||||
-rw-r--r-- | src/react/Button.js | 37 | ||||
-rw-r--r-- | src/react/Checkbox.js | 45 | ||||
-rw-r--r-- | src/react/Checklist.js | 43 | ||||
-rw-r--r-- | src/react/Input.js | 88 | ||||
-rw-r--r-- | src/react/Modal.js | 55 | ||||
-rw-r--r-- | src/react/ModalBody.js | 19 | ||||
-rw-r--r-- | src/react/ModalFooter.js | 36 | ||||
-rw-r--r-- | src/react/ModalHeader.js | 41 | ||||
-rw-r--r-- | src/react/ModalTitle.js | 19 | ||||
-rw-r--r-- | src/react/Panel.js | 18 | ||||
-rw-r--r-- | src/react/PopupMenu.js | 39 | ||||
-rw-r--r-- | src/react/PopupMenuItem.js | 34 | ||||
-rw-r--r-- | src/react/Portal.js | 52 | ||||
-rw-r--r-- | src/react/Radio.js | 58 | ||||
-rw-r--r-- | src/react/RadioGroup.js | 40 | ||||
-rw-r--r-- | src/react/SVGIcon.js | 47 | ||||
-rw-r--r-- | src/react/Tab.js | 20 | ||||
-rw-r--r-- | src/react/TabPane.js | 12 | ||||
-rw-r--r-- | src/react/Tabs.js | 29 | ||||
-rw-r--r-- | src/react/Tile.js | 33 | ||||
-rw-r--r-- | src/react/TileFooter.js | 10 | ||||
-rw-r--r-- | src/react/TileFooterCell.js | 7 | ||||
-rw-r--r-- | src/react/TileInfo.js | 10 | ||||
-rw-r--r-- | src/react/TileInfoLine.js | 7 | ||||
-rw-r--r-- | src/react/index.js | 74 |
26 files changed, 913 insertions, 0 deletions
diff --git a/src/react/Accordion.js b/src/react/Accordion.js new file mode 100644 index 0000000..3acdd24 --- /dev/null +++ b/src/react/Accordion.js @@ -0,0 +1,40 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +class Accordion extends React.Component { + constructor(props) { + super(props); + this.state = { + open: props.defaultExpanded + }; + } + render() { + const { children, title, className, dataTestId } = this.props; + const { open } = this.state; + return ( + <div className={`sdc-accordion ${className}`}> + <div data-test-id={dataTestId} onClick={() => this.setState({ open: !open })} className='sdc-accordion-header'> + <SVGIcon name='chevronDown' iconClassName={open ? 'down' : ''} /> + <div className='title'>{title}</div> + </div> + <div className={`sdc-accordion-body ${open ? 'open' : ''}`}>{children}</div> + </div> + ); + } +} + +Accordion.propTypes = { + title: PropTypes.string, + children: PropTypes.node, + expandByDefault: PropTypes.bool, + dataTestId: PropTypes.string +}; + +Accordion.defaultProps = { + title: '', + className: '', + defaultExpanded: false +}; + +export default Accordion; diff --git a/src/react/Button.js b/src/react/Button.js new file mode 100644 index 0000000..c628455 --- /dev/null +++ b/src/react/Button.js @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +const Button = ({btnType, size, className, iconName, onClick, disabled, children, ...other}) => ( + <button + onClick={onClick} + className={`sdc-button sdc-button__${btnType} ${size && `btn-${size}`} ${className} ${iconName}`} + disabled={disabled} + {...other}> + { + iconName ? + <SVGIcon name={iconName} label={children} labelPosition='right' /> + : + children + } + </button> +); + +Button.propTypes = { + btnType: PropTypes.string, + size: PropTypes.oneOf(['', 'default', 'x-small', 'small', 'medium', 'large']), + className: PropTypes.string, + iconName: PropTypes.string, + onClick: PropTypes.func, + disabled: PropTypes.bool +}; + +Button.defaultProps = { + btnType: 'primary', + size: '', + className: '', + iconName: '', + disabled: false +}; + +export default Button; diff --git a/src/react/Checkbox.js b/src/react/Checkbox.js new file mode 100644 index 0000000..bef6945 --- /dev/null +++ b/src/react/Checkbox.js @@ -0,0 +1,45 @@ +import React from 'react'; + +class Checkbox extends React.Component { + + render() { + let {checked = false, disabled, value, label, inputRef, className, name} = this.props; + let dataTestId = this.props['data-test-id']; + + return ( + <div className={`sdc-checkbox ${className || ''}`}> + <label> + <input + className='sdc-checkbox__input' + ref={inputRef} + data-test-id={dataTestId} + type='checkbox' + checked={checked} + name={name} + value={value} + onChange={(e) => this.onChange(e)} + disabled={disabled} /> + <span className='sdc-checkbox__label'>{label}</span> + </label> + </div> + ); + } + + onChange(e) { + let {onChange} = this.props; + if (onChange) { + onChange(e.target.checked); + } + } + + getChecked() { + return this.props.checked; + } + + getValue() { + return this.props.value; + } + +} + +export default Checkbox; diff --git a/src/react/Checklist.js b/src/react/Checklist.js new file mode 100644 index 0000000..1a42aee --- /dev/null +++ b/src/react/Checklist.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Checkbox from './Checkbox.js'; + +const Checklist = ({ items = [], className, onChange }) => ( + <div className={className}> + {items.map((item, index) => { + return ( + <div key={`checkbox-item-${index}`} className='checkbox-item'> + <Checkbox + key={`${item.label}${index}`} + label={item.label} + value={item.value} + checked={item.checked} + disabled={item.disabled} + onChange={value => { + let obj = {}; + obj[item.value] = value; + onChange(obj); + }} + data-test-id={item.dataTestId} + /> + </div> + ); + })} + </div> +); + +Checklist.propTypes = { + items: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.string, + checked: PropTypes.bool, + disabled: PropTypes.bool, + dataTestId: PropTypes.string + }) + ), + className: PropTypes.string, + onChange: PropTypes.func +}; + +export default Checklist;
\ No newline at end of file diff --git a/src/react/Input.js b/src/react/Input.js new file mode 100644 index 0000000..5760637 --- /dev/null +++ b/src/react/Input.js @@ -0,0 +1,88 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +class Input extends React.Component { + + render() { + let {className, disabled, errorMessage, readOnly, label, name, value, type, placeholder, isRequired} = this.props; + let dataTestId = this.props['data-test-id']; + let inputClasses = `sdc-input__input ${errorMessage ? 'error' : ''} ${readOnly ? 'view-only' : ''}`; + let labelClasses = `sdc-input__label ${readOnly ? 'view-only' : ''} ${isRequired ? 'required' : ''}`; + + return ( + <div className={`sdc-input ${className || ''}`}> + + <label className={labelClasses} htmlFor={name}>{label}</label> + <input className={inputClasses} + disabled={disabled} + readOnly={readOnly} + type={type} + id={name} + name={name} + value={this.props.value} + placeholder={placeholder} + data-test-id={dataTestId} + onBlur={(e) => this.onBlur(e)} + onKeyDown={(e) => this.onKeyDown(e)} + onChange={(e) => this.onChange(e)}/> + { errorMessage && <div className="sdc-label__error"> + <SVGIcon + label={errorMessage} + labelPosition='right' + color='negative' + name='exclamationTriangleFull' /> + </div>} + </div> + ); + } + + onChange(e) { + let {onChange, readOnly, disabled} = this.props; + if (onChange && !readOnly && !disabled) { + onChange(e.target.value); + } + } + + onBlur(e) { + let {onBlur, readOnly} = this.props; + if (!readOnly && onBlur) { + onBlur(e); + } + } + + onKeyDown(e) { + let {onKeyDown, readOnly} = this.props; + if (!readOnly && onKeyDown) { + onKeyDown(e); + } + } + + getValue() { + return this.props.value; + } + +} +Input.propTypes = { + name: PropTypes.string, + value: PropTypes.string, + type: PropTypes.oneOf(['text', 'number']), + placeholder : PropTypes.string, + onChange: PropTypes.func, + onBlur: PropTypes.func, + onKeyDown: PropTypes.func, + errorMessage: PropTypes.string, + readOnly: PropTypes.bool, + isRequired: PropTypes.bool, + disabled: PropTypes.bool, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + className: PropTypes.string +}; + +Input.defaultProps = { + type: 'text', + readOnly: false, + isRequired: false, + disabled: false +}; +export default Input; diff --git a/src/react/Modal.js b/src/react/Modal.js new file mode 100644 index 0000000..ab2f7d7 --- /dev/null +++ b/src/react/Modal.js @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Portal from './Portal.js'; +import Body from './ModalBody.js'; +import Header from './ModalHeader.js'; +import Footer from './ModalFooter.js'; +import Title from './ModalTitle.js'; + +export const modalSize = { + medium: 'md', + large: 'l', + extraLarge: 'xl', + small: 'sm', + extraSmall: 'xsm' +}; + + +class Modal extends React.Component { + + render() { + const {size, type, children, show} = this.props; + return ( + <Portal> + <div ref={el => { this.modalRef = el;}}> + {show && <div className={`sdc-modal ${modalSize[size]}`}> + <div className={`sdc-modal__wrapper sdc-modal-type-${type}`}> + {children} + </div> + </div>} + {show && <div className='modal-background' />} + </div> + </Portal> + ); + } +} + +Modal.defaultProps = { + show: false, + size: 'medium', + type: 'info' +}; + +Modal.propTypes = { + show: PropTypes.bool, + size: PropTypes.string, + children: PropTypes.node, + type: PropTypes.string +}; + +Modal.Body = Body; +Modal.Header = Header; +Modal.Footer = Footer; +Modal.Title = Title; +export default Modal;
\ No newline at end of file diff --git a/src/react/ModalBody.js b/src/react/ModalBody.js new file mode 100644 index 0000000..4fae0f6 --- /dev/null +++ b/src/react/ModalBody.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const ModalBody = ({children, className}) => ( + <div className={`sdc-modal__content ${className}`} > + {children} + </div> +); + +ModalBody.propTypes = { + children: PropTypes.node, + className: PropTypes.string +}; + +ModalBody.defaultProps = { + className: '' +}; + +export default ModalBody;
\ No newline at end of file diff --git a/src/react/ModalFooter.js b/src/react/ModalFooter.js new file mode 100644 index 0000000..607895d --- /dev/null +++ b/src/react/ModalFooter.js @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Button from './Button.js'; + +const Footer = ({onClose, closeButtonText, actionButtonText, actionButtonClick, withButtons, children}) => { + const closeBtnType = actionButtonClick ? 'secondary' : 'primary'; + return ( + <div className='sdc-modal__footer'> + {children} + { + withButtons && <div> + {actionButtonClick && + <Button onClick={actionButtonClick}>{actionButtonText}</Button> + } + <Button btnType={closeBtnType} onClick={onClose}>{closeButtonText}</Button> + </div> + } + </div> + ); +}; + +Footer.propTypes = { + onClose: PropTypes.func, + closeButtonText: PropTypes.string, + actionButtonText: PropTypes.string, + actionButtonClick: PropTypes.func, + withButtons: PropTypes.bool, + children: PropTypes.node +}; + +Footer.defaultProps = { + closeButtonText: 'Close', + withButtons: true +}; + +export default Footer;
\ No newline at end of file diff --git a/src/react/ModalHeader.js b/src/react/ModalHeader.js new file mode 100644 index 0000000..c6be5ef --- /dev/null +++ b/src/react/ModalHeader.js @@ -0,0 +1,41 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import SVGIcon from './SVGIcon.js'; + +const iconMaper = { + error: 'error', + info: 'errorCircle', + alert: 'exclamationTriangleLine' +}; + +const headerTypes = { + error: 'sdc-error__header', + info: 'sdc-info__header', + alert: 'sdc-alert__header', + custom: 'sdc-custom__header' +} + + + +const Header = ({children, onClose, type}) => ( + <div className={ headerTypes[type] + ' sdc-modal__header'} > + {type !== 'custom' + && + <SVGIcon iconClassName='sdc-modal__icon' className='sdc-modal__svg-use' name={iconMaper[type]}/> + + } + {children} + <SVGIcon iconClassName ='sdc-modal__close-button-svg' className='sdc-modal__close-button' onClick={onClose} name='close'/> + </div> +); + +Header.propTypes = { + children: PropTypes.node, + onClose: PropTypes.func +}; + +Header.defaultProps = { + type: 'info' +}; + +export default Header;
\ No newline at end of file diff --git a/src/react/ModalTitle.js b/src/react/ModalTitle.js new file mode 100644 index 0000000..b48cc8a --- /dev/null +++ b/src/react/ModalTitle.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const Title = ({children, className}) => ( + <div className={`title ${className}`} > + {children} + </div> +); + +Title.PropTypes = { + children: PropTypes.node, + className: PropTypes.string +}; + +Title.defaultProps = { + className: '' +}; + +export default Title;
\ No newline at end of file diff --git a/src/react/Panel.js b/src/react/Panel.js new file mode 100644 index 0000000..34d2e62 --- /dev/null +++ b/src/react/Panel.js @@ -0,0 +1,18 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const Panel = ({ className, children }) => ( + <div className={`sdc-panel ${className}`}> + {children} + </div> +); + +Panel.propTypes = { + className: PropTypes.string, + children: PropTypes.node +}; + +Panel.defaultProps = { + className: '' +}; +export default Panel;
\ No newline at end of file diff --git a/src/react/PopupMenu.js b/src/react/PopupMenu.js new file mode 100644 index 0000000..d2cd29a --- /dev/null +++ b/src/react/PopupMenu.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import PopupMenuItem from './PopupMenuItem'; + +class PopupMenu extends React.Component { + render() { + const {children = [], onMenuItemClick, position = {}, relative} = this.props; + const style = relative ? {left: position.x, top: position.y} : {}; + + return ( + <ul className={`sdc-menu-list ${relative ? 'relative' : ''}`} style={style}> + {React.Children.toArray(children).map((child, i) => React.cloneElement(child, + { + onClick: child.props.onClick || onMenuItemClick, + key: i + }))} + </ul> + ); + } +} + +PopupMenu.propTypes = { + relative: PropTypes.bool, + position: PropTypes.shape({ + x: PropTypes.number, + y: PropTypes.number + }), + onMenuItemClick: PropTypes.func +}; + +PopupMenu.defaultProps = { + relative: false +}; + +export const PopupMenuSeparator = () => <li className='separator' />; + +PopupMenu.Separator = PopupMenuSeparator; +PopupMenu.Item = PopupMenuItem; +export default PopupMenu; diff --git a/src/react/PopupMenuItem.js b/src/react/PopupMenuItem.js new file mode 100644 index 0000000..98e3f49 --- /dev/null +++ b/src/react/PopupMenuItem.js @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class PopupMenuItem extends React.Component { + render() { + const {itemId, value, onClick, selected, disabled} = this.props; + const additionalClasses = selected ? 'selected' : disabled ? 'disabled' : ''; + return ( + <li + className={`sdc-menu-item ${additionalClasses}`} + onClick={event => { + event.stopPropagation(); + onClick && !disabled && onClick(itemId); + }}> + {value} + </li> + ); + } +} + +PopupMenuItem.propTypes = { + itemId: PropTypes.any, + value: PropTypes.any, + selected: PropTypes.bool, + onClick: PropTypes.func, + disabled: PropTypes.bool +}; + +PopupMenuItem.defaultProps = { + selected: false, + disabled: false +}; + +export default PopupMenuItem; diff --git a/src/react/Portal.js b/src/react/Portal.js new file mode 100644 index 0000000..90e0675 --- /dev/null +++ b/src/react/Portal.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactDOM from 'react-dom'; + +class Portal extends React.Component { + componentDidMount() { + this.renderPortal(); + } + + componentDidUpdate() { + this.renderPortal(); + } + + componentWillUnmount() { + if (this.defaultNode) { + document.body.removeChild(this.defaultNode); + } + this.defaultNode = null; + this.portal = null; + } + + renderPortal() { + if (!this.defaultNode) { + this.defaultNode = document.createElement('div'); + this.defaultNode.className = 'onap-sdc-portal'; + document.body.appendChild(this.defaultNode); + } + + let children = this.props.children; + if (typeof this.props.children.type === 'function') { + children = React.cloneElement(this.props.children); + } + /** + * Change this to ReactDOM.CreatePortal after upgrading to React 16 + */ + this.portal = ReactDOM.unstable_renderSubtreeIntoContainer( + this, + children, + this.defaultNode + ); + } + render() { + return null; + } + +} + +Portal.propTypes = { + children: PropTypes.node.isRequired +}; + +export default Portal;
\ No newline at end of file diff --git a/src/react/Radio.js b/src/react/Radio.js new file mode 100644 index 0000000..483521a --- /dev/null +++ b/src/react/Radio.js @@ -0,0 +1,58 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class Radio extends React.Component { + render() { + let {checked, disabled, value, label, className, inputRef, name} = this.props; + let dataTestId = this.props['data-test-id']; + return ( + <div className={`sdc-radio ${className}`}> + <label> + <input + ref={inputRef} + className='sdc-radio__input' + value={value} + data-test-id={dataTestId} + type='radio' + name={name} + checked={checked} + onChange={(e) => this.onChange(e)} + disabled={disabled} /> + <span className='sdc-radio__label'>{label}</span> + </label> + </div> + ); + } + + onChange(e) { + let {onChange} = this.props; + if (onChange) { + onChange(e.target.checked); + } + } + + getChecked() { + return this.props.checked; + } + + getValue() { + return this.props.value; + } +} + +Radio.propTypes = { + checked: PropTypes.bool, + value: PropTypes.any, + label: PropTypes.string, + className: PropTypes.string, + inputRef: PropTypes.func, + name: PropTypes.string, + disabled: PropTypes.bool +}; + +Radio.defaultProps = { + checked: false, + className: '' +}; + +export default Radio; diff --git a/src/react/RadioGroup.js b/src/react/RadioGroup.js new file mode 100644 index 0000000..59eaca7 --- /dev/null +++ b/src/react/RadioGroup.js @@ -0,0 +1,40 @@ +import React from 'react'; +import Radio from './Radio.js'; + +class RadioGroup extends React.Component { + constructor(props) { + super(props); + this.radios = {}; + } + + render() { + let {name, disabled, title, options, value, className} = this.props; + let dataTestId = this.props['data-test-id']; + return (<div data-test-id={dataTestId} className={`sdc-radio-group ${className || ''}`}> + { title && <label className='sdc-radio-group__legend'>{title}</label> } + <div className='sdc-radio-group__radios'> + {options.map(option => { + let rName = name + '_' + option.value; + return (<Radio ref={(radio) => {this.radios[rName] = radio;}} data-test-id={dataTestId + '_' + option.value} + key={rName} value={option.value} + label={option.label} checked={value === option.value} disabled={disabled} + name={name} onChange={() => this.onChange(rName)} /> + );})} + </div> + </div>); + } + + onChange(rName) { + let {onChange} = this.props; + let val = this.radios[rName].getValue(); + if (onChange) { + onChange(val); + } + } + + getValue() { + return this.props.value; + } +} + +export default RadioGroup; diff --git a/src/react/SVGIcon.js b/src/react/SVGIcon.js new file mode 100644 index 0000000..8a5b1ae --- /dev/null +++ b/src/react/SVGIcon.js @@ -0,0 +1,47 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import iconMap from './utils/iconMap.js'; + +const SVGIcon = ({name, onClick, label, className, iconClassName, labelClassName, labelPosition, color, disabled, ...other}) => { + + let colorClass = (color !== '') ? '__' + color : ''; + let classes = `svg-icon-wrapper ${iconClassName} ${className} ${colorClass} ${onClick ? 'clickable' : ''} ${labelPosition}`; + let camelCasedName = name.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); + let IconComponent = iconMap[camelCasedName]; + if (!IconComponent) { + console.error('Icon by the name ' + camelCasedName + ' is missing.'); + } + + return ( + <div {...other} onClick={onClick} className={classes} disabled={disabled}> + { IconComponent && <IconComponent className={`svg-icon __${name}`} /> } + { !IconComponent && <span className='svg-icon-missing'>Missing Icon</span> } + {label && <span className={`svg-icon-label ${labelClassName}`}>{label}</span>} + </div> + ); + +}; + +SVGIcon.propTypes = { + name: PropTypes.string.isRequired, + onClick: PropTypes.func, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + labelPosition: PropTypes.string, + className: PropTypes.string, + iconClassName: PropTypes.string, + labelClassName: PropTypes.string, + color: PropTypes.string +}; + +SVGIcon.defaultProps = { + name: '', + label: '', + className: '', + iconClassName: '', + labelClassName: '', + labelPosition: 'bottom', + color: '' +}; + +export default SVGIcon; diff --git a/src/react/Tab.js b/src/react/Tab.js new file mode 100644 index 0000000..5aa0f16 --- /dev/null +++ b/src/react/Tab.js @@ -0,0 +1,20 @@ +import React from 'react'; + +class Tab extends React.Component { + render() { + const {activeTab, tabId, title, onClick, disabled, className = ''} = this.props; + const dataTestId = this.props['data-test-id']; + return ( + <li + className={`sdc-tab ${activeTab === tabId ? 'sdc-tab-active' : ''} ${className}`} + onClick={!disabled && onClick} + data-test-id={dataTestId} + role='tab' + disabled={disabled}> + {title} + </li> + ); + } +} + +export default Tab; diff --git a/src/react/TabPane.js b/src/react/TabPane.js new file mode 100644 index 0000000..56a4bf0 --- /dev/null +++ b/src/react/TabPane.js @@ -0,0 +1,12 @@ +import React from 'react'; + +class TabPane extends React.Component { + render() { + const {children} = this.props; + return (<div className='sdc-tab-content' role='tabpanel'> + {children} + </div>); + } +} + +export default TabPane; diff --git a/src/react/Tabs.js b/src/react/Tabs.js new file mode 100644 index 0000000..c502038 --- /dev/null +++ b/src/react/Tabs.js @@ -0,0 +1,29 @@ +import React from 'react'; +import TabPane from './TabPane.js'; + +class Tabs extends React.Component { + render() { + const {type, children = [], activeTab, onTabClick, className} = this.props; + return ( + <div className={type === 'header' ? `sdc-tabs sdc-tabs-header ${className || ''}` : `sdc-tabs sdc-tabs-menu ${className || ''}`} > + <ul className='sdc-tabs-list' role='tablist'> + {children.map(child => React.cloneElement(child, + { + key: child.props.tabId, + onClick: () => onTabClick(child.props.tabId), + activeTab + }))} + </ul> + <TabPane> + {children.map(child => { + if (child.props.tabId === activeTab) { + return child.props.children; + } + })} + </TabPane> + </div> + ); + } +} + +export default Tabs; diff --git a/src/react/Tile.js b/src/react/Tile.js new file mode 100644 index 0000000..f47f88d --- /dev/null +++ b/src/react/Tile.js @@ -0,0 +1,33 @@ +import React, {Children} from 'react'; +import PropTypes from 'prop-types'; +import TileInfo from './TileInfo.js'; +import TileFooter from './TileFooter.js'; +import SVGIcon from './SVGIcon.js'; + +const Tile = ({headerText, headerColor, iconName, iconColor, className, onClick, children, dataTestId}) => { + let childrenArr = Children.toArray(children); + return ( + <div className={`sdc-tile ${className || ''}`} onClick={onClick} data-test-id={dataTestId}> + <div className={`sdc-tile-header ${headerColor || ''}`}>{headerText}</div> + <div className='sdc-tile-content'> + <div className={`sdc-tile-content-icon ${iconColor || ''}`}> + {iconName && <SVGIcon name={iconName}/>} + </div> + {childrenArr.find(e => e.type === TileInfo)} + </div> + {childrenArr.find(e => e.type === TileFooter)} + </div> + ); +}; + +Tile.propTypes = { + headerText: PropTypes.string, + headerColor: PropTypes.string, + iconName: PropTypes.string, + iconColor: PropTypes.string, + className: PropTypes.string, + onClick: PropTypes.func, + dataTestId: PropTypes.string +}; + +export default Tile; diff --git a/src/react/TileFooter.js b/src/react/TileFooter.js new file mode 100644 index 0000000..3a56908 --- /dev/null +++ b/src/react/TileFooter.js @@ -0,0 +1,10 @@ +import React, {Children} from 'react'; +import TileFooterCell from './TileFooterCell.js'; + +const TileFooter = ({children, align}) => ( + <div className={`sdc-tile-footer ${align === 'center' ? 'centered' : ''}`}> + {Children.toArray(children).filter(e => e.type === TileFooterCell)} + </div> +); + +export default TileFooter; diff --git a/src/react/TileFooterCell.js b/src/react/TileFooterCell.js new file mode 100644 index 0000000..37e6416 --- /dev/null +++ b/src/react/TileFooterCell.js @@ -0,0 +1,7 @@ +import React from 'react'; + +const TileFooterCell = ({className, children, dataTestId}) => ( + <span className={`sdc-tile-footer-cell ${className || ''}`} data-test-id={dataTestId}>{children}</span> +); + +export default TileFooterCell; diff --git a/src/react/TileInfo.js b/src/react/TileInfo.js new file mode 100644 index 0000000..bda8e74 --- /dev/null +++ b/src/react/TileInfo.js @@ -0,0 +1,10 @@ +import React, {Children} from 'react'; +import TileInfoLine from './TileInfoLine.js'; + +const TileInfo = ({align, children}) => ( + <div className={`sdc-tile-content-info ${align === 'center' ? 'centered' : ''}`}> + {Children.toArray(children).filter(e => e.type === TileInfoLine)} + </div> +); + +export default TileInfo; diff --git a/src/react/TileInfoLine.js b/src/react/TileInfoLine.js new file mode 100644 index 0000000..5b0e2c9 --- /dev/null +++ b/src/react/TileInfoLine.js @@ -0,0 +1,7 @@ +import React from 'react'; + +const TileInfoLine = ({type, className, children, dataTestId}) => ( + <div className={`sdc-tile-info-line ${type || ''} ${className || ''}`} data-test-id={dataTestId}>{children}</div> +); + +export default TileInfoLine; diff --git a/src/react/index.js b/src/react/index.js new file mode 100644 index 0000000..cbe0161 --- /dev/null +++ b/src/react/index.js @@ -0,0 +1,74 @@ +import Accordion from './Accordion.js'; +import Button from './Button.js'; +import Checkbox from './Checkbox.js'; +import Checklist from './Checklist.js'; +import Input from './Input.js'; +import Modal from './Modal.js'; +import ModalBody from './ModalBody.js'; +import ModalFooter from './ModalFooter.js'; +import ModalHeader from './ModalHeader.js'; +import ModalTitle from './ModalTitle.js'; +import Panel from './Panel.js'; +import PopupMenu from './PopupMenu.js'; +import Portal from './Portal.js'; +import Radio from './Radio.js'; +import RadioGroup from './RadioGroup.js'; +import SVGIcon from './SVGIcon.js'; +import Tab from './Tab.js'; +import Tabs from './Tabs.js'; +import Tile from './Tile.js'; +import TileInfo from './TileInfo.js'; +import TileInfoLine from './TileInfoLine.js'; +import TileFooter from './TileFooter.js'; +import TileFooterCell from './TileFooterCell.js'; + + +export { Accordion }; +export { Button }; +export { Checkbox }; +export { Checklist }; +export { Input }; +export { Modal }; +export { ModalBody }; +export { ModalFooter }; +export { ModalHeader }; +export { ModalTitle }; +export { Panel }; +export { PopupMenu }; +export { Portal }; +export { Radio }; +export { RadioGroup }; +export { SVGIcon }; +export { Tab }; +export { Tabs }; +export { Tile }; +export { TileInfo }; +export { TileInfoLine }; +export { TileFooter }; +export { TileFooterCell }; + +export default { + Accordion, + Button, + Checkbox, + Checklist, + Input, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + ModalTitle, + Panel, + PopupMenu, + Portal, + Radio, + RadioGroup, + SVGIcon, + Tab, + Tabs, + Tile, + TileInfo, + TileInfoLine, + TileFooter, + TileFooterCell +}; |