Adding menu options in sidebar, and improving order (#3713)

* Adding category and board options at the top

* Updating UI

* Updating test

* Updating test

* Updating icon

* Fixing bug and translation

* Updating createCategory component

* Removing unused vars

* Removing unused vars

* Updating UI

* fixed tests

* fixed tests

* fixed tests

* Updating test

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com>
This commit is contained in:
Asaad Mahmood 2022-08-25 18:09:09 +05:00 committed by GitHub
parent 61a8af8f34
commit 62ffa9c39a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 355 additions and 246 deletions

View file

@ -23,7 +23,7 @@ exports[`components/boardSelector renders with no results 1`] = `
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>
@ -126,7 +126,7 @@ exports[`components/boardSelector renders with some results 1`] = `
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>
@ -327,7 +327,7 @@ exports[`components/boardSelector renders without start searching 1`] = `
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>

View file

@ -23,15 +23,8 @@
position: relative;
width: 600px;
height: 450px;
.toolbar {
flex-direction: row-reverse;
padding: 0;
position: absolute;
right: 18px;
top: 18px;
}
}
.confirmation-dialog-box {
.dialog {
position: fixed;

View file

@ -242,6 +242,7 @@
"Sidebar.untitled-view": "(Untitled View)",
"SidebarCategories.BlocksMenu.Move": "Move To...",
"SidebarCategories.CategoryMenu.CreateNew": "Create New Category",
"SidebarCategories.CategoryMenu.CreateBoard": "Create New Board",
"SidebarCategories.CategoryMenu.Delete": "Delete Category",
"SidebarCategories.CategoryMenu.DeleteModal.Body": "Boards in <b>{categoryName}</b> will move back to the Boards categories. You're not removed from any boards.",
"SidebarCategories.CategoryMenu.DeleteModal.Title": "Delete this category?",
@ -400,4 +401,4 @@
"tutorial_tip.ok": "Next",
"tutorial_tip.out": "Opt out of these tips.",
"tutorial_tip.seen": "Seen this before?"
}
}

View file

@ -20,7 +20,7 @@ exports[`/components/confirmationDialogBox confirmDialog should match snapshot 1
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>
@ -95,7 +95,7 @@ exports[`/components/confirmationDialogBox confirmDialog with Confirm Button Tex
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>

View file

@ -20,7 +20,7 @@ exports[`components/dialog should match snapshot 1`] = `
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>
@ -61,7 +61,7 @@ exports[`components/dialog should return dialog and click on cancel button 1`] =
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>

View file

@ -9,8 +9,8 @@
background-color: rgba(var(--sidebar-text-rgb), 0.08);
color: rgba(var(--sidebar-text-rgb), 0.56);
flex: 1;
padding: 6px 8px;
gap: 6px;
padding: 6px 8px 6px 4px;
gap: 4px;
border-radius: 4px;
cursor: pointer;
height: 28px;
@ -28,9 +28,17 @@
}
.add-board-icon {
margin-left: 4px;
border-radius: 28px;
margin-left: 8px;
width: 28px;
height: 28px;
flex: 0 0 28px;
background-color: rgba(var(--sidebar-text-rgb), 0.08);
color: rgba(var(--sidebar-text-rgb), 0.72);
&:hover {
background-color: rgba(var(--sidebar-text-rgb), 0.16);
color: var(--sidebar-text);
}
}
}

View file

@ -2,17 +2,19 @@
// See LICENSE.txt for license information.
import React, {useEffect, useState} from 'react'
import {useIntl} from 'react-intl'
import {FormattedMessage, useIntl} from 'react-intl'
import MenuWrapper from '../../widgets/menuWrapper'
import CompassIcon from '../../widgets/icons/compassIcon'
import Menu from '../../widgets/menu'
import Search from '../../widgets/icons/search'
import CreateCategory from '../createCategory/createCategory'
import {useAppSelector} from '../../store/hooks'
import {
getOnboardingTourCategory,
getOnboardingTourStep,
} from '../../store/users'
import {getCurrentCard} from '../../store/cards'
import './boardsSwitcher.scss'
@ -26,7 +28,7 @@ import IconButton from '../../widgets/buttons/iconButton'
import SearchForBoardsTourStep from '../../components/onboardingTour/searchForBoards/searchForBoards'
type Props = {
onBoardTemplateSelectorOpen?: () => void,
onBoardTemplateSelectorOpen: () => void,
userIsGuest?: boolean,
}
@ -35,11 +37,11 @@ const BoardsSwitcher = (props: Props): JSX.Element => {
const [showSwitcher, setShowSwitcher] = useState<boolean>(false)
const onboardingTourCategory = useAppSelector(getOnboardingTourCategory)
const [showCreateCategoryModal, setShowCreateCategoryModal] = useState(false)
const onboardingTourStep = useAppSelector(getOnboardingTourStep)
const currentCard = useAppSelector(getCurrentCard)
const noCardOpen = !currentCard
const shouldViewSearchForBoardsTour = noCardOpen &&
onboardingTourCategory === TOUR_SIDEBAR &&
onboardingTourStep === SidebarTourSteps.SEARCH_FOR_BOARDS.toString()
@ -68,6 +70,10 @@ const BoardsSwitcher = (props: Props): JSX.Element => {
}
}
const handleCreateNewCategory = () => {
setShowCreateCategoryModal(true)
}
useEffect(() => {
document.addEventListener('keydown', handleQuickSwitchKeyPress)
document.addEventListener('keydown', handleEscKeyPress)
@ -95,19 +101,53 @@ const BoardsSwitcher = (props: Props): JSX.Element => {
{shouldViewSearchForBoardsTour && <div><SearchForBoardsTourStep/></div>}
{
Utils.isFocalboardPlugin() && !props.userIsGuest &&
<IconButton
size='small'
inverted={true}
className='add-board-icon'
onClick={props.onBoardTemplateSelectorOpen}
icon={<AddIcon/>}
/>
<MenuWrapper>
<IconButton
size='small'
inverted={true}
className='add-board-icon'
icon={<AddIcon/>}
/>
<Menu>
<Menu.Text
id='create-new-board-option'
icon={<CompassIcon icon='plus' />}
onClick={props.onBoardTemplateSelectorOpen}
name='Create new board'
/>
<Menu.Text
id='createNewCategory'
name={intl.formatMessage({id: 'SidebarCategories.CategoryMenu.CreateNew', defaultMessage: 'Create New Category'})}
icon={
<CompassIcon
icon='folder-plus-outline'
className='CreateNewFolderIcon'
/>
}
onClick={handleCreateNewCategory}
/>
</Menu>
</MenuWrapper>
}
{
showSwitcher &&
<BoardSwitcherDialog onClose={() => setShowSwitcher(false)} />
}
{
showCreateCategoryModal && (
<CreateCategory
onClose={() => setShowCreateCategoryModal(false)}
title={(
<FormattedMessage
id='SidebarCategories.CategoryMenu.CreateNew'
defaultMessage='Create New Category'
/>
)}
/>
)
}
</div>
)
}

View file

@ -20,7 +20,7 @@ exports[`component/BoardSwitcherDialog base case 1`] = `
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>

View file

@ -10,10 +10,6 @@
color: rgba(var(--center-channel-color-rgb), 0.56);
}
.toolbar {
padding: 4px;
}
span {
display: inline-block;
height: 100%;

View file

@ -20,7 +20,7 @@ exports[`components/createCategory/CreateCategory base case should match snapsho
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>
@ -35,7 +35,9 @@ exports[`components/createCategory/CreateCategory base case should match snapsho
<div
class="CreateCategory"
>
<h3>
<h3
class="dialog-title"
>
<span>
title
</span>

View file

@ -4,20 +4,14 @@
.wrapper {
.dialog {
width: 600px;
height: 260px;
}
.toolbar {
flex-direction: row-reverse;
padding: 2px 6px;
height: auto;
}
.CreateCategory {
display: flex;
flex-direction: column;
padding: 0 40px 30px;
gap: 12px;
flex: 1;
padding: 0 32px 24px;
gap: 24px;
.inputWrapper {
position: relative;
@ -38,18 +32,20 @@
width: 100%;
}
h3 {
margin-top: 0;
}
input {
height: 48px;
font-size: 16px;
padding: 6px 12px;
border-radius: 4px;
border: 2px solid rgba(var(--center-channel-color-rgb), 0.16);
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
background: var(--center-channel-bg);
color: var(--center-channel-color);
padding: 0 16px;
flex: 1;
transition: border 0.15s ease-in;
&:focus {
border-color: var(--button-bg);
box-shadow: inset 0 0 0 1px var(--button-bg);
}
}

View file

@ -7,20 +7,59 @@ import {render} from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import {wrapIntl} from "../../testUtils"
import thunk from "redux-thunk"
import {Provider as ReduxProvider} from 'react-redux'
import {mocked} from "jest-mock"
import {mockStateStore, wrapIntl} from "../../testUtils"
import {IUser} from "../../user"
import mutator from "../../mutator"
import CreateCategory from "./createCategory"
jest.mock('../../mutator')
const mockedMutator = mocked(mutator, true)
describe('components/createCategory/CreateCategory', () => {
const me: IUser = {
id: 'user-id-1',
username: 'username_1',
email: '',
nickname: '',
firstname: '',
lastname: '',
props: {},
create_at: 0,
update_at: 0,
is_bot: false,
roles: 'system_user',
is_guest: false,
}
const state = {
teams: {
current: {id: 'team-id', title: 'Test Team'},
},
users: {
me,
},
}
const store = mockStateStore([thunk], state)
it('base case should match snapshot', () => {
const component = wrapIntl(
<CreateCategory
onClose={jest.fn()}
onCreate={jest.fn()}
title={
<span>{'title'}</span>
}
/>
<ReduxProvider store={store}>
<CreateCategory
onClose={jest.fn()}
title={
<span>{'title'}</span>
}
/>
</ReduxProvider>
)
const {container} = render(component)
@ -30,13 +69,14 @@ describe('components/createCategory/CreateCategory', () => {
it('should call onClose on being closed', () => {
const onCloseHandler = jest.fn()
const component = wrapIntl(
<CreateCategory
onClose={onCloseHandler}
onCreate={jest.fn()}
title={
<span>{'title'}</span>
}
/>
<ReduxProvider store={store}>
<CreateCategory
onClose={onCloseHandler}
title={
<span>{'title'}</span>
}
/>
</ReduxProvider>
)
const {container} = render(component)
@ -52,35 +92,39 @@ describe('components/createCategory/CreateCategory', () => {
})
it('should call onCreate on pressing enter', () => {
const onCreateHandler = jest.fn()
const component = wrapIntl(
<CreateCategory
onClose={jest.fn()}
onCreate={onCreateHandler}
title={
<span>{'title'}</span>
}
/>
<ReduxProvider store={store}>
<CreateCategory
onClose={jest.fn()}
title={
<span>{'title'}</span>
}
/>
</ReduxProvider>
)
const {container} = render(component)
const inputField = container.querySelector('.categoryNameInput')
expect(inputField).toBeTruthy()
userEvent.type(inputField as Element, 'category name{enter}')
expect(onCreateHandler).toBeCalledWith('category name')
expect(mockedMutator.createCategory).toBeCalledWith({
name: "category name",
teamID: "team-id",
userID: "user-id-1",
})
})
it('should show initial value', () => {
const onCreateHandler = jest.fn()
const component = wrapIntl(
<CreateCategory
initialValue='Dwight prank ideas'
onClose={jest.fn()}
onCreate={onCreateHandler}
title={
<span>{'title'}</span>
}
/>
<ReduxProvider store={store}>
<CreateCategory
initialValue='Dwight prank ideas'
onClose={jest.fn()}
title={
<span>{'title'}</span>
}
/>
</ReduxProvider>
)
const {container} = render(component)
@ -90,16 +134,16 @@ describe('components/createCategory/CreateCategory', () => {
})
it('should clear input field on clicking clear icon', () => {
const onCreateHandler = jest.fn()
const component = wrapIntl(
<CreateCategory
initialValue='Dunder Mifflin'
onClose={jest.fn()}
onCreate={onCreateHandler}
title={
<span>{'title'}</span>
}
/>
<ReduxProvider store={store}>
<CreateCategory
initialValue='Dunder Mifflin'
onClose={jest.fn()}
title={
<span>{'title'}</span>
}
/>
</ReduxProvider>
)
const {container} = render(component)

View file

@ -5,6 +5,17 @@ import React, {useState, KeyboardEvent} from 'react'
import {useIntl} from 'react-intl'
import {IUser} from '../../user'
import {Category} from '../../store/sidebar'
import {getCurrentTeam} from '../../store/teams'
import mutator from '../../mutator'
import {useAppSelector} from '../../store/hooks'
import {
getMe,
} from '../../store/users'
import {Utils} from '../../utils'
import Dialog from '../dialog'
import Button from '../../widgets/buttons/button'
@ -12,15 +23,18 @@ import './createCategory.scss'
import CloseCircle from "../../widgets/icons/closeCircle"
type Props = {
boardCategoryId?: string
renameModal?: boolean
initialValue?: string
onClose: () => void
onCreate: (name: string) => void
title: JSX.Element
}
const CreateCategory = (props: Props): JSX.Element => {
const intl = useIntl()
const me = useAppSelector<IUser|null>(getMe)
const team = useAppSelector(getCurrentTeam)
const teamID = team?.id || ''
const placeholder = intl.formatMessage({id: 'Categories.CreateCategoryDialog.Placeholder', defaultMessage: 'Name your category' })
const cancelText = intl.formatMessage({id: 'Categories.CreateCategoryDialog.CancelText', defaultMessage: 'Cancel' })
const createText = intl.formatMessage({id: 'Categories.CreateCategoryDialog.CreateText', defaultMessage: 'Create' })
@ -30,17 +44,45 @@ const CreateCategory = (props: Props): JSX.Element => {
const handleKeypress = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
props.onCreate(name)
onCreate(name)
}
}
const onCreate = async (categoryName: string) => {
if (!me) {
Utils.logError('me not initialized')
return
}
if (props.renameModal) {
const category: Category = {
name: categoryName,
id: props.boardCategoryId,
userID: me.id,
teamID,
} as Category
await mutator.updateCategory(category)
} else {
const category: Category = {
name: categoryName,
userID: me.id,
teamID,
} as Category
await mutator.createCategory(category)
}
props.onClose()
}
return (
<Dialog
className='CreateCategoryModal'
onClose={props.onClose}
>
<div className='CreateCategory'>
<h3>{props.title}</h3>
<h3 className='dialog-title'>{props.title}</h3>
<div className='inputWrapper'>
<input
className='categoryNameInput'
@ -70,7 +112,7 @@ const CreateCategory = (props: Props): JSX.Element => {
<Button
size={'medium'}
filled={Boolean(name.trim())}
onClick={() => props.onCreate(name.trim())}
onClick={() => onCreate(name.trim())}
disabled={!(name.trim())}
>
{props.initialValue ? updateText : createText}

View file

@ -10,6 +10,19 @@
bottom: 0;
}
.dialog-title {
margin-top: 24px;
font-weight: 600;
font-size: 22px;
line-height: 28px;
}
.dialog__close {
top: 18px;
right: 18px;
position: absolute;
}
.backdrop {
@include z-index(dialog-backdrop);
position: fixed;
@ -26,14 +39,8 @@
justify-content: center;
}
.toolbar {
display: flex;
flex-direction: row;
padding: 16px;
justify-content: space-between;
}
.dialog {
position: relative;
display: flex;
flex-direction: column;
background-color: rgb(var(--center-channel-bg-rgb));
@ -55,6 +62,7 @@
display: none !important;
}
}
@media screen and (max-width: 975px) {
position: fixed;
top: 0;
@ -63,26 +71,33 @@
bottom: 0;
}
> * {
>* {
flex-shrink: 0;
}
> .banner {
>.banner {
background-color: rgba(230, 220, 192, 0.9);
text-align: center;
padding: 10px;
color: #222;
}
> .banner.error {
>.banner.error {
background-color: rgba(230, 192, 192, 0.9);
}
> .toolbar {
display: flex;
flex-direction: row;
padding: 24px;
justify-content: space-between;
.IconButton {
color: rgba(var(--center-channel-color-rgb), 0.56);
&:hover {
color: rgba(var(--center-channel-color-rgb), 0.72);
background-color: rgba(var(--center-channel-color-rgb), 0.08);
}
&:active {
background-color: rgba(var(--button-bg-rgb), 0.08);
color: rgba(var(--button-bg-rgb), 1);
}
}
.toolbar--right {
@ -90,7 +105,7 @@
gap: 8px;
}
> .content {
>.content {
display: flex;
flex-direction: column;
align-items: flex-start;
@ -98,12 +113,13 @@
@media not screen and (max-width: 975px) {
padding: 10px 126px;
}
@media screen and (max-width: 975px) {
padding: 10px;
}
}
> .content.fullwidth {
>.content.fullwidth {
padding-left: 78px;
}
}

View file

@ -45,7 +45,7 @@ const Dialog = (props: Props) => {
}
isBackdropClickedRef.current = false
props.onClose()
}}
onMouseDown={(e) => {
if(e.target === e.currentTarget){
@ -58,10 +58,11 @@ const Dialog = (props: Props) => {
className='dialog'
>
<div className='toolbar'>
{title && <h1 className='text-heading5 mt-2'>{title}</h1>}
{title && <h1 className='dialog-title'>{title}</h1>}
{
!props.hideCloseButton &&
<IconButton
className='dialog__close'
onClick={props.onClose}
icon={<CloseIcon/>}
title={closeDialogText}

View file

@ -8,18 +8,6 @@
}
}
.wrapper {
.dialog {
.toolbar {
flex-direction: row-reverse;
padding: 0;
position: absolute;
right: 18px;
top: 18px;
}
}
}
.wrapper {
.dialog {
position: relative;
@ -58,7 +46,7 @@
padding: 0 32px;
cursor: pointer;
overflow: hidden;
&.freesize {
height: unset;
}

View file

@ -107,33 +107,6 @@ exports[`components/sidebarBoardItem sidebar board item 1`] = `
<div
class="menu-options"
>
<div>
<div
aria-label="Delete board"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Delete board
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option boardMoveToCategorySubmenu"
@ -239,6 +212,33 @@ exports[`components/sidebarBoardItem sidebar board item 1`] = `
/>
</div>
</div>
<div>
<div
aria-label="Delete board"
class="MenuOption TextOption menu-option text-danger"
role="button"
>
<div
class="d-flex"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Delete board
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
@ -349,33 +349,6 @@ exports[`components/sidebarBoardItem sidebar board item for guest 1`] = `
<div
class="menu-options"
>
<div>
<div
aria-label="Delete board"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Delete board
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option boardMoveToCategorySubmenu"
@ -429,6 +402,33 @@ exports[`components/sidebarBoardItem sidebar board item for guest 1`] = `
/>
</div>
</div>
<div>
<div
aria-label="Delete board"
class="MenuOption TextOption menu-option text-danger"
role="button"
>
<div
class="d-flex"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Delete board
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"

View file

@ -69,11 +69,12 @@ describe('components/sidebarSidebar', () => {
})
const history = createMemoryHistory()
const onBoardTemplateSelectorOpen = jest.fn()
const component = wrapIntl(
<ReduxProvider store={store}>
<Router history={history}>
<Sidebar/>
<Sidebar onBoardTemplateSelectorOpen={onBoardTemplateSelectorOpen}/>
</Router>
</ReduxProvider>,
)
@ -131,11 +132,12 @@ describe('components/sidebarSidebar', () => {
})
const history = createMemoryHistory()
const onBoardTemplateSelectorOpen = jest.fn()
const component = wrapIntl(
<ReduxProvider store={store}>
<Router history={history}>
<Sidebar/>
<Sidebar onBoardTemplateSelectorOpen={onBoardTemplateSelectorOpen}/>
</Router>
</ReduxProvider>,
)
@ -192,11 +194,12 @@ describe('components/sidebarSidebar', () => {
})
const history = createMemoryHistory()
const onBoardTemplateSelectorOpen = jest.fn()
const component = wrapIntl(
<ReduxProvider store={store}>
<Router history={history}>
<Sidebar/>
<Sidebar onBoardTemplateSelectorOpen={onBoardTemplateSelectorOpen}/>
</Router>
</ReduxProvider>,
)
@ -254,11 +257,12 @@ describe('components/sidebarSidebar', () => {
})
const history = createMemoryHistory()
const onBoardTemplateSelectorOpen = jest.fn()
const component = wrapIntl(
<ReduxProvider store={store}>
<Router history={history}>
<Sidebar/>
<Sidebar onBoardTemplateSelectorOpen={onBoardTemplateSelectorOpen}/>
</Router>
</ReduxProvider>,
)
@ -305,7 +309,7 @@ describe('components/sidebarSidebar', () => {
// const component = wrapIntl(
// <ReduxProvider store={store}>
// <Router history={history}>
// <Sidebar/>
// <Sidebar onBoardTemplateSelectorOpen={onBoardTemplateSelectorOpen}/>
// </Router>
// </ReduxProvider>,
// )

View file

@ -40,7 +40,7 @@ import {addMissingItems} from './utils'
type Props = {
activeBoardId?: string
onBoardTemplateSelectorOpen?: () => void
onBoardTemplateSelectorOpen: () => void
}
function getWindowDimensions() {

View file

@ -235,20 +235,6 @@ const SidebarBoardItem = (props: Props) => {
position='auto'
parentRef={boardItemRef}
>
<BoardPermissionGate
boardId={board.id}
permissions={[Permission.DeleteBoard]}
>
<Menu.Text
key={`deleteBlock-${board.id}`}
id='deleteBlock'
name={intl.formatMessage({id: 'Sidebar.delete-board', defaultMessage: 'Delete board'})}
icon={<DeleteIcon/>}
onClick={() => {
props.onDeleteRequest(board)
}}
/>
</BoardPermissionGate>
<Menu.SubMenu
key={`moveBlock-${board.id}`}
id='moveBlock'
@ -279,6 +265,21 @@ const SidebarBoardItem = (props: Props) => {
icon={<CloseIcon/>}
onClick={() => handleHideBoard()}
/>
<BoardPermissionGate
boardId={board.id}
permissions={[Permission.DeleteBoard]}
>
<Menu.Text
key={`deleteBlock-${board.id}`}
id='deleteBlock'
className='text-danger'
name={intl.formatMessage({id: 'Sidebar.delete-board', defaultMessage: 'Delete board'})}
icon={<DeleteIcon/>}
onClick={() => {
props.onDeleteRequest(board)
}}
/>
</BoardPermissionGate>
</Menu>
</MenuWrapper>
</div>

View file

@ -10,6 +10,7 @@ import {Board} from '../../blocks/board'
import mutator from '../../mutator'
import IconButton from '../../widgets/buttons/iconButton'
import DeleteIcon from '../../widgets/icons/delete'
import CompassIcon from '../../widgets/icons/compassIcon'
import OptionsIcon from '../../widgets/icons/options'
import Menu from '../../widgets/menu'
import MenuWrapper from '../../widgets/menuWrapper'
@ -30,7 +31,6 @@ import {
import {getCurrentCard} from '../../store/cards'
import {Utils} from '../../utils'
import Update from '../../widgets/icons/update'
import { TOUR_SIDEBAR, SidebarTourSteps, TOUR_BOARD, FINISHED } from '../../components/onboardingTour/index'
import telemetryClient, {TelemetryActions, TelemetryCategory} from '../../telemetry/telemetryClient'
@ -244,6 +244,12 @@ const SidebarCategory = (props: Props) => {
{
props.categoryBoards.id !== '' &&
<React.Fragment>
<Menu.Text
id='updateCategory'
name={intl.formatMessage({id: 'SidebarCategories.CategoryMenu.Update', defaultMessage: 'Rename Category'})}
icon={<CompassIcon icon='pencil-outline'/>}
onClick={handleUpdateCategory}
/>
<Menu.Text
id='deleteCategory'
className='text-danger'
@ -251,11 +257,12 @@ const SidebarCategory = (props: Props) => {
icon={<DeleteIcon/>}
onClick={() => setShowDeleteCategoryDialog(true)}
/>
<Menu.Separator/>
<Menu.Text
id='updateCategory'
name={intl.formatMessage({id: 'SidebarCategories.CategoryMenu.Update', defaultMessage: 'Rename Category'})}
icon={<Update/>}
onClick={handleUpdateCategory}
id='createNewCategory'
name={intl.formatMessage({id: 'SidebarCategories.CategoryMenu.CreateNew', defaultMessage: 'Create New Category'})}
icon={<CreateNewFolder/>}
onClick={handleCreateNewCategory}
/>
</React.Fragment>
}
@ -315,21 +322,6 @@ const SidebarCategory = (props: Props) => {
defaultMessage='Create New Category'
/>
)}
onCreate={async (name) => {
if (!me) {
Utils.logError('me not initialized')
return
}
const category: Category = {
name,
userID: me.id,
teamID,
} as Category
await mutator.createCategory(category)
setShowCreateCategoryModal(false)
}}
/>
)
}
@ -345,22 +337,6 @@ const SidebarCategory = (props: Props) => {
/>
)}
onClose={() => setShowUpdateCategoryModal(false)}
onCreate={async (name) => {
if (!me) {
Utils.logError('me not initialized')
return
}
const category: Category = {
name,
id: props.categoryBoards.id,
userID: me.id,
teamID,
} as Category
await mutator.updateCategory(category)
setShowUpdateCategoryModal(false)
}}
/>
)
}

View file

@ -20,7 +20,7 @@ exports[`components/viewLimitDialog/ViewLiimitDialog show notify upgrade button
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>
@ -96,7 +96,7 @@ exports[`components/viewLimitDialog/ViewLiimitDialog show upgrade button for sys
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>

View file

@ -20,7 +20,7 @@ exports[`components/viewLimitDialog/ViewL]imitDialog show notify admin confirmat
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>
@ -130,7 +130,7 @@ exports[`components/viewLimitDialog/ViewL]imitDialog show view limit dialog 1`]
>
<button
aria-label="Close dialog"
class="IconButton size--medium"
class="IconButton dialog__close size--medium"
title="Close dialog"
type="button"
>

View file

@ -61,6 +61,7 @@
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
position: relative;
font-size: 14px;
line-height: 24px;