Add proptypes and a few more refactors
This commit is contained in:
parent
c87f3054b9
commit
4fa10ec656
@ -1,8 +1,9 @@
|
||||
import React, { Component, Children, cloneElement } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './styles';
|
||||
import cx from 'classnames';
|
||||
import UserAction from '/imports/ui/components/user-list/user-list-item/user-action/component';
|
||||
import KEY_CODES from '/imports/utils/keyCodes';
|
||||
import styles from './styles';
|
||||
import ListItem from './item/component';
|
||||
import ListSeparator from './separator/component';
|
||||
import ListTitle from './title/component';
|
||||
@ -10,14 +11,16 @@ import ListTitle from './title/component';
|
||||
const propTypes = {
|
||||
children: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => {
|
||||
if (propValue[key].type !== ListItem &&
|
||||
propValue[key].type !== ListSeparator &&
|
||||
propValue[key].type !== ListTitle) {
|
||||
propValue[key].type !== ListSeparator &&
|
||||
propValue[key].type !== ListTitle &&
|
||||
propValue[key].type !== UserAction) {
|
||||
return new Error(
|
||||
`Invalid prop \`${propFullName}\` supplied to` +
|
||||
` \`${componentName}\`. Validation failed.`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
return true;
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default class DropdownList extends Component {
|
||||
@ -29,36 +32,31 @@ export default class DropdownList extends Component {
|
||||
this.handleItemClick = this.handleItemClick.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._menu.addEventListener('keydown', event=>this.handleItemKeyDown(event));
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
focusedIndex: 0,
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
let { focusedIndex } = this.state;
|
||||
|
||||
this.menuRefs = [];
|
||||
componentDidMount() {
|
||||
this._menu.addEventListener('keydown', event => this.handleItemKeyDown(event));
|
||||
}
|
||||
|
||||
for (let i = 0; i < (this._menu.children.length); i++) {
|
||||
if (this._menu.children[i].getAttribute("role") === 'menuitem') {
|
||||
this.menuRefs.push(this._menu.children[i]);
|
||||
}
|
||||
}
|
||||
componentDidUpdate() {
|
||||
const { focusedIndex } = this.state;
|
||||
|
||||
const childrens = [].slice.call(this._menu.children);
|
||||
this.menuRefs = childrens.filter(child => child.getAttribute('role') === 'menuitem');
|
||||
|
||||
const activeRef = this.menuRefs[focusedIndex];
|
||||
|
||||
|
||||
if (activeRef) {
|
||||
activeRef.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleItemKeyDown(event, callback) {
|
||||
const { onActionsHide, getDropdownMenuParent, } = this.props;
|
||||
const { getDropdownMenuParent } = this.props;
|
||||
let nextFocusedIndex = this.state.focusedIndex;
|
||||
|
||||
if (KEY_CODES.ARROW_UP === event.which) {
|
||||
@ -68,7 +66,7 @@ export default class DropdownList extends Component {
|
||||
|
||||
if (nextFocusedIndex < 0) {
|
||||
nextFocusedIndex = this.menuRefs.length - 1;
|
||||
}else if (nextFocusedIndex > this.menuRefs.length - 1) {
|
||||
} else if (nextFocusedIndex > this.menuRefs.length - 1) {
|
||||
nextFocusedIndex = 0;
|
||||
}
|
||||
}
|
||||
@ -90,30 +88,29 @@ export default class DropdownList extends Component {
|
||||
|
||||
if ([KEY_CODES.ESCAPE, KEY_CODES.TAB, KEY_CODES.ARROW_LEFT].includes(event.keyCode)) {
|
||||
const { dropdownHide } = this.props;
|
||||
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
dropdownHide();
|
||||
if (getDropdownMenuParent) {
|
||||
getDropdownMenuParent().focus();
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({focusedIndex: nextFocusedIndex});
|
||||
|
||||
this.setState({ focusedIndex: nextFocusedIndex });
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
|
||||
handleItemClick(event, callback) {
|
||||
const { getDropdownMenuParent, onActionsHide} = this.props;
|
||||
const { dropdownHide } = this.props;
|
||||
const { getDropdownMenuParent, onActionsHide, dropdownHide } = this.props;
|
||||
|
||||
if ( getDropdownMenuParent ) {
|
||||
if (getDropdownMenuParent) {
|
||||
onActionsHide();
|
||||
}else{
|
||||
} else {
|
||||
this.setState({ focusedIndex: null });
|
||||
dropdownHide();
|
||||
}
|
||||
@ -127,7 +124,7 @@ export default class DropdownList extends Component {
|
||||
const { children, style, className } = this.props;
|
||||
|
||||
const boundChildren = Children.map(children,
|
||||
(item, i) => {
|
||||
(item) => {
|
||||
if (item.type === ListSeparator) {
|
||||
return item;
|
||||
}
|
||||
@ -140,13 +137,13 @@ export default class DropdownList extends Component {
|
||||
|
||||
onClick: (event) => {
|
||||
let { onClick } = item.props;
|
||||
onClick = onClick ? onClick.bind(item) : null;
|
||||
onClick = onClick ? () => onClick.call(item) : null;
|
||||
this.handleItemClick(event, onClick);
|
||||
},
|
||||
|
||||
onKeyDown: (event) => {
|
||||
let { onKeyDown } = item.props;
|
||||
onKeyDown = onKeyDown ? onKeyDown.bind(item) : null;
|
||||
onKeyDown = onKeyDown ? () => onKeyDown.call(item) : null;
|
||||
|
||||
this.handleItemKeyDown(event, onKeyDown);
|
||||
},
|
||||
@ -154,11 +151,16 @@ export default class DropdownList extends Component {
|
||||
});
|
||||
|
||||
return (
|
||||
<ul
|
||||
style={style}
|
||||
className={cx(styles.list, className)}
|
||||
role="menu" ref={(r) => this._menu = r}>
|
||||
{boundChildren}
|
||||
<ul
|
||||
style={style}
|
||||
className={cx(styles.list, className)}
|
||||
role="menu"
|
||||
ref={(r) => {
|
||||
this._menu = r;
|
||||
return r;
|
||||
}}
|
||||
>
|
||||
{boundChildren}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ const defaultProps = {
|
||||
icon: '',
|
||||
label: '',
|
||||
description: '',
|
||||
tabIndex: 0,
|
||||
};
|
||||
|
||||
export default class DropdownListItem extends Component {
|
||||
|
@ -2,9 +2,9 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
|
||||
import _ from 'lodash';
|
||||
import UserListContent from './user-list-content/component';
|
||||
import UserAction from './user-action/component';
|
||||
|
||||
const normalizeEmojiName = (emoji) => {
|
||||
const emojisNormalized = {
|
||||
@ -32,14 +32,33 @@ const propTypes = {
|
||||
currentUser: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
|
||||
compact: PropTypes.bool.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
userActions: PropTypes.object.isRequired,
|
||||
router: PropTypes.object.isRequired,
|
||||
isBreakoutRoom: PropTypes.bool.isRequired,
|
||||
getAvailableActions: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
shouldShowActions: false,
|
||||
isBreakoutRoom: false,
|
||||
};
|
||||
|
||||
class UserListItem extends Component {
|
||||
|
||||
static createAction(action, ...options) {
|
||||
return (
|
||||
<UserAction
|
||||
key={_.uniqueId('action-item-')}
|
||||
icon={action.icon}
|
||||
label={action.label}
|
||||
handler={action.handler}
|
||||
options={[...options]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
getUsersActions() {
|
||||
const {
|
||||
currentUser,
|
||||
@ -69,32 +88,16 @@ class UserListItem extends Component {
|
||||
allowedToKick,
|
||||
allowedToSetPresenter } = actions;
|
||||
|
||||
|
||||
return _.compact([
|
||||
(allowedToChatPrivately ? this.renderUserAction(openChat, router, user) : null),
|
||||
(allowedToMuteAudio ? this.renderUserAction(unmute, user) : null),
|
||||
(allowedToUnmuteAudio ? this.renderUserAction(mute, user) : null),
|
||||
(allowedToResetStatus ? this.renderUserAction(clearStatus, user) : null),
|
||||
(allowedToSetPresenter ? this.renderUserAction(setPresenter, user) : null),
|
||||
(allowedToKick ? this.renderUserAction(kick, user) : null),
|
||||
(allowedToChatPrivately ? UserListItem.createAction(openChat, router, user) : null),
|
||||
(allowedToMuteAudio ? UserListItem.createAction(unmute, user) : null),
|
||||
(allowedToUnmuteAudio ? UserListItem.createAction(mute, user) : null),
|
||||
(allowedToResetStatus ? UserListItem.createAction(clearStatus, user) : null),
|
||||
(allowedToSetPresenter ? UserListItem.createAction(setPresenter, user) : null),
|
||||
(allowedToKick ? UserListItem.createAction(kick, user) : null),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
renderUserAction(action, ...parameters) {
|
||||
const userAction = (
|
||||
<DropdownListItem
|
||||
key={_.uniqueId('action-item-')}
|
||||
icon={action.icon}
|
||||
label={action.label}
|
||||
defaultMessage={action.label}
|
||||
onClick={action.handler.bind(this, ...parameters)}
|
||||
/>
|
||||
);
|
||||
|
||||
return userAction;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
compact,
|
||||
|
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
|
||||
|
||||
const propTypes = {
|
||||
icon: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
handler: PropTypes.func.isRequired,
|
||||
options: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
const UserActions = (props) => {
|
||||
const { key, icon, label, handler, options } = props;
|
||||
|
||||
const userAction = (
|
||||
<DropdownListItem
|
||||
key={key}
|
||||
icon={icon}
|
||||
label={label}
|
||||
defaultMessage={label}
|
||||
onClick={() => handler.call(this, ...options)}
|
||||
/>
|
||||
);
|
||||
|
||||
return userAction;
|
||||
};
|
||||
|
||||
UserActions.propTypes = propTypes;
|
||||
export default UserActions;
|
@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import cx from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import UserAvatar from '/imports/ui/components/user-avatar/component';
|
||||
import Icon from '/imports/ui/components/icon/component';
|
||||
@ -41,6 +42,15 @@ const messages = defineMessages({
|
||||
},
|
||||
});
|
||||
|
||||
const propTypes = {
|
||||
compact: PropTypes.bool.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
normalizeEmojiName: PropTypes.func.isRequired,
|
||||
actions: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
|
||||
class UserListContent extends Component {
|
||||
|
||||
/**
|
||||
@ -108,7 +118,6 @@ class UserListContent extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the dropdown is visible, if so, check if should be draw on top or bottom direction.
|
||||
*/
|
||||
@ -126,7 +135,7 @@ class UserListContent extends Component {
|
||||
|
||||
const isDropdownVisible =
|
||||
UserListContent.checkIfDropdownIsVisible(dropdownContent.offsetTop,
|
||||
dropdownContent.offsetHeight);
|
||||
dropdownContent.offsetHeight);
|
||||
|
||||
if (!isDropdownVisible) {
|
||||
const offsetPageTop =
|
||||
@ -159,19 +168,20 @@ class UserListContent extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
compact,
|
||||
compact,
|
||||
user,
|
||||
intl,
|
||||
normalizeEmojiName,
|
||||
actions,
|
||||
} = this.props;
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
isActionsOpen,
|
||||
isActionsOpen,
|
||||
dropdownVisible,
|
||||
dropdownDirection,
|
||||
dropdownOffset,
|
||||
} = this.state;
|
||||
} = this.state;
|
||||
|
||||
const userItemContentsStyle = {};
|
||||
|
||||
userItemContentsStyle[styles.userItemContentsCompact] = compact;
|
||||
@ -276,4 +286,5 @@ class UserListContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
UserListContent.propTypes = propTypes;
|
||||
export default UserListContent;
|
||||
|
Loading…
Reference in New Issue
Block a user