GH-2812 Hide delete template and edit template icons unless user has permission (#2818)
* hide delete template and edit template icons unless user has permission * fix jest tests
This commit is contained in:
parent
ebf02366b4
commit
554453c9e6
3 changed files with 139 additions and 60 deletions
|
@ -15,6 +15,7 @@ import {MemoryRouter, Router} from 'react-router-dom'
|
|||
import Mutator from '../../mutator'
|
||||
import {Utils} from '../../utils'
|
||||
import {Team} from '../../store/teams'
|
||||
import {IUser} from '../../user'
|
||||
import {mockDOM, mockStateStore, wrapDNDIntl} from '../../testUtils'
|
||||
|
||||
import BoardTemplateSelector from './boardTemplateSelector'
|
||||
|
@ -47,6 +48,15 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
|||
updateAt: 0,
|
||||
modifiedBy: 'user-1',
|
||||
}
|
||||
const me: IUser = {
|
||||
id: 'user-id-1',
|
||||
username: 'username_1',
|
||||
email: '',
|
||||
props: {},
|
||||
create_at: 0,
|
||||
update_at: 0,
|
||||
is_bot: false
|
||||
}
|
||||
const template1Title = 'Template 1'
|
||||
const globalTemplateTitle = 'Template Global'
|
||||
const boardTitle = 'Board 1'
|
||||
|
@ -59,9 +69,8 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
|||
current: team1,
|
||||
},
|
||||
users: {
|
||||
me: {
|
||||
id: 'user_id_1',
|
||||
},
|
||||
me,
|
||||
boardUsers: [me],
|
||||
},
|
||||
boards: {
|
||||
boards: [
|
||||
|
@ -88,6 +97,14 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
|||
dateDisplayPropertyId: 'id-5',
|
||||
},
|
||||
],
|
||||
membersInBoards: {
|
||||
['1']: {userId: me.id, schemeAdmin: true},
|
||||
['2']: {userId: me.id, schemeAdmin: true},
|
||||
},
|
||||
myBoardMemberships: {
|
||||
['1']: {userId: me.id, schemeAdmin: true},
|
||||
['2']: {userId: me.id, schemeAdmin: true},
|
||||
},
|
||||
cards: [],
|
||||
views: [],
|
||||
},
|
||||
|
|
|
@ -3,9 +3,14 @@
|
|||
import {render, within, act, waitFor} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import React from 'react'
|
||||
import {MockStoreEnhanced} from 'redux-mock-store'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
import {Board, IPropertyTemplate} from '../../blocks/board'
|
||||
import {wrapDNDIntl} from '../../testUtils'
|
||||
import {mockStateStore, wrapDNDIntl} from '../../testUtils'
|
||||
|
||||
import {IUser} from '../../user'
|
||||
import {Team} from '../../store/teams'
|
||||
|
||||
import BoardTemplateSelectorItem from './boardTemplateSelectorItem'
|
||||
|
||||
|
@ -42,6 +47,14 @@ jest.mock('../../utils')
|
|||
jest.mock('../../mutator')
|
||||
|
||||
describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
||||
const team1: Team = {
|
||||
id: 'team-1',
|
||||
title: 'Team 1',
|
||||
signupToken: '',
|
||||
updateAt: 0,
|
||||
modifiedBy: 'user-1',
|
||||
}
|
||||
|
||||
const template: Board = {
|
||||
id: '1',
|
||||
teamId: 'team-1',
|
||||
|
@ -80,19 +93,44 @@ describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
|||
properties: {},
|
||||
}
|
||||
|
||||
const me: IUser = {
|
||||
id: 'user-id-1',
|
||||
username: 'username_1',
|
||||
email: '',
|
||||
props: {},
|
||||
create_at: 0,
|
||||
update_at: 0,
|
||||
is_bot: false
|
||||
}
|
||||
|
||||
let store:MockStoreEnhanced<unknown, unknown>
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
const state = {
|
||||
teams: {
|
||||
current: team1,
|
||||
},
|
||||
boards: {
|
||||
current: '1',
|
||||
myBoardMemberships: {
|
||||
['1']: {userId: me.id, schemeAdmin: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
store = mockStateStore([], state)
|
||||
})
|
||||
|
||||
test('should match snapshot', async () => {
|
||||
const {container} = render(wrapDNDIntl(
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={jest.fn()}
|
||||
onDelete={jest.fn()}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
<ReduxProvider store={store}>
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={jest.fn()}
|
||||
onDelete={jest.fn()}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
,
|
||||
))
|
||||
expect(container).toMatchSnapshot()
|
||||
|
@ -100,13 +138,15 @@ describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
|||
|
||||
test('should match snapshot when active', async () => {
|
||||
const {container} = render(wrapDNDIntl(
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={true}
|
||||
template={template}
|
||||
onSelect={jest.fn()}
|
||||
onDelete={jest.fn()}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
<ReduxProvider store={store}>
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={true}
|
||||
template={template}
|
||||
onSelect={jest.fn()}
|
||||
onDelete={jest.fn()}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
,
|
||||
))
|
||||
expect(container).toMatchSnapshot()
|
||||
|
@ -114,13 +154,15 @@ describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
|||
|
||||
test('should match snapshot with global template', async () => {
|
||||
const {container} = render(wrapDNDIntl(
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={globalTemplate}
|
||||
onSelect={jest.fn()}
|
||||
onDelete={jest.fn()}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
<ReduxProvider store={store}>
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={globalTemplate}
|
||||
onSelect={jest.fn()}
|
||||
onDelete={jest.fn()}
|
||||
onEdit={jest.fn()}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
,
|
||||
))
|
||||
expect(container).toMatchSnapshot()
|
||||
|
@ -131,13 +173,15 @@ describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
|||
const onDelete = jest.fn()
|
||||
const onEdit = jest.fn()
|
||||
const {container} = render(wrapDNDIntl(
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
onDelete={onDelete}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
<ReduxProvider store={store}>
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
onDelete={onDelete}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
,
|
||||
))
|
||||
userEvent.click(container.querySelector('.BoardTemplateSelectorItem')!)
|
||||
|
@ -152,13 +196,15 @@ describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
|||
const onDelete = jest.fn()
|
||||
const onEdit = jest.fn()
|
||||
const {container} = render(wrapDNDIntl(
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
onDelete={onDelete}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
<ReduxProvider store={store}>
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
onDelete={onDelete}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
,
|
||||
))
|
||||
userEvent.click(container.querySelector('.BoardTemplateSelectorItem .EditIcon')!)
|
||||
|
@ -176,13 +222,15 @@ describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
|||
const root = document.createElement('div')
|
||||
root.setAttribute('id', 'focalboard-root-portal')
|
||||
render(wrapDNDIntl(
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
onDelete={onDelete}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
<ReduxProvider store={store}>
|
||||
<BoardTemplateSelectorItem
|
||||
isActive={false}
|
||||
template={template}
|
||||
onSelect={onSelect}
|
||||
onDelete={onDelete}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
,
|
||||
), {container: document.body.appendChild(root)})
|
||||
act(() => {
|
||||
|
|
|
@ -9,8 +9,10 @@ import DeleteIcon from '../../widgets/icons/delete'
|
|||
import EditIcon from '../../widgets/icons/edit'
|
||||
import DeleteBoardDialog from '../sidebar/deleteBoardDialog'
|
||||
|
||||
import BoardPermissionGate from '../permissions/boardPermissionGate'
|
||||
|
||||
import './boardTemplateSelectorItem.scss'
|
||||
import {Constants} from "../../constants"
|
||||
import {Constants, Permission} from "../../constants"
|
||||
|
||||
type Props = {
|
||||
isActive: boolean
|
||||
|
@ -43,19 +45,31 @@ const BoardTemplateSelectorItem = (props: Props) => {
|
|||
{/* don't show template menu options for default templates */}
|
||||
{template.teamId !== Constants.globalTeamId &&
|
||||
<div className='actions'>
|
||||
<IconButton
|
||||
icon={<DeleteIcon/>}
|
||||
title={intl.formatMessage({id: 'BoardTemplateSelector.delete-template', defaultMessage: 'Delete'})}
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
setDeleteOpen(true)
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
icon={<EditIcon/>}
|
||||
title={intl.formatMessage({id: 'BoardTemplateSelector.edit-template', defaultMessage: 'Edit'})}
|
||||
onClick={onEditHandler}
|
||||
/>
|
||||
<BoardPermissionGate
|
||||
boardId={template.id}
|
||||
teamId={template.teamId}
|
||||
permissions={[Permission.DeleteBoard]}
|
||||
>
|
||||
<IconButton
|
||||
icon={<DeleteIcon/>}
|
||||
title={intl.formatMessage({id: 'BoardTemplateSelector.delete-template', defaultMessage: 'Delete'})}
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
setDeleteOpen(true)
|
||||
}}
|
||||
/>
|
||||
</BoardPermissionGate>
|
||||
<BoardPermissionGate
|
||||
boardId={template.id}
|
||||
teamId={template.teamId}
|
||||
permissions={[Permission.ManageBoardCards, Permission.ManageBoardProperties]}
|
||||
>
|
||||
<IconButton
|
||||
icon={<EditIcon/>}
|
||||
title={intl.formatMessage({id: 'BoardTemplateSelector.edit-template', defaultMessage: 'Edit'})}
|
||||
onClick={onEditHandler}
|
||||
/>
|
||||
</BoardPermissionGate>
|
||||
</div>}
|
||||
{deleteOpen &&
|
||||
<DeleteBoardDialog
|
||||
|
|
Loading…
Reference in a new issue