Browse Source

Merge branch 'fix/83698-dot-button-design' into fix/83698-84265-adjast-changes-from-dev5

* fix/83698-dot-button-design:
  remove box-shadow of sidebar nav buttons
  fix: Downgrade node image for build (#4956)
  84033 fb
  fb
  refs #83419: fix search result right page - changes for FB
  fix dropdown background
  Added a condition
  add breakpoint
  Highlight target page even when empty
  refs #83419: fix search result right page - fix nav height
  refs #83419: fix search result right page - fix styles
  refs #83419: fix search result right page - remove adding tags icon
  refs #83419: fix search result right page - right page vertical flex
  refs #83419: fix search result right page - fix copy link btn shadow
  84033 btn click color
  refs #83419: fix search result right page
Mao 4 years ago
parent
commit
7713e67bc8

+ 3 - 3
packages/app/docker/Dockerfile

@@ -7,7 +7,7 @@ ARG flavor=default
 ##
 ## deps-resolver
 ##
-FROM node:16-slim AS deps-resolver
+FROM node:14-slim AS deps-resolver
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 
 ENV appDir /opt/growi
@@ -51,7 +51,7 @@ RUN tar cf node_modules.tar \
 ##
 ## prebuilder-default
 ##
-FROM node:16-slim AS prebuilder-default
+FROM node:14-slim AS prebuilder-default
 
 ENV appDir /opt/growi
 
@@ -124,7 +124,7 @@ RUN tar cf packages.tar \
 ##
 ## release
 ##
-FROM node:16-slim
+FROM node:14-slim
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 
 ENV NODE_ENV production

+ 5 - 1
packages/app/src/client/services/ContextExtractor.tsx

@@ -5,7 +5,8 @@ import {
   useCreatedAt, useDeleteUsername, useDeletedAt, useHasChildren, useHasDraftOnHackmd, useIsAbleToDeleteCompletely,
   useIsDeletable, useIsDeleted, useIsNotCreatable, useIsPageExist, useIsTrashPage, useIsUserPage, useLastUpdateUsername,
   useCurrentPageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
-  useShareLinkId, useShareLinksNumber, useTemplateTagData, useUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors, useSlackChannels,
+  useShareLinkId, useShareLinksNumber, useTemplateTagData, useUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
+  useSlackChannels, useNotFoundTargetPathOrId,
 } from '../../stores/context';
 import {
   useIsDeviceSmallerThanMd,
@@ -20,6 +21,7 @@ const jsonNull = 'null';
 const ContextExtractorOnce: FC = () => {
 
   const mainContent = document.querySelector('#content-main');
+  const notFoundContent = document.getElementById('growi-pagetree-not-found-context');
 
   /*
    * App Context from DOM
@@ -61,6 +63,7 @@ const ContextExtractorOnce: FC = () => {
   const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
   const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
   const targetAndAncestors = JSON.parse(document.getElementById('growi-pagetree-target-and-ancestors')?.textContent || jsonNull);
+  const notFoundTargetPathOrId = JSON.parse(notFoundContent?.getAttribute('data-not-found-target-path-or-id') || jsonNull);
   const slackChannels = mainContent?.getAttribute('data-slack-channels') || '';
 
   /*
@@ -104,6 +107,7 @@ const ContextExtractorOnce: FC = () => {
   useCreator(creator);
   useRevisionAuthor(revisionAuthor);
   useTargetAndAncestors(targetAndAncestors);
+  useNotFoundTargetPathOrId(notFoundTargetPathOrId);
 
   // Navigation
   usePreferDrawerModeByUser();

+ 2 - 2
packages/app/src/components/Page/PageManagement.jsx

@@ -206,7 +206,7 @@ const LegacyPageManagemenet = (props) => {
           className={`btn-link nav-link dropdown-toggle dropdown-toggle-no-caret border-0 rounded grw-btn-page-management ${isCompactMode && 'py-0'}`}
           data-toggle="dropdown"
         >
-          <i className="icon-options"></i>
+          <i className="text-muted icon-options"></i>
         </button>
       </>
     );
@@ -220,7 +220,7 @@ const LegacyPageManagemenet = (props) => {
           className={`btn nav-link bg-transparent dropdown-toggle dropdown-toggle-no-caret disabled ${isCompactMode && 'py-0'}`}
           id="icon-options-guest-tltips"
         >
-          <i className="icon-options"></i>
+          <i className="text-muted icon-options"></i>
         </button>
         <UncontrolledTooltip placement="top" target="icon-options-guest-tltips" fade={false}>
           {t('Not available for guest')}

+ 10 - 8
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -22,20 +22,22 @@ const SearchResultContent: FC<Props> = (props: Props) => {
   if (page == null) return <></>;
   const growiRenderer = props.appContainer.getRenderer('searchresult');
   return (
-    <div key={page._id} className="search-result-page mb-5">
+    <div key={page._id} className="search-result-page grw-page-path-text-muted-container d-flex flex-column">
       <SearchResultContentSubNavigation
         pageId={page._id}
         revisionId={page.revision}
         path={page.path}
       >
       </SearchResultContentSubNavigation>
-      <RevisionLoader
-        growiRenderer={growiRenderer}
-        pageId={page._id}
-        pagePath={page.path}
-        revisionId={page.revision}
-        highlightKeywords={props.searchingKeyword}
-      />
+      <div className="search-result-page-content">
+        <RevisionLoader
+          growiRenderer={growiRenderer}
+          pageId={page._id}
+          pagePath={page.path}
+          revisionId={page.revision}
+          highlightKeywords={props.searchingKeyword}
+        />
+      </div>
     </div>
   );
 };

+ 26 - 43
packages/app/src/components/SearchPage/SearchResultContentSubNavigation.tsx

@@ -1,11 +1,8 @@
-import React, { FC, useCallback } from 'react';
+import React, { FC } from 'react';
 import { pagePathUtils } from '@growi/core';
 import PagePathNav from '../PagePathNav';
 import { withUnstatedContainers } from '../UnstatedUtils';
 import AppContainer from '../../client/services/AppContainer';
-import TagLabels from '../Page/TagLabels';
-import { toastSuccess, toastError } from '../../client/util/apiNotification';
-import { apiPost } from '../../client/util/apiv1-client';
 import { useSWRTagsInfo } from '../../stores/page';
 import SubNavButtons from '../Navbar/SubNavButtons';
 
@@ -26,18 +23,7 @@ const SearchResultContentSubNavigation: FC<Props> = (props : Props) => {
 
   const { isTrashPage, isDeletablePage } = pagePathUtils;
 
-  const { data: tagInfoData, error: tagInfoError, mutate: mutateTagInfo } = useSWRTagsInfo(pageId);
-
-  const tagsUpdatedHandler = useCallback(async(newTags) => {
-    try {
-      await apiPost('/tags.update', { pageId, tags: newTags });
-      toastSuccess('updated tags successfully');
-      mutateTagInfo();
-    }
-    catch (err) {
-      toastError(err, 'fail to update tags');
-    }
-  }, [pageId, mutateTagInfo]);
+  const { data: tagInfoData, error: tagInfoError } = useSWRTagsInfo(pageId);
 
   if (tagInfoError != null || tagInfoData == null) {
     return <></>;
@@ -46,33 +32,30 @@ const SearchResultContentSubNavigation: FC<Props> = (props : Props) => {
   const { isSharedUser } = appContainer;
   const isAbleToShowPageManagement = !(isTrashPage(path)) && !isSharedUser;
   return (
-    <div className={`grw-subnav container-fluid d-flex align-items-center justify-content-between ${isCompactMode ? 'grw-subnav-compact d-print-none' : ''}`}>
-      {/* Left side */}
-      <div className="grw-path-nav-container">
-        {!isSharedUser && !isCompactMode && (
-          <div className="grw-taglabels-container">
-            <TagLabels tags={tagInfoData.tags} tagsUpdateInvoked={tagsUpdatedHandler} />
-          </div>
-        )}
-        <PagePathNav pageId={pageId} pagePath={path} isCompactMode={isCompactMode} isSingleLineMode={isSignleLineMode} />
-      </div>
-      {/* Right side */}
-      {/*
-        DeleteCompletely is currently disabled
-        TODO : Retrive isAbleToDeleteCompleltly state everywhere in the system via swr.
-        story: https://redmine.weseek.co.jp/issues/82222
-      */}
-      <div className="d-flex">
-        <SubNavButtons
-          isCompactMode={isCompactMode}
-          pageId={pageId}
-          revisionId={revisionId}
-          path={path}
-          isDeletable={isPageDeletable}
-          // isAbleToDeleteCompletely={}
-          willShowPageManagement={isAbleToShowPageManagement}
-        >
-        </SubNavButtons>
+    <div className="position-sticky fixed-top shadow-sm search-result-content-nav">
+      <div className={`grw-subnav container-fluid d-flex align-items-start justify-content-between ${isCompactMode ? 'grw-subnav-compact d-print-none' : ''}`}>
+        {/* Left side */}
+        <div className="grw-path-nav-container">
+          <PagePathNav pageId={pageId} pagePath={path} isCompactMode={isCompactMode} isSingleLineMode={isSignleLineMode} />
+        </div>
+        {/* Right side */}
+        {/*
+          DeleteCompletely is currently disabled
+          TODO : Retrive isAbleToDeleteCompleltly state everywhere in the system via swr.
+          story: https://redmine.weseek.co.jp/issues/82222
+        */}
+        <div className="d-flex">
+          <SubNavButtons
+            isCompactMode={isCompactMode}
+            pageId={pageId}
+            revisionId={revisionId}
+            path={path}
+            isDeletable={isPageDeletable}
+            // isAbleToDeleteCompletely={}
+            willShowPageManagement={isAbleToShowPageManagement}
+          >
+          </SubNavButtons>
+        </div>
       </div>
     </div>
   );

+ 3 - 3
packages/app/src/components/SearchPage/SearchResultListItem.tsx

@@ -4,7 +4,7 @@ import Clamp from 'react-multiline-clamp';
 
 import { UserPicture, PageListMeta, PagePathLabel } from '@growi/ui';
 import { pagePathUtils, DevidedPagePath } from '@growi/core';
-import { useIsDeviceSmallerThanMd } from '~/stores/ui';
+import { useIsDeviceSmallerThanLg } from '~/stores/ui';
 
 import { IPageSearchResultData } from '../../interfaces/search';
 import PageItemControl from '../Common/Dropdown/PageItemControl';
@@ -28,7 +28,7 @@ const SearchResultListItem: FC<Props> = memo((props:Props) => {
     page: { pageData, pageMeta }, isSelected, onClickSearchResultItem, onClickCheckbox, isChecked, isEnableActions, shortBody,
   } = props;
 
-  const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
+  const { data: isDeviceSmallerThanLg } = useIsDeviceSmallerThanLg();
 
   const pagePath: DevidedPagePath = new DevidedPagePath(pageData.path, true);
 
@@ -48,7 +48,7 @@ const SearchResultListItem: FC<Props> = memo((props:Props) => {
     />
   );
 
-  const responsiveListStyleClass = `${isDeviceSmallerThanMd ? '' : `list-group-item-action ${isSelected ? 'active' : ''}`}`;
+  const responsiveListStyleClass = `${isDeviceSmallerThanLg ? '' : `list-group-item-action ${isSelected ? 'active' : ''}`}`;
 
   return (
     <li

+ 5 - 2
packages/app/src/components/Sidebar/PageTree.tsx

@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
 
 import { useSWRxV5MigrationStatus } from '~/stores/page-listing';
 import {
-  useCurrentPagePath, useCurrentPageId, useTargetAndAncestors, useIsGuestUser,
+  useCurrentPagePath, useCurrentPageId, useTargetAndAncestors, useIsGuestUser, useNotFoundTargetPathOrId,
 } from '~/stores/context';
 
 import ItemsTree from './PageTree/ItemsTree';
@@ -18,6 +18,7 @@ const PageTree: FC = memo(() => {
   const { data: currentPath } = useCurrentPagePath();
   const { data: targetId } = useCurrentPageId();
   const { data: targetAndAncestorsData } = useTargetAndAncestors();
+  const { data: notFoundTargetPathOrId } = useNotFoundTargetPathOrId();
 
   const { data: migrationStatus } = useSWRxV5MigrationStatus();
 
@@ -25,6 +26,8 @@ const PageTree: FC = memo(() => {
   const [isDeleteModalOpen, setDeleteModalOpen] = useState(false);
   const [pagesToDelete, setPagesToDelete] = useState<IPageForPageDeleteModal[]>([]);
 
+  const targetPathOrId = targetId || notFoundTargetPathOrId;
+
   if (migrationStatus == null) {
     return (
       <>
@@ -81,7 +84,7 @@ const PageTree: FC = memo(() => {
         <ItemsTree
           isEnableActions={!isGuestUser}
           targetPath={path}
-          targetId={targetId}
+          targetPathOrId={targetPathOrId}
           targetAndAncestorsData={targetAndAncestorsData}
           isDeleteModalOpen={isDeleteModalOpen}
           pagesToDelete={pagesToDelete}

+ 8 - 8
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -21,19 +21,19 @@ const { isTopPage } = pagePathUtils;
 interface ItemProps {
   isEnableActions: boolean
   itemNode: ItemNode
-  targetId?: string
+  targetPathOrId?: string
   isOpen?: boolean
   onClickDeleteByPage?(page: IPageForPageDeleteModal): void
 }
 
 // Utility to mark target
-const markTarget = (children: ItemNode[], targetId?: string): void => {
-  if (targetId == null) {
+const markTarget = (children: ItemNode[], targetPathOrId?: string): void => {
+  if (targetPathOrId == null) {
     return;
   }
 
   children.forEach((node) => {
-    if (node.page._id === targetId) {
+    if (node.page._id === targetPathOrId || node.page.path === targetPathOrId) {
       node.page.isTarget = true;
     }
     return node;
@@ -96,7 +96,7 @@ const ItemCount: FC = () => {
 const Item: FC<ItemProps> = (props: ItemProps) => {
   const { t } = useTranslation();
   const {
-    itemNode, targetId, isOpen: _isOpen = false, onClickDeleteByPage, isEnableActions,
+    itemNode, targetPathOrId, isOpen: _isOpen = false, onClickDeleteByPage, isEnableActions,
   } = props;
 
   const { page, children } = itemNode;
@@ -162,7 +162,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
    */
   useEffect(() => {
     if (children.length > currentChildren.length) {
-      markTarget(children, targetId);
+      markTarget(children, targetPathOrId);
       setCurrentChildren(children);
     }
   }, []);
@@ -173,7 +173,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
   useEffect(() => {
     if (isOpen && error == null && data != null) {
       const newChildren = ItemNode.generateNodesFromPages(data.children);
-      markTarget(newChildren, targetId);
+      markTarget(newChildren, targetPathOrId);
       setCurrentChildren(newChildren);
     }
   }, [data, isOpen]);
@@ -223,7 +223,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
               isEnableActions={isEnableActions}
               itemNode={node}
               isOpen={false}
-              targetId={targetId}
+              targetPathOrId={targetPathOrId}
               onClickDeleteByPage={onClickDeleteByPage}
             />
           </div>

+ 7 - 6
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -50,7 +50,7 @@ const generateInitialNodeAfterResponse = (ancestorsChildren: Record<string, Part
 type ItemsTreeProps = {
   isEnableActions: boolean
   targetPath: string
-  targetId?: string
+  targetPathOrId?: string
   targetAndAncestorsData?: TargetAndAncestors
 
   // for deleteModal
@@ -63,13 +63,14 @@ type ItemsTreeProps = {
 }
 
 const renderByInitialNode = (
-    initialNode: ItemNode, DeleteModal: JSX.Element, isEnableActions: boolean, targetId?: string, onClickDeleteByPage?: (page: IPageForPageDeleteModal) => void,
+    // eslint-disable-next-line max-len
+    initialNode: ItemNode, DeleteModal: JSX.Element, isEnableActions: boolean, targetPathOrId?: string, onClickDeleteByPage?: (page: IPageForPageDeleteModal) => void,
 ): JSX.Element => {
   return (
     <div className="grw-pagetree p-3">
       <Item
         key={initialNode.page.path}
-        targetId={targetId}
+        targetPathOrId={targetPathOrId}
         itemNode={initialNode}
         isOpen
         isEnableActions={isEnableActions}
@@ -86,7 +87,7 @@ const renderByInitialNode = (
  */
 const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
   const {
-    targetPath, targetId, targetAndAncestorsData, isDeleteModalOpen, pagesToDelete, isAbleToDeleteCompletely, isDeleteCompletelyModal, onCloseDelete,
+    targetPath, targetPathOrId, targetAndAncestorsData, isDeleteModalOpen, pagesToDelete, isAbleToDeleteCompletely, isDeleteCompletelyModal, onCloseDelete,
     onClickDeleteByPage, isEnableActions,
   } = props;
 
@@ -114,7 +115,7 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
    */
   if (ancestorsChildrenData != null && rootPageData != null) {
     const initialNode = generateInitialNodeAfterResponse(ancestorsChildrenData.ancestorsChildren, new ItemNode(rootPageData.rootPage));
-    return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetId, onClickDeleteByPage);
+    return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetPathOrId, onClickDeleteByPage);
   }
 
   /*
@@ -122,7 +123,7 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
    */
   if (targetAndAncestorsData != null) {
     const initialNode = generateInitialNodeBeforeResponse(targetAndAncestorsData.targetAndAncestors);
-    return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetId, onClickDeleteByPage);
+    return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetPathOrId, onClickDeleteByPage);
   }
 
   return null;

+ 5 - 0
packages/app/src/interfaces/page-listing-results.ts

@@ -23,6 +23,11 @@ export interface TargetAndAncestors {
 }
 
 
+export interface NotFoundTargetPathOrId {
+  notFoundTargetPathOrId: string
+}
+
+
 export interface V5MigrationStatus {
   isV5Compatible : boolean,
   migratablePagesCount: number

+ 1 - 1
packages/app/src/server/routes/apiv3/pages.js

@@ -716,7 +716,7 @@ module.exports = (crowi) => {
   router.get('/v5-migration-status', accessTokenParser, loginRequired, async(req, res) => {
     try {
       const isV5Compatible = crowi.configManager.getConfig('crowi', 'app:isV5Compatible');
-      const migratablePagesCount = await crowi.pageService.v5MigratablePrivatePagesCount(req.user);
+      const migratablePagesCount = req.user != null ? await crowi.pageService.v5MigratablePrivatePagesCount(req.user) : null;
       return res.apiv3({ isV5Compatible, migratablePagesCount });
     }
     catch (err) {

+ 9 - 0
packages/app/src/server/routes/page.js

@@ -274,6 +274,14 @@ module.exports = function(crowi, app) {
     renderVars.targetAndAncestors = { targetAndAncestors, rootPage };
   }
 
+  function addRenderVarsWhenNotFound(renderVars, pathOrId) {
+    if (pathOrId == null) {
+      return;
+    }
+
+    renderVars.notFoundTargetPathOrId = pathOrId;
+  }
+
   function replacePlaceholdersOfTemplate(template, req) {
     if (req.user == null) {
       return '';
@@ -328,6 +336,7 @@ module.exports = function(crowi, app) {
     const offset = parseInt(req.query.offset) || 0;
     await addRenderVarsForDescendants(renderVars, path, req.user, offset, limit, true);
     await addRenderVarsForPageTree(renderVars, pathOrId, req.user);
+    addRenderVarsWhenNotFound(renderVars, pathOrId);
 
     return res.render(view, renderVars);
   }

+ 5 - 0
packages/app/src/server/views/layout-growi/not_found.html

@@ -3,6 +3,11 @@
 {% block html_base_css %}not-found-page{% endblock %}
 
 {% block content_main_before %}
+  <div
+    id="growi-pagetree-not-found-context"
+    data-not-found-target-path-or-id="{% if notFoundTargetPathOrId %}{{notFoundTargetPathOrId|json}}{% endif %}"
+  >
+  </div>
   <div class="grw-container-convertible">
     {% include '../widget/page_alerts.html' %}
   </div>

+ 5 - 1
packages/app/src/stores/context.tsx

@@ -7,7 +7,7 @@ import { IUser } from '../interfaces/user';
 
 import { useStaticSWR } from './use-static-swr';
 
-import { TargetAndAncestors } from '../interfaces/page-listing-results';
+import { TargetAndAncestors, NotFoundTargetPathOrId } from '../interfaces/page-listing-results';
 
 type Nullable<T> = T | null;
 
@@ -165,3 +165,7 @@ export const useIsSharedUser = (): SWRResponse<boolean, Error> => {
 export const useTargetAndAncestors = (initialData?: TargetAndAncestors): SWRResponse<TargetAndAncestors, Error> => {
   return useStaticSWR<TargetAndAncestors, Error>('targetAndAncestors', initialData || null);
 };
+
+export const useNotFoundTargetPathOrId = (initialData?: NotFoundTargetPathOrId): SWRResponse<NotFoundTargetPathOrId, Error> => {
+  return useStaticSWR<NotFoundTargetPathOrId, Error>('notFoundTargetPathOrId', initialData || null);
+};

+ 24 - 0
packages/app/src/stores/ui.tsx

@@ -154,6 +154,30 @@ export const useIsDeviceSmallerThanMd = (): SWRResponse<boolean|null, Error> =>
   return useStaticSWR(key);
 };
 
+export const useIsDeviceSmallerThanLg = (): SWRResponse<boolean|null, Error> => {
+  const key: Key = isServer ? null : 'isDeviceSmallerThanLg';
+
+  const { cache, mutate } = useSWRConfig();
+
+  if (!isServer) {
+    const lgOrAvobeHandler = function(this: MediaQueryList): void {
+      // md -> lg: matches will be true
+      // lg -> md: matches will be false
+      mutate(key, !this.matches);
+    };
+    const mql = addBreakpointListener(Breakpoint.LG, lgOrAvobeHandler);
+
+    // initialize
+    if (cache.get(key) == null) {
+      document.addEventListener('DOMContentLoaded', () => {
+        mutate(key, !mql.matches);
+      });
+    }
+  }
+
+  return useStaticSWR(key);
+};
+
 export const usePreferDrawerModeByUser = (initialData?: boolean): SWRResponse<boolean, Error> => {
   return useStaticSWR('preferDrawerModeByUser', initialData ?? null, { fallbackData: false });
 };

+ 0 - 2
packages/app/src/styles/_page-tree.scss

@@ -7,8 +7,6 @@ $grw-pagetree-item-padding-left: 10px;
 
   .grw-pagetree-item {
     &:hover {
-      opacity: 0.7;
-
       .grw-pagetree-control {
         display: flex !important;
       }

+ 17 - 8
packages/app/src/styles/_search.scss

@@ -250,12 +250,17 @@
     }
   }
   .search-result-content {
-    padding-bottom: 36px;
+    .search-result-content-nav {
+      min-height: $grw-subnav-search-preview-min-height;
+      overflow: auto;
+
+      .grw-subnav {
+        min-height: inherit;
+      }
+    }
 
     .search-result-page {
-      padding-top: 64px;
-      // adjust for anchor links by the height of fixed .search-page-input
-      margin-top: -64px;
+      height: calc(100vh - ($grw-navbar-height + $grw-navbar-border-width));
 
       > h2 {
         margin-right: 10px;
@@ -267,10 +272,14 @@
         margin-top: 0;
       }
 
-      .wiki {
-        padding: 16px;
-        font-size: 13px;
-        border: solid 1px $gray-300;
+      .search-result-page-content {
+        overflow-y: auto;
+
+        .wiki {
+          padding: 16px;
+          font-size: 13px;
+          border: solid 1px $gray-300;
+        }
       }
     }
   }

+ 1 - 0
packages/app/src/styles/_sidebar.scss

@@ -185,6 +185,7 @@
       width: $grw-sidebar-nav-width;
       line-height: 1em;
       border-radius: 0;
+      box-shadow: none !important;
 
       // icon opacity
       &:not(.active) {

+ 2 - 0
packages/app/src/styles/_variables.scss

@@ -21,6 +21,8 @@ $grw-subnav-min-height-md: 115px;
 $grw-subnav-height-on-edit: 95px;
 $grw-subnav-height-lg-on-edit: 50px;
 
+$grw-subnav-search-preview-min-height: 90px;
+
 $grw-navbar-bottom-height: 48px;
 $grw-editor-navbar-bottom-height: 48px;
 

+ 8 - 3
packages/app/src/styles/theme/_apply-colors.scss

@@ -706,9 +706,14 @@ mark.rbt-highlight-text {
 }
 
 // Page Management Dropdown icon
-.grw-btn-page-management:hover,
-.grw-btn-page-management:focus {
-  background-color: rgba($color-link, 0.15);
+.grw-btn-page-management {
+  &:hover,
+  &:focus {
+    background-color: rgba($color-link, 0.15);
+  }
+  &:active {
+    background-color: rgba($color-link, 0.2);
+  }
 }
 
 /*

+ 3 - 3
packages/slackbot-proxy/docker/Dockerfile

@@ -3,7 +3,7 @@
 ##
 ## deps-resolver-base
 ##
-FROM node:16-slim AS deps-resolver-base
+FROM node:14-slim AS deps-resolver-base
 
 ENV appDir /opt
 
@@ -46,7 +46,7 @@ RUN tar cf dependencies.tar \
 ##
 ## builder
 ##
-FROM node:16-slim AS builder
+FROM node:14-slim AS builder
 
 ENV appDir /opt
 
@@ -83,7 +83,7 @@ RUN tar cf packages.tar \
 ##
 ## release
 ##
-FROM node:16-slim
+FROM node:14-slim
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 
 ENV NODE_ENV production