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