Port to new Slot property passthrough

This commit is contained in:
Chi Vinh Le
2018-03-09 17:34:05 +01:00
parent 1b55684a73
commit 44b8809db1
23 changed files with 228 additions and 166 deletions
@@ -63,6 +63,7 @@ export default class Embed extends React.Component {
} = this.props;
const hasHighlightedComment = !!commentId;
const popupUrl = `login?parentUrl=${encodeURIComponent(parentUrl)}`;
const slotPassthrough = { data, root };
return (
<div
@@ -84,7 +85,7 @@ export default class Embed extends React.Component {
/>
</IfSlotIsNotEmpty>
<Slot data={data} queryData={{ root }} fill="embed" />
<Slot passthrough={slotPassthrough} fill="embed" />
<ExtendableTabPanel
className="talk-embed-stream-tab-bar"
@@ -94,8 +95,7 @@ export default class Embed extends React.Component {
tabSlot="embedStreamTabs"
tabSlotPrepend="embedStreamTabsPrepend"
tabPaneSlot="embedStreamTabPanes"
slotProps={{ data }}
queryData={{ root }}
slotPassthrough={slotPassthrough}
tabs={this.getTabs()}
tabPanes={[
<TabPane
@@ -53,8 +53,7 @@ class ExtendableTabPanelContainer extends React.Component {
return plugins.getSlotElements(
slot,
props.reduxState,
props.slotProps,
props.queryData
props.slotPassthrough
);
}
@@ -132,8 +131,7 @@ ExtendableTabPanelContainer.propTypes = {
tabSlot: PropTypes.string.isRequired,
tabSlotPrepend: PropTypes.string.isRequired,
tabPaneSlot: PropTypes.string.isRequired,
slotProps: PropTypes.object.isRequired,
queryData: PropTypes.object,
slotPassthrough: PropTypes.object,
className: PropTypes.string,
sub: PropTypes.bool,
loading: PropTypes.bool,
@@ -25,9 +25,9 @@ class Settings extends React.Component {
onQuestionBoxContentChange,
canSave,
onApply,
slotProps,
queryData,
slotPassthrough,
} = this.props;
return (
<div className={styles.wrapper}>
<div className={styles.container}>
@@ -77,7 +77,7 @@ class Settings extends React.Component {
</div>
)}
</Configuration>
<Slot fill="streamSettings" queryData={queryData} {...slotProps} />
<Slot fill="streamSettings" passthrough={slotPassthrough} />
</div>
</div>
);
@@ -85,8 +85,7 @@ class Settings extends React.Component {
}
Settings.propTypes = {
queryData: PropTypes.object.isRequired,
slotProps: PropTypes.object.isRequired,
slotPassthrough: PropTypes.object.isRequired,
settings: PropTypes.object.isRequired,
canSave: PropTypes.bool.isRequired,
onToggleModeration: PropTypes.func.isRequired,
@@ -63,11 +63,20 @@ class SettingsContainer extends React.Component {
errors,
updatePending,
} = this.props;
const slotPassthrough = {
root,
asset,
settings: mergedSettings,
data,
updatePending,
errors,
};
return (
<Settings
settings={mergedSettings}
queryData={{ root, asset, settings: mergedSettings }}
slotProps={{ data, updatePending, errors }}
slotPassthrough={slotPassthrough}
savePending={this.savePending}
onToggleModeration={this.toggleModeration}
onTogglePremodLinks={this.togglePremodLinks}
@@ -24,7 +24,7 @@ class Comment extends React.Component {
render() {
const { comment, data, root } = this.props;
const reactionCount = getTotalReactionsCount(comment.action_summaries);
const queryData = { root, comment, asset: comment.asset };
const slotPassthrough = { data, root, comment, asset: comment.asset };
return (
<div className={styles.myComment}>
@@ -33,8 +33,7 @@ class Comment extends React.Component {
fill="commentContent"
defaultComponent={CommentContent}
className={cn(styles.commentBody, 'my-comment-body')}
data={data}
queryData={queryData}
passthrough={slotPassthrough}
/>
<div className={cn(styles.commentSummary, 'comment-summary')}>
<span
@@ -98,9 +97,10 @@ class Comment extends React.Component {
fill="historyCommentTimestamp"
defaultComponent={CommentTimestamp}
className={'talk-history-comment-published-date'}
created_at={comment.created_at}
data={data}
queryData={queryData}
passthrough={{
created_at: comment.created_at,
...slotPassthrough,
}}
inline
/>
</li>
@@ -14,36 +14,41 @@ const Profile = ({
root,
activeTab,
setActiveTab,
}) => (
<div className="talk-my-profile talk-profile-container">
<div className={styles.userInfo}>
<h2 className={styles.username}>{username}</h2>
{emailAddress ? <p className={styles.email}>{emailAddress}</p> : null}
}) => {
const slotPassthrough = {
data,
root,
};
return (
<div className="talk-my-profile talk-profile-container">
<div className={styles.userInfo}>
<h2 className={styles.username}>{username}</h2>
{emailAddress ? <p className={styles.email}>{emailAddress}</p> : null}
</div>
<Slot fill="profileSections" passthrough={slotPassthrough} />
<ExtendableTabPanel
activeTab={activeTab}
setActiveTab={setActiveTab}
fallbackTab="comments"
tabSlot="profileTabs"
tabSlotPrepend="profileTabsPrepend"
tabPaneSlot="profileTabPanes"
slotPassthrough={slotPassthrough}
tabs={[
<Tab key="comments" tabId="comments">
{t('framework.my_comments')}
</Tab>,
]}
tabPanes={[
<TabPane key="comments" tabId="comments">
<CommentHistory data={data} root={root} />
</TabPane>,
]}
sub
/>
</div>
<Slot fill="profileSections" data={data} queryData={{ root }} />
<ExtendableTabPanel
activeTab={activeTab}
setActiveTab={setActiveTab}
fallbackTab="comments"
tabSlot="profileTabs"
tabSlotPrepend="profileTabsPrepend"
tabPaneSlot="profileTabPanes"
slotProps={{ data }}
queryData={{ root }}
tabs={[
<Tab key="comments" tabId="comments">
{t('framework.my_comments')}
</Tab>,
]}
tabPanes={[
<TabPane key="comments" tabId="comments">
<CommentHistory data={data} root={root} />
</TabPane>,
]}
sub
/>
</div>
);
);
};
Profile.propTypes = {
username: PropTypes.string,
@@ -535,12 +535,9 @@ export default class Comment extends React.Component {
);
// props that are passed down the slots.
const slotProps = {
const slotPassthrough = {
data,
depth,
};
const queryData = {
root,
asset,
comment,
@@ -551,8 +548,7 @@ export default class Comment extends React.Component {
<Slot
className={cn(styles.commentAvatar, 'talk-stream-comment-avatar')}
fill="commentAvatar"
{...slotProps}
queryData={queryData}
passthrough={slotPassthrough}
inline
/>
@@ -573,8 +569,7 @@ export default class Comment extends React.Component {
className={cn(styles.username, 'talk-stream-comment-user-name')}
fill="commentAuthorName"
defaultComponent={CommentAuthorName}
queryData={queryData}
{...slotProps}
passthrough={slotPassthrough}
/>
<div
@@ -591,8 +586,7 @@ export default class Comment extends React.Component {
'talk-stream-comment-author-tags'
)}
fill="commentAuthorTags"
queryData={queryData}
{...slotProps}
passthrough={slotPassthrough}
inline
/>
</div>
@@ -607,9 +601,10 @@ export default class Comment extends React.Component {
fill="commentTimestamp"
defaultComponent={CommentTimestamp}
className={'talk-stream-comment-published-date'}
created_at={comment.created_at}
queryData={queryData}
{...slotProps}
passthrough={{
created_at: comment.created_at,
...slotPassthrough,
}}
/>
{comment.editing && comment.editing.edited ? (
<span>
@@ -624,8 +619,7 @@ export default class Comment extends React.Component {
<Slot
className={styles.commentInfoBar}
fill="commentInfoBar"
{...slotProps}
queryData={queryData}
passthrough={slotPassthrough}
/>
{isActive &&
@@ -665,9 +659,8 @@ export default class Comment extends React.Component {
fill="commentContent"
className="talk-stream-comment-content"
defaultComponent={CommentContent}
{...slotProps}
queryData={queryData}
size={1}
passthrough={slotPassthrough}
/>
</div>
)}
@@ -678,8 +671,7 @@ export default class Comment extends React.Component {
<div className="talk-embed-stream-comment-actions-container-left commentActionsLeft comment__action-container">
<Slot
fill="commentReactions"
{...slotProps}
queryData={queryData}
passthrough={slotPassthrough}
inline
/>
@@ -696,9 +688,7 @@ export default class Comment extends React.Component {
<div className="talk-embed-stream-comment-actions-container-right commentActionsRight comment__action-container">
<Slot
fill="commentActions"
wrapperComponent={ActionButton}
{...slotProps}
queryData={queryData}
passthrough={slotPassthrough}
inline
/>
<ActionButton>
@@ -43,20 +43,13 @@ export default class DraftArea extends React.Component {
charCountEnable,
maxCharCount,
onChange,
queryData,
isReply,
registerHook,
unregisterHook,
root,
comment,
} = this.props;
const tASettings = {
value,
placeholder,
id,
onChange,
rows,
disabled,
isReply,
};
return (
<div>
<div
@@ -69,10 +62,19 @@ export default class DraftArea extends React.Component {
fill="draftArea"
defaultComponent={DraftAreaContent}
className={styles.content}
queryData={queryData}
registerHook={this.props.registerHook}
unregisterHook={this.props.unregisterHook}
{...tASettings}
passthrough={{
root,
comment,
registerHook,
unregisterHook,
value,
placeholder,
id,
onChange,
rows,
disabled,
isReply,
}}
/>
<Slot fill="commentInputArea" />
</div>
@@ -96,7 +98,8 @@ DraftArea.propTypes = {
onChange: PropTypes.func,
disabled: PropTypes.bool,
rows: PropTypes.number,
queryData: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
comment: PropTypes.object,
registerHook: PropTypes.func,
unregisterHook: PropTypes.func,
isReply: PropTypes.bool,
@@ -147,15 +147,14 @@ class Stream extends React.Component {
loading,
} = this.props;
const slotProps = { data };
const slotQueryData = { root, asset };
const slotPassthrough = { data, root, asset };
// `key` of `ExtendableTabPanel` depends on sorting so that we always reset
// the state when changing sorting.
return (
<div className={cn('talk-stream-tab-container', styles.tabContainer)}>
<div className={cn('talk-stream-filter-wrapper', styles.filterWrapper)}>
<Slot fill="streamFilter" queryData={slotQueryData} {...slotProps} />
<Slot fill="streamFilter" passthrough={slotPassthrough} />
</div>
<ExtendableTabPanel
@@ -166,8 +165,7 @@ class Stream extends React.Component {
tabSlot="streamTabs"
tabSlotPrepend="streamTabsPrepend"
tabPaneSlot="streamTabPanes"
slotProps={slotProps}
queryData={slotQueryData}
slotPassthrough={slotPassthrough}
loading={loading}
tabs={
<Tab tabId={'all'} key="all">
@@ -243,13 +241,13 @@ class Stream extends React.Component {
!changedUsername &&
!highlightedComment) ||
keepCommentBox);
const slotProps = { data };
const slotQueryData = { root, asset };
if (highlightedComment === null) {
return <StreamError>{t('stream.comment_not_found')}</StreamError>;
}
const slotPassthrough = { data, root, asset };
return (
<div id="stream" className={styles.root}>
{open ? (
@@ -263,11 +261,7 @@ class Stream extends React.Component {
content={asset.settings.questionBoxContent}
icon={asset.settings.questionBoxIcon}
>
<Slot
fill="streamQuestionArea"
queryData={slotQueryData}
{...slotProps}
/>
<Slot fill="streamQuestionArea" passthrough={slotPassthrough} />
</QuestionBox>
)}
{!banned &&
@@ -304,7 +298,7 @@ class Stream extends React.Component {
<p>{asset.settings.closedMessage}</p>
)}
<Slot fill="stream" queryData={slotQueryData} {...slotProps} />
<Slot fill="stream" passthrough={slotPassthrough} />
{currentUser && (
<ModerationLink
@@ -190,9 +190,11 @@ class CommentBox extends React.Component {
buttonContainerStart={
<Slot
fill="commentInputDetailArea"
registerHook={this.registerHook}
unregisterHook={this.unregisterHook}
isReply={isReply}
passthrough={{
registerHook: this.registerHook,
unregisterHook: this.unregisterHook,
isReply,
}}
inline
/>
}
@@ -42,11 +42,10 @@ class DraftAreaContainer extends React.Component {
}
render() {
const queryData = { comment: this.props.comment, root: this.props.root };
return (
<DraftArea
queryData={queryData}
root={this.props.root}
comment={this.props.comment}
value={this.props.value}
placeholder={this.props.placeholder}
id={this.props.id}
@@ -1,14 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import Menu from './Menu';
import styles from './AuthorName.css';
import { ClickOutside } from 'plugin-api/beta/client/components';
import cn from 'classnames';
export default ({
data,
root,
asset,
comment,
const AuthorName = ({
slotPassthrough,
username,
contentSlot,
menuVisible,
toggleMenu,
@@ -21,18 +20,23 @@ export default ({
className={cn(styles.button, 'talk-plugin-author-menu-button')}
onClick={toggleMenu}
>
<span className={styles.name}>{comment.user.username}</span>
<span className={styles.name}>{username}</span>
</button>
{menuVisible && (
<Menu
data={data}
root={root}
asset={asset}
comment={comment}
contentSlot={contentSlot}
/>
<Menu slotPassthrough={slotPassthrough} contentSlot={contentSlot} />
)}
</div>
</ClickOutside>
);
};
AuthorName.propTypes = {
slotPassthrough: PropTypes.object.isRequired,
username: PropTypes.string.isRequired,
menuVisible: PropTypes.bool.isRequired,
toggleMenu: PropTypes.func.isRequired,
hideMenu: PropTypes.func.isRequired,
contentSlot: PropTypes.string,
};
export default AuthorName;
@@ -1,17 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Menu.css';
import { Slot } from 'plugin-api/beta/client/components';
import cn from 'classnames';
export default ({ data, root, asset, comment, contentSlot }) => {
const Menu = ({ slotPassthrough, contentSlot }) => {
if (contentSlot) {
return (
<div className={cn(styles.menu, 'talk-plugin-author-menu-popup')}>
<Slot
fill={contentSlot}
data={data}
queryData={{ asset, root, comment }}
/>
<Slot fill={contentSlot} passthrough={slotPassthrough} />
</div>
);
}
@@ -21,15 +18,20 @@ export default ({ data, root, asset, comment, contentSlot }) => {
<Slot
className={cn('talk-plugin-author-menu-infos')}
fill={'authorMenuInfos'}
data={data}
queryData={{ asset, root, comment }}
passthrough={slotPassthrough}
/>
<Slot
className={cn(styles.actions, 'talk-plugin-author-menu-actions')}
fill={'authorMenuActions'}
data={data}
queryData={{ asset, root, comment }}
passthrough={slotPassthrough}
/>
</div>
);
};
Menu.propTypes = {
slotPassthrough: PropTypes.object.isRequired,
contentSlot: PropTypes.string,
};
export default Menu;
@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect, withFragments } from 'plugin-api/beta/client/hocs';
import { bindActionCreators } from 'redux';
import AuthorName from '../components/AuthorName';
@@ -47,21 +48,41 @@ class AuthorNameContainer extends React.Component {
};
render() {
const {
data,
root,
asset,
comment,
contentSlot,
showMenuForComment,
} = this.props;
const slotPassthrough = { data, root, asset, comment };
return (
<AuthorName
data={this.props.data}
root={this.props.root}
asset={this.props.asset}
comment={this.props.comment}
contentSlot={this.props.contentSlot}
menuVisible={this.props.showMenuForComment === this.props.comment.id}
username={comment.user.username}
contentSlot={contentSlot}
menuVisible={showMenuForComment === comment.id}
toggleMenu={this.toggleMenu}
hideMenu={this.hideMenu}
slotPassthrough={slotPassthrough}
/>
);
}
}
AuthorNameContainer.propTypes = {
data: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
asset: PropTypes.object.isRequired,
comment: PropTypes.object.isRequired,
contentSlot: PropTypes.string,
showMenuForComment: PropTypes.string,
openMenu: PropTypes.func.isRequired,
closeMenu: PropTypes.func.isRequired,
};
const slots = ['authorMenuInfos', 'authorMenuActions'];
const mapStateToProps = ({ talkPluginAuthorMenu: state }) => ({
@@ -19,7 +19,7 @@ class Comment extends React.Component {
render() {
const { comment, asset, root, data } = this.props;
const queryData = { comment, asset, root };
const slotPassthrough = { data, comment, asset, root };
return (
<div className={cn(styles.root, `${pluginName}-comment`)}>
<Slot
@@ -27,8 +27,7 @@ class Comment extends React.Component {
className={cn(styles.quote, `${pluginName}-comment-body`)}
fill="commentContent"
defaultComponent={CommentContent}
data={data}
queryData={queryData}
passthrough={slotPassthrough}
/>
<div className={cn(`${pluginName}-comment-username-box`)}>
@@ -36,8 +35,7 @@ class Comment extends React.Component {
className={cn(styles.username, `${pluginName}-comment-username`)}
fill="commentAuthorName"
defaultComponent={CommentAuthorName}
queryData={queryData}
data={data}
passthrough={slotPassthrough}
inline
/>
@@ -45,9 +43,7 @@ class Comment extends React.Component {
fill="commentTimestamp"
defaultComponent={CommentTimestamp}
className={cn(styles.timestamp, `${pluginName}-comment-timestamp`)}
created_at={comment.created_at}
data={data}
queryData={queryData}
passthrough={{ created_at: comment.created_at, ...slotPassthrough }}
inline
/>
</div>
@@ -62,10 +58,7 @@ class Comment extends React.Component {
>
<Slot
fill="commentReactions"
root={root}
data={data}
comment={comment}
asset={asset}
passthrough={slotPassthrough}
inline
/>
@@ -26,7 +26,8 @@ class FlagDetails extends Component {
}, {});
const reasons = Object.keys(summaries);
const queryData = {
const slotPassthrough = {
data,
root,
comment,
};
@@ -52,12 +53,11 @@ class FlagDetails extends Component {
{more && (
<IfSlotIsNotEmpty
slot="adminCommentMoreFlagDetails"
queryData={queryData}
passthrough={slotPassthrough}
>
<Slot
fill="adminCommentMoreFlagDetails"
data={data}
queryData={queryData}
passthrough={slotPassthrough}
/>
</IfSlotIsNotEmpty>
)}
@@ -22,6 +22,12 @@ export default class ModerationActions extends React.Component {
hideMenu,
} = this.props;
const slotPassthrough = {
comment,
asset,
data,
};
return (
<ClickOutside onClickOutside={hideMenu}>
<div
@@ -45,8 +51,7 @@ export default class ModerationActions extends React.Component {
<Slot
className="talk-plugin-modetarion-actions-slot"
fill="moderationActions"
queryData={{ comment, asset }}
data={data}
passthrough={slotPassthrough}
/>
<ApproveCommentAction comment={comment} hideMenu={hideMenu} />
<RejectCommentAction comment={comment} hideMenu={hideMenu} />
@@ -29,8 +29,15 @@ class Settings extends React.Component {
email,
} = this.props;
const slotPassthrough = {
root,
setTurnOffInputFragment,
updateNotificationSettings,
disabled: needEmailVerification,
};
return (
<IfSlotIsNotEmpty slot="notificationSettings" queryData={{ root }}>
<IfSlotIsNotEmpty slot="notificationSettings" passthrough={slotPassthrough}>
<div className={styles.root}>
<h3>{t('talk-plugin-notifications.settings_title')}</h3>
{needEmailVerification && <EmailVerificationBanner email={email} />}
@@ -45,11 +52,8 @@ class Settings extends React.Component {
<Slot
className={styles.notifcationSettingsSlot}
fill="notificationSettings"
queryData={{ root }}
childFactory={this.childFactory}
setTurnOffInputFragment={setTurnOffInputFragment}
updateNotificationSettings={updateNotificationSettings}
disabled={needEmailVerification}
passthrough={slotPassthrough}
/>
<BareButton
className={styles.turnOffButton}
@@ -5,9 +5,10 @@ import { Slot } from 'plugin-api/beta/client/components';
class TabPane extends React.Component {
render() {
const { data, root } = this.props;
const slotPassthrough = { data, root };
return (
<div>
<Slot fill="profileSettings" data={data} queryData={{ root }} />
<Slot fill="profileSettings" passthrough={slotPassthrough} />
</div>
);
}
@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Category.css';
import { Slot } from 'plugin-api/beta/client/components';
@@ -8,7 +9,7 @@ const childFactory = child => (
</li>
);
const ViewingOptions = ({ slot, title, data, asset, root }) => {
const Category = ({ slot, title, slotPassthrough }) => {
return (
<div className={styles.root}>
<div className={styles.title}>{title}</div>
@@ -17,11 +18,16 @@ const ViewingOptions = ({ slot, title, data, asset, root }) => {
childFactory={childFactory}
className={styles.list}
component={'ul'}
data={data}
queryData={{ asset, root }}
passthrough={slotPassthrough}
/>
</div>
);
};
export default ViewingOptions;
Category.propTypes = {
slot: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
slotPassthrough: PropTypes.object.isRequired,
};
export default Category;
@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';
import styles from './Menu.css';
import { capitalize } from 'plugin-api/beta/client/utils';
@@ -13,16 +14,19 @@ class Menu extends React.Component {
};
render() {
const { slotPassthrough } = this.props;
return (
<div className={cn([styles.menu, 'talk-plugin-viewing-options-menu'])}>
{Object.keys(this.categories).map(category => (
<IfSlotIsNotEmpty
slot={`viewingOptions${capitalize(category)}`}
key={category}
passthrough={slotPassthrough}
>
<Category
slot={`viewingOptions${capitalize(category)}`}
title={this.categories[category]}
passthrough={slotPassthrough}
/>
</IfSlotIsNotEmpty>
))}
@@ -31,4 +35,8 @@ class Menu extends React.Component {
}
}
Menu.propTypes = {
slotPassthrough: PropTypes.object.isRequired,
};
export default Menu;
@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';
import styles from './ViewingOptions.css';
import { t } from 'plugin-api/beta/client/services';
@@ -24,7 +25,7 @@ class ViewingOptions extends React.Component {
};
render() {
const { open, data, root, asset } = this.props;
const { open, slotPassthrough } = this.props;
return (
<ClickOutside onClickOutside={this.handleClickOutside}>
<div className={cn([styles.root, 'talk-plugin-viewing-options'])}>
@@ -41,11 +42,18 @@ class ViewingOptions extends React.Component {
)}
</button>
</div>
{open && <Menu data={data} root={root} asset={asset} />}
{open && <Menu slotPassthrough={slotPassthrough} />}
</div>
</ClickOutside>
);
}
}
ViewingOptions.propTypes = {
slotPassthrough: PropTypes.object.isRequired,
open: PropTypes.bool.isRequired,
openMenu: PropTypes.func.isRequired,
closeMenu: PropTypes.func.isRequired,
};
export default ViewingOptions;
@@ -4,6 +4,7 @@ import ViewingOptions from '../components/ViewingOptions';
import { openMenu, closeMenu } from '../actions';
import { compose, gql } from 'react-apollo';
import { getSlotFragmentSpreads } from 'plugin-api/beta/client/utils';
import { mapProps } from 'recompose';
const slots = ['viewingOptionsSort', 'viewingOptionsFilter'];
@@ -29,7 +30,17 @@ const withViewingOptionsFragments = withFragments({
const enhance = compose(
connect(mapStateToProps, mapDispatchToProps),
withViewingOptionsFragments
withViewingOptionsFragments,
mapProps(({ root, asset, data, open, openMenu, closeMenu }) => ({
slotPassthrough: {
data,
root,
asset,
},
open,
openMenu,
closeMenu,
}))
);
export default enhance(ViewingOptions);