parent
8fd3b99887
commit
3cc4f8b64b
@ -0,0 +1,130 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { render, screen } from 'soapbox/jest/test-helpers';
|
||||||
|
import { normalizeGroup, normalizeGroupRelationship } from 'soapbox/normalizers';
|
||||||
|
import { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
import GroupActionButton from '../group-action-button';
|
||||||
|
|
||||||
|
let group: Group;
|
||||||
|
|
||||||
|
describe('<GroupActionButton />', () => {
|
||||||
|
describe('with no group relationship', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
relationship: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a private group', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = group.set('locked', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Request Access button', () => {
|
||||||
|
render(<GroupActionButton group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button')).toHaveTextContent('Request Access');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a public group', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = group.set('locked', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Join Group button', () => {
|
||||||
|
render(<GroupActionButton group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button')).toHaveTextContent('Join Group');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with no group relationship member', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
relationship: normalizeGroupRelationship({
|
||||||
|
member: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a private group', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = group.set('locked', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Request Access button', () => {
|
||||||
|
render(<GroupActionButton group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button')).toHaveTextContent('Request Access');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a public group', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = group.set('locked', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Join Group button', () => {
|
||||||
|
render(<GroupActionButton group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button')).toHaveTextContent('Join Group');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the user has requested to join', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
relationship: normalizeGroupRelationship({
|
||||||
|
requested: true,
|
||||||
|
member: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Cancel Request button', () => {
|
||||||
|
render(<GroupActionButton group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button')).toHaveTextContent('Cancel Request');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the user is an Admin', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
relationship: normalizeGroupRelationship({
|
||||||
|
requested: false,
|
||||||
|
member: true,
|
||||||
|
role: 'admin',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Manage Group button', () => {
|
||||||
|
render(<GroupActionButton group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button')).toHaveTextContent('Manage Group');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the user is just a member', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
relationship: normalizeGroupRelationship({
|
||||||
|
requested: false,
|
||||||
|
member: true,
|
||||||
|
role: 'user',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Leave Group button', () => {
|
||||||
|
render(<GroupActionButton group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button')).toHaveTextContent('Leave Group');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { render, screen } from 'soapbox/jest/test-helpers';
|
||||||
|
import { normalizeGroup } from 'soapbox/normalizers';
|
||||||
|
import { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
import GroupMemberCount from '../group-member-count';
|
||||||
|
|
||||||
|
let group: Group;
|
||||||
|
|
||||||
|
describe('<GroupMemberCount />', () => {
|
||||||
|
describe('without support for "members_count"', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
members_count: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null', () => {
|
||||||
|
render(<GroupMemberCount group={group} />);
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('group-member-count')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with support for "members_count"', () => {
|
||||||
|
describe('with 1 member', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
members_count: 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly', () => {
|
||||||
|
render(<GroupMemberCount group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('group-member-count').textContent).toEqual('1 member');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with 2 members', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
members_count: 2,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly', () => {
|
||||||
|
render(<GroupMemberCount group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('group-member-count').textContent).toEqual('2 members');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with 1000 members', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
members_count: 1000,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly', () => {
|
||||||
|
render(<GroupMemberCount group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('group-member-count').textContent).toEqual('1k members');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { render, screen } from 'soapbox/jest/test-helpers';
|
||||||
|
import { normalizeGroup } from 'soapbox/normalizers';
|
||||||
|
import { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
import GroupPrivacy from '../group-privacy';
|
||||||
|
|
||||||
|
let group: Group;
|
||||||
|
|
||||||
|
describe('<GroupPrivacy />', () => {
|
||||||
|
describe('with a Private group', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
locked: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the correct text', () => {
|
||||||
|
render(<GroupPrivacy group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('group-privacy')).toHaveTextContent('Private');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a Public group', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
group = normalizeGroup({
|
||||||
|
locked: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the correct text', () => {
|
||||||
|
render(<GroupPrivacy group={group} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('group-privacy')).toHaveTextContent('Public');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,83 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { joinGroup, leaveGroup } from 'soapbox/actions/groups';
|
||||||
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
|
import { Button } from 'soapbox/components/ui';
|
||||||
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
|
import { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
interface IGroupActionButton {
|
||||||
|
group: Group
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
confirmationHeading: { id: 'confirmations.leave_group.heading', defaultMessage: 'Leave group' },
|
||||||
|
confirmationMessage: { id: 'confirmations.leave_group.message', defaultMessage: 'You are about to leave the group. Do you want to continue?' },
|
||||||
|
confirmationConfirm: { id: 'confirmations.leave_group.confirm', defaultMessage: 'Leave' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const GroupActionButton = ({ group }: IGroupActionButton) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const isNonMember = !group.relationship || !group.relationship.member;
|
||||||
|
const isRequested = group.relationship?.requested;
|
||||||
|
const isAdmin = group.relationship?.role === 'admin';
|
||||||
|
|
||||||
|
const onJoinGroup = () => dispatch(joinGroup(group.id));
|
||||||
|
|
||||||
|
const onLeaveGroup = () =>
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
heading: intl.formatMessage(messages.confirmationHeading),
|
||||||
|
message: intl.formatMessage(messages.confirmationMessage),
|
||||||
|
confirm: intl.formatMessage(messages.confirmationConfirm),
|
||||||
|
onConfirm: () => dispatch(leaveGroup(group.id)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (isNonMember) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
theme='primary'
|
||||||
|
onClick={onJoinGroup}
|
||||||
|
>
|
||||||
|
{group.locked
|
||||||
|
? <FormattedMessage id='group.join.private' defaultMessage='Request Access' />
|
||||||
|
: <FormattedMessage id='group.join.public' defaultMessage='Join Group' />}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRequested) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
theme='secondary'
|
||||||
|
onClick={onLeaveGroup}
|
||||||
|
>
|
||||||
|
<FormattedMessage id='group.cancel_request' defaultMessage='Cancel Request' />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAdmin) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
theme='secondary'
|
||||||
|
to={`/groups/${group.id}/manage`}
|
||||||
|
>
|
||||||
|
<FormattedMessage id='group.manage' defaultMessage='Manage Group' />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
theme='secondary'
|
||||||
|
onClick={onLeaveGroup}
|
||||||
|
>
|
||||||
|
<FormattedMessage id='group.leave' defaultMessage='Leave Group' />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GroupActionButton;
|
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { Text } from 'soapbox/components/ui';
|
||||||
|
import { Group } from 'soapbox/types/entities';
|
||||||
|
import { shortNumberFormat } from 'soapbox/utils/numbers';
|
||||||
|
|
||||||
|
interface IGroupMemberCount {
|
||||||
|
group: Group
|
||||||
|
}
|
||||||
|
|
||||||
|
const GroupMemberCount = ({ group }: IGroupMemberCount) => {
|
||||||
|
if (typeof group.members_count === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text theme='inherit' tag='span' size='sm' weight='medium' data-testid='group-member-count'>
|
||||||
|
{shortNumberFormat(group.members_count)}
|
||||||
|
{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id='groups.discover.search.results.member_count'
|
||||||
|
defaultMessage='{members, plural, one {member} other {members}}'
|
||||||
|
values={{
|
||||||
|
members: group.members_count,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GroupMemberCount;
|
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { HStack, Icon, Text } from 'soapbox/components/ui';
|
||||||
|
import { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
interface IGroupPolicy {
|
||||||
|
group: Group
|
||||||
|
}
|
||||||
|
|
||||||
|
const GroupPrivacy = ({ group }: IGroupPolicy) => (
|
||||||
|
<HStack space={1} alignItems='center' data-testid='group-privacy'>
|
||||||
|
<Icon
|
||||||
|
className='h-4 w-4'
|
||||||
|
src={
|
||||||
|
group.locked
|
||||||
|
? require('@tabler/icons/lock.svg')
|
||||||
|
: require('@tabler/icons/world.svg')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text theme='inherit' tag='span' size='sm' weight='medium'>
|
||||||
|
{group.locked ? (
|
||||||
|
<FormattedMessage id='group.privacy.locked' defaultMessage='Private' />
|
||||||
|
) : (
|
||||||
|
<FormattedMessage id='group.privacy.public' defaultMessage='Public' />
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default GroupPrivacy;
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { HStack, Icon, Text } from 'soapbox/components/ui';
|
||||||
|
import { Group } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
interface IGroupRelationship {
|
||||||
|
group: Group
|
||||||
|
}
|
||||||
|
|
||||||
|
const GroupRelationship = ({ group }: IGroupRelationship) => {
|
||||||
|
const isAdmin = group.relationship?.role === 'admin';
|
||||||
|
const isModerator = group.relationship?.role === 'moderator';
|
||||||
|
|
||||||
|
if (!isAdmin || !isModerator) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HStack space={1} alignItems='center'>
|
||||||
|
<Icon
|
||||||
|
className='h-4 w-4'
|
||||||
|
src={
|
||||||
|
isAdmin
|
||||||
|
? require('@tabler/icons/users.svg')
|
||||||
|
: require('@tabler/icons/gavel.svg')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text tag='span' weight='medium' size='sm' theme='inherit'>
|
||||||
|
{isAdmin
|
||||||
|
? <FormattedMessage id='group.role.admin' defaultMessage='Admin' />
|
||||||
|
: <FormattedMessage id='group.role.moderator' defaultMessage='Moderator' />}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GroupRelationship;
|
Loading…
Reference in new issue