[MM-48515] Reuse ChannelListRow component to make it selectable maintaining compatibility (#6778)

* Moving ChannelListRow to component

* selectable
This commit is contained in:
Javier Aguirre 2022-11-22 10:00:20 +01:00 committed by GitHub
parent 4bacba92bb
commit da31c9c3cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 645 additions and 3 deletions

View file

@ -0,0 +1,470 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/channel_list_row should be selected 1`] = `
<View
style={
{
"paddingVertical": 9,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
testID="ChannelListRow.channel"
>
<Icon
name="circle-multiple-outline"
size={20}
style={
{
"color": "rgba(63,67,80,0.56)",
"padding": 2,
}
}
/>
<View
style={
{
"flexDirection": "column",
"marginLeft": 16,
}
}
>
<Text
style={
{
"color": "#3f4350",
"fontFamily": "OpenSans",
"fontSize": 16,
"fontWeight": "400",
"lineHeight": 24,
}
}
testID="ChannelListRow.channel.display_name"
>
channel
</Text>
<Text
ellipsizeMode="tail"
numberOfLines={1}
style={
{
"color": "rgba(63,67,80,0.64)",
"fontFamily": "OpenSans",
"fontSize": 12,
"fontWeight": "400",
"lineHeight": 16,
}
}
>
My purpose
</Text>
</View>
<View>
<Icon
color="#1c58d9"
name="check-circle"
size={28}
/>
</View>
</View>
</View>
</View>
`;
exports[`components/channel_list_row should match snapshot with delete_at filled in 1`] = `
<View
style={
{
"paddingVertical": 9,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
testID="ChannelListRow.channel"
>
<Icon
name="archive-outline"
size={20}
style={
{
"color": "rgba(63,67,80,0.56)",
"padding": 2,
}
}
/>
<View
style={
{
"flexDirection": "column",
"marginLeft": 16,
}
}
>
<Text
style={
{
"color": "#3f4350",
"fontFamily": "OpenSans",
"fontSize": 16,
"fontWeight": "400",
"lineHeight": 24,
}
}
testID="ChannelListRow.channel.display_name"
>
channel
</Text>
</View>
</View>
</View>
</View>
`;
exports[`components/channel_list_row should match snapshot with open channel icon 1`] = `
<View
style={
{
"paddingVertical": 9,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
testID="ChannelListRow.channel"
>
<Icon
name="circle-multiple-outline"
size={20}
style={
{
"color": "rgba(63,67,80,0.56)",
"padding": 2,
}
}
/>
<View
style={
{
"flexDirection": "column",
"marginLeft": 16,
}
}
>
<Text
style={
{
"color": "#3f4350",
"fontFamily": "OpenSans",
"fontSize": 16,
"fontWeight": "400",
"lineHeight": 24,
}
}
testID="ChannelListRow.channel.display_name"
>
channel
</Text>
</View>
</View>
</View>
</View>
`;
exports[`components/channel_list_row should match snapshot with private channel icon 1`] = `
<View
style={
{
"paddingVertical": 9,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
testID="ChannelListRow.channel"
>
<Icon
name="circle-multiple-outline"
size={20}
style={
{
"color": "rgba(63,67,80,0.56)",
"padding": 2,
}
}
/>
<View
style={
{
"flexDirection": "column",
"marginLeft": 16,
}
}
>
<Text
style={
{
"color": "#3f4350",
"fontFamily": "OpenSans",
"fontSize": 16,
"fontWeight": "400",
"lineHeight": 24,
}
}
testID="ChannelListRow.channel.display_name"
>
channel
</Text>
</View>
</View>
</View>
</View>
`;
exports[`components/channel_list_row should match snapshot with purpose filled in 1`] = `
<View
style={
{
"paddingVertical": 9,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
testID="ChannelListRow.channel"
>
<Icon
name="circle-multiple-outline"
size={20}
style={
{
"color": "rgba(63,67,80,0.56)",
"padding": 2,
}
}
/>
<View
style={
{
"flexDirection": "column",
"marginLeft": 16,
}
}
>
<Text
style={
{
"color": "#3f4350",
"fontFamily": "OpenSans",
"fontSize": 16,
"fontWeight": "400",
"lineHeight": 24,
}
}
testID="ChannelListRow.channel.display_name"
>
channel
</Text>
<Text
ellipsizeMode="tail"
numberOfLines={1}
style={
{
"color": "rgba(63,67,80,0.64)",
"fontFamily": "OpenSans",
"fontSize": 12,
"fontWeight": "400",
"lineHeight": 16,
}
}
>
My purpose
</Text>
</View>
</View>
</View>
</View>
`;
exports[`components/channel_list_row should match snapshot with shared filled in 1`] = `
<View
style={
{
"paddingVertical": 9,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
testID="ChannelListRow.channel"
>
<Icon
name="circle-multiple-outline"
size={20}
style={
{
"color": "rgba(63,67,80,0.56)",
"padding": 2,
}
}
/>
<View
style={
{
"flexDirection": "column",
"marginLeft": 16,
}
}
>
<Text
style={
{
"color": "#3f4350",
"fontFamily": "OpenSans",
"fontSize": 16,
"fontWeight": "400",
"lineHeight": 24,
}
}
testID="ChannelListRow.channel.display_name"
>
channel
</Text>
</View>
</View>
</View>
</View>
`;

View file

@ -0,0 +1,151 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import Database from '@nozbe/watermelondb/Database';
import React from 'react';
import {renderWithEverything} from '@test/intl-test-helper';
import TestHelper from '@test/test_helper';
import ChannelListRow from '.';
describe('components/channel_list_row', () => {
let database: Database;
const channel: Channel = {
id: '1',
create_at: 1111,
update_at: 1111,
delete_at: 0,
team_id: 'my team',
type: 'O',
display_name: 'channel',
name: 'channel',
header: 'channel',
purpose: '',
last_post_at: 1,
total_msg_count: 1,
extra_update_at: 1,
creator_id: '1',
scheme_id: null,
group_constrained: null,
shared: true,
};
beforeAll(async () => {
const server = await TestHelper.setupServerDatabase();
database = server.database;
});
it('should match snapshot with open channel icon', () => {
const wrapper = renderWithEverything(
<ChannelListRow
channel={channel}
selected={false}
selectable={false}
testID='ChannelListRow'
onPress={() => {
// noop
}}
/>,
{database},
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should match snapshot with private channel icon', () => {
channel.type = 'P';
const wrapper = renderWithEverything(
<ChannelListRow
channel={channel}
selected={false}
selectable={false}
testID='ChannelListRow'
onPress={() => {
// noop
}}
/>,
{database},
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should match snapshot with delete_at filled in', () => {
channel.delete_at = 1111;
channel.shared = false;
channel.type = 'O';
const wrapper = renderWithEverything(
<ChannelListRow
channel={channel}
testID='ChannelListRow'
selectable={false}
selected={false}
onPress={() => {
// noop
}}
/>,
{database},
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should match snapshot with shared filled in', () => {
channel.delete_at = 0;
channel.shared = true;
channel.type = 'O';
const wrapper = renderWithEverything(
<ChannelListRow
channel={channel}
testID='ChannelListRow'
selectable={false}
selected={false}
onPress={() => {
// noop
}}
/>,
{database},
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should match snapshot with purpose filled in', () => {
channel.purpose = 'My purpose';
const wrapper = renderWithEverything(
<ChannelListRow
channel={channel}
testID='ChannelListRow'
selectable={false}
selected={false}
onPress={() => {
// noop
}}
/>,
{database},
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('should be selected', () => {
const wrapper = renderWithEverything(
<ChannelListRow
channel={channel}
testID='ChannelListRow'
selectable={true}
selected={true}
onPress={() => {
// noop
}}
/>,
{database},
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
});

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import React, {useMemo} from 'react';
import {
Text,
TouchableOpacity,
@ -17,6 +17,8 @@ type Props = {
channel: Channel;
onPress: (channel: Channel) => void;
testID?: string;
selectable?: boolean;
selected?: boolean;
}
const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
@ -51,6 +53,8 @@ export default function ChannelListRow({
channel,
onPress,
testID,
selectable = false,
selected = false,
}: Props) {
const theme = useTheme();
const style = getStyleFromTheme(theme);
@ -59,6 +63,23 @@ export default function ChannelListRow({
onPress(channel);
};
const selectionIcon = useMemo(() => {
if (!selectable) {
return null;
}
const color = selected ? theme.buttonBg : theme.centerChannelColor;
return (
<View style={style.selector}>
<CompassIcon
name={selected ? 'check-circle' : 'circle-outline'}
size={28}
color={color}
/>
</View>
);
}, [selectable, selected, theme]);
let purposeComponent;
if (channel.purpose) {
purposeComponent = (
@ -105,6 +126,7 @@ export default function ChannelListRow({
</Text>
{purposeComponent}
</View>
{selectionIcon}
</View>
</TouchableOpacity>
</View>

View file

@ -4,6 +4,7 @@
import React, {useCallback, useMemo} from 'react';
import {View, FlatList} from 'react-native';
import ChannelListRow from '@components/channel_list_row';
import FormattedText from '@components/formatted_text';
import Loading from '@components/loading';
import NoResultsWithTerm from '@components/no_results_with_term';
@ -11,8 +12,6 @@ import {useTheme} from '@context/theme';
import {useKeyboardHeight} from '@hooks/device';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import ChannelListRow from './channel_list_row';
type Props = {
onEndReached: () => void;
loading: boolean;