Просмотр исходного кода

Merge pull request #6410 from weseek/master

Release v5.1.3
Yuki Takei 3 лет назад
Родитель
Сommit
d8547e43cb
29 измененных файлов с 318 добавлено и 74 удалено
  1. 1 1
      .devcontainer/devcontainer.json
  2. 1 1
      .github/workflows/reusable-app-prod.yml
  3. 8 13
      .vscode/settings.json
  4. 1 1
      lerna.json
  5. 1 1
      package.json
  6. 9 8
      packages/app/package.json
  7. 1 0
      packages/app/public/static/locales/en_US/translation.json
  8. 1 0
      packages/app/public/static/locales/ja_JP/translation.json
  9. 1 0
      packages/app/public/static/locales/zh_CN/translation.json
  10. 22 2
      packages/app/src/components/Admin/AuditLog/ActivityTable.tsx
  11. 61 19
      packages/app/src/components/Admin/AuditLogManagement.tsx
  12. 1 1
      packages/app/src/components/DescendantsPageList.tsx
  13. 5 0
      packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx
  14. 2 2
      packages/app/src/components/PageEditor/Editor.tsx
  15. 8 7
      packages/app/src/components/Sidebar/RecentChanges.tsx
  16. 2 1
      packages/app/src/linter-checker/test.scss
  17. 6 0
      packages/app/src/server/views/widget/headers/drawio.html
  18. 3 0
      packages/app/src/styles/_admin.scss
  19. 167 0
      packages/app/test/cypress/integration/50-sidebar/access-to-side-bar.spec.ts
  20. 4 4
      packages/app/test/cypress/integration/50-sidebar/switching-sidebar-mode.spec.ts
  21. 1 1
      packages/codemirror-textlint/package.json
  22. 1 1
      packages/core/package.json
  23. 1 1
      packages/plugin-attachment-refs/package.json
  24. 1 1
      packages/plugin-lsx/package.json
  25. 1 1
      packages/plugin-pukiwiki-like-linker/package.json
  26. 1 1
      packages/slack/package.json
  27. 2 2
      packages/slackbot-proxy/package.json
  28. 1 1
      packages/ui/package.json
  29. 4 4
      yarn.lock

+ 1 - 1
.devcontainer/devcontainer.json

@@ -25,7 +25,7 @@
     "editorconfig.editorconfig",
     "esbenp.prettier-vscode",
     "shinnn.stylelint",
-    "hex-ci.stylelint-plus",
+    "stylelint.vscode-stylelint"
 	],
 
 	// Uncomment the next line if you want start specific services in your Docker Compose config.

+ 1 - 1
.github/workflows/reusable-app-prod.yml

@@ -189,7 +189,7 @@ jobs:
       fail-fast: false
       matrix:
         # List string expressions that is comma separated ids of tests in "test/cypress/integration"
-        spec-group: ['10', '20', '21', '30', '40', '60']
+        spec-group: ['10', '20', '21', '30', '40', '50', '60']
 
     services:
       mongodb:

+ 8 - 13
.vscode/settings.json

@@ -3,25 +3,20 @@
 
   "eslint.workingDirectories": [{ "mode": "auto" }],
 
-  // use stylelint-plus
-  // see https://qiita.com/y-w/items/bd7f11013fe34b69f0df#vs-code%E3%81%A8%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E3%82%8B
+  // use vscode-stylelint
+  // see https://github.com/stylelint/vscode-stylelint
+  "stylelint.validate": ["css", "less", "scss"],
+  "stylelint.ignoreDisables": true,
   "css.validate": false,
+  "less.validate": false,
   "scss.validate": false,
-  "[css]": {
-    "editor.formatOnSave": true
-  },
-  "[scss]": {
-    "editor.formatOnSave": true
-  },
 
-  // for vscode-eslint
-  "[javascript]": {
-    "editor.formatOnSave": false
-  },
   "editor.codeActionsOnSave": {
     "source.fixAll.eslint": true,
-    "source.fixAll.markdownlint": true
+    "source.fixAll.markdownlint": true,
+    "source.fixAll.stylelint": true
   },
+
   "githubPullRequests.ignoredPullRequestBranches": [
     "master"
   ]

+ 1 - 1
lerna.json

@@ -1,7 +1,7 @@
 {
   "npmClient": "yarn",
   "useWorkspaces": true,
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "packages": [
     "packages/*"
   ]

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",

+ 9 - 8
packages/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "license": "MIT",
   "scripts": {
     "//// for production": "",
@@ -64,11 +64,12 @@
     "@elastic/elasticsearch7": "npm:@elastic/elasticsearch@^7.17.0",
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
-    "@growi/codemirror-textlint": "^5.1.2",
-    "@growi/plugin-attachment-refs": "^5.1.2",
-    "@growi/plugin-lsx": "^5.1.2",
-    "@growi/plugin-pukiwiki-like-linker": "^5.1.2",
-    "@growi/slack": "^5.1.2",
+    "@growi/codemirror-textlint": "^5.1.3-RC.0",
+    "@growi/core": "^5.1.3-RC.0",
+    "@growi/plugin-attachment-refs": "^5.1.3-RC.0",
+    "@growi/plugin-lsx": "^5.1.3-RC.0",
+    "@growi/plugin-pukiwiki-like-linker": "^5.1.3-RC.0",
+    "@growi/slack": "^5.1.3-RC.0",
     "@promster/express": "^7.0.2",
     "@promster/server": "^7.0.4",
     "@slack/events-api": "^3.0.0",
@@ -171,7 +172,7 @@
   },
   "devDependencies": {
     "@alienfast/i18next-loader": "^1.1.4",
-    "@growi/ui": "^5.1.2",
+    "@growi/ui": "^5.1.3-RC.0",
     "@handsontable/react": "=2.1.0",
     "@types/compression": "^1.7.0",
     "@types/express": "^4.17.11",
@@ -207,7 +208,7 @@
     "lodash-webpack-plugin": "^0.11.5",
     "markdown-it": "^10.0.0",
     "markdown-it-blockdiag": "^1.1.1",
-    "markdown-it-drawio-viewer": "^1.3.1",
+    "markdown-it-drawio-viewer": "^1.4.0",
     "markdown-it-emoji": "^1.4.0",
     "markdown-it-emoji-mart": "^0.1.1",
     "markdown-it-footnote": "^3.0.1",

+ 1 - 0
packages/app/public/static/locales/en_US/translation.json

@@ -538,6 +538,7 @@
     "create_failed": "Failed to create {{target}}",
     "update_successed": "Succeeded to update {{target}}",
     "update_failed": "Failed to update {{target}}",
+    "file_upload_succeeded": "File upload succeeded.",
     "file_upload_failed": "File upload failed.",
     "initialize_successed": "Succeeded to initialize {{target}}",
     "give_user_admin": "Succeeded to give {{username}} admin",

+ 1 - 0
packages/app/public/static/locales/ja_JP/translation.json

@@ -538,6 +538,7 @@
     "create_failed": "{{target}}の作成に失敗しました",
     "update_successed": "{{target}}を更新しました",
     "update_failed": "{{target}}の更新に失敗しました",
+    "file_upload_succeeded": "ファイルをアップロードしました",
     "file_upload_failed": "ファイルのアップロードに失敗しました",
     "initialize_successed": "{{target}}を初期化しました",
     "give_user_admin": "{{username}}を管理者に設定しました",

+ 1 - 0
packages/app/public/static/locales/zh_CN/translation.json

@@ -516,6 +516,7 @@
     "create_failed": "Failed to create {{target}}",
 		"update_successed": "Succeeded to update {{target}}",
     "update_failed": "Failed to update {{target}}",
+    "file_upload_succeeded": "文件上传成功",
     "file_upload_failed": "文件上传失败",
     "initialize_successed": "Succeeded to initialize {{target}}",
 		"give_user_admin": "Succeeded to give {{username}} admin",

+ 22 - 2
packages/app/src/components/Admin/AuditLog/ActivityTable.tsx

@@ -1,9 +1,11 @@
-import React, { FC } from 'react';
+import React, { FC, useState, useCallback } from 'react';
 
 import { pagePathUtils } from '@growi/core';
 import { UserPicture } from '@growi/ui';
 import { format } from 'date-fns';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { useTranslation } from 'react-i18next';
+import { Tooltip } from 'reactstrap';
 
 import { IActivityHasId } from '~/interfaces/activity';
 
@@ -17,6 +19,14 @@ const formatDate = (date) => {
 
 export const ActivityTable : FC<Props> = (props: Props) => {
   const { t } = useTranslation();
+  const [tooltopOpen, setTooltipOpen] = useState(false);
+
+  const showToolTip = useCallback(() => {
+    setTooltipOpen(true);
+    setTimeout(() => {
+      setTooltipOpen(false);
+    }, 1000);
+  }, [setTooltipOpen]);
 
   return (
     <div className="table-responsive text-nowrap h-100">
@@ -45,7 +55,17 @@ export const ActivityTable : FC<Props> = (props: Props) => {
                 <td>{formatDate(activity.createdAt)}</td>
                 <td>{t(`admin:audit_log_action.${activity.action}`)}</td>
                 <td>{activity.ip}</td>
-                <td>{activity.endpoint}</td>
+                <td>
+                  {activity.endpoint}
+                  <CopyToClipboard text={activity.endpoint} onCopy={showToolTip}>
+                    <button type="button" className="btn btn-outline-secondary border-0 pull-right" id="tooltipTarget">
+                      <i className="fa fa-clipboard" aria-hidden="true"></i>
+                    </button>
+                  </CopyToClipboard>
+                  <Tooltip placement="top" isOpen={tooltopOpen} fade={false} target="tooltipTarget">
+                    copied!
+                  </Tooltip>
+                </td>
               </tr>
             );
           })}

+ 61 - 19
packages/app/src/components/Admin/AuditLogManagement.tsx

@@ -40,8 +40,9 @@ export const AuditLogManagement: FC = () => {
    * State
    */
   const [isSettingPage, setIsSettingPage] = useState<boolean>(false);
-  const [activePage, setActivePage] = useState<number>(1);
-  const offset = (activePage - 1) * PAGING_LIMIT;
+  const [activePageNumber, setActivePageNumber] = useState<number>(1);
+  const [jumpPageNumber, setJumpPageNumber] = useState<number>(1);
+  const offset = (activePageNumber - 1) * PAGING_LIMIT;
   const [startDate, setStartDate] = useState<Date | null>(null);
   const [endDate, setEndDate] = useState<Date | null>(null);
   const [selectedUsernames, setSelectedUsernames] = useState<string[]>([]);
@@ -59,6 +60,7 @@ export const AuditLogManagement: FC = () => {
   const { data: activityData, mutate: mutateActivity, error } = useSWRxActivity(PAGING_LIMIT, offset, searchFilter);
   const activityList = activityData?.docs != null ? activityData.docs : [];
   const totalActivityNum = activityData?.totalDocs != null ? activityData.totalDocs : 0;
+  const totalPagingPages = activityData?.totalPages != null ? activityData.totalPages : 0;
   const isLoading = activityData === undefined && error == null;
 
   if (error != null) {
@@ -71,34 +73,34 @@ export const AuditLogManagement: FC = () => {
    * Functions
    */
   const setActivePageHandler = useCallback((selectedPageNum: number) => {
-    setActivePage(selectedPageNum);
+    setActivePageNumber(selectedPageNum);
   }, []);
 
   const datePickerChangedHandler = useCallback((dateList: Date[] | null[]) => {
-    setActivePage(1);
+    setActivePageNumber(1);
     setStartDate(dateList[0]);
     setEndDate(dateList[1]);
   }, []);
 
   const actionCheckboxChangedHandler = useCallback((action: SupportedActionType) => {
-    setActivePage(1);
+    setActivePageNumber(1);
     actionMap.set(action, !actionMap.get(action));
     setActionMap(new Map(actionMap.entries()));
   }, [actionMap, setActionMap]);
 
   const multipleActionCheckboxChangedHandler = useCallback((actions: SupportedActionType[], isChecked) => {
-    setActivePage(1);
+    setActivePageNumber(1);
     actions.forEach(action => actionMap.set(action, isChecked));
     setActionMap(new Map(actionMap.entries()));
   }, [actionMap, setActionMap]);
 
   const setUsernamesHandler = useCallback((usernames: string[]) => {
-    setActivePage(1);
+    setActivePageNumber(1);
     setSelectedUsernames(usernames);
   }, []);
 
   const clearButtonPushedHandler = useCallback(() => {
-    setActivePage(1);
+    setActivePageNumber(1);
     setStartDate(null);
     setEndDate(null);
     setSelectedUsernames([]);
@@ -107,15 +109,39 @@ export const AuditLogManagement: FC = () => {
     if (auditLogAvailableActionsData != null) {
       setActionMap(new Map<SupportedActionType, boolean>(auditLogAvailableActionsData.map(action => [action, true])));
     }
-  }, [setActivePage, setStartDate, setEndDate, setSelectedUsernames, setActionMap, auditLogAvailableActionsData]);
+  }, [setActivePageNumber, setStartDate, setEndDate, setSelectedUsernames, setActionMap, auditLogAvailableActionsData]);
 
   const reloadButtonPushedHandler = useCallback(() => {
-    setActivePage(1);
+    setActivePageNumber(1);
     mutateActivity();
   }, [mutateActivity]);
 
+  const jumpPageInputChangeHandler = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
+    const inputNumber = Number(e.target.value);
+    const isNan = Number.isNaN(inputNumber);
+
+    if (!isNan) {
+      // eslint-disable-next-line no-nested-ternary
+      const jumpPageNumber = inputNumber > totalPagingPages ? totalPagingPages : inputNumber <= 0 ? activePageNumber : inputNumber;
+      setJumpPageNumber(jumpPageNumber);
+    }
+    else {
+      setJumpPageNumber(activePageNumber);
+    }
+  }, [totalPagingPages, activePageNumber, setJumpPageNumber]);
+
+  const jumpPageInputKeyDownHandler = useCallback((e) => {
+    if (e.key === 'Enter') {
+      setActivePageNumber(jumpPageNumber);
+    }
+  }, [setActivePageNumber, jumpPageNumber]);
+
+  const jumpPageButtonPushedHandler = useCallback(() => {
+    setActivePageNumber(jumpPageNumber);
+  }, [jumpPageNumber]);
+
   // eslint-disable-next-line max-len
-  const activityCounter = `<b>${activityList.length === 0 ? 0 : offset + 1}</b> - <b>${(PAGING_LIMIT * activePage) - (PAGING_LIMIT - activityList.length)}</b> of <b>${totalActivityNum}<b/>`;
+  const activityCounter = `<b>${activityList.length === 0 ? 0 : offset + 1}</b> - <b>${(PAGING_LIMIT * activePageNumber) - (PAGING_LIMIT - activityList.length)}</b> of <b>${totalActivityNum}<b/>`;
 
   if (!auditLogEnabled) {
     return <AuditLogDisableMode />;
@@ -187,14 +213,30 @@ export const AuditLogManagement: FC = () => {
             )
           }
 
-          <PaginationWrapper
-            activePage={activePage}
-            changePage={setActivePageHandler}
-            totalItemsCount={totalActivityNum}
-            pagingLimit={PAGING_LIMIT}
-            align="center"
-            size="sm"
-          />
+          <div className="d-flex flex-row justify-content-center">
+            <PaginationWrapper
+              activePage={activePageNumber}
+              changePage={setActivePageHandler}
+              totalItemsCount={totalActivityNum}
+              pagingLimit={PAGING_LIMIT}
+              align="center"
+              size="sm"
+            />
+
+            <div className="admin-audit-log ml-3">
+              <label htmlFor="jumpPageInput" className="mr-1 text-secondary">Jump To Page</label>
+              <input
+                id="jumpPageInput"
+                type="text"
+                className="jump-page-input"
+                onChange={jumpPageInputChangeHandler}
+                onKeyDown={jumpPageInputKeyDownHandler}
+              />
+              <button className="btn btn-sm" type="button" onClick={jumpPageButtonPushedHandler}>
+                <b>Go</b>
+              </button>
+            </div>
+          </div>
         </>
       )}
     </div>

+ 1 - 1
packages/app/src/components/DescendantsPageList.tsx

@@ -103,7 +103,7 @@ export const DescendantsPageListSubstance = (props: SubstanceProps): JSX.Element
     );
   }
 
-  const showPager = pagingResult.items.length > pagingResult.limit;
+  const showPager = pagingResult.totalCount > pagingResult.limit;
 
   return (
     <>

+ 5 - 0
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -15,6 +15,7 @@ import {
 } from '~/interfaces/page';
 import { IResTagsUpdateApiv1 } from '~/interfaces/tag';
 import { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
+import { IUser } from '~/interfaces/user';
 import {
   useCurrentCreatedAt, useCurrentUpdatedAt, useCurrentPageId, useRevisionId, useCurrentPagePath,
   useCreator, useRevisionAuthor, useCurrentUser, useIsGuestUser, useIsSharedUser, useShareLinkId, useEmptyPageId, useTemplateTagData,
@@ -211,9 +212,13 @@ const GrowiContextualSubNavigation = (props) => {
   const tagsUpdatedHandlerForViewMode = useCallback(async(newTags: string[]) => {
     try {
       const res: IResTagsUpdateApiv1 = await apiPost('/tags.update', { pageId, revisionId, tags: newTags });
+
       const updatedRevisionId = getIdForRef(res.savedPage.revision);
       await pageContainer.setState({ revisionId: updatedRevisionId });
 
+      const lastUpdateUser = res.savedPage?.lastUpdateUser as IUser;
+      await pageContainer.setState({ lastUpdateUsername: lastUpdateUser.username });
+
       // revalidate SWRTagsInfo
       mutateSWRTagsInfo();
       mutatePageTagsForEditors(newTags);

+ 2 - 2
packages/app/src/components/PageEditor/Editor.tsx

@@ -8,7 +8,7 @@ import {
   Modal, ModalHeader, ModalBody,
 } from 'reactstrap';
 
-import { toastError } from '~/client/util/apiNotification';
+import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import { useDefaultIndentSize } from '~/stores/context';
 import { useEditorSettings } from '~/stores/editor';
 import { useIsMobile } from '~/stores/ui';
@@ -141,7 +141,7 @@ const Editor = React.forwardRef((props: EditorPropsType, ref): JSX.Element => {
   const pasteFilesHandler = useCallback((event) => {
     const items = event.clipboardData.items || event.clipboardData.files || [];
 
-    toastError(t('toaster.file_upload_failed'));
+    toastSuccess(t('toaster.file_upload_succeeded'));
 
     // abort if length is not 1
     if (items.length < 1) {

+ 8 - 7
packages/app/src/components/Sidebar/RecentChanges.tsx

@@ -2,21 +2,22 @@ import React, {
   FC,
   useCallback, useEffect, useState,
 } from 'react';
-import PropTypes from 'prop-types';
 
+import { DevidedPagePath } from '@growi/core';
+import { UserPicture, FootstampIcon } from '@growi/ui';
+import PropTypes from 'prop-types';
 import { useTranslation } from 'react-i18next';
 
-import { UserPicture, FootstampIcon } from '@growi/ui';
-import { DevidedPagePath } from '@growi/core';
 
 import PagePathHierarchicalLink from '~/components/PagePathHierarchicalLink';
+import LinkedPagePath from '~/models/linked-page-path';
 import { useSWRInifinitexRecentlyUpdated } from '~/stores/page';
 import loggerFactory from '~/utils/logger';
 
-import LinkedPagePath from '~/models/linked-page-path';
+import FormattedDistanceDate from '../FormattedDistanceDate';
+
 import InfiniteScroll from './InfiniteScroll';
 
-import FormattedDistanceDate from '../FormattedDistanceDate';
 
 const logger = loggerFactory('growi:History');
 
@@ -144,7 +145,7 @@ const RecentChanges = (): JSX.Element => {
   }, [retrieveSizePreferenceFromLocalStorage]);
 
   return (
-    <>
+    <div data-testid="grw-recent-changes">
       <div className="grw-sidebar-content-header p-3 d-flex">
         <h3 className="mb-0  text-nowrap">{t('Recent Changes')}</h3>
         <button type="button" className="btn btn-sm ml-auto grw-btn-reload" onClick={() => swr.mutate()}>
@@ -179,7 +180,7 @@ const RecentChanges = (): JSX.Element => {
           </InfiniteScroll>
         </ul>
       </div>
-    </>
+    </div>
   );
 
 };

+ 2 - 1
packages/app/src/linter-checker/test.scss

@@ -1,7 +1,8 @@
 /*
  * VSCode の Stylelint 設定チェック方法
  *
- * 1. .stylelintrc.json ファイル中の `src/linter-checker/test.scss` 行を削除
+ * 1. .stylelintrc.json ファイル中の `src/linter-checker/test.scss` 行を削除し、
+ *    このファイルを VSCode 上で開き直す
  *
  * 2. VSCode で以下のエラーが表示されていることを確認
  *   - color で stylelint(order/properties-order)

+ 6 - 0
packages/app/src/server/views/widget/headers/drawio.html

@@ -27,6 +27,12 @@
       // Set responsive option.
       // refs: https://github.com/jgraph/drawio/blob/v13.9.1/src/main/webapp/js/diagramly/GraphViewer.js#L89-L95
       DrawioViewer.prototype.responsive = true;
+
+      // Set z-index ($zindex-dropdown + 200) for lightbox.
+      // 'lightbox' is like a modal dialog that appears when click on a drawio diagram.
+      // z-index refs: https://github.com/twbs/bootstrap/blob/v4.6.2/scss/_variables.scss#L681
+      DrawioViewer.prototype.lightboxZIndex = 1200;
+      DrawioViewer.prototype.toolbarZIndex = 1200;
     }
   };
 </script>

+ 3 - 0
packages/app/src/styles/_admin.scss

@@ -236,6 +236,9 @@ $slack-work-space-name-card-border: #efc1f6;
     .date-range-picker {
       width: 188px;
     }
+    .jump-page-input {
+      width: 50px;
+    }
   }
 
   #layoutOptions {

+ 167 - 0
packages/app/test/cypress/integration/50-sidebar/access-to-side-bar.spec.ts

@@ -0,0 +1,167 @@
+context('Access to sidebar', () => {
+  const ssPrefix = 'access-to-sidebar-';
+
+  beforeEach(() => {
+    // login
+    cy.fixture("user-admin.json").then(user => {
+      cy.login(user.username, user.password);
+    });
+    // collapse sidebar
+    cy.collapseSidebar(false);
+  });
+
+  it('Successfully show/collapse sidebar', () => {
+    cy.visit('/');
+    cy.screenshot(`${ssPrefix}-1-sidebar-shown`, {capture: 'viewport'});
+    cy.getByTestid('grw-navigation-resize-button').click({force: true});
+    cy.screenshot(`${ssPrefix}-2-sidebar-collapsed`, {capture: 'viewport'});
+
+  });
+  it('Successfully access recent changes side bar ', () => {
+    cy.visit('/');
+    cy.getByTestid('grw-sidebar-nav-primary-recent-changes').click();
+    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
+      if($el.hasClass('d-none')){
+        cy.getByTestid('grw-navigation-resize-button').click({force: true});
+      }
+    });
+
+    cy.getByTestid('grw-recent-changes').should('be.visible');
+
+    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}recent-changes-1-page-list`);
+
+    cy.get('#grw-sidebar-contents-wrapper').within(() => {
+      cy.get('#recentChangesResize').click({force: true});
+      cy.screenshot(`${ssPrefix}recent-changes-2-switch-sidebar-size`);
+    });
+  });
+
+  it('Successfully create a custom sidebar page', () => {
+    cy.visit('/');
+    cy.getByTestid('grw-sidebar-nav-primary-custom-sidebar').click();
+    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
+      if($el.hasClass('d-none')){
+        cy.getByTestid('grw-navigation-resize-button').click({force: true});
+      }
+    });
+
+    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}custom-sidebar-1-click-on-custom-sidebar`);
+
+    // create /Sidebar contents
+    const content = '# HELLO \n ## Hello\n ### Hello';
+    cy.get('.grw-sidebar-content-header.h5').find('a').click();
+    cy.get('.CodeMirror textarea').type(content, {force: true});
+    cy.screenshot(`${ssPrefix}custom-sidebar-2-custom-sidebar-editor`);
+    cy.get('.dropup > .btn-submit').click();
+    cy.get('body').should('not.have.class', 'on-edit');
+    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}custom-sidebar-3-custom-sidebar-created`);
+  });
+
+  it('Successfully performed page operation from "page tree"', () => {
+    cy.visit('/');
+    cy.getByTestid('grw-sidebar-nav-primary-page-tree').click();
+    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
+      if($el.hasClass('d-none')){
+        cy.getByTestid('grw-navigation-resize-button').click({force: true});
+      }
+    });
+    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}page-tree-1-access-to-page-tree`);
+    cy.get('.grw-pagetree-triangle-btn').eq(0).click();
+    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}page-tree-2-hide-page-tree-item`);
+    cy.get('.grw-pagetree-triangle-btn').eq(0).click();
+
+    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
+    });
+
+    cy.screenshot(`${ssPrefix}page-tree-3-click-three-dots-menu`);
+    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
+      cy.getByTestid('add-remove-bookmark-btn').click();
+    });
+    cy.screenshot(`${ssPrefix}page-tree-4-add-bookmark`);
+
+
+    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
+    });
+    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
+      cy.getByTestid('open-page-duplicate-modal-btn').click();
+    });
+
+    cy.getByTestid('page-duplicate-modal').should('be.visible').within(() => {
+      cy.get('.rbt-input-main').type('_test');
+      cy.screenshot(`${ssPrefix}page-tree-5-duplicate-page`);
+      cy.get('.modal-header > button').click();
+    });
+
+    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
+    });
+    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
+      cy.getByTestid('open-page-move-rename-modal-btn').click();
+    });
+
+    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+      cy.get('.flex-fill > input').type('_newname');
+    });
+
+    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}page-tree-6-rename-page`);
+    cy.get('body').click(0,0);
+
+    cy.get('.grw-pagetree-item-children').eq(0).within(() => {
+      cy.getByTestid('open-page-item-control-btn').find('button').eq(0).invoke('css','display','block').click()
+    });
+    cy.get('.dropdown-menu.show').should('be.visible').within(() => {
+      cy.getByTestid('open-page-delete-modal-btn').click();
+    });
+
+    cy.getByTestid('page-delete-modal').should('be.visible').within(() => {
+      cy.screenshot(`${ssPrefix}page-tree-7-delete-page`);
+      cy.get('.modal-header > button').click();
+    });
+
+  });
+
+  it('Successfully performed page operation from "Tags" ', () => {
+    cy.visit('/');
+    cy.getByTestid('grw-sidebar-nav-primary-tags').click();
+    cy.getByTestid('grw-contextual-navigation-sub').then(($el) => {
+      if($el.hasClass('d-none')){
+        cy.getByTestid('grw-navigation-resize-button').click({force: true});
+      }
+    });
+    cy.getByTestid('grw-contextual-navigation-sub').screenshot(`${ssPrefix}tags-1-access-to-tags`);
+
+    cy.get('.grw-container-convertible > div > .btn-primary').click({force: true});
+
+    // collapse sidebar
+    cy.collapseSidebar(true);
+
+    cy.screenshot(`${ssPrefix}tags-2-check-all-tags`);
+  });
+
+  it('Successfully access to My Drafts page', () => {
+    cy.visit('/');
+    cy.get('.grw-sidebar-nav-secondary-container').within(() => {
+      cy.get('a[href*="/me/drafts"]').click();
+    });
+    cy.screenshot(`${ssPrefix}access-to-drafts-page`);
+  });
+  it('Successfully access to Growi Docs page', () => {
+    cy.visit('/');
+    cy.get('.grw-sidebar-nav-secondary-container').within(() => {
+      cy.get('a[href*="https://docs.growi.org"]').then(($a) => {
+        const url = $a.prop('href')
+        cy.request(url).its('body').should('include', '</html>');
+      });
+    });
+  });
+
+  it('Successfully access to trash page', () => {
+    cy.visit('/');
+    cy.get('.grw-sidebar-nav-secondary-container').within(() => {
+      cy.get('a[href*="/trash"]').click();
+    });
+    cy.screenshot(`${ssPrefix}access-to-trash-page`);
+  });
+});

+ 4 - 4
packages/app/test/cypress/integration/50-switch-sidebar-mode/switching-sidebar-mode.spec.ts → packages/app/test/cypress/integration/50-sidebar/switching-sidebar-mode.spec.ts

@@ -21,13 +21,13 @@ context('Switch sidebar mode', () => {
 
   it('Switching sidebar mode', () => {
     cy.visit('/');
-    cy.get('.grw-personal-dropdown').click();
+    cy.get('.grw-personal-dropdown').first().click();
 
-    cy.get('[for="swSidebarModeOnEditor"]').click();
+    cy.get('[for="swSidebarMode"]').click({force: true});
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode`, { capture: 'viewport' });
 
-    cy.get('[for="swSidebarModeOnEditor"]').click();
+    cy.get('[for="swSidebarMode"]').click({force: true});
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode-back`, { capture: 'viewport' });
   });
 
-});
+});

+ 1 - 1
packages/codemirror-textlint/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/codemirror-textlint",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "license": "MIT",
   "main": "dist/index.js",
   "scripts": {

+ 1 - 1
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/core",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "description": "GROWI Core Libraries",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/plugin-attachment-refs/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-attachment-refs",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/plugin-lsx/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-lsx",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "description": "GROWI plugin to list pages",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/plugin-pukiwiki-like-linker/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-pukiwiki-like-linker",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "description": "GROWI plugin to add PukiwikiLikeLinker",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slack",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "license": "MIT",
   "main": "dist/index.js",
   "typings": "dist/index.d.ts",

+ 2 - 2
packages/slackbot-proxy/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slackbot-proxy",
-  "version": "5.1.2",
+  "version": "5.1.3-slackbot-proxy.0",
   "license": "MIT",
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
@@ -25,7 +25,7 @@
   },
   "dependencies": {
     "@godaddy/terminus": "^4.9.0",
-    "@growi/slack": "^5.1.2",
+    "@growi/slack": "^5.1.3-RC.0",
     "@slack/oauth": "^2.0.1",
     "@slack/web-api": "^6.2.4",
     "@tsed/common": "^6.43.0",

+ 1 - 1
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/ui",
-  "version": "5.1.2",
+  "version": "5.1.3-RC.0",
   "description": "GROWI UI Libraries",
   "license": "MIT",
   "keywords": [

+ 4 - 4
yarn.lock

@@ -13950,10 +13950,10 @@ markdown-it-blockdiag@^1.1.1:
     url-join "^4.0.0"
     utf8-bytes "0.0.1"
 
-markdown-it-drawio-viewer@^1.3.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/markdown-it-drawio-viewer/-/markdown-it-drawio-viewer-1.3.1.tgz#96ec4d02c159cb1ccb07760ea3ce7bfb07ac8730"
-  integrity sha512-jNjKM6ULboy1VCYePZkNzqRUZDGrhlvo7giIGSnSX2X4DAWSTZ4bGtH0gBUGsRVRjj2BjsRm0mah8RMi8I1qGQ==
+markdown-it-drawio-viewer@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/markdown-it-drawio-viewer/-/markdown-it-drawio-viewer-1.4.0.tgz#4a4b590775a75f85e8c83b9042018b57657a172f"
+  integrity sha512-2xorLkpvwl+ncqlvF2CWEFRqfmYjq/SNy2Avc0tW8am+1BpoaIHfyM+sHH6XoLEJ3ZgkAsNnkIjmxssqNsbWBg==
   dependencies:
     "@kaishuu0123/markdown-it-fence" "^1.0.1"
     xmldoc "^1.1.2"