Skip to content

Commit

Permalink
fix: get rid of positioning wrapper for message actions box (#2246)
Browse files Browse the repository at this point in the history
### 🎯 Goal

In #2241 we've added a new positioning wrapper for `MessageActionsBox`.
That was not the greatest idea, since adding new wrappers can break
custom CSS for the users.

Having a wrapper is not necessary in this case anyway. This PR
implements the same positioning as #2241, but without additional
wrappers.
  • Loading branch information
myandrienko authored Jan 18, 2024
1 parent d07861f commit 32c0180
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 169 deletions.
26 changes: 11 additions & 15 deletions src/components/MessageActions/MessageActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,24 +134,20 @@ export const MessageActions = <
inline={inline}
setActionsBoxOpen={setActionsBoxOpen}
>
<div
<MessageActionsBox
{...attributes.popper}
className='str-chat__message-actions-box-wrapper'
getMessageActions={getMessageActions}
handleDelete={handleDelete}
handleEdit={setEditingState}
handleFlag={handleFlag}
handleMute={handleMute}
handlePin={handlePin}
isUserMuted={isMuted}
mine={isMine}
open={actionsBoxOpen}
ref={popperElementRef}
style={styles.popper}
>
<MessageActionsBox
getMessageActions={getMessageActions}
handleDelete={handleDelete}
handleEdit={setEditingState}
handleFlag={handleFlag}
handleMute={handleMute}
handlePin={handlePin}
isUserMuted={isMuted}
mine={isMine}
open={actionsBoxOpen}
/>
</div>
/>
<button
aria-expanded={actionsBoxOpen}
aria-haspopup='true'
Expand Down
238 changes: 121 additions & 117 deletions src/components/MessageActions/MessageActionsBox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { ComponentProps } from 'react';
import clsx from 'clsx';

import { MESSAGE_ACTIONS } from '../Message/utils';
Expand Down Expand Up @@ -29,123 +29,127 @@ export type MessageActionsBoxProps<
isUserMuted: () => boolean;
mine: boolean;
open: boolean;
};

const UnMemoizedMessageActionsBox = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>(
props: MessageActionsBoxProps<StreamChatGenerics>,
) => {
const {
getMessageActions,
handleDelete,
handleEdit,
handleFlag,
handleMute,
handlePin,
isUserMuted,
open = false,
} = props;

const {
CustomMessageActionsList = DefaultCustomMessageActionsList,
} = useComponentContext<StreamChatGenerics>('MessageActionsBox');
const { setQuotedMessage } = useChannelActionContext<StreamChatGenerics>('MessageActionsBox');
const { customMessageActions, message } = useMessageContext<StreamChatGenerics>(
'MessageActionsBox',
);

const { t } = useTranslationContext('MessageActionsBox');

const messageActions = getMessageActions();

const handleQuote = () => {
setQuotedMessage(message);

const elements = message.parent_id
? document.querySelectorAll('.str-chat__thread .str-chat__textarea__textarea')
: document.getElementsByClassName('str-chat__textarea__textarea');
const textarea = elements.item(0);

if (textarea instanceof HTMLTextAreaElement) {
textarea.focus();
}
};

const rootClassName = clsx('str-chat__message-actions-box', {
'str-chat__message-actions-box--open': open,
});
const buttonClassName =
'str-chat__message-actions-list-item str-chat__message-actions-list-item-button';

return (
<div className={rootClassName} data-testid='message-actions-box'>
<div aria-label='Message Options' className='str-chat__message-actions-list' role='listbox'>
<CustomMessageActionsList customMessageActions={customMessageActions} message={message} />
{messageActions.indexOf(MESSAGE_ACTIONS.quote) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleQuote}
role='option'
>
{t<string>('Reply')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.pin) > -1 && !message.parent_id && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handlePin}
role='option'
>
{!message.pinned ? t<string>('Pin') : t<string>('Unpin')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.flag) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleFlag}
role='option'
>
{t<string>('Flag')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleMute}
role='option'
>
{isUserMuted() ? t<string>('Unmute') : t<string>('Mute')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleEdit}
role='option'
>
{t<string>('Edit Message')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleDelete}
role='option'
>
{t<string>('Delete')}
</button>
)}
} & ComponentProps<'div'>;

const UnMemoizedMessageActionsBox = React.forwardRef(
<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(
props: MessageActionsBoxProps<StreamChatGenerics>,
ref: React.ForwardedRef<HTMLDivElement | null>,
) => {
const {
getMessageActions,
handleDelete,
handleEdit,
handleFlag,
handleMute,
handlePin,
isUserMuted,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
mine,
open = false,
...restDivProps
} = props;

const {
CustomMessageActionsList = DefaultCustomMessageActionsList,
} = useComponentContext<StreamChatGenerics>('MessageActionsBox');
const { setQuotedMessage } = useChannelActionContext<StreamChatGenerics>('MessageActionsBox');
const { customMessageActions, message } = useMessageContext<StreamChatGenerics>(
'MessageActionsBox',
);

const { t } = useTranslationContext('MessageActionsBox');

const messageActions = getMessageActions();

const handleQuote = () => {
setQuotedMessage(message);

const elements = message.parent_id
? document.querySelectorAll('.str-chat__thread .str-chat__textarea__textarea')
: document.getElementsByClassName('str-chat__textarea__textarea');
const textarea = elements.item(0);

if (textarea instanceof HTMLTextAreaElement) {
textarea.focus();
}
};

const rootClassName = clsx('str-chat__message-actions-box', {
'str-chat__message-actions-box--open': open,
});
const buttonClassName =
'str-chat__message-actions-list-item str-chat__message-actions-list-item-button';

return (
<div {...restDivProps} className={rootClassName} data-testid='message-actions-box' ref={ref}>
<div aria-label='Message Options' className='str-chat__message-actions-list' role='listbox'>
<CustomMessageActionsList customMessageActions={customMessageActions} message={message} />
{messageActions.indexOf(MESSAGE_ACTIONS.quote) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleQuote}
role='option'
>
{t<string>('Reply')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.pin) > -1 && !message.parent_id && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handlePin}
role='option'
>
{!message.pinned ? t<string>('Pin') : t<string>('Unpin')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.flag) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleFlag}
role='option'
>
{t<string>('Flag')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleMute}
role='option'
>
{isUserMuted() ? t<string>('Unmute') : t<string>('Mute')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleEdit}
role='option'
>
{t<string>('Edit Message')}
</button>
)}
{messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1 && (
<button
aria-selected='false'
className={buttonClassName}
onClick={handleDelete}
role='option'
>
{t<string>('Delete')}
</button>
)}
</div>
</div>
</div>
);
};
);
},
);

/**
* A popup box that displays the available actions on a message, such as edit, delete, pin, etc.
Expand Down
41 changes: 4 additions & 37 deletions src/components/MessageActions/__tests__/MessageActions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,7 @@ describe('<MessageActions /> component', () => {
data-testid="message-actions"
onClick={[Function]}
>
<div
className="str-chat__message-actions-box-wrapper"
style={
Object {
"left": "0",
"position": "absolute",
"top": "0",
}
}
>
<div />
</div>
<div />
<button
aria-expanded={false}
aria-haspopup="true"
Expand Down Expand Up @@ -117,7 +106,7 @@ describe('<MessageActions /> component', () => {

it('should open message actions box on click', () => {
const { getByTestId } = renderMessageActions();
expect(MessageActionsBoxMock).toHaveBeenLastCalledWith(
expect(MessageActionsBoxMock).toHaveBeenCalledWith(
expect.objectContaining({ open: false }),
{},
);
Expand Down Expand Up @@ -254,18 +243,7 @@ describe('<MessageActions /> component', () => {
data-testid="message-actions"
onClick={[Function]}
>
<div
className="str-chat__message-actions-box-wrapper"
style={
Object {
"left": "0",
"position": "absolute",
"top": "0",
}
}
>
<div />
</div>
<div />
<button
aria-expanded={false}
aria-haspopup="true"
Expand Down Expand Up @@ -305,18 +283,7 @@ describe('<MessageActions /> component', () => {
data-testid="message-actions"
onClick={[Function]}
>
<div
className="str-chat__message-actions-box-wrapper"
style={
Object {
"left": "0",
"position": "absolute",
"top": "0",
}
}
>
<div />
</div>
<div />
<button
aria-expanded={false}
aria-haspopup="true"
Expand Down

0 comments on commit 32c0180

Please sign in to comment.