Fix misalignment in quick actions item (#9000)

* Fix missalign in quick actions item

* Fix flaky test

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Daniel Espino García 2025-07-21 18:39:37 +02:00 committed by GitHub
parent 4444826147
commit 19a258c755
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 69 additions and 13 deletions

View file

@ -22,6 +22,7 @@ const serverUrl = 'baseHandler.test.com';
const mockPlaybookList = TestHelper.createPlaybookRuns(1, 1, 1);
const mockPlaybookRun = mockPlaybookList[0];
mockPlaybookRun.update_at = 1; // Set the update_at to a value that is before the current time to avoid flakiness
const channelId = mockPlaybookRun.channel_id;
const playbookRunId = mockPlaybookRun.id;

View file

@ -27,6 +27,7 @@ describe('PlaybookRunsOption', () => {
function getBaseProps(): ComponentProps<typeof PlaybookRunsOption> {
return {
channelId,
location: 'channel_actions',
};
}

View file

@ -1,10 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import React, {type ComponentProps} from 'react';
import {Platform} from 'react-native';
import OptionItem from '@components/option_item';
import SlideUpPanelItem from '@components/slide_up_panel_item';
import {goToPlaybookRuns} from '@playbooks/screens/navigation';
import {dismissBottomSheet} from '@screens/navigation';
import {renderWithIntl, waitFor} from '@test/intl-test-helper';
@ -14,17 +15,24 @@ import PlaybookRunsOption from './playbook_runs_option';
jest.mock('@components/option_item');
jest.mocked(OptionItem).mockImplementation((props) => React.createElement('OptionItem', {testID: 'option-item', ...props}));
jest.mock('@components/slide_up_panel_item');
jest.mocked(SlideUpPanelItem).mockImplementation((props) => React.createElement('SlideUpPanelItem', {testID: 'slide-up-panel-item', ...props}));
jest.mock('@playbooks/screens/navigation');
describe('PlaybookRunsOption', () => {
const baseProps = {
channelId: 'channel-id',
playbooksActiveRuns: 3,
channelName: 'channel-name',
};
function getBaseProps(): ComponentProps<typeof PlaybookRunsOption> {
return {
channelId: 'channel-id',
playbooksActiveRuns: 3,
channelName: 'channel-name',
location: 'channel_actions',
};
}
it('renders correctly', () => {
const {getByTestId} = renderWithIntl(<PlaybookRunsOption {...baseProps}/>);
const props = getBaseProps();
const {getByTestId, queryByTestId, rerender} = renderWithIntl(<PlaybookRunsOption {...props}/>);
const optionItem = getByTestId('option-item');
expect(optionItem).toBeTruthy();
@ -33,19 +41,36 @@ describe('PlaybookRunsOption', () => {
expect(optionItem.props.icon).toBe('product-playbooks');
expect(optionItem.props.type).toBe('arrow');
expect(optionItem.props.action).toBeDefined();
expect(queryByTestId('slide-up-panel-item')).toBeNull();
props.location = 'quick_actions';
rerender(<PlaybookRunsOption {...props}/>);
const slideUpPanelItem = getByTestId('slide-up-panel-item');
expect(slideUpPanelItem).toBeTruthy();
expect(slideUpPanelItem.props.text).toBe('Playbook runs');
expect(slideUpPanelItem.props.leftIcon).toBe('product-playbooks');
expect(slideUpPanelItem.props.rightIcon).toBe('chevron-right');
expect(slideUpPanelItem.props.onPress).toBeDefined();
expect(queryByTestId('option-item')).toBeNull();
});
it('uses correct type based on platform', () => {
const props = getBaseProps();
// We need to use any because typescript is not inferring the correct type for the dict
jest.mocked(Platform.select).mockImplementation((dict) => dict.android ?? (dict as any).default);
const {getByTestId} = renderWithIntl(<PlaybookRunsOption {...baseProps}/>);
const {getByTestId} = renderWithIntl(<PlaybookRunsOption {...props}/>);
const optionItem = getByTestId('option-item');
expect(optionItem.props.type).toBe('default');
});
it('calls goToPlaybookRuns when pressed', async () => {
const {getByTestId} = renderWithIntl(<PlaybookRunsOption {...baseProps}/>);
const props = getBaseProps();
const {getByTestId} = renderWithIntl(<PlaybookRunsOption {...props}/>);
const optionItem = getByTestId('option-item');
optionItem.props.action();

View file

@ -2,10 +2,11 @@
// See LICENSE.txt for license information.
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {defineMessages, useIntl} from 'react-intl';
import {Platform} from 'react-native';
import OptionItem from '@components/option_item';
import SlideUpPanelItem from '@components/slide_up_panel_item';
import {goToPlaybookRuns} from '@playbooks/screens/navigation';
import {dismissBottomSheet} from '@screens/navigation';
@ -13,12 +14,21 @@ type Props = {
channelId: string;
playbooksActiveRuns: number;
channelName: string;
location: 'channel_actions' | 'quick_actions';
}
const messages = defineMessages({
playbookRuns: {
id: 'playbooks.playbooks_runs.title',
defaultMessage: 'Playbook runs',
},
});
const PlaybookRunsOption = ({
channelId,
playbooksActiveRuns,
channelName,
location,
}: Props) => {
const intl = useIntl();
@ -27,12 +37,23 @@ const PlaybookRunsOption = ({
goToPlaybookRuns(intl, channelId, channelName);
}, [intl, channelId, channelName]);
if (location === 'quick_actions') {
return (
<SlideUpPanelItem
onPress={onPress}
text={intl.formatMessage(messages.playbookRuns)}
leftIcon='product-playbooks'
rightIcon='chevron-right'
/>
);
}
return (
<OptionItem
type={Platform.select({ios: 'arrow', default: 'default'})}
icon='product-playbooks'
action={onPress}
label={intl.formatMessage({id: 'playbooks.playbooks_runs.title', defaultMessage: 'Playbook runs'})}
label={intl.formatMessage(messages.playbookRuns)}
info={playbooksActiveRuns.toString()}
/>
);

View file

@ -52,5 +52,6 @@ describe('ChannelQuickAction', () => {
const playbookRunsOption = getByTestId('playbook-runs-option');
expect(playbookRunsOption).toBeTruthy();
expect(playbookRunsOption.props.channelId).toBe('channel-id');
expect(playbookRunsOption.props.location).toBe('quick_actions');
});
});

View file

@ -65,7 +65,10 @@ const ChannelQuickAction = ({
testID='channel.quick_actions.channel_info.action'
/>
{hasPlaybookRuns &&
<PlaybookRunsOption channelId={channelId}/>
<PlaybookRunsOption
channelId={channelId}
location='quick_actions'
/>
}
{callsEnabled && !isDMorGM && // if calls is not enabled, copy link will show in the channel actions
<CopyChannelLinkOption

View file

@ -39,6 +39,7 @@ describe('ChannelInfoOptions', () => {
const props = getBaseProps();
const {getByTestId, rerender} = renderWithEverything(<ChannelInfoOptions {...props}/>, {database});
expect(getByTestId('playbook-runs-option')).toHaveProp('channelId', 'channel-id');
expect(getByTestId('playbook-runs-option')).toHaveProp('location', 'channel_actions');
props.channelId = 'channel-id-2';
rerender(<ChannelInfoOptions {...props}/>);

View file

@ -49,7 +49,10 @@ const Options = ({
<NotificationPreference channelId={channelId}/>
<PinnedMessages channelId={channelId}/>
<ChannelFiles channelId={channelId}/>
<PlaybookRunsOption channelId={channelId}/>
<PlaybookRunsOption
channelId={channelId}
location='channel_actions'
/>
{type !== General.DM_CHANNEL &&
<Members channelId={channelId}/>
}