diff --git a/.browserslistrc b/.browserslistrc index 8289333..2e8bf2b 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,11 +1,12 @@ > 1% ie >= 8 -edge >= 15 +edge >= 16 ie_mob >= 10 -ff >= 45 -chrome >= 45 -safari >= 7 -opera >= 23 -ios >= 7 -android >= 4 +ff >= 52 +chrome >= 57 +safari >= 10.1 +opera >= 44 +ios >= 10.3 +and_uc >= 12.12 +and_chr >= 97 bb >= 10 diff --git a/.eslintrc b/.eslintrc index cd6f870..4bdcadc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,45 +8,57 @@ }, "extends": "airbnb", "rules": { - "jsx-a11y/label-has-associated-control": 0, - "space-before-function-paren": 0, - "react/prefer-stateless-function": 0, - "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], - "linebreak-style": 0, - "global-require": 0, + "camelcase": 0, + "class-methods-use-this": 0, + "comma-dangle": [ + "error", + "always-multiline" + ], "eol-last": 0, - "comma-dangle": ["error", "always-multiline"], - "spaced-comment": 0, - "react/require-default-props": 0, - "react/forbid-prop-types": 0, - "jsx-a11y/label-has-for": 0, + "function-paren-newline": 0, + "global-require": 0, + "import/extensions": 0, + "import/no-extraneous-dependencies": 0, + "import/no-named-as-default": 0, + "import/no-unresolved": 0, + "import/prefer-default-export": 0, "jsx-a11y/anchor-is-valid": 0, + "jsx-a11y/click-events-have-key-events": 0, "jsx-a11y/href-no-hash": 0, "jsx-a11y/interactive-supports-focus": 0, - "jsx-a11y/click-events-have-key-events": 0, + "jsx-a11y/label-has-associated-control": 0, + "jsx-a11y/label-has-for": 0, "jsx-a11y/mouse-events-have-key-events": 0, "jsx-a11y/no-static-element-interactions": 0, - "react/no-did-mount-set-state": 0, + "linebreak-style": 0, "max-len": 0, - "react/jsx-no-bind": 0, - "class-methods-use-this" :0, - "react/prop-types": 0, + "no-console": 0, + "no-debugger": 0, + "no-multi-assign": 0, + "no-plusplus": 0, + "no-shadow": 0, "no-unused-expressions": 0, - "radix": 0, "object-curly-newline": 0, - "no-console": 0, - "import/no-unresolved": 0, - "import/no-named-as-default": 0, - "import/no-extraneous-dependencies": 0, - "import/extensions": 0, "prefer-destructuring": 0, - "no-multi-assign": 0, - "function-paren-newline": 0, + "radix": 0, + "react/forbid-prop-types": 0, + "react/jsx-filename-extension": [ + 1, + { + "extensions": [ + ".js", + ".jsx" + ] + } + ], + "react/jsx-no-bind": 0, + "react/jsx-props-no-spreading": 0, + "react/no-did-mount-set-state": 0, + "react/no-multi-comp": 0, "react/no-unescaped-entities": 0, - "no-debugger": 0, - "no-shadow": 0, - "no-plusplus": 0, - "camelcase": 0, + "react/prefer-stateless-function": 0, + "react/prop-types": 0, + "react/require-default-props": 0, "react/sort-comp": [ 1, { @@ -59,8 +71,8 @@ ] } ], - "react/no-multi-comp": 0, - "import/prefer-default-export": 0 + "space-before-function-paren": 0, + "spaced-comment": 0 }, "globals": { "zE": true, diff --git a/CHANGELOG.md b/CHANGELOG.md index ae56773..737903a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## Release 2.0.4 - Unreleased +### Fixed +* BubbleChat: Use bottom alignment for avatar +* BubbleChat.Image: Set `lineHeight` to `0` to make image in the same baseline with avatar +* Dropdown.Container: Correct extra props that are passed to internal `div`, so incompatible props (such as `popperConfig`) are excluded +* Composer: Allow passing `className` and `style` to `attachButtonProps`, `inputProps`, and `sendButtonProps` +* Button: add className `u-fontMedium` to sync value with UI +* Message, Modal, Toggle: add aria-label for accessibility ## Release 2.0.3 - January 18, 2022 ### Fixed * Code base: use absolute imports, rearrange hooks and utils diff --git a/package.json b/package.json index fbd3d41..3ae8b50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ahaui/react", - "version": "2.0.3", + "version": "2.0.4", "main": "lib/index.js", "module": "es/index.js", "files": [ @@ -72,7 +72,6 @@ "webpack-node-externals": "1.7.2" }, "peerDependencies": { - "@ahaui/css": "^2.0.4", "react": "^16.9.0 || 17.x", "react-dom": "^16.9.0 || 17.x" }, diff --git a/src/components/BubbleChat/Image.js b/src/components/BubbleChat/Image.js index c09381d..50bc8a2 100644 --- a/src/components/BubbleChat/Image.js +++ b/src/components/BubbleChat/Image.js @@ -10,7 +10,7 @@ const BubbleChatImage = React.forwardRef(({ className, ...props }, ref) => { ref={ref} className={classNames( 'BubbleChat-image', - 'u-marginBottomTiny', + 'u-lineHeightNone', (type === 'inbound') && 'u-textRight', className && className )} diff --git a/src/components/BubbleChat/index.js b/src/components/BubbleChat/index.js index 503538c..406dee9 100644 --- a/src/components/BubbleChat/index.js +++ b/src/components/BubbleChat/index.js @@ -94,7 +94,7 @@ const typeThemeClassNames = { const typeRadiusClassNames = { inbound: 'u-roundedExtraLarge u-roundedBottomRightNone', - outbound: 'u-roundedExtraLarge u-roundedBottomLeftNone', + outbound: 'u-roundedExtraLarge u-roundedBottomLeftNone', system: 'u-roundedExtraLarge', }; @@ -105,7 +105,69 @@ const BubbleChat = React.forwardRef(({ className, isTyping, text, type, variant, else if (type === 'outbound') variantOri = 'light'; else if (type === 'system') variantOri = 'primaryLight'; } + const context = useMemo(() => ({ type }), [type]); + + const renderTime = () => ( + <> + {/* Empty
to occupy the bottom left corner of grid template */} + {type !== 'inbound' &&
} +
+ {time} +
+ {/* Empty
to occupy the bottom right corner of grid template */} + {type === 'inbound' &&
} + + ); + + const renderTyping = () => ( +
+
+
+
+
+ ); + + const renderAvatar = (type = 'outbound') => ( +
+ {typeof (avatar) === 'function' + ? avatar() + : + } +
+ ); + return (
-
- {(avatar && type !== 'inbound') && ( -
- { - typeof (avatar) === 'function' ? avatar() : - } -
+
+ {(type !== 'inbound' && avatar) && renderAvatar('outbound')} + {/* Empty
to occupy the top left corner of grid template */} + {(type !== 'inbound' && !avatar) &&
}
- {isTyping && ( -
-
-
-
-
- )} + {isTyping && renderTyping()} {!isTyping && ( - - {children || ( -
- {actionBar && ( -
- {actionBar} -
- )} -
-
+ {children || ( +
+ {actionBar && ( +
+ {actionBar} +
)} - onClick={onClickText} - > - {text} -
- {options && ( -
- {options.map((option, idx) => { - let cn; - let handleClick; +
+
+ {text} +
+ {options && ( +
+ {options.map((option, idx) => { + let cn; + let handleClick; - if (option.id === currentOption) { - cn = `u-backgroundPrimary ${textClassName || 'u-textWhite'} ${disabledOption ? 'u-cursorNotAllow' : ''}`; - handleClick = null; - } else if (disabledOption) { - cn = 'u-backgroundLighter u-textGray u-cursorNotAllow'; - handleClick = null; - } else { - cn = 'u-backgroundWhite hover:u-backgroundLightest u-textPrimary u-cursorPointer'; - handleClick = () => onSelectOption(option.id); - } + if (option.id === currentOption) { + cn = `u-backgroundPrimary ${textClassName || 'u-textWhite'} ${disabledOption ? 'u-cursorNotAllow' : ''}`; + handleClick = null; + } else if (disabledOption) { + cn = 'u-backgroundLighter u-textGray u-cursorNotAllow'; + handleClick = null; + } else { + cn = 'u-backgroundWhite hover:u-backgroundLightest u-textPrimary u-cursorPointer'; + handleClick = () => onSelectOption(option.id); + } - return ( - - ); - })} + return ( + + ); + })} +
+ )} +
- )} -
-
- )} - {time && ( -
- {time} -
- )} - + )} + )}
- {(avatar && (type !== 'outbound' && type !== 'system')) && ( -
- { - typeof (avatar) === 'function' ? avatar() : - } -
- )} + + {((type !== 'outbound' && type !== 'system') && avatar) && renderAvatar('inbound')} + {/* Empty
to occupy the top right corner of grid template */} + {((type !== 'outbound' && type !== 'system') && !avatar) &&
} + + {time && renderTime()}
diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 5743c10..ce47829 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -86,7 +86,7 @@ const Button = React.forwardRef(({ className, variant, textClassName, children, ref={ref} {...props} className={classNames( - 'Button u-flexInline u-justifyContentCenter u-alignItemsCenter u-textDecorationNone u-roundedMedium', + 'Button u-flexInline u-justifyContentCenter u-alignItemsCenter u-textDecorationNone u-roundedMedium u-fontMedium', variant && variantsClassName[variant], variant !== 'link' && 'hover:u-textDecorationNone', sizeOri && `Button--${sizeOri}`, diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index 64c5d7e..b255b40 100644 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -6,7 +6,6 @@ import Icon from 'components/Icon'; import Overlay from 'components/Overlay'; import Tooltip from 'components/Tooltip'; - const propTypes = { /** * A set of input props passed directly to `react-textarea-autosize`'s component. @@ -61,12 +60,11 @@ const Composer = React.forwardRef(({ className, children, sendButtonIcon, iconLe className={classNames( 'Composer', 'u-flex u-alignItemsEnd u-borderTop u-paddingTiny', - className && className + className && className, )} > {!disabledAttachButton && ( -
{tooltipAttachButton ? ( {typeof (tooltipAttachButton) === 'function' ? tooltipAttachButton() - : tooltipAttachButton - } + : tooltipAttachButton} )} >
)} @@ -133,8 +135,7 @@ const Composer = React.forwardRef(({ className, children, sendButtonIcon, iconLe {typeof (tooltipSendButton) === 'function' ? tooltipSendButton() - : tooltipSendButton - } + : tooltipSendButton} )} > @@ -142,11 +143,13 @@ const Composer = React.forwardRef(({ className, children, sendButtonIcon, iconLe {...sendButtonProps} className={classNames( 'u-roundedMedium u-flex u-alignItemsCenter u-justifyContentCenter u-flexShrink0 u-marginLeftTiny', - sendButtonActive ? 'hover:u-backgroundPrimary hover:u-textWhite u-textPrimary u-cursorPointer' : 'u-textLight u-cursorNotAllow u-pointerEventsNone' + sendButtonActive ? 'hover:u-backgroundPrimary hover:u-textWhite u-textPrimary u-cursorPointer' : 'u-textLight u-cursorNotAllow u-pointerEventsNone', + sendButtonProps.className && sendButtonProps.className, )} style={{ width: 42, height: 42, + ...sendButtonProps.style, }} > {typeof (sendButtonIcon) === 'function' @@ -159,11 +162,13 @@ const Composer = React.forwardRef(({ className, children, sendButtonIcon, iconLe {...sendButtonProps} className={classNames( 'u-roundedMedium u-flex u-alignItemsCenter u-justifyContentCenter u-flexShrink0 u-marginLeftTiny', - sendButtonActive ? 'hover:u-backgroundPrimary hover:u-textWhite u-textPrimary u-cursorPointer' : 'u-textLight u-cursorNotAllow u-pointerEventsNone' + sendButtonActive ? 'hover:u-backgroundPrimary hover:u-textWhite u-textPrimary u-cursorPointer' : 'u-textLight u-cursorNotAllow u-pointerEventsNone', + sendButtonProps.className && sendButtonProps.className, )} style={{ width: 42, height: 42, + ...sendButtonProps.style, }} > {typeof (sendButtonIcon) === 'function' diff --git a/src/components/Dropdown/Container.js b/src/components/Dropdown/Container.js index 6aa1b36..1c37582 100644 --- a/src/components/Dropdown/Container.js +++ b/src/components/Dropdown/Container.js @@ -6,36 +6,61 @@ import usePopper from 'hooks/usePopper'; import useRootClose from 'hooks/useRootClose'; import DropdownContext from './Context'; - const propTypes = { + /** + * Custom style + */ + additionalStyles: PropTypes.object, /** * You can use a custom element type for this component. * @default div - * */ + */ as: PropTypes.elementType, + /** + * Custom className + */ + className: PropTypes.string, + /** + * Popper's `flip` modifier config. + * @see https://popper.js.org/docs/v2/modifiers/flip/ + */ + flip: PropTypes.boolean, /** * A set of popper options and props passed directly to react-popper's Popper component. + * @default {} */ popperConfig: PropTypes.object, - /** A `react-transition-group` Transition component used to animate the Message on dismissal. */ + /** + * The DOM event name (click, mousedown, etc) that will close the dropdown + * @default click + */ + rootCloseEvent: PropTypes.string, + /** + * Whether PopperJS should be used + * @default true + */ + shouldUsePopper: PropTypes.bool, + /** + * A `react-transition-group` Transition component used to animate the Message on dismissal. + */ transition: elementType, - /** Custom style */ - additionalStyles: PropTypes.object, }; const defaultProps = { }; -const Container = React.forwardRef(({ additionalStyles, ...props }, ref) => { +const Container = React.forwardRef((props, ref) => { const { - className, + additionalStyles, + as: Component = 'div', flip, - rootCloseEvent, children, + className, popperConfig = {}, - as: Component = 'div', + rootCloseEvent, shouldUsePopper = true, transition: Transition, + ...restProps } = props; const context = useContext(DropdownContext); @@ -64,7 +89,7 @@ const Container = React.forwardRef(({ additionalStyles, ...props }, ref) => { ref: setContainer, style: { ...popper.styles, ...additionalStyles }, 'aria-labelledby': toggleElement && toggleElement.id, - ...props, + ...restProps, }; useRootClose(context.containerElement, handleClose, { clickTrigger: rootCloseEvent, @@ -77,7 +102,7 @@ const Container = React.forwardRef(({ additionalStyles, ...props }, ref) => { className={classNames( 'Dropdown-container', 'u-zIndexDropdownContainer u-backgroundWhite u-roundedMedium u-border u-positionAbsolute u-textLeft u-positionLeft u-marginVerticalTiny', - className && className + className && className, )} > {children} diff --git a/src/components/Message/index.js b/src/components/Message/index.js index b7c3cee..5e370e0 100644 --- a/src/components/Message/index.js +++ b/src/components/Message/index.js @@ -90,21 +90,19 @@ const Message = React.forwardRef((uncontrolledProps, ref) => { {children} {dismissible && (
setDismissButtonHover(dismissButtonHover => !dismissButtonHover)} + onMouseLeave={() => setDismissButtonHover(dismissButtonHover => !dismissButtonHover)} + onClick={handleClose} + className={classNames( + 'Message-button u-marginRightSmall u-marginTopSmall', + dismissButtonHover ? 'u-opacityReset' : 'u-opacityHalf', + variantOri.textClassName + )} + data-testid="message-close" + role="button" + aria-label="dismiss alert" > - setDismissButtonHover(dismissButtonHover => !dismissButtonHover)} - onMouseLeave={() => setDismissButtonHover(dismissButtonHover => !dismissButtonHover)} - onClick={handleClose} - className={classNames( - 'u-cursorPointer', - dismissButtonHover ? 'u-opacityReset' : 'u-opacityHalf', - variantOri.textClassName - )} - data-testid="message-close" - /> +
)}
diff --git a/src/components/Modal/Header.js b/src/components/Modal/Header.js index 9942b3d..85e2ebf 100644 --- a/src/components/Modal/Header.js +++ b/src/components/Modal/Header.js @@ -40,6 +40,7 @@ const Header = React.forwardRef(({ className, children, closeButton, onHide, ... onMouseLeave={() => setCloseHover(closeHover => !closeHover)} onClick={handleClick} data-testid="modal-close-button" + aria-label="Close modal" > ( +const Toggle = React.forwardRef(({ className, checked, disabled, nonLabel, textLabelOn, textLabelOff, ariaLabel, as: Component = 'button', ...props }, ref) => (