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) => (