1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-09-16 08:56:19 +02:00

Merge branch 'main' into category-websocket-broadcast-fix

This commit is contained in:
Harshil Sharma
2022-04-13 15:09:49 +05:30
89 changed files with 3402 additions and 1406 deletions

13
.vscode/launch.json vendored
View File

@@ -13,6 +13,19 @@
"program": "${workspaceFolder}/server/main",
"cwd": "${workspaceFolder}"
},
{
"name": "Go: Launch Single-user Server",
"type": "go",
"request": "launch",
"mode": "debug",
"buildFlags": "-tags 'json1'",
"program": "${workspaceFolder}/server/main",
"cwd": "${workspaceFolder}",
"args": ["-single-user"],
"env": {
"FOCALBOARD_SINGLE_USER_TOKEN": "testtest"
}
},
{
"name": "Go: Launch Windows App",
"type": "go",

View File

@@ -115,6 +115,8 @@ watch-server-test: modd-precheck ## Run server tests watching for changes
server-test: server-test-sqlite server-test-mysql server-test-postgres ## Run server tests
server-test-sqlite: export FB_UNIT_TESTING=1
server-test-sqlite: templates-archive ## Run server tests using sqlite
cd server; go test -tags '$(BUILD_TAGS)' -race -v -count=1 -timeout=30m ./...
@@ -162,7 +164,8 @@ mac-app: server-mac webapp ## Build Mac application.
cp app-config.json mac/resources/config.json
cp -R webapp/pack mac/resources/pack
mkdir -p mac/temp
xcodebuild archive -workspace mac/Focalboard.xcworkspace -scheme Focalboard -archivePath mac/temp/focalboard.xcarchive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED="NO" CODE_SIGNING_ALLOWED="NO"
xcodebuild archive -workspace mac/Focalboard.xcworkspace -scheme Focalboard -archivePath mac/temp/focalboard.xcarchive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED="NO" CODE_SIGNING_ALLOWED="NO" \
|| { echo "xcodebuild failed, did you install the full Xcode and not just the CLI tools?"; exit 1; }
mkdir -p mac/dist
cp -R mac/temp/focalboard.xcarchive/Products/Applications/Focalboard.app mac/dist/
# xcodebuild -exportArchive -archivePath mac/temp/focalboard.xcarchive -exportPath mac/dist -exportOptionsPlist mac/export.plist

View File

@@ -1,8 +1,8 @@
{
"serverRoot": "http://localhost:8000",
"port": 8000,
"dbtype": "sqlite3",
"dbconfig": "./focalboard.db?_busy_timeout=5000",
"port": 8000,
"dbtype": "sqlite3",
"dbconfig": "./focalboard.db?_busy_timeout=5000",
"dbtableprefix": "",
"postgres_dbconfig": "dbname=focalboard sslmode=disable",
"useSSL": false,
@@ -10,7 +10,7 @@
"filesdriver": "local",
"filespath": "./files",
"telemetry": true,
"prometheus_address": ":9092",
"prometheusaddress": ":9092",
"webhook_update": [],
"session_expire_time": 2592000,
"session_refresh_time": 18000,

View File

@@ -8,7 +8,7 @@
"webpath": "./pack",
"filespath": "./files",
"telemetry": true,
"prometheus_address": ":9092",
"prometheusaddress": ":9092",
"session_expire_time": 2592000,
"session_refresh_time": 18000,
"localOnly": false,

25
mac/README.md Normal file
View File

@@ -0,0 +1,25 @@
# Focalboard Mac Personal Desktop
This folder contains the code for the Mac Personal Desktop. It packages a lightweight Swift Mac App with the Mac build of the server, and the webapp. The server is run in a single-user mode.
## Debugging in Xcode
Open `Focalboard.xcworkspace` in Xcode to debug it.
To debug the client webapp:
1. Run the Focalboard desktop app from Xcode
2. Open Safari
3. Enable Safari's [developer tools]
4. Select the Focalboard app from the develop menu, under your computer's name
### Testing the single-user server
You can also run the server in single-user mode and connect to it via a browser:
1. Run `FOCALBOARD_SINGLE_USER_TOKEN=testtest make watch-single-user`
* This runs the server with the `-single-user` flag
* Alternatively, select `Go: Launch Single-user Server` from VSCode's run and debug options
2. Open a browser to `http://localhost:8000`
3. Open the browser developer tools to Application \ Local Storage \ localhost:8000
4. Set `focalboardSessionId` to `testtest`
5. Navigate to `http://localhost:8000`

View File

@@ -8,6 +8,7 @@ import (
"github.com/mattermost/focalboard/server/services/notify/notifymentions"
"github.com/mattermost/focalboard/server/services/notify/notifysubscriptions"
"github.com/mattermost/focalboard/server/services/notify/plugindelivery"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/ws"
@@ -26,19 +27,20 @@ const (
)
type notifyBackendParams struct {
cfg *config.Configuration
client *pluginapi.Client
serverRoot string
logger *mlog.Logger
cfg *config.Configuration
client *pluginapi.Client
permissions permissions.PermissionsService
serverRoot string
logger *mlog.Logger
}
func createMentionsNotifyBackend(params notifyBackendParams) (*notifymentions.Backend, error) {
delivery, err := createDelivery(params.client, params.serverRoot)
delivery, err := createDelivery(params.client, params.serverRoot, params.permissions)
if err != nil {
return nil, err
}
backend := notifymentions.New(delivery, params.logger)
backend := notifymentions.New(delivery, params.permissions, params.logger)
return backend, nil
}
@@ -46,7 +48,7 @@ func createMentionsNotifyBackend(params notifyBackendParams) (*notifymentions.Ba
func createSubscriptionsNotifyBackend(params notifyBackendParams, store store.Store,
wsPluginAdapter ws.PluginAdapterInterface) (*notifysubscriptions.Backend, error) {
//
delivery, err := createDelivery(params.client, params.serverRoot)
delivery, err := createDelivery(params.client, params.serverRoot, params.permissions)
if err != nil {
return nil, err
}
@@ -54,6 +56,7 @@ func createSubscriptionsNotifyBackend(params notifyBackendParams, store store.St
backendParams := notifysubscriptions.BackendParams{
ServerRoot: params.serverRoot,
Store: store,
Permissions: params.permissions,
Delivery: delivery,
WSAdapter: wsPluginAdapter,
Logger: params.logger,
@@ -65,7 +68,7 @@ func createSubscriptionsNotifyBackend(params notifyBackendParams, store store.St
return backend, nil
}
func createDelivery(client *pluginapi.Client, serverRoot string) (*plugindelivery.PluginDelivery, error) {
func createDelivery(client *pluginapi.Client, serverRoot string, permissions permissions.PermissionsService) (*plugindelivery.PluginDelivery, error) {
bot := &model.Bot{
Username: botUsername,
DisplayName: botDisplayname,
@@ -78,7 +81,7 @@ func createDelivery(client *pluginapi.Client, serverRoot string) (*plugindeliver
pluginAPI := &pluginAPIAdapter{client: client}
return plugindelivery.New(botID, serverRoot, pluginAPI), nil
return plugindelivery.New(botID, serverRoot, pluginAPI, permissions), nil
}
type pluginAPIAdapter struct {

View File

@@ -120,10 +120,11 @@ func (p *Plugin) OnActivate() error {
p.wsPluginAdapter = ws.NewPluginAdapter(p.API, auth.New(cfg, db, permissionsService), db, logger)
backendParams := notifyBackendParams{
cfg: cfg,
client: client,
serverRoot: baseURL + "/boards",
logger: logger,
cfg: cfg,
client: client,
permissions: permissionsService,
serverRoot: baseURL + "/boards",
logger: logger,
}
var notifyBackends []notify.Backend

View File

@@ -1,14 +1,14 @@
{
"serverRoot": "http://localhost:8000",
"port": 8000,
"dbtype": "sqlite3",
"serverRoot": "http://localhost:8000",
"port": 8000,
"dbtype": "sqlite3",
"dbconfig": "./focalboard.db",
"postgres_dbconfig": "dbname=focalboard sslmode=disable",
"useSSL": false,
"webpath": "./pack",
"filespath": "./files",
"postgres_dbconfig": "dbname=focalboard sslmode=disable",
"useSSL": false,
"webpath": "./pack",
"filespath": "./files",
"telemetry": true,
"prometheus_address": ":9092",
"prometheusaddress": ":9092",
"session_expire_time": 2592000,
"session_refresh_time": 18000,
"localOnly": false,

View File

@@ -81,6 +81,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
apiv1.HandleFunc("/boards/{boardID}", a.sessionRequired(a.handlePatchBoard)).Methods("PATCH")
apiv1.HandleFunc("/boards/{boardID}", a.sessionRequired(a.handleDeleteBoard)).Methods("DELETE")
apiv1.HandleFunc("/boards/{boardID}/duplicate", a.sessionRequired(a.handleDuplicateBoard)).Methods("POST")
apiv1.HandleFunc("/boards/{boardID}/undelete", a.sessionRequired(a.handleUndeleteBoard)).Methods("POST")
apiv1.HandleFunc("/boards/{boardID}/blocks", a.attachSession(a.handleGetBlocks, false)).Methods("GET")
apiv1.HandleFunc("/boards/{boardID}/blocks", a.sessionRequired(a.handlePostBlocks)).Methods("POST")
apiv1.HandleFunc("/boards/{boardID}/blocks", a.sessionRequired(a.handlePatchBlocks)).Methods("PATCH")
@@ -1103,6 +1104,58 @@ func (a *API) handleUndeleteBlock(w http.ResponseWriter, r *http.Request) {
auditRec.Success()
}
func (a *API) handleUndeleteBoard(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/boards/{boardID}/undelete undeleteBoard
//
// Undeletes a board
//
// ---
// produces:
// - application/json
// parameters:
// - name: boardID
// in: path
// description: ID of board to undelete
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
ctx := r.Context()
session := ctx.Value(sessionContextKey).(*model.Session)
userID := session.UserID
vars := mux.Vars(r)
boardID := vars["boardID"]
auditRec := a.makeAuditRecord(r, "undeleteBoard", audit.Fail)
defer a.audit.LogRecord(audit.LevelModify, auditRec)
auditRec.AddMeta("boardID", boardID)
if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionDeleteBoard) {
a.errorResponse(w, r.URL.Path, http.StatusForbidden, "", PermissionError{"access denied to undelete board"})
return
}
err := a.app.UndeleteBoard(boardID, userID)
if err != nil {
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
return
}
a.logger.Debug("UNDELETE Board", mlog.String("boardID", boardID))
jsonStringResponse(w, http.StatusOK, "{}")
auditRec.Success()
}
func (a *API) handlePatchBlock(w http.ResponseWriter, r *http.Request) {
// swagger:operation PATCH /api/v1/boards/{boardID}/blocks/{blockID} patchBlock
//

View File

@@ -344,7 +344,8 @@ func (a *App) GetBlocksForBoard(boardID string) ([]model.Block, error) {
}
func (a *App) notifyBlockChanged(action notify.Action, block *model.Block, oldBlock *model.Block, modifiedByID string) {
if a.notifications == nil {
// don't notify if notifications service disabled, or block change is generated via system user.
if a.notifications == nil || modifiedByID == model.SystemUserID {
return
}

View File

@@ -85,7 +85,7 @@ func (a *App) getBoardForBlock(blockID string) (*model.Board, error) {
}
func (a *App) getBoardHistory(boardID string, latest bool) (*model.Board, error) {
opts := model.QueryBlockHistoryOptions{
opts := model.QueryBoardHistoryOptions{
Limit: 1,
Descending: latest,
}
@@ -368,3 +368,37 @@ func (a *App) DeleteBoardMember(boardID, userID string) error {
func (a *App) SearchBoardsForUserAndTeam(term, userID, teamID string) ([]*model.Board, error) {
return a.store.SearchBoardsForUserAndTeam(term, userID, teamID)
}
func (a *App) UndeleteBoard(boardID string, modifiedBy string) error {
boards, err := a.store.GetBoardHistory(boardID, model.QueryBoardHistoryOptions{Limit: 1, Descending: true})
if err != nil {
return err
}
if len(boards) == 0 {
// undeleting non-existing board not considered an error
return nil
}
err = a.store.UndeleteBoard(boardID, modifiedBy)
if err != nil {
return err
}
board, err := a.store.GetBoard(boardID)
if err != nil {
return err
}
if board == nil {
a.logger.Error("Error loading the board after undelete, not propagating through websockets or notifications")
return nil
}
a.blockChangeNotifier.Enqueue(func() error {
a.wsAdapter.BroadcastBoardChange(board.TeamID, board)
return nil
})
return nil
}

View File

@@ -49,7 +49,7 @@ func (a *App) initializeTemplates() (bool, error) {
opt := model.ImportArchiveOptions{
TeamID: model.GlobalTeamID,
ModifiedBy: "system",
ModifiedBy: model.SystemUserID,
BlockModifier: fixTemplateBlock,
BoardModifier: fixTemplateBoard,
}
@@ -69,7 +69,7 @@ func (a *App) isInitializationNeeded(boards []*model.Board) (bool, string) {
// look for any built-in template boards with the wrong version number (or no version #).
for _, board := range boards {
// if not built-in board...skip
if board.CreatedBy != "system" {
if board.CreatedBy != model.SystemUserID {
continue
}
if board.TemplateVersion < defaultTemplateVersion {

View File

@@ -472,6 +472,16 @@ func (c *Client) DeleteBoard(boardID string) (bool, *Response) {
return true, BuildResponse(r)
}
func (c *Client) UndeleteBoard(boardID string) (bool, *Response) {
r, err := c.DoAPIPost(c.GetBoardRoute(boardID)+"/undelete", "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
func (c *Client) GetBoard(boardID, readToken string) (*model.Board, *Response) {
url := c.GetBoardRoute(boardID)
if readToken != "" {

View File

@@ -375,58 +375,20 @@ func TestUndeleteBlock(t *testing.T) {
require.NoError(t, resp.Error)
require.Len(t, blocks, initialCount+1)
})
}
func TestGetSubtree(t *testing.T) {
t.Skip("TODO: fix flaky test")
t.Run("Try to undelete a block without permissions", func(t *testing.T) {
// this avoids triggering uniqueness constraint of
// id,insert_at on block history
time.Sleep(10 * time.Millisecond)
th := SetupTestHelperWithToken(t).Start()
defer th.TearDown()
board := th.CreateBoard("team-id", model.BoardTypeOpen)
parentBlockID := utils.NewID(utils.IDTypeBlock)
childBlockID1 := utils.NewID(utils.IDTypeBlock)
childBlockID2 := utils.NewID(utils.IDTypeBlock)
t.Run("Create the block structure", func(t *testing.T) {
newBlocks := []model.Block{
{
ID: parentBlockID,
BoardID: board.ID,
CreateAt: 1,
UpdateAt: 1,
Type: model.TypeCard,
},
{
ID: childBlockID1,
BoardID: board.ID,
ParentID: parentBlockID,
CreateAt: 2,
UpdateAt: 2,
Type: model.TypeCard,
},
{
ID: childBlockID2,
BoardID: board.ID,
ParentID: parentBlockID,
CreateAt: 2,
UpdateAt: 2,
Type: model.TypeCard,
},
}
_, resp := th.Client.InsertBlocks(board.ID, newBlocks)
_, resp := th.Client.DeleteBlock(board.ID, blockID)
require.NoError(t, resp.Error)
_, resp = th.Client2.UndeleteBlock(board.ID, blockID)
th.CheckForbidden(resp)
blocks, resp := th.Client.GetBlocksForBoard(board.ID)
require.NoError(t, resp.Error)
require.Len(t, blocks, 1) // GetBlocks returns root blocks (null ParentID)
blockIDs := make([]string, len(blocks))
for i, b := range blocks {
blockIDs[i] = b.ID
}
require.Contains(t, blockIDs, parentBlockID)
require.Len(t, blocks, initialCount)
})
}

View File

@@ -2,6 +2,7 @@ package integrationtests
import (
"encoding/json"
"sort"
"testing"
"time"
@@ -255,6 +256,68 @@ func TestCreateBoard(t *testing.T) {
})
}
func TestGetAllBlocksForBoard(t *testing.T) {
th := SetupTestHelperWithToken(t).Start()
defer th.TearDown()
board := th.CreateBoard("board-id", model.BoardTypeOpen)
parentBlockID := utils.NewID(utils.IDTypeBlock)
childBlockID1 := utils.NewID(utils.IDTypeBlock)
childBlockID2 := utils.NewID(utils.IDTypeBlock)
t.Run("Create the block structure", func(t *testing.T) {
newBlocks := []model.Block{
{
ID: parentBlockID,
BoardID: board.ID,
CreateAt: 1,
UpdateAt: 1,
Type: model.TypeCard,
},
{
ID: childBlockID1,
BoardID: board.ID,
ParentID: parentBlockID,
CreateAt: 2,
UpdateAt: 2,
Type: model.TypeCard,
},
{
ID: childBlockID2,
BoardID: board.ID,
ParentID: parentBlockID,
CreateAt: 2,
UpdateAt: 2,
Type: model.TypeCard,
},
}
insertedBlocks, resp := th.Client.InsertBlocks(board.ID, newBlocks)
require.NoError(t, resp.Error)
require.Len(t, insertedBlocks, len(newBlocks))
insertedBlockIDs := make([]string, len(insertedBlocks))
for i, b := range insertedBlocks {
insertedBlockIDs[i] = b.ID
}
fetchedBlocks, resp := th.Client.GetAllBlocksForBoard(board.ID)
require.NoError(t, resp.Error)
require.Len(t, fetchedBlocks, len(newBlocks))
fetchedblockIDs := make([]string, len(fetchedBlocks))
for i, b := range fetchedBlocks {
fetchedblockIDs[i] = b.ID
}
sort.Strings(insertedBlockIDs)
sort.Strings(fetchedblockIDs)
require.Equal(t, insertedBlockIDs, fetchedblockIDs)
})
}
func TestSearchBoards(t *testing.T) {
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
@@ -853,6 +916,128 @@ func TestDeleteBoard(t *testing.T) {
})
}
func TestUndeleteBoard(t *testing.T) {
teamID := testTeamID
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "user-id", false)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckUnauthorized(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Nil(t, dbBoard)
})
t.Run("a user without membership should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "some-user-id", false)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "some-user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckForbidden(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Nil(t, dbBoard)
})
t.Run("a user with membership but without permissions should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "some-user-id", false)
require.NoError(t, err)
newUser2Member := &model.BoardMember{
UserID: "user-id",
BoardID: board.ID,
SchemeEditor: true,
}
_, err = th.Server.App().AddMemberToBoard(newUser2Member)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "some-user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckForbidden(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Nil(t, dbBoard)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
success, resp := th.Client.UndeleteBoard("non-existing-board")
th.CheckForbidden(resp)
require.False(t, success)
})
t.Run("an existing deleted board should be correctly undeleted", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckOK(resp)
require.True(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.NotNil(t, dbBoard)
})
}
func TestGetMembersForBoard(t *testing.T) {
teamID := testTeamID
@@ -1479,6 +1664,9 @@ func TestGetTemplates(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
err := th.Server.App().InitTemplates()
require.NoError(t, err, "InitTemplates should not fail")
teamID := "my-team-id"
rBoards, resp := th.Client.GetTemplatesForTeam("0")
th.CheckOK(resp)

View File

@@ -259,6 +259,9 @@ func TestPermissionsGetTeamTemplates(t *testing.T) {
testData := setupData(t, th)
clients := setupClients(th)
err := th.Server.App().InitTemplates()
require.NoError(t, err, "InitTemplates should succeed")
builtInTemplateCount := 7
ttCases := []TestCase{
@@ -843,6 +846,57 @@ func TestPermissionsUndeleteBoardBlock(t *testing.T) {
runTestCases(t, ttCases, testData, clients)
}
func TestPermissionsUndeleteBoard(t *testing.T) {
th := SetupTestHelperPluginMode(t)
defer th.TearDown()
testData := setupData(t, th)
clients := setupClients(th)
err := th.Server.App().DeleteBoard(testData.publicBoard.ID, userAdmin)
require.NoError(t, err)
err = th.Server.App().DeleteBoard(testData.privateBoard.ID, userAdmin)
require.NoError(t, err)
err = th.Server.App().DeleteBoard(testData.publicTemplate.ID, userAdmin)
require.NoError(t, err)
err = th.Server.App().DeleteBoard(testData.privateTemplate.ID, userAdmin)
require.NoError(t, err)
ttCases := []TestCase{
{"/boards/{PRIVATE_BOARD_ID}/undelete", methodPost, "", userAnon, http.StatusUnauthorized, 0},
{"/boards/{PRIVATE_BOARD_ID}/undelete", methodPost, "", userNoTeamMember, http.StatusForbidden, 0},
{"/boards/{PRIVATE_BOARD_ID}/undelete", methodPost, "", userTeamMember, http.StatusForbidden, 0},
{"/boards/{PRIVATE_BOARD_ID}/undelete", methodPost, "", userViewer, http.StatusForbidden, 0},
{"/boards/{PRIVATE_BOARD_ID}/undelete", methodPost, "", userCommenter, http.StatusForbidden, 0},
{"/boards/{PRIVATE_BOARD_ID}/undelete", methodPost, "", userEditor, http.StatusForbidden, 0},
{"/boards/{PRIVATE_BOARD_ID}/undelete", methodPost, "", userAdmin, http.StatusOK, 0},
{"/boards/{PUBLIC_BOARD_ID}/undelete", methodPost, "", userAnon, http.StatusUnauthorized, 0},
{"/boards/{PUBLIC_BOARD_ID}/undelete", methodPost, "", userNoTeamMember, http.StatusForbidden, 0},
{"/boards/{PUBLIC_BOARD_ID}/undelete", methodPost, "", userTeamMember, http.StatusForbidden, 0},
{"/boards/{PUBLIC_BOARD_ID}/undelete", methodPost, "", userViewer, http.StatusForbidden, 0},
{"/boards/{PUBLIC_BOARD_ID}/undelete", methodPost, "", userCommenter, http.StatusForbidden, 0},
{"/boards/{PUBLIC_BOARD_ID}/undelete", methodPost, "", userEditor, http.StatusForbidden, 0},
{"/boards/{PUBLIC_BOARD_ID}/undelete", methodPost, "", userAdmin, http.StatusOK, 0},
{"/boards/{PRIVATE_TEMPLATE_ID}/undelete", methodPost, "", userAnon, http.StatusUnauthorized, 0},
{"/boards/{PRIVATE_TEMPLATE_ID}/undelete", methodPost, "", userNoTeamMember, http.StatusForbidden, 0},
{"/boards/{PRIVATE_TEMPLATE_ID}/undelete", methodPost, "", userTeamMember, http.StatusForbidden, 0},
{"/boards/{PRIVATE_TEMPLATE_ID}/undelete", methodPost, "", userViewer, http.StatusForbidden, 0},
{"/boards/{PRIVATE_TEMPLATE_ID}/undelete", methodPost, "", userCommenter, http.StatusForbidden, 0},
{"/boards/{PRIVATE_TEMPLATE_ID}/undelete", methodPost, "", userEditor, http.StatusForbidden, 0},
{"/boards/{PRIVATE_TEMPLATE_ID}/undelete", methodPost, "", userAdmin, http.StatusOK, 0},
{"/boards/{PUBLIC_TEMPLATE_ID}/undelete", methodPost, "", userAnon, http.StatusUnauthorized, 0},
{"/boards/{PUBLIC_TEMPLATE_ID}/undelete", methodPost, "", userNoTeamMember, http.StatusForbidden, 0},
{"/boards/{PUBLIC_TEMPLATE_ID}/undelete", methodPost, "", userTeamMember, http.StatusForbidden, 0},
{"/boards/{PUBLIC_TEMPLATE_ID}/undelete", methodPost, "", userViewer, http.StatusForbidden, 0},
{"/boards/{PUBLIC_TEMPLATE_ID}/undelete", methodPost, "", userCommenter, http.StatusForbidden, 0},
{"/boards/{PUBLIC_TEMPLATE_ID}/undelete", methodPost, "", userEditor, http.StatusForbidden, 0},
{"/boards/{PUBLIC_TEMPLATE_ID}/undelete", methodPost, "", userAdmin, http.StatusOK, 0},
}
runTestCases(t, ttCases, testData, clients)
}
func TestPermissionsDuplicateBoardBlock(t *testing.T) {
th := SetupTestHelperPluginMode(t)
defer th.TearDown()
@@ -2075,6 +2129,9 @@ func TestPermissionsOnboard(t *testing.T) {
testData := setupData(t, th)
clients := setupClients(th)
err := th.Server.App().InitTemplates()
require.NoError(t, err, "InitTemplates should not fail")
ttCases := []TestCase{
{"/teams/test-team/onboard", methodPost, "", userAnon, http.StatusUnauthorized, 0},
{"/teams/test-team/onboard", methodPost, "", userNoTeamMember, http.StatusForbidden, 0},

View File

@@ -187,6 +187,14 @@ type QueryBlockHistoryOptions struct {
Descending bool // if true then the records are sorted by insert_at in descending order
}
// QueryBoardHistoryOptions are query options that can be passed to GetBoardHistory.
type QueryBoardHistoryOptions struct {
BeforeUpdateAt int64 // if non-zero then filter for records with update_at less than BeforeUpdateAt
AfterUpdateAt int64 // if non-zero then filter for records with update_at greater than AfterUpdateAt
Limit uint64 // if non-zero then limit the number of returned records
Descending bool // if true then the records are sorted by insert_at in descending order
}
func StampModificationMetadata(userID string, blocks []Block, auditRec *audit.Record) {
if userID == SingleUser {
userID = ""

View File

@@ -8,6 +8,7 @@ import (
const (
SingleUser = "single-user"
GlobalTeamID = "0"
SystemUserID = "system"
)
// User is a user

View File

@@ -130,14 +130,15 @@ func New(params Params) (*Server, error) {
}
appServices := app.Services{
Auth: authenticator,
Store: params.DBStore,
FilesBackend: filesBackend,
Webhook: webhookClient,
Metrics: metricsService,
Notifications: notificationService,
Logger: params.Logger,
Permissions: params.PermissionsService,
Auth: authenticator,
Store: params.DBStore,
FilesBackend: filesBackend,
Webhook: webhookClient,
Metrics: metricsService,
Notifications: notificationService,
Logger: params.Logger,
Permissions: params.PermissionsService,
SkipTemplateInit: utils.IsRunningUnitTests(),
}
app := app.New(params.Cfg, wsAdapter, appServices)
@@ -203,11 +204,6 @@ func New(params Params) (*Server, error) {
server.initHandlers()
if err := app.InitTemplates(); err != nil {
params.Logger.Error("Unable initialize team templates", mlog.Err(err))
return nil, err
}
return &server, nil
}

View File

@@ -40,7 +40,7 @@ type Configuration struct {
MaxFileSize int64 `json:"maxfilesize" mapstructure:"mafilesize"`
Telemetry bool `json:"telemetry" mapstructure:"telemetry"`
TelemetryID string `json:"telemetryid" mapstructure:"telemetryid"`
PrometheusAddress string `json:"prometheus_address" mapstructure:"prometheus_address"`
PrometheusAddress string `json:"prometheusaddress" mapstructure:"prometheusaddress"`
WebhookUpdate []string `json:"webhook_update" mapstructure:"webhook_update"`
Secret string `json:"secret" mapstructure:"secret"`
SessionExpireTime int64 `json:"session_expire_time" mapstructure:"session_expire_time"`
@@ -95,6 +95,7 @@ func ReadConfigFile(configFilePath string) (*Configuration, error) {
viper.SetDefault("AuthMode", "native")
viper.SetDefault("NotifyFreqCardSeconds", 120) // 2 minutes after last card edit
viper.SetDefault("NotifyFreqBoardSeconds", 86400) // 1 day after last card edit
viper.SetDefault("PrometheusAddress", "")
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file

View File

@@ -9,6 +9,7 @@ import (
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/notify"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/wiggin77/merror"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -24,17 +25,19 @@ type MentionListener interface {
// Backend provides the notification backend for @mentions.
type Backend struct {
delivery MentionDelivery
logger *mlog.Logger
delivery MentionDelivery
permissions permissions.PermissionsService
logger *mlog.Logger
mux sync.RWMutex
listeners []MentionListener
}
func New(delivery MentionDelivery, logger *mlog.Logger) *Backend {
func New(delivery MentionDelivery, permissions permissions.PermissionsService, logger *mlog.Logger) *Backend {
return &Backend{
delivery: delivery,
logger: logger,
delivery: delivery,
permissions: permissions,
logger: logger,
}
}
@@ -80,7 +83,9 @@ func (b *Backend) BlockChanged(evt notify.BlockChangeEvent) error {
return nil
}
if evt.BlockChanged.Type != model.TypeText && evt.BlockChanged.Type != model.TypeComment {
switch evt.BlockChanged.Type {
case model.TypeText, model.TypeComment, model.TypeImage:
default:
return nil
}
@@ -110,6 +115,11 @@ func (b *Backend) BlockChanged(evt notify.BlockChangeEvent) error {
merr.Append(fmt.Errorf("cannot deliver notification for @%s: %w", username, err))
}
if userID == "" {
// was a `@` followed by something other than a username.
continue
}
b.logger.Debug("Mention notification delivered",
mlog.String("user", username),
mlog.Int("listener_count", len(listeners)),

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/utils"
"github.com/wiggin77/merror"
@@ -31,10 +32,11 @@ var (
// via notifications hints written to the database so that fewer notifications are sent for active
// blocks.
type notifier struct {
serverRoot string
store Store
delivery SubscriptionDelivery
logger *mlog.Logger
serverRoot string
store Store
permissions permissions.PermissionsService
delivery SubscriptionDelivery
logger *mlog.Logger
hints chan *model.NotificationHint
@@ -44,12 +46,13 @@ type notifier struct {
func newNotifier(params BackendParams) *notifier {
return &notifier{
serverRoot: params.ServerRoot,
store: params.Store,
delivery: params.Delivery,
logger: params.Logger,
done: nil,
hints: make(chan *model.NotificationHint, hintQueueSize),
serverRoot: params.ServerRoot,
store: params.Store,
permissions: params.Permissions,
delivery: params.Delivery,
logger: params.Logger,
done: nil,
hints: make(chan *model.NotificationHint, hintQueueSize),
}
}
@@ -216,7 +219,7 @@ func (n *notifier) notifySubscribers(hint *model.NotificationHint) error {
// don't notify the author of their own changes.
authorName, isAuthor := diffAuthors[sub.SubscriberID]
if isAuthor && len(diffAuthors) == 1 {
n.logger.Debug("notifySubscribers - deliver, skipping author",
n.logger.Debug("notifySubscribers - skipping author",
mlog.Any("hint", hint),
mlog.String("author_id", sub.SubscriberID),
mlog.String("author_username", authorName),
@@ -224,6 +227,16 @@ func (n *notifier) notifySubscribers(hint *model.NotificationHint) error {
continue
}
// make sure the subscriber still has permissions for the board.
if !n.permissions.HasPermissionToBoard(sub.SubscriberID, board.ID, model.PermissionViewBoard) {
n.logger.Debug("notifySubscribers - skipping non-board member",
mlog.Any("hint", hint),
mlog.String("subscriber_id", sub.SubscriberID),
mlog.String("board_id", board.ID),
)
continue
}
n.logger.Debug("notifySubscribers - deliver",
mlog.Any("hint", hint),
mlog.String("modified_by_id", hint.ModifiedByID),

View File

@@ -17,6 +17,8 @@ type Store interface {
GetUserByID(userID string) (*model.User, error)
GetMemberForBoard(boardID, userID string) (*model.BoardMember, error)
CreateSubscription(sub *model.Subscription) (*model.Subscription, error)
GetSubscribersForBlock(blockID string) ([]*model.Subscriber, error)
GetSubscribersCountForBlock(blockID string) (int, error)

View File

@@ -9,6 +9,7 @@ import (
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/notify"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/ws"
"github.com/wiggin77/merror"
@@ -22,6 +23,7 @@ const (
type BackendParams struct {
ServerRoot string
Store Store
Permissions permissions.PermissionsService
Delivery SubscriptionDelivery
WSAdapter ws.Adapter
Logger *mlog.Logger
@@ -32,6 +34,7 @@ type BackendParams struct {
// Backend provides the notification backend for subscriptions.
type Backend struct {
store Store
permissions permissions.PermissionsService
delivery SubscriptionDelivery
notifier *notifier
wsAdapter ws.Adapter
@@ -44,6 +47,7 @@ func New(params BackendParams) *Backend {
return &Backend{
store: params.Store,
delivery: params.Delivery,
permissions: params.Permissions,
notifier: newNotifier(params),
wsAdapter: params.WSAdapter,
logger: params.Logger,
@@ -179,6 +183,10 @@ func (b *Backend) OnMention(userID string, evt notify.BlockChangeEvent) {
return
}
// TODO: Automatically add user to board? Fail and show UI? Waiting for PM decision.
// Currently the subscription created below will only notify if the user is already
// a member of the board.
sub := &model.Subscription{
BlockType: model.TypeCard,
BlockID: evt.Card.ID,

View File

@@ -4,6 +4,7 @@
package plugindelivery
import (
"errors"
"fmt"
"github.com/mattermost/focalboard/server/services/notify"
@@ -12,9 +13,13 @@ import (
"github.com/mattermost/mattermost-server/v6/model"
)
var (
ErrMentionPermission = errors.New("mention not permitted")
)
// MentionDeliver notifies a user they have been mentioned in a block.
func (pd *PluginDelivery) MentionDeliver(mentionUsername string, extract string, evt notify.BlockChangeEvent) (string, error) {
member, err := teamMemberFromUsername(pd.api, mentionUsername, evt.TeamID)
user, err := userByUsername(pd.api, mentionUsername)
if err != nil {
if isErrNotFound(err) {
// not really an error; could just be someone typed "@sometext"
@@ -24,12 +29,17 @@ func (pd *PluginDelivery) MentionDeliver(mentionUsername string, extract string,
}
}
// make sure mentioned user has permissions to team.
if !pd.permissions.HasPermissionToTeam(user.Id, evt.TeamID, model.PermissionViewTeam) {
return "", fmt.Errorf("mentioned user %s not member of team %s: %w", user.Id, evt.TeamID, ErrMentionPermission)
}
author, err := pd.api.GetUserByID(evt.ModifiedByID)
if err != nil {
return "", fmt.Errorf("cannot find user: %w", err)
}
channel, err := pd.api.GetDirectChannel(member.UserId, pd.botID)
channel, err := pd.api.GetDirectChannel(user.Id, pd.botID)
if err != nil {
return "", fmt.Errorf("cannot get direct channel: %w", err)
}
@@ -40,5 +50,5 @@ func (pd *PluginDelivery) MentionDeliver(mentionUsername string, extract string,
ChannelId: channel.Id,
Message: formatMessage(author.Username, extract, evt.Card.Title, link, evt.BlockChanged),
}
return member.UserId, pd.api.CreatePost(post)
return user.Id, pd.api.CreatePost(post)
}

View File

@@ -4,10 +4,7 @@
package plugindelivery
import (
"fmt"
"github.com/mattermost/focalboard/server/services/notify"
"github.com/mattermost/focalboard/server/utils"
"github.com/mattermost/focalboard/server/services/permissions"
mm_model "github.com/mattermost/mattermost-server/v6/model"
)
@@ -42,45 +39,17 @@ type PluginAPI interface {
// PluginDelivery provides ability to send notifications to direct message channels via Mattermost plugin API.
type PluginDelivery struct {
botID string
serverRoot string
api PluginAPI
botID string
serverRoot string
api PluginAPI
permissions permissions.PermissionsService
}
func New(botID string, serverRoot string, api PluginAPI) *PluginDelivery {
func New(botID string, serverRoot string, api PluginAPI, permissions permissions.PermissionsService) *PluginDelivery {
return &PluginDelivery{
botID: botID,
serverRoot: serverRoot,
api: api,
botID: botID,
serverRoot: serverRoot,
api: api,
permissions: permissions,
}
}
func (pd *PluginDelivery) Deliver(mentionUsername string, extract string, evt notify.BlockChangeEvent) error {
member, err := teamMemberFromUsername(pd.api, mentionUsername, evt.TeamID)
if err != nil {
if isErrNotFound(err) {
// not really an error; could just be someone typed "@sometext"
return nil
} else {
return fmt.Errorf("cannot lookup mentioned user: %w", err)
}
}
author, err := pd.api.GetUserByID(evt.ModifiedByID)
if err != nil {
return fmt.Errorf("cannot find user: %w", err)
}
channel, err := pd.api.GetDirectChannel(member.UserId, pd.botID)
if err != nil {
return fmt.Errorf("cannot get direct channel: %w", err)
}
link := utils.MakeCardLink(pd.serverRoot, evt.TeamID, evt.Board.ID, evt.Card.ID)
post := &mm_model.Post{
UserId: pd.botID,
ChannelId: channel.Id,
Message: formatMessage(author.Username, extract, evt.Card.Title, link, evt.BlockChanged),
}
return pd.api.CreatePost(post)
}

View File

@@ -13,7 +13,7 @@ const (
usernameSpecialChars = ".-_ "
)
func teamMemberFromUsername(api PluginAPI, username string, teamID string) (*mm_model.TeamMember, error) {
func userByUsername(api PluginAPI, username string) (*mm_model.User, error) {
// check for usernames that might have trailing punctuation
var user *mm_model.User
var err error
@@ -36,13 +36,7 @@ func teamMemberFromUsername(api PluginAPI, username string, teamID string) (*mm_
return nil, err
}
// make sure user is member of team.
member, err := api.GetTeamMember(teamID, user.Id)
if err != nil {
return nil, err
}
return member, nil
return user, nil
}
// trimUsernameSpecialChar tries to remove the last character from word if it

View File

@@ -42,43 +42,35 @@ var (
}
)
func userToMember(user *mm_model.User, teamID string) *mm_model.TeamMember {
return &mm_model.TeamMember{
TeamId: teamID,
UserId: user.Id,
}
}
func Test_teamMemberFromUsername(t *testing.T) {
func Test_userByUsername(t *testing.T) {
delivery := newPlugAPIMock(mockUsers)
tests := []struct {
name string
uname string
teamID string
want *mm_model.TeamMember
want *mm_model.User
wantErr bool
}{
{name: "user1", uname: user1.Username, teamID: defTeamID, want: userToMember(user1, defTeamID), wantErr: false},
{name: "user1 with period", uname: user1.Username + ".", teamID: defTeamID, want: userToMember(user1, defTeamID), wantErr: false},
{name: "user1 with period plus more", uname: user1.Username + ". ", teamID: defTeamID, want: userToMember(user1, defTeamID), wantErr: false},
{name: "user2 with periods", uname: user2.Username + "...", teamID: defTeamID, want: userToMember(user2, defTeamID), wantErr: false},
{name: "user2 with underscore", uname: user2.Username + "_", teamID: defTeamID, want: userToMember(user2, defTeamID), wantErr: false},
{name: "user2 with hyphen plus more", uname: user2.Username + "- ", teamID: defTeamID, want: userToMember(user2, defTeamID), wantErr: false},
{name: "user2 with hyphen plus all", uname: user2.Username + ".-_ ", teamID: defTeamID, want: userToMember(user2, defTeamID), wantErr: false},
{name: "user3 with underscore", uname: user3.Username + "_", teamID: defTeamID, want: userToMember(user3, defTeamID), wantErr: false},
{name: "user4 missing", uname: user4.Username, want: nil, teamID: defTeamID, wantErr: true},
{name: "user5 wrong team", uname: user5.Username, teamID: "bogus_team", want: nil, wantErr: true},
{name: "user1", uname: user1.Username, want: user1, wantErr: false},
{name: "user1 with period", uname: user1.Username + ".", want: user1, wantErr: false},
{name: "user1 with period plus more", uname: user1.Username + ". ", want: user1, wantErr: false},
{name: "user2 with periods", uname: user2.Username + "...", want: user2, wantErr: false},
{name: "user2 with underscore", uname: user2.Username + "_", want: user2, wantErr: false},
{name: "user2 with hyphen plus more", uname: user2.Username + "- ", want: user2, wantErr: false},
{name: "user2 with hyphen plus all", uname: user2.Username + ".-_ ", want: user2, wantErr: false},
{name: "user3 with underscore", uname: user3.Username + "_", want: user3, wantErr: false},
{name: "user4 missing", uname: user4.Username, want: nil, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := teamMemberFromUsername(delivery, tt.uname, tt.teamID)
got, err := userByUsername(delivery, tt.uname)
if (err != nil) != tt.wantErr {
t.Errorf("userFromUsername() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("userByUsername() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("userFromUsername()\ngot:\n%v\nwant:\n%v\n", got, tt.want)
t.Errorf("userByUsername()\ngot:\n%v\nwant:\n%v\n", got, tt.want)
}
})
}

View File

@@ -44,9 +44,16 @@ func (s *Service) HasPermissionToBoard(userID, boardID string, permission *mmMod
board, err := s.store.GetBoard(boardID)
if errors.Is(err, sql.ErrNoRows) {
return false
}
if err != nil {
var boards []*model.Board
boards, err = s.store.GetBoardHistory(boardID, model.QueryBoardHistoryOptions{Limit: 1, Descending: true})
if err != nil {
return false
}
if len(boards) == 0 {
return false
}
board = boards[0]
} else if err != nil {
s.api.LogError("error getting board",
"boardID", boardID,
"userID", userID,

View File

@@ -94,6 +94,11 @@ func TestHasPermissionToBoard(t *testing.T) {
Return(nil, sql.ErrNoRows).
Times(1)
th.store.EXPECT().
GetBoardHistory(boardID, model.QueryBoardHistoryOptions{Limit: 1, Descending: true}).
Return(nil, sql.ErrNoRows).
Times(1)
hasPermission := th.permissions.HasPermissionToBoard(userID, boardID, model.PermissionManageBoardCards)
assert.False(t, hasPermission)
})

View File

@@ -49,6 +49,21 @@ func (mr *MockStoreMockRecorder) GetBoard(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoard", reflect.TypeOf((*MockStore)(nil).GetBoard), arg0)
}
// GetBoardHistory mocks base method.
func (m *MockStore) GetBoardHistory(arg0 string, arg1 model.QueryBoardHistoryOptions) ([]*model.Board, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBoardHistory", arg0, arg1)
ret0, _ := ret[0].([]*model.Board)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetBoardHistory indicates an expected call of GetBoardHistory.
func (mr *MockStoreMockRecorder) GetBoardHistory(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoardHistory", reflect.TypeOf((*MockStore)(nil).GetBoardHistory), arg0, arg1)
}
// GetMemberForBoard mocks base method.
func (m *MockStore) GetMemberForBoard(arg0, arg1 string) (*model.BoardMember, error) {
m.ctrl.T.Helper()

View File

@@ -18,4 +18,5 @@ type PermissionsService interface {
type Store interface {
GetBoard(boardID string) (*model.Board, error)
GetMemberForBoard(boardID, userID string) (*model.BoardMember, error)
GetBoardHistory(boardID string, opts model.QueryBoardHistoryOptions) ([]*model.Board, error)
}

View File

@@ -522,7 +522,7 @@ func (mr *MockStoreMockRecorder) GetBoardAndCardByID(arg0 interface{}) *gomock.C
}
// GetBoardHistory mocks base method.
func (m *MockStore) GetBoardHistory(arg0 string, arg1 model.QueryBlockHistoryOptions) ([]*model.Board, error) {
func (m *MockStore) GetBoardHistory(arg0 string, arg1 model.QueryBoardHistoryOptions) ([]*model.Board, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBoardHistory", arg0, arg1)
ret0, _ := ret[0].([]*model.Board)
@@ -1215,6 +1215,20 @@ func (mr *MockStoreMockRecorder) UndeleteBlock(arg0, arg1 interface{}) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UndeleteBlock", reflect.TypeOf((*MockStore)(nil).UndeleteBlock), arg0, arg1)
}
// UndeleteBoard mocks base method.
func (m *MockStore) UndeleteBoard(arg0, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UndeleteBoard", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UndeleteBoard indicates an expected call of UndeleteBoard.
func (mr *MockStoreMockRecorder) UndeleteBoard(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UndeleteBoard", reflect.TypeOf((*MockStore)(nil).UndeleteBoard), arg0, arg1)
}
// UpdateCategory mocks base method.
func (m *MockStore) UpdateCategory(arg0 model.Category) error {
m.ctrl.T.Helper()

View File

@@ -27,8 +27,8 @@ func boardFields(prefix string) []string {
fields := []string{
"id",
"team_id",
"channel_id",
"created_by",
"COALESCE(channel_id, '')",
"COALESCE(created_by, '')",
"modified_by",
"type",
"title",
@@ -657,7 +657,7 @@ func (s *SQLStore) searchBoardsForUserAndTeam(db sq.BaseRunner, term, userID, te
return s.boardsFromRows(rows)
}
func (s *SQLStore) getBoardHistory(db sq.BaseRunner, boardID string, opts model.QueryBlockHistoryOptions) ([]*model.Board, error) {
func (s *SQLStore) getBoardHistory(db sq.BaseRunner, boardID string, opts model.QueryBoardHistoryOptions) ([]*model.Board, error) {
var order string
if opts.Descending {
order = " DESC "
@@ -691,6 +691,98 @@ func (s *SQLStore) getBoardHistory(db sq.BaseRunner, boardID string, opts model.
return s.boardsFromRows(rows)
}
func (s *SQLStore) undeleteBoard(db sq.BaseRunner, boardID string, modifiedBy string) error {
boards, err := s.getBoardHistory(db, boardID, model.QueryBoardHistoryOptions{Limit: 1, Descending: true})
if err != nil {
return err
}
if len(boards) == 0 {
s.logger.Warn("undeleteBlock board not found", mlog.String("board_id", boardID))
return nil // undeleting non-existing board is not considered an error (for now)
}
board := boards[0]
if board.DeleteAt == 0 {
s.logger.Warn("undeleteBlock board not deleted", mlog.String("board_id", board.ID))
return nil // undeleting not deleted board is not considered an error (for now)
}
propertiesJSON, err := json.Marshal(board.Properties)
if err != nil {
return err
}
cardPropertiesJSON, err := json.Marshal(board.CardProperties)
if err != nil {
return err
}
columnCalculationsJSON, err := json.Marshal(board.ColumnCalculations)
if err != nil {
return err
}
now := utils.GetMillis()
columns := []string{
"id",
"team_id",
"channel_id",
"created_by",
"modified_by",
"type",
"title",
"description",
"icon",
"show_description",
"is_template",
"template_version",
"properties",
"card_properties",
"column_calculations",
"create_at",
"update_at",
"delete_at",
}
values := []interface{}{
board.ID,
board.TeamID,
"",
board.CreatedBy,
modifiedBy,
board.Type,
board.Title,
board.Description,
board.Icon,
board.ShowDescription,
board.IsTemplate,
board.TemplateVersion,
propertiesJSON,
cardPropertiesJSON,
columnCalculationsJSON,
board.CreateAt,
now,
0,
}
insertHistoryQuery := s.getQueryBuilder(db).Insert(s.tablePrefix + "boards_history").
Columns(columns...).
Values(values...)
insertQuery := s.getQueryBuilder(db).Insert(s.tablePrefix + "boards").
Columns(columns...).
Values(values...)
if _, err := insertHistoryQuery.Exec(); err != nil {
return err
}
if _, err := insertQuery.Exec(); err != nil {
return err
}
return nil
}
func (s *SQLStore) getBoardMemberHistory(db sq.BaseRunner, boardID, userID string, limit uint64) ([]*model.BoardMemberHistoryEntry, error) {
query := s.getQueryBuilder(db).
Select("board_id", "user_id", "action", "insert_at").

View File

@@ -10,7 +10,6 @@ import (
"github.com/mattermost/focalboard/server/utils"
"path/filepath"
"strconv"
"text/template"
@@ -147,7 +146,7 @@ func (s *SQLStore) Migrate() error {
migrationAssets := &embedded.AssetSource{
Names: assetNamesForDriver,
AssetFunc: func(name string) ([]byte, error) {
asset, mErr := assets.ReadFile(filepath.Join("migrations", name))
asset, mErr := assets.ReadFile("migrations/" + name)
if mErr != nil {
return nil, mErr
}

View File

@@ -228,7 +228,7 @@ CREATE TABLE {{.prefix}}boards_history (
COALESCE(title, ''),
json_extract(fields, '$.description'),
json_extract(fields, '$.icon'), json_extract(fields, '$.showDescription'), json_extract(fields, '$.isTemplate'),
json_extract(fields, '$.templateVer'),
COALESCE(json_extract(fields, '$.templateVer'), 0),
'{}', json_extract(fields, '$.cardProperties'), json_extract(fields, '$.columnCalculations'), create_at,
update_at, delete_at
FROM {{.prefix}}blocks
@@ -239,7 +239,7 @@ CREATE TABLE {{.prefix}}boards_history (
COALESCE(title, ''),
json_extract(fields, '$.description'),
json_extract(fields, '$.icon'), json_extract(fields, '$.showDescription'), json_extract(fields, '$.isTemplate'),
json_extract(fields, '$.templateVer'),
COALESCE(json_extract(fields, '$.templateVer'), 0),
'{}', json_extract(fields, '$.cardProperties'), json_extract(fields, '$.columnCalculations'), create_at,
update_at, delete_at
FROM {{.prefix}}blocks_history

View File

@@ -315,7 +315,7 @@ func (s *SQLStore) GetBoardAndCardByID(blockID string) (*model.Board, *model.Blo
}
func (s *SQLStore) GetBoardHistory(boardID string, opts model.QueryBlockHistoryOptions) ([]*model.Board, error) {
func (s *SQLStore) GetBoardHistory(boardID string, opts model.QueryBoardHistoryOptions) ([]*model.Board, error) {
return s.getBoardHistory(s.db, boardID, opts)
}
@@ -692,6 +692,30 @@ func (s *SQLStore) UndeleteBlock(blockID string, modifiedBy string) error {
}
func (s *SQLStore) UndeleteBoard(boardID string, modifiedBy string) error {
if s.dbType == model.SqliteDBType {
return s.undeleteBoard(s.db, boardID, modifiedBy)
}
tx, txErr := s.db.BeginTx(context.Background(), nil)
if txErr != nil {
return txErr
}
err := s.undeleteBoard(tx, boardID, modifiedBy)
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
s.logger.Error("transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "UndeleteBoard"))
}
return err
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}
func (s *SQLStore) UpdateCategory(category model.Category) error {
return s.updateCategory(s.db, category)

View File

@@ -18,7 +18,7 @@ var (
func (s *SQLStore) removeDefaultTemplates(db sq.BaseRunner, boards []*model.Board) error {
count := 0
for _, board := range boards {
if board.CreatedBy != "system" {
if board.CreatedBy != model.SystemUserID {
continue
}
// default template deletion does not need to go to blocks_history

View File

@@ -27,13 +27,15 @@ type Store interface {
InsertBlocks(blocks []model.Block, userID string) error
// @withTransaction
UndeleteBlock(blockID string, modifiedBy string) error
// @withTransaction
UndeleteBoard(boardID string, modifiedBy string) error
GetBlockCountsByType() (map[string]int64, error)
GetBlock(blockID string) (*model.Block, error)
// @withTransaction
PatchBlock(blockID string, blockPatch *model.BlockPatch, userID string) error
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error)
GetBlockHistoryDescendants(boardID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error)
GetBoardHistory(boardID string, opts model.QueryBlockHistoryOptions) ([]*model.Board, error)
GetBoardHistory(boardID string, opts model.QueryBoardHistoryOptions) ([]*model.Board, error)
GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error)
GetBoardAndCard(block *model.Block) (board *model.Board, card *model.Block, err error)
// @withTransaction

View File

@@ -38,6 +38,11 @@ func StoreTestBoardStore(t *testing.T, setup func(t *testing.T) (store.Store, fu
defer tearDown()
testDeleteBoard(t, store)
})
t.Run("UndeleteBoard", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testUndeleteBoard(t, store)
})
t.Run("InsertBoardWithAdmin", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
@@ -803,6 +808,93 @@ func testSearchBoardsForUserAndTeam(t *testing.T, store store.Store) {
}
}
func testUndeleteBoard(t *testing.T, store store.Store) {
userID := testUserID
t.Run("existing id", func(t *testing.T) {
boardID := utils.NewID(utils.IDTypeBoard)
board := &model.Board{
ID: boardID,
TeamID: testTeamID,
Type: model.BoardTypeOpen,
}
newBoard, err := store.InsertBoard(board, userID)
require.NoError(t, err)
require.NotNil(t, newBoard)
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err = store.DeleteBoard(boardID, userID)
require.NoError(t, err)
board, err = store.GetBoard(boardID)
require.Error(t, err)
require.Nil(t, board)
time.Sleep(1 * time.Millisecond)
err = store.UndeleteBoard(boardID, userID)
require.NoError(t, err)
board, err = store.GetBoard(boardID)
require.NoError(t, err)
require.NotNil(t, board)
})
t.Run("existing id multiple times", func(t *testing.T) {
boardID := utils.NewID(utils.IDTypeBoard)
board := &model.Board{
ID: boardID,
TeamID: testTeamID,
Type: model.BoardTypeOpen,
}
newBoard, err := store.InsertBoard(board, userID)
require.NoError(t, err)
require.NotNil(t, newBoard)
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err = store.DeleteBoard(boardID, userID)
require.NoError(t, err)
board, err = store.GetBoard(boardID)
require.Error(t, err)
require.Nil(t, board)
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err = store.UndeleteBoard(boardID, userID)
require.NoError(t, err)
board, err = store.GetBoard(boardID)
require.NoError(t, err)
require.NotNil(t, board)
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err = store.UndeleteBoard(boardID, userID)
require.NoError(t, err)
board, err = store.GetBoard(boardID)
require.NoError(t, err)
require.NotNil(t, board)
})
t.Run("from not existing id", func(t *testing.T) {
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err := store.UndeleteBoard("not-exists", userID)
require.NoError(t, err)
block, err := store.GetBoard("not-exists")
require.Error(t, err)
require.Nil(t, block)
})
}
func testGetBoardHistory(t *testing.T, store store.Store) {
userID := testUserID
@@ -819,7 +911,7 @@ func testGetBoardHistory(t *testing.T, store store.Store) {
rBoard1, err := store.InsertBoard(board, userID)
require.NoError(t, err)
opts := model.QueryBlockHistoryOptions{
opts := model.QueryBoardHistoryOptions{
Limit: 0,
Descending: false,
}
@@ -867,7 +959,7 @@ func testGetBoardHistory(t *testing.T, store store.Store) {
require.NoError(t, err)
// Updated history
opts = model.QueryBlockHistoryOptions{
opts = model.QueryBoardHistoryOptions{
Limit: 1,
Descending: true,
}
@@ -883,7 +975,7 @@ func testGetBoardHistory(t *testing.T, store store.Store) {
require.NoError(t, err)
// Updated history after delete
opts = model.QueryBlockHistoryOptions{
opts = model.QueryBoardHistoryOptions{
Limit: 0,
Descending: true,
}
@@ -897,7 +989,7 @@ func testGetBoardHistory(t *testing.T, store store.Store) {
})
t.Run("testGetBoardHistory: nonexisting board", func(t *testing.T) {
opts := model.QueryBlockHistoryOptions{
opts := model.QueryBoardHistoryOptions{
Limit: 0,
Descending: false,
}

20
server/utils/debug.go Normal file
View File

@@ -0,0 +1,20 @@
package utils
import (
"os"
"strings"
)
// IsRunningUnitTests returns true if this instance of FocalBoard is running unit or integration tests.
func IsRunningUnitTests() bool {
testing := os.Getenv("FB_UNIT_TESTING")
if testing == "" {
return false
}
switch strings.ToLower(testing) {
case "1", "t", "y", "true", "yes":
return true
}
return false
}

View File

@@ -14,8 +14,6 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
const singleUserID = "single-user-id"
func (wss *websocketSession) WriteJSON(v interface{}) error {
wss.mu.Lock()
defer wss.mu.Unlock()
@@ -224,7 +222,7 @@ func (ws *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
// if single user mode, check that the userID is valid and
// assume that the user has permission if so
if len(ws.singleUserToken) != 0 {
if wsSession.userID != singleUserID {
if wsSession.userID != model.SingleUser {
continue
}
@@ -426,7 +424,7 @@ func (ws *Server) removeListenerFromBlock(listener *websocketSession, blockID st
func (ws *Server) getUserIDForToken(token string) string {
if len(ws.singleUserToken) > 0 {
if token == ws.singleUserToken {
return singleUserID
return model.SingleUser
} else {
return ""
}

View File

@@ -5,6 +5,7 @@ import (
"testing"
"github.com/mattermost/focalboard/server/auth"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -235,6 +236,6 @@ func TestGetUserIDForTokenInSingleUserMode(t *testing.T) {
})
t.Run("Should return the single user ID if the token is correct", func(t *testing.T) {
require.Equal(t, singleUserID, server.getUserIDForToken(singleUserToken))
require.Equal(t, model.SingleUser, server.getUserIDForToken(singleUserToken))
})
}

View File

@@ -7,15 +7,19 @@ exports[`components/addContentMenuItem return a checkbox menu item 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -35,15 +39,19 @@ exports[`components/addContentMenuItem return a divider menu item 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="DividerIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M 432,224 H 16 c -8.836556,0 -16,7.16344 -16,16 v 32 c 0,8.83656 7.163444,16 16,16 h 416 c 8.83656,0 16,-7.16344 16,-16 v -32 c 0,-8.83656 -7.16344,-16 -16,-16 z"
/>
</svg>
<svg
class="DividerIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M 432,224 H 16 c -8.836556,0 -16,7.16344 -16,16 v 32 c 0,8.83656 7.163444,16 16,16 h 416 c 8.83656,0 16,-7.16344 16,-16 v -32 c 0,-8.83656 -7.16344,-16 -16,-16 z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -63,15 +71,19 @@ exports[`components/addContentMenuItem return a text menu item 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TextIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M432 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
/>
</svg>
<svg
class="TextIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M432 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -93,15 +105,19 @@ exports[`components/addContentMenuItem return an image menu item 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="ImageIcon Icon"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z"
/>
</svg>
<svg
class="ImageIcon Icon"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z"
/>
</svg>
</div>
<div
class="menu-name"
>

View File

@@ -54,15 +54,19 @@ exports[`components/blockIconSelector return menu on click 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="EmojiIcon Icon"
viewBox="0 0 496 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm4 72.6c-20.8 25-51.5 39.4-84 39.4s-63.2-14.3-84-39.4c-8.5-10.2-23.7-11.5-33.8-3.1-10.2 8.5-11.5 23.6-3.1 33.8 30 36 74.1 56.6 120.9 56.6s90.9-20.6 120.9-56.6c8.5-10.2 7.1-25.3-3.1-33.8-10.1-8.4-25.3-7.1-33.8 3.1z"
/>
</svg>
<svg
class="EmojiIcon Icon"
viewBox="0 0 496 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm4 72.6c-20.8 25-51.5 39.4-84 39.4s-63.2-14.3-84-39.4c-8.5-10.2-23.7-11.5-33.8-3.1-10.2 8.5-11.5 23.6-3.1 33.8 30 36 74.1 56.6 120.9 56.6s90.9-20.6 120.9-56.6c8.5-10.2 7.1-25.3-3.1-33.8-10.1-8.4-25.3-7.1-33.8 3.1z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -109,9 +113,13 @@ exports[`components/blockIconSelector return menu on click 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -135,8 +143,12 @@ exports[`components/blockIconSelector return menu on click 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -24,9 +24,7 @@ exports[`components/cardDialog already following card 1`] = `
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<button
type="button"
>
@@ -302,9 +300,7 @@ exports[`components/cardDialog return a cardDialog readonly 1`] = `
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<button
type="button"
>
@@ -409,9 +405,7 @@ exports[`components/cardDialog return cardDialog menu content 1`] = `
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<button
type="button"
>
@@ -447,9 +441,13 @@ exports[`components/cardDialog return cardDialog menu content 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -466,9 +464,13 @@ exports[`components/cardDialog return cardDialog menu content 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -485,9 +487,13 @@ exports[`components/cardDialog return cardDialog menu content 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-plus undefined"
/>
<div
class=""
>
<i
class="CompassIcon icon-plus undefined"
/>
</div>
<div
class="menu-name"
>
@@ -511,8 +517,12 @@ exports[`components/cardDialog return cardDialog menu content 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -780,9 +790,7 @@ exports[`components/cardDialog return cardDialog menu content and cancel delete
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<button
type="button"
>
@@ -1058,9 +1066,7 @@ exports[`components/cardDialog should match snapshot 1`] = `
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<button
type="button"
>
@@ -1336,9 +1342,7 @@ exports[`components/cardDialog should match snapshot without permissions 1`] = `
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<button
type="button"
>

View File

@@ -7645,53 +7645,6 @@ exports[`components/centerPanel should match snapshot for Kanban, not shared 1`]
class="octo-editor-preview"
data-testid="preview-element"
/>
<div
class="MarkdownEditorInput MarkdownEditorInput--IsNotEditing"
>
<div
class="DraftEditor-root"
>
<div
class="DraftEditor-editorContainer"
>
<div
aria-autocomplete="list"
aria-expanded="false"
class="notranslate public-DraftEditor-content"
contenteditable="true"
role="combobox"
spellcheck="false"
style="outline: none; user-select: text; white-space: pre-wrap; word-wrap: break-word;"
>
<div
data-contents="true"
>
<div
class=""
data-block="true"
data-editor="123"
data-offset-key="123-0-0"
>
<div
class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
data-offset-key="123-0-0"
>
<span
data-offset-key="123-0-0"
>
<span
data-text="true"
>
description
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -38,18 +38,22 @@ exports[`components/contentBlock return commentBlock and click on menuwrapper 1`
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="SortUpIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,40 50,20 70,40"
/>
</svg>
<svg
class="SortUpIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,40 50,20 70,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -66,18 +70,22 @@ exports[`components/contentBlock return commentBlock and click on menuwrapper 1`
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="SortDownIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,60 50,80 70,60"
/>
</svg>
<svg
class="SortDownIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="50,20 50,80"
/>
<polyline
points="30,60 50,80 70,60"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -118,9 +126,13 @@ exports[`components/contentBlock return commentBlock and click on menuwrapper 1`
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -144,8 +156,12 @@ exports[`components/contentBlock return commentBlock and click on menuwrapper 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -20,25 +20,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -55,25 +59,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -95,9 +103,13 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -114,9 +126,13 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -164,8 +180,12 @@ Object {
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -196,25 +216,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -231,25 +255,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -271,9 +299,13 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -290,9 +322,13 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -340,8 +376,12 @@ Object {
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -429,25 +469,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -464,25 +508,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -514,8 +562,12 @@ Object {
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -546,25 +598,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -581,25 +637,29 @@ Object {
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<g
opacity="0.8"
<svg
class="TableIcon Icon"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
<g
opacity="0.8"
>
<path
clip-rule="evenodd"
d="M20 4H10V8L20 8V4ZM8 4V8H4V4H8ZM4 14L4 10H8V14H4ZM4 16L4 20H8V16H4ZM10 16V20H20V16L10 16ZM20 14V10L10 10V14L20 14ZM4 2C2.89543 2 2 2.89543 2 4V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V4C22 2.89543 21.1046 2 20 2H4Z"
fill="currentColor"
fill-rule="evenodd"
/>
</g>
</svg>
</div>
<div
class="menu-name"
>
@@ -631,8 +691,12 @@ Object {
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -7,11 +7,20 @@
display: flex;
flex-direction: row;
background-color: rgba(var(--sidebar-text-rgb), 0.08);
color: rgba(var(--sidebar-text-rgb), 0.56);
flex: 1;
padding: 6px;
padding: 6px 8px;
gap: 6px;
border-radius: 4px;
cursor: pointer;
height: 28px;
align-items: center;
font-size: 12px;
&:hover {
background: rgba(var(--sidebar-text-rgb), 0.16);
color: var(--sidebar-text);
}
}
.CompassIcon.icon-magnify.CompassIcon {
@@ -19,10 +28,8 @@
}
.add-board-icon {
margin-left: 8px;
padding-top: 4px;
cursor: pointer;
font-size: 16px;
width: 16px;
margin-left: 4px;
width: 28px;
height: 28px;
}
}

View File

@@ -12,6 +12,8 @@ import BoardSwitcherDialog from '../boardsSwitcherDialog/boardSwitcherDialog'
import {Utils} from '../../utils'
import {Constants} from '../../constants'
import IconButton from '../../widgets/buttons/iconButton'
type Props = {
onBoardTemplateSelectorOpen?: () => void,
}
@@ -63,12 +65,13 @@ const BoardsSwitcher = (props: Props): JSX.Element => {
{
Utils.isFocalboardPlugin() &&
<span
<IconButton
size='small'
inverted={true}
className='add-board-icon'
onClick={props.onBoardTemplateSelectorOpen}
>
<AddIcon/>
</span>
icon={<AddIcon/>}
/>
}
{

View File

@@ -9,6 +9,10 @@
color: #484848;
}
.toolbar {
padding: 4px;
}
span {
display: inline-block;
height: 100%;

View File

@@ -33,15 +33,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TextIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M432 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
/>
</svg>
<svg
class="TextIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M432 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -58,15 +62,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="ImageIcon Icon"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z"
/>
</svg>
<svg
class="ImageIcon Icon"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -83,15 +91,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="DividerIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M 432,224 H 16 c -8.836556,0 -16,7.16344 -16,16 v 32 c 0,8.83656 7.163444,16 16,16 h 416 c 8.83656,0 16,-7.16344 16,-16 v -32 c 0,-8.83656 -7.16344,-16 -16,-16 z"
/>
</svg>
<svg
class="DividerIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M 432,224 H 16 c -8.836556,0 -16,7.16344 -16,16 v 32 c 0,8.83656 7.163444,16 16,16 h 416 c 8.83656,0 16,-7.16344 16,-16 v -32 c 0,-8.83656 -7.16344,-16 -16,-16 z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -108,15 +120,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -140,8 +156,12 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -192,15 +212,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="TextIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M432 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
/>
</svg>
<svg
class="TextIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M432 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-128H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -217,15 +241,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="ImageIcon Icon"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z"
/>
</svg>
<svg
class="ImageIcon Icon"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -242,15 +270,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="DividerIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M 432,224 H 16 c -8.836556,0 -16,7.16344 -16,16 v 32 c 0,8.83656 7.163444,16 16,16 h 416 c 8.83656,0 16,-7.16344 16,-16 v -32 c 0,-8.83656 -7.16344,-16 -16,-16 z"
/>
</svg>
<svg
class="DividerIcon Icon"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M 432,224 H 16 c -8.836556,0 -16,7.16344 -16,16 v 32 c 0,8.83656 7.163444,16 16,16 h 416 c 8.83656,0 16,-7.16344 16,-16 v -32 c 0,-8.83656 -7.16344,-16 -16,-16 z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -267,15 +299,19 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -299,8 +335,12 @@ exports[`components/cardDetail/cardDetailContentsMenu return cardDetailContentsM
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -406,8 +406,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -423,8 +427,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -440,8 +448,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -457,8 +469,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -474,8 +490,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -491,8 +511,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -508,8 +532,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -525,8 +553,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -542,8 +574,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -559,8 +595,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -576,8 +616,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -593,8 +637,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -610,8 +658,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -627,8 +679,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -652,8 +708,12 @@ exports[`components/cardDetail/CardDetailProperties should show property types m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -53,9 +53,13 @@ exports[`components/cardDetail/comment return comment 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -79,8 +83,12 @@ exports[`components/cardDetail/comment return comment 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -159,9 +167,13 @@ exports[`components/cardDetail/comment return comment and delete comment 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -185,8 +197,12 @@ exports[`components/cardDetail/comment return comment and delete comment 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -57,7 +57,7 @@ const Dialog = (props: Props) => {
size='medium'
/>
}
{toolbar && <div className='cardToolbar'>{toolbar}</div>}
{toolbar && <div>{toolbar}</div>}
{toolsMenu && <MenuWrapper>
<IconButton
size='medium'

View File

@@ -139,9 +139,13 @@ exports[`src/components/gallery/Gallery should match snapshot 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -156,9 +160,13 @@ exports[`src/components/gallery/Gallery should match snapshot 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -175,9 +183,13 @@ exports[`src/components/gallery/Gallery should match snapshot 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -201,8 +213,12 @@ exports[`src/components/gallery/Gallery should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -298,9 +314,13 @@ exports[`src/components/gallery/Gallery should match snapshot without permission
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -324,8 +344,12 @@ exports[`src/components/gallery/Gallery should match snapshot without permission
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -60,9 +60,13 @@ exports[`src/components/gallery/GalleryCard with a comment content should match
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -77,9 +81,13 @@ exports[`src/components/gallery/GalleryCard with a comment content should match
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -96,9 +104,13 @@ exports[`src/components/gallery/GalleryCard with a comment content should match
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -122,8 +134,12 @@ exports[`src/components/gallery/GalleryCard with a comment content should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -190,9 +206,13 @@ exports[`src/components/gallery/GalleryCard with an image content should match s
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -207,9 +227,13 @@ exports[`src/components/gallery/GalleryCard with an image content should match s
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -226,9 +250,13 @@ exports[`src/components/gallery/GalleryCard with an image content should match s
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -252,8 +280,12 @@ exports[`src/components/gallery/GalleryCard with an image content should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -356,9 +388,13 @@ exports[`src/components/gallery/GalleryCard with many contents should match snap
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -373,9 +409,13 @@ exports[`src/components/gallery/GalleryCard with many contents should match snap
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -392,9 +432,13 @@ exports[`src/components/gallery/GalleryCard with many contents should match snap
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -418,8 +462,12 @@ exports[`src/components/gallery/GalleryCard with many contents should match snap
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -490,9 +538,13 @@ exports[`src/components/gallery/GalleryCard with many images content should matc
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -507,9 +559,13 @@ exports[`src/components/gallery/GalleryCard with many images content should matc
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -526,9 +582,13 @@ exports[`src/components/gallery/GalleryCard with many images content should matc
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -552,8 +612,12 @@ exports[`src/components/gallery/GalleryCard with many images content should matc
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -782,9 +846,13 @@ exports[`src/components/gallery/GalleryCard without block content should match s
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -799,9 +867,13 @@ exports[`src/components/gallery/GalleryCard without block content should match s
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -818,9 +890,13 @@ exports[`src/components/gallery/GalleryCard without block content should match s
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -844,8 +920,12 @@ exports[`src/components/gallery/GalleryCard without block content should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -63,8 +63,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -80,8 +84,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -97,8 +105,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -114,8 +126,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -131,8 +147,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -148,8 +168,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -172,8 +196,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -195,8 +223,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -261,8 +293,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -286,8 +322,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -362,8 +402,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -411,8 +455,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -434,8 +482,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -451,8 +503,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -468,8 +524,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -485,8 +545,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -502,8 +566,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -519,8 +587,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -536,8 +608,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -553,8 +629,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -570,8 +650,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -587,8 +671,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -604,8 +692,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -621,8 +713,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -638,8 +734,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -655,8 +755,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -672,8 +776,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -689,8 +797,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -713,8 +825,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -759,8 +875,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -784,8 +904,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -882,8 +1006,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu settings menu open should m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -948,8 +1076,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu settings menu open should m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -973,8 +1105,12 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu settings menu open should m
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -34,9 +34,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on copy li
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -51,9 +55,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on copy li
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -70,9 +78,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on copy li
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -96,8 +108,12 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on copy li
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -171,9 +187,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on delete
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -188,9 +208,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on delete
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -207,9 +231,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on delete
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -233,8 +261,12 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on delete
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -308,9 +340,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on duplica
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -325,9 +361,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on duplica
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
<div
class=""
>
<i
class="CompassIcon icon-content-copy content-copy"
/>
</div>
<div
class="menu-name"
>
@@ -344,9 +384,13 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on duplica
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-link-variant LinkIcon"
/>
</div>
<div
class="menu-name"
>
@@ -370,8 +414,12 @@ exports[`src/components/kanban/kanbanCard return kanbanCard and click on duplica
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -62,15 +62,19 @@ exports[`src/components/kanban/kanbanColumnHeader return kanbanColumnHeader and
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="HideIcon Icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M634 471L36 3.51A16 16 0 0 0 13.51 6l-10 12.49A16 16 0 0 0 6 41l598 467.49a16 16 0 0 0 22.49-2.49l10-12.49A16 16 0 0 0 634 471zM296.79 146.47l134.79 105.38C429.36 191.91 380.48 144 320 144a112.26 112.26 0 0 0-23.21 2.47zm46.42 219.07L208.42 260.16C210.65 320.09 259.53 368 320 368a113 113 0 0 0 23.21-2.46zM320 112c98.65 0 189.09 55 237.93 144a285.53 285.53 0 0 1-44 60.2l37.74 29.5a333.7 333.7 0 0 0 52.9-75.11 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64c-36.7 0-71.71 7-104.63 18.81l46.41 36.29c18.94-4.3 38.34-7.1 58.22-7.1zm0 288c-98.65 0-189.08-55-237.93-144a285.47 285.47 0 0 1 44.05-60.19l-37.74-29.5a333.6 333.6 0 0 0-52.89 75.1 32.35 32.35 0 0 0 0 29.19C89.72 376.41 197.08 448 320 448c36.7 0 71.71-7.05 104.63-18.81l-46.41-36.28C359.28 397.2 339.89 400 320 400z"
/>
</svg>
<svg
class="HideIcon Icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M634 471L36 3.51A16 16 0 0 0 13.51 6l-10 12.49A16 16 0 0 0 6 41l598 467.49a16 16 0 0 0 22.49-2.49l10-12.49A16 16 0 0 0 634 471zM296.79 146.47l134.79 105.38C429.36 191.91 380.48 144 320 144a112.26 112.26 0 0 0-23.21 2.47zm46.42 219.07L208.42 260.16C210.65 320.09 259.53 368 320 368a113 113 0 0 0 23.21-2.46zM320 112c98.65 0 189.09 55 237.93 144a285.53 285.53 0 0 1-44 60.2l37.74 29.5a333.7 333.7 0 0 0 52.9-75.11 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64c-36.7 0-71.71 7-104.63 18.81l46.41 36.29c18.94-4.3 38.34-7.1 58.22-7.1zm0 288c-98.65 0-189.08-55-237.93-144a285.47 285.47 0 0 1 44.05-60.19l-37.74-29.5a333.6 333.6 0 0 0-52.89 75.1 32.35 32.35 0 0 0 0 29.19C89.72 376.41 197.08 448 320 448c36.7 0 71.71-7.05 104.63-18.81l-46.41-36.28C359.28 397.2 339.89 400 320 400z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -87,9 +91,13 @@ exports[`src/components/kanban/kanbanColumnHeader return kanbanColumnHeader and
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -286,8 +294,12 @@ exports[`src/components/kanban/kanbanColumnHeader return kanbanColumnHeader and
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -30,15 +30,19 @@ exports[`src/components/kanban/kanbanHiddenColumnItem return kanbanHiddenColumnI
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="ShowIcon Icon"
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
/>
</svg>
<svg
class="ShowIcon Icon"
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -62,8 +66,12 @@ exports[`src/components/kanban/kanbanHiddenColumnItem return kanbanHiddenColumnI
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -119,15 +127,19 @@ exports[`src/components/kanban/kanbanHiddenColumnItem return kanbanHiddenColumnI
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="ShowIcon Icon"
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
/>
</svg>
<svg
class="ShowIcon Icon"
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -151,8 +163,12 @@ exports[`src/components/kanban/kanbanHiddenColumnItem return kanbanHiddenColumnI
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -1,11 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useState} from 'react'
import React, {useState, Suspense} from 'react'
import {Utils} from '../utils'
import './markdownEditor.scss'
import MarkdownEditorInput from './markdownEditorInput/markdownEditorInput'
const MarkdownEditorInput = React.lazy(() => import('./markdownEditorInput/markdownEditorInput'))
type Props = {
id?: string
@@ -50,14 +50,16 @@ const MarkdownEditor = (props: Props): JSX.Element => {
}
const editorElement = (
<MarkdownEditorInput
id={id}
onChange={onChange}
onFocus={onFocus}
onBlur={editorOnBlur}
initialText={text}
isEditing={isEditing}
/>
<Suspense fallback={<></>}>
<MarkdownEditorInput
id={id}
onChange={onChange}
onFocus={onFocus}
onBlur={editorOnBlur}
initialText={text}
isEditing={isEditing}
/>
</Suspense>
)
const element = (

View File

@@ -119,18 +119,29 @@
.MagnifyIcon {
position: absolute;
left: 4px;
font-size: 22px;
top: 6px;
left: 10px;
font-size: 18px;
top: 10px;
width: 20px;
height: 20px;
opacity: 0.48;
}
.searchQuery {
height: 40px;
font-size: 14px;
border-radius: 4px;
border: 2px solid rgb(var(--button-bg-rgb));
padding: 0 33px;
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
background: var(--center-channel-bg);
color: var(--center-channel-color);
padding: 0 34px;
flex: 1;
transition: border 0.15s ease-in;
&:focus {
padding: 0 33px;
border: 2px solid var(--button-bg);
}
}
}
}

View File

@@ -44,7 +44,7 @@ const SearchDialog = (props: Props): JSX.Element => {
<div className='BoardSwitcherDialogBody'>
<div className='head'>
<h3 className='text-heading4'>{props.title}</h3>
<h5 className='text-heading1'>{props.subTitle}</h5>
<h5>{props.subTitle}</h5>
<div className='queryWrapper'>
<Search/>
<input

View File

@@ -24,9 +24,7 @@ exports[`src/components/shareBoard/shareBoard return shareBoard and click Copy l
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -248,9 +246,7 @@ exports[`src/components/shareBoard/shareBoard return shareBoard and click Copy l
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -472,9 +468,7 @@ exports[`src/components/shareBoard/shareBoard return shareBoard and click Regene
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -719,9 +713,7 @@ exports[`src/components/shareBoard/shareBoard return shareBoard, and click switc
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -966,9 +958,7 @@ exports[`src/components/shareBoard/shareBoard return shareBoardComponent and cli
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -1213,9 +1203,7 @@ exports[`src/components/shareBoard/shareBoard should match snapshot 1`] = `
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -1437,9 +1425,7 @@ exports[`src/components/shareBoard/shareBoard should match snapshot with sharing
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -1661,9 +1647,7 @@ exports[`src/components/shareBoard/shareBoard should match snapshot with sharing
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>
@@ -1885,9 +1869,7 @@ exports[`src/components/shareBoard/shareBoard should match snapshot with sharing
class="CompassIcon icon-close CloseIcon"
/>
</button>
<div
class="cardToolbar"
>
<div>
<span
class="text-heading5"
>

View File

@@ -207,6 +207,9 @@
border: 0;
color: rgba(var(--link-color-rgb), 1);
padding: 0;
display: flex;
align-items: center;
gap: 4px;
&:hover {
text-decoration: underline;
@@ -215,8 +218,8 @@
.Menu {
position: fixed;
left: 55%;
right: calc(45% - 240px);
left: 53%;
right: auto;
.menu-contents {
min-width: 240px;

View File

@@ -60,12 +60,14 @@ const TeamPermissionsRow = (): JSX.Element => {
<Menu position='left'>
<Menu.Text
id='Editor'
check={true}
icon={currentRole === 'Editor' ? <CheckIcon/> : null}
name={intl.formatMessage({id: 'BoardMember.schemeEditor', defaultMessage: 'Editor'})}
onClick={() => updateBoardType(board, BoardTypeOpen)}
/>
<Menu.Text
id='None'
check={true}
icon={currentRole === 'None' ? <CheckIcon/> : null}
name={intl.formatMessage({id: 'BoardMember.schemeNone', defaultMessage: 'None'})}
onClick={() => updateBoardType(board, BoardTypePrivate)}

View File

@@ -63,18 +63,21 @@ const UserPermissionsRow = (props: Props): JSX.Element => {
<Menu position='left'>
<Menu.Text
id='Viewer'
check={true}
icon={currentRole === 'Viewer' ? <CheckIcon/> : null}
name={intl.formatMessage({id: 'BoardMember.schemeViewer', defaultMessage: 'Viewer'})}
onClick={() => props.onUpdateBoardMember(member, 'Viewer')}
/>
<Menu.Text
id='Editor'
check={true}
icon={currentRole === 'Editor' ? <CheckIcon/> : null}
name={intl.formatMessage({id: 'BoardMember.schemeEditor', defaultMessage: 'Editor'})}
onClick={() => props.onUpdateBoardMember(member, 'Editor')}
/>
<Menu.Text
id='Admin'
check={true}
icon={currentRole === 'Admin' ? <CheckIcon/> : null}
name={intl.formatMessage({id: 'BoardMember.schemeAdmin', defaultMessage: 'Admin'})}
onClick={() => props.onUpdateBoardMember(member, 'Admin')}

View File

@@ -61,8 +61,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -78,8 +82,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -95,8 +103,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -112,8 +124,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -129,8 +145,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -146,8 +166,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -170,8 +194,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -193,8 +221,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -289,8 +321,12 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -363,8 +399,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -412,8 +452,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -435,8 +479,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -452,8 +500,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -469,8 +521,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -486,8 +542,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -503,8 +563,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -520,8 +584,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -537,8 +605,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -554,8 +626,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -571,8 +647,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -588,8 +668,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -605,8 +689,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -622,8 +710,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -639,8 +731,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -656,8 +752,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -673,8 +773,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -690,8 +794,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -714,8 +822,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -790,8 +902,12 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -884,8 +1000,12 @@ exports[`components/sidebar/SidebarSettingsMenu settings menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -980,8 +1100,12 @@ exports[`components/sidebar/SidebarSettingsMenu settings menu open should match
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1054,8 +1178,12 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1127,8 +1255,12 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1150,8 +1282,12 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1167,8 +1303,12 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1184,8 +1324,12 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1208,8 +1352,12 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1260,8 +1408,12 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -18,8 +18,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -37,8 +41,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -56,8 +64,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -75,8 +87,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -94,8 +110,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -111,8 +131,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -128,8 +152,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -153,8 +181,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, other column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -188,8 +220,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, title column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -207,8 +243,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, title column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -226,8 +266,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, title column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -245,8 +289,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, title column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -271,8 +319,12 @@ exports[`components/table/TableHeaderMenu should match snapshot, title column 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -7,20 +7,24 @@ exports[`components/viewHeader/emptyCardButton return EmptyCardButton 1`] = `
class="MenuOption TextOption menu-option bold-menu-text"
role="button"
>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -51,20 +55,24 @@ exports[`components/viewHeader/emptyCardButton return EmptyCardButton and Set Te
class="MenuOption TextOption menu-option bold-menu-text"
role="button"
>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -98,15 +106,19 @@ exports[`components/viewHeader/emptyCardButton return EmptyCardButton and Set Te
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -130,8 +142,12 @@ exports[`components/viewHeader/emptyCardButton return EmptyCardButton and Set Te
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -156,20 +172,24 @@ exports[`components/viewHeader/emptyCardButton return EmptyCardButton and addCar
class="MenuOption TextOption menu-option bold-menu-text"
role="button"
>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
</div>
<div
class="menu-name"
>

View File

@@ -54,8 +54,12 @@ exports[`components/viewHeader/filterComponent return filterComponent 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -73,8 +77,12 @@ exports[`components/viewHeader/filterComponent return filterComponent 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -92,8 +100,12 @@ exports[`components/viewHeader/filterComponent return filterComponent 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -111,8 +123,12 @@ exports[`components/viewHeader/filterComponent return filterComponent 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -136,8 +152,12 @@ exports[`components/viewHeader/filterComponent return filterComponent 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -245,8 +265,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and add Fi
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -264,8 +288,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and add Fi
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -283,8 +311,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and add Fi
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -302,8 +334,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and add Fi
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -327,8 +363,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and add Fi
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -450,8 +490,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and click
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -469,8 +513,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and click
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -488,8 +536,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and click
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -507,8 +559,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and click
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -532,8 +588,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and click
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -627,8 +687,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and filter
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -646,8 +710,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and filter
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -665,8 +733,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and filter
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -684,8 +756,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and filter
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -709,8 +785,12 @@ exports[`components/viewHeader/filterComponent return filterComponent and filter
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -34,8 +34,12 @@ exports[`components/viewHeader/filterEntry return filterEntry 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -53,8 +57,12 @@ exports[`components/viewHeader/filterEntry return filterEntry 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -72,8 +80,12 @@ exports[`components/viewHeader/filterEntry return filterEntry 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -91,8 +103,12 @@ exports[`components/viewHeader/filterEntry return filterEntry 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -116,8 +132,12 @@ exports[`components/viewHeader/filterEntry return filterEntry 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -208,8 +228,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on delet
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -227,8 +251,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on delet
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -246,8 +274,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on delet
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -265,8 +297,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on delet
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -290,8 +326,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on delet
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -368,8 +408,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on doesn
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -387,8 +431,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on doesn
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -406,8 +454,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on doesn
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -425,8 +477,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on doesn
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -450,8 +506,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on doesn
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -528,8 +588,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on inclu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -547,8 +611,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on inclu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -566,8 +634,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on inclu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -585,8 +657,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on inclu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -610,8 +686,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on inclu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -688,8 +768,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is em
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -707,8 +791,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is em
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -726,8 +814,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is em
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -745,8 +837,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is em
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -770,8 +866,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is em
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -848,8 +948,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is no
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -867,8 +971,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is no
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -886,8 +994,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is no
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -905,8 +1017,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is no
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -930,8 +1046,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on is no
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -994,8 +1114,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on statu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1013,8 +1137,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on statu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1032,8 +1160,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on statu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1051,8 +1183,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on statu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -1076,8 +1212,12 @@ exports[`components/viewHeader/filterEntry return filterEntry and click on statu
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -60,8 +60,12 @@ exports[`components/viewHeader/filterValue return filterValue 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -38,20 +38,24 @@ exports[`components/viewHeader/newCardButton return NewCardButton 1`] = `
class="MenuOption TextOption menu-option bold-menu-text"
role="button"
>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -79,9 +83,13 @@ exports[`components/viewHeader/newCardButton return NewCardButton 1`] = `
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-plus AddIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-plus AddIcon"
/>
</div>
<div
class="menu-name"
>
@@ -105,8 +113,12 @@ exports[`components/viewHeader/newCardButton return NewCardButton 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -162,20 +174,24 @@ exports[`components/viewHeader/newCardButton return NewCardButton and addCard 1`
class="MenuOption TextOption menu-option bold-menu-text"
role="button"
>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -203,9 +219,13 @@ exports[`components/viewHeader/newCardButton return NewCardButton and addCard 1`
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-plus AddIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-plus AddIcon"
/>
</div>
<div
class="menu-name"
>
@@ -229,8 +249,12 @@ exports[`components/viewHeader/newCardButton return NewCardButton and addCard 1`
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -286,20 +310,24 @@ exports[`components/viewHeader/newCardButton return NewCardButton and addCardTem
class="MenuOption TextOption menu-option bold-menu-text"
role="button"
>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
<svg
class="CardIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="40"
rx="3"
ry="3"
width="60"
x="20"
y="30"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -327,9 +355,13 @@ exports[`components/viewHeader/newCardButton return NewCardButton and addCardTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-plus AddIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-plus AddIcon"
/>
</div>
<div
class="menu-name"
>
@@ -353,8 +385,12 @@ exports[`components/viewHeader/newCardButton return NewCardButton and addCardTem
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -8,9 +8,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="Icon"
class=""
>
i
<div
class="Icon"
>
i
</div>
</div>
<div
class="menu-name"
@@ -45,15 +49,19 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -70,9 +78,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
</div>
<div
class="menu-name"
>
@@ -89,9 +101,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -115,8 +131,12 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -142,9 +162,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="Icon"
class=""
>
i
<div
class="Icon"
>
i
</div>
</div>
<div
class="menu-name"
@@ -179,15 +203,19 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -204,9 +232,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
</div>
<div
class="menu-name"
>
@@ -223,9 +255,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -249,8 +285,12 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -276,9 +316,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="Icon"
class=""
>
i
<div
class="Icon"
>
i
</div>
</div>
<div
class="menu-name"
@@ -311,9 +355,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="Icon"
class=""
>
i
<div
class="Icon"
>
i
</div>
</div>
<div
class="menu-name"
@@ -348,15 +396,19 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -373,9 +425,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
</div>
<div
class="menu-name"
>
@@ -392,9 +448,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -418,8 +478,12 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -445,9 +509,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="Icon"
class=""
>
i
<div
class="Icon"
>
i
</div>
</div>
<div
class="menu-name"
@@ -482,15 +550,19 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
<div
class=""
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
class="menu-name"
>
@@ -507,9 +579,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
<div
class=""
>
<i
class="CompassIcon icon-pencil-outline EditIcon"
/>
</div>
<div
class="menu-name"
>
@@ -526,9 +602,13 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
class="MenuOption TextOption menu-option"
role="button"
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
<div
class=""
>
<i
class="CompassIcon icon-trash-can-outline DeleteIcon trash-can-outline"
/>
</div>
<div
class="menu-name"
>
@@ -552,8 +632,12 @@ exports[`components/viewHeader/newCardButtonTemplateItem return NewCardButtonTem
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -34,8 +34,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -53,8 +57,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -78,8 +86,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -131,8 +143,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu and verify call
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -150,8 +166,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu and verify call
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -175,8 +195,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu and verify call
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -228,8 +252,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu and verify call
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -247,8 +275,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu and verify call
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -272,8 +304,12 @@ exports[`components/viewHeader/viewHeaderActionsMenu return menu and verify call
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -36,8 +36,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -59,8 +63,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -81,8 +89,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -106,8 +118,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -125,8 +141,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -144,8 +164,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -169,8 +193,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -223,8 +251,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -246,8 +278,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -268,8 +304,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -293,8 +333,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -312,8 +356,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -331,8 +379,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -356,8 +408,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu For viewType table render o
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -411,8 +467,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -436,8 +496,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -455,8 +519,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -474,8 +542,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -499,8 +571,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -577,8 +653,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -600,8 +680,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -623,8 +707,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -645,8 +733,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -670,8 +762,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -689,8 +785,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -708,8 +808,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -733,8 +837,12 @@ exports[`components/viewHeader/viewHeaderGroupByMenu return groupBy menu, hideEm
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -153,8 +153,12 @@ exports[`components/viewHeader/viewHeaderPropertiesMenu return properties menu 1
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -346,8 +350,12 @@ exports[`components/viewHeader/viewHeaderPropertiesMenu return properties menu w
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -31,8 +31,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -48,8 +52,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -70,8 +78,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -89,8 +101,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -108,8 +124,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -136,8 +156,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -155,8 +179,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -180,8 +208,12 @@ exports[`components/viewHeader/viewHeaderSortMenu return sort menu 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -71,7 +71,7 @@ const ViewMenu = (props: Props) => {
Utils.log('deleteView')
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.DeleteBoardView, {board: board.id, view: activeView.id})
const view = activeView
const nextView = views.find((o) => o !== view)
const nextView = views.find((o) => o.id !== view.id)
mutator.deleteBlock(view, 'delete view')
if (nextView) {
showView(nextView.id)

View File

@@ -171,7 +171,7 @@ class Mutator {
await octoClient.deleteBlock(block.boardId, block.id)
},
async () => {
await octoClient.undeleteBlock(block.id)
await octoClient.undeleteBlock(block.boardId, block.id)
await afterUndo?.()
},
actualDescription,
@@ -221,7 +221,7 @@ class Mutator {
},
async () => {
await beforeUndo?.(board)
await octoClient.createBoard(board)
await octoClient.undeleteBoard(board.id)
},
description,
this.undoGroupId,

View File

@@ -326,9 +326,17 @@ class OctoClient {
})
}
async undeleteBlock(blockId: string): Promise<Response> {
async undeleteBlock(boardId: string, blockId: string): Promise<Response> {
Utils.log(`undeleteBlock: ${blockId}`)
return fetch(this.getBaseURL() + this.teamPath() + `/blocks/${encodeURIComponent(blockId)}/undelete`, {
return fetch(`${this.getBaseURL()}/api/v1/boards/${encodeURIComponent(boardId)}/blocks/${encodeURIComponent(blockId)}/undelete`, {
method: 'POST',
headers: this.headers(),
})
}
async undeleteBoard(boardId: string): Promise<Response> {
Utils.log(`undeleteBoard: ${boardId}`)
return fetch(`${this.getBaseURL()}/api/v1/boards/${boardId}/undelete`, {
method: 'POST',
headers: this.headers(),
})

View File

@@ -77,8 +77,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -94,8 +98,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -111,8 +119,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -128,8 +140,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -145,8 +161,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -162,8 +182,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -179,8 +203,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -196,8 +224,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -213,8 +245,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -230,8 +266,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -247,8 +287,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -264,8 +308,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -281,8 +329,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -298,8 +350,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -322,8 +378,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -345,8 +405,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>
@@ -370,8 +434,12 @@ exports[`widgets/PropertyMenu should match snapshot 1`] = `
role="button"
>
<div
class="noicon"
/>
class=""
>
<div
class="noicon"
/>
</div>
<div
class="menu-name"
>

View File

@@ -27,12 +27,12 @@
&:hover {
color: rgba(var(--sidebar-text-rgb), 1);
background-color: rgba(var(--button-bg-rgb), 0.08);
background-color: rgba(var(--sidebar-text-rgb), 0.08);
}
&:active {
color: rgba(var(--sidebar-text-rgb), 1);
background-color: rgba(var(--button-bg-rgb), 0.16);
background-color: rgba(var(--sidebar-text-rgb), 0.16);
}
}

View File

@@ -52,7 +52,7 @@
display: flex;
flex-direction: row;
align-items: center;
position: relative;
font-size: 14px;
line-height: 24px;
font-weight: 400;
@@ -102,6 +102,16 @@
}
}
.menu-option__check {
position: absolute;
left: 14px;
color: rgba(var(--button-bg-rgb), 1);
svg {
stroke: currentColor;
}
}
.menu-spacer {
height: 20px;
width: 20px;

View File

@@ -5,13 +5,14 @@ import React from 'react'
import {MenuOptionProps} from './menuItem'
type TextOptionProps = MenuOptionProps & {
check?: boolean
icon?: React.ReactNode,
rightIcon?: React.ReactNode,
className?: string
}
function TextOption(props:TextOptionProps): JSX.Element {
const {name, icon, rightIcon} = props
const {name, icon, rightIcon, check} = props
let className = 'MenuOption TextOption menu-option'
if (props.className) {
className += ' ' + props.className
@@ -26,7 +27,7 @@ function TextOption(props:TextOptionProps): JSX.Element {
props.onClick(props.id)
}}
>
{icon ?? <div className='noicon'/>}
<div className={`${check ? 'menu-option__check' : ''}`}>{icon ?? <div className='noicon'/>}</div>
<div className='menu-name'>{name}</div>
{rightIcon ?? <div className='noicon'/>}
</div>

19
win-wpf/README.md Normal file
View File

@@ -0,0 +1,19 @@
# Focalboard Windows Personal Desktop
This folder contains the code for the Windows Personal Desktop. It packages a lightweight C# Windows App with the Windows build of the server, and the webapp. The server is run in a single-user mode.
## Debugging in Visual Studio
Open `Focalboard.sln` in Visual Studio to debug it.
### Testing the single-user server
You can also run the server in single-user mode and connect to it via a browser:
1. Run `FOCALBOARD_SINGLE_USER_TOKEN=testtest make watch-single-user`
* This runs the server with the `-single-user` flag
* Alternatively, select `Go: Launch Single-user Server` from VSCode's run and debug options
2. Open a browser to `http://localhost:8000`
3. Open the browser developer tools to Application \ Local Storage \ localhost:8000
4. Set `focalboardSessionId` to `testtest`
5. Navigate to `http://localhost:8000`