[MM-61641]: Ensure implicit list markup is avoided (#29787)
* MM-61641: Update post options to use semantic list elements * MM-61641: Fix lint * Apply suggestions from code review --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3b69d36643
Коммит
a672aa5be5
@@ -59,7 +59,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const PostOptions = (props: Props): JSX.Element => {
|
const PostOptions = (props: Props): JSX.Element => {
|
||||||
const dotMenuRef = useRef<HTMLDivElement>(null);
|
const dotMenuRef = useRef<HTMLUListElement>(null);
|
||||||
|
|
||||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||||
const [showDotMenu, setShowDotMenu] = useState(false);
|
const [showDotMenu, setShowDotMenu] = useState(false);
|
||||||
@@ -120,12 +120,14 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
let commentIcon;
|
let commentIcon;
|
||||||
if (showCommentIcon) {
|
if (showCommentIcon) {
|
||||||
commentIcon = (
|
commentIcon = (
|
||||||
<CommentIcon
|
<li>
|
||||||
handleCommentClick={props.handleCommentClick}
|
<CommentIcon
|
||||||
postId={post.id}
|
handleCommentClick={props.handleCommentClick}
|
||||||
extraClass={commentIconExtraClass}
|
postId={post.id}
|
||||||
commentCount={props.collapsedThreadsEnabled ? 0 : props.replyCount}
|
extraClass={commentIconExtraClass}
|
||||||
/>
|
commentCount={props.collapsedThreadsEnabled ? 0 : props.replyCount}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,38 +154,44 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
let postReaction;
|
let postReaction;
|
||||||
if (showReactionIcon) {
|
if (showReactionIcon) {
|
||||||
postReaction = (
|
postReaction = (
|
||||||
<PostReaction
|
<li>
|
||||||
channelId={post.channel_id}
|
<PostReaction
|
||||||
location={props.location}
|
channelId={post.channel_id}
|
||||||
postId={post.id}
|
location={props.location}
|
||||||
teamId={props.teamId}
|
postId={post.id}
|
||||||
getDotMenuRef={getDotMenuRef}
|
teamId={props.teamId}
|
||||||
showEmojiPicker={showEmojiPicker}
|
getDotMenuRef={getDotMenuRef}
|
||||||
toggleEmojiPicker={toggleEmojiPicker}
|
showEmojiPicker={showEmojiPicker}
|
||||||
/>
|
toggleEmojiPicker={toggleEmojiPicker}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let flagIcon: ReactNode = null;
|
let flagIcon: ReactNode = null;
|
||||||
if (!isMobileView && (!isEphemeral && !post.failed && !systemMessage)) {
|
if (!isMobileView && (!isEphemeral && !post.failed && !systemMessage)) {
|
||||||
flagIcon = (
|
flagIcon = (
|
||||||
<PostFlagIcon
|
<li>
|
||||||
location={props.location}
|
<PostFlagIcon
|
||||||
postId={post.id}
|
location={props.location}
|
||||||
isFlagged={props.isFlagged}
|
postId={post.id}
|
||||||
/>
|
isFlagged={props.isFlagged}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action menus
|
// Action menus
|
||||||
const showActionsMenuIcon = props.shouldShowActionsMenu && (isMobileView || hoverLocal);
|
const showActionsMenuIcon = props.shouldShowActionsMenu && (isMobileView || hoverLocal);
|
||||||
const actionsMenu = showActionsMenuIcon && (
|
const actionsMenu = showActionsMenuIcon && (
|
||||||
<ActionsMenu
|
<li>
|
||||||
post={post}
|
<ActionsMenu
|
||||||
location={props.location}
|
post={post}
|
||||||
handleDropdownOpened={handleActionsMenuOpened}
|
location={props.location}
|
||||||
isMenuOpen={showActionsMenu}
|
handleDropdownOpened={handleActionsMenuOpened}
|
||||||
/>
|
isMenuOpen={showActionsMenu}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
let pluginItems: ReactNode = null;
|
let pluginItems: ReactNode = null;
|
||||||
@@ -193,10 +201,11 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
if (item.component) {
|
if (item.component) {
|
||||||
const Component = item.component;
|
const Component = item.component;
|
||||||
return (
|
return (
|
||||||
<Component
|
<li key={item.id}>
|
||||||
post={props.post}
|
<Component
|
||||||
key={item.id}
|
post={props.post}
|
||||||
/>
|
/>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -204,17 +213,19 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dotMenu = (
|
const dotMenu = (
|
||||||
<DotMenu
|
<li>
|
||||||
post={props.post}
|
<DotMenu
|
||||||
location={props.location}
|
post={props.post}
|
||||||
isFlagged={props.isFlagged}
|
location={props.location}
|
||||||
handleDropdownOpened={handleDotMenuOpened}
|
isFlagged={props.isFlagged}
|
||||||
handleCommentClick={props.handleCommentClick}
|
handleDropdownOpened={handleDotMenuOpened}
|
||||||
handleAddReactionClick={toggleEmojiPicker}
|
handleCommentClick={props.handleCommentClick}
|
||||||
isReadOnly={isReadOnly || channelIsArchived}
|
handleAddReactionClick={toggleEmojiPicker}
|
||||||
isMenuOpen={showDotMenu}
|
isReadOnly={isReadOnly || channelIsArchived}
|
||||||
enableEmojiPicker={props.enableEmojiPicker}
|
isMenuOpen={showDotMenu}
|
||||||
/>
|
enableEmojiPicker={props.enableEmojiPicker}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Build post options
|
// Build post options
|
||||||
@@ -235,10 +246,11 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
} else if (props.location === Locations.SEARCH) {
|
} else if (props.location === Locations.SEARCH) {
|
||||||
const hasCRTFooter = props.collapsedThreadsEnabled && !post.root_id && (post.reply_count > 0 || post.is_following);
|
const hasCRTFooter = props.collapsedThreadsEnabled && !post.root_id && (post.reply_count > 0 || post.is_following);
|
||||||
options = (
|
options = (
|
||||||
<div className='col__controls post-menu'>
|
<ul className='col__controls post-menu'>
|
||||||
{dotMenu}
|
{dotMenu}
|
||||||
{flagIcon}
|
{flagIcon}
|
||||||
{props.canReply && !hasCRTFooter &&
|
{props.canReply && !hasCRTFooter &&
|
||||||
|
<li>
|
||||||
<CommentIcon
|
<CommentIcon
|
||||||
location={props.location}
|
location={props.location}
|
||||||
handleCommentClick={props.handleCommentClick}
|
handleCommentClick={props.handleCommentClick}
|
||||||
@@ -247,22 +259,25 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
searchStyle={'search-item__comment'}
|
searchStyle={'search-item__comment'}
|
||||||
extraClass={props.replyCount ? 'icon--visible' : ''}
|
extraClass={props.replyCount ? 'icon--visible' : ''}
|
||||||
/>
|
/>
|
||||||
|
</li>
|
||||||
}
|
}
|
||||||
<a
|
<li>
|
||||||
href='#'
|
<a
|
||||||
onClick={props.handleJumpClick}
|
href='#'
|
||||||
className='search-item__jump'
|
onClick={props.handleJumpClick}
|
||||||
>
|
className='search-item__jump'
|
||||||
<FormattedMessage
|
>
|
||||||
id='search_item.jump'
|
<FormattedMessage
|
||||||
defaultMessage='Jump'
|
id='search_item.jump'
|
||||||
/>
|
defaultMessage='Jump'
|
||||||
</a>
|
/>
|
||||||
</div>
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
);
|
);
|
||||||
} else if (!props.isPostBeingEdited) {
|
} else if (!props.isPostBeingEdited) {
|
||||||
options = (
|
options = (
|
||||||
<div
|
<ul
|
||||||
ref={dotMenuRef}
|
ref={dotMenuRef}
|
||||||
data-testid={`post-menu-${props.post.id}`}
|
data-testid={`post-menu-${props.post.id}`}
|
||||||
className={classnames('col post-menu', {'post-menu--position': !hoverLocal && showCommentIcon})}
|
className={classnames('col post-menu', {'post-menu--position': !hoverLocal && showCommentIcon})}
|
||||||
@@ -275,7 +290,7 @@ const PostOptions = (props: Props): JSX.Element => {
|
|||||||
{actionsMenu}
|
{actionsMenu}
|
||||||
{commentIcon}
|
{commentIcon}
|
||||||
{(collapsedThreadsEnabled || showRecentlyUsedReactions) && dotMenu}
|
{(collapsedThreadsEnabled || showRecentlyUsedReactions) && dotMenu}
|
||||||
</div>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export type Props = WrappedComponentProps & {
|
|||||||
channelId?: string;
|
channelId?: string;
|
||||||
postId: string;
|
postId: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
getDotMenuRef: () => HTMLDivElement | null;
|
getDotMenuRef: () => HTMLUListElement | null;
|
||||||
location?: keyof typeof Locations;
|
location?: keyof typeof Locations;
|
||||||
showEmojiPicker: boolean;
|
showEmojiPicker: boolean;
|
||||||
toggleEmojiPicker: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
toggleEmojiPicker: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||||
|
|||||||
@@ -96,13 +96,13 @@ export default class PostRecentReactions extends React.PureComponent<Props, Stat
|
|||||||
emoji={getEmojiName(emoji)}
|
emoji={getEmojiName(emoji)}
|
||||||
isEmojiLarge={true}
|
isEmojiLarge={true}
|
||||||
>
|
>
|
||||||
<div>
|
<li>
|
||||||
<EmojiItem
|
<EmojiItem
|
||||||
emoji={emoji}
|
emoji={emoji}
|
||||||
onItemClick={this.handleToggleEmoji}
|
onItemClick={this.handleToggleEmoji}
|
||||||
order={n}
|
order={n}
|
||||||
/>
|
/>
|
||||||
</div>
|
</li>
|
||||||
</WithTooltip>
|
</WithTooltip>
|
||||||
</ChannelPermissionGate>
|
</ChannelPermissionGate>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
list-style: none;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user