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

Merge pull request #7882 from weseek/fix/access-to-page-with-anchor

fix: Auto-scroll does not work when accessing the page when the header string is CJK
Yuki Takei 2 лет назад
Родитель
Сommit
b027585a44

+ 1 - 1
apps/app/src/components/Layout/BasicLayout.tsx

@@ -41,7 +41,7 @@ export const BasicLayout = ({ children, className }: Props): JSX.Element => {
         <GrowiNavbar />
 
         <div className="page-wrapper d-flex d-print-block">
-          <div className="grw-sidebar-wrapper" data-testid="grw-sidebar-wrapper">
+          <div className="grw-sidebar-wrapper">
             <Sidebar />
           </div>
 

+ 1 - 1
apps/app/src/components/Page/PageView.tsx

@@ -83,7 +83,7 @@ export const PageView = (props: Props): JSX.Element => {
 
     const targetId = hash.slice(1);
 
-    const target = document.getElementById(targetId);
+    const target = document.getElementById(decodeURIComponent(targetId));
     target?.scrollIntoView();
 
   }, [isCommentsLoaded]);

+ 1 - 1
apps/app/src/components/ReactMarkdownComponents/Header.tsx

@@ -75,7 +75,7 @@ export const Header = (props: HeaderProps): JSX.Element => {
   const activateByHash = useCallback((url: string) => {
     try {
       const hash = (new URL(url, 'https://example.com')).hash.slice(1);
-      setActive(hash === id);
+      setActive(decodeURIComponent(hash) === id);
     }
     catch (err) {
       logger.debug(err);

+ 1 - 1
apps/app/src/components/Sidebar.tsx

@@ -297,7 +297,7 @@ const Sidebar = memo((): JSX.Element => {
   const isOpenClass = `${isDrawerOpened ? 'open' : ''}`;
   return (
     <>
-      <div className={`${grwSidebarClass} ${sidebarModeClass} ${isOpenClass} d-print-none`}>
+      <div className={`${grwSidebarClass} ${sidebarModeClass} ${isOpenClass} d-print-none`} data-testid="grw-sidebar">
         <div className="data-layout-container">
           <div
             className='navigation transition-enabled'

+ 13 - 10
apps/app/test/cypress/e2e/20-basic-features/20-basic-features--access-to-page.cy.ts

@@ -23,20 +23,20 @@ context('Access to page', () => {
 
   it('/Sandbox is successfully loaded', () => {
     cy.visit('/Sandbox');
-    cy.waitUntilSkeletonDisappear();
+    cy.collapseSidebar(true, true);
 
     // for check download toc data
     // https://redmine.weseek.co.jp/issues/111384
     // cy.get('.toc-link').should('be.visible');
 
-    cy.collapseSidebar(true, true);
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-sandbox`);
   });
 
   // TODO: https://redmine.weseek.co.jp/issues/109939
   it('/Sandbox with anchor hash is successfully loaded', () => {
     cy.visit('/Sandbox#headers');
-    cy.waitUntilSkeletonDisappear();
+    cy.collapseSidebar(true);
 
     // for check download toc data
     // https://redmine.weseek.co.jp/issues/111384
@@ -45,18 +45,21 @@ context('Access to page', () => {
     // hide fab
     cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
 
+    // assert the element is in viewport
+    cy.get('#headers').should('be.inViewport');
+
     // remove animation for screenshot
     // remove 'blink' class because ::after element cannot be operated
     // https://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin/21709814#21709814
     cy.get('#headers').invoke('removeClass', 'blink');
 
-    cy.collapseSidebar(true);
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-sandbox-headers`);
   });
 
   it('/Sandbox/Math is successfully loaded', () => {
     cy.visit('/Sandbox/Math');
-    cy.waitUntilSkeletonDisappear();
+    cy.collapseSidebar(true);
 
     // for check download toc data
     // https://redmine.weseek.co.jp/issues/111384
@@ -64,20 +67,20 @@ context('Access to page', () => {
 
     cy.get('.math').should('be.visible');
 
-    cy.collapseSidebar(true);
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-sandbox-math`);
   });
 
   it('/Sandbox with edit is successfully loaded', () => {
     cy.visit('/Sandbox#edit');
-    cy.waitUntilSkeletonDisappear();
+    cy.collapseSidebar(true);
 
     cy.getByTestid('navbar-editor').should('be.visible');
     cy.get('.grw-editor-navbar-bottom').should('be.visible');
     cy.getByTestid('save-page-btn').should('be.visible');
     cy.get('.grw-grant-selector').should('be.visible');
 
-    cy.collapseSidebar(true);
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-Sandbox-edit-page`);
   })
 
@@ -116,15 +119,15 @@ context('Access to page', () => {
 
   it('/user/admin is successfully loaded', () => {
     cy.visit('/user/admin');
+    cy.collapseSidebar(true);
 
-    cy.waitUntilSkeletonDisappear();
     // for check download toc data
     // https://redmine.weseek.co.jp/issues/111384
     // cy.get('.toc-link').should('be.visible');
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting
     cy.wait(2000); // wait for calcViewHeight and rendering
-    cy.collapseSidebar(true);
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-user-admin`);
   });
 

+ 2 - 4
apps/app/test/cypress/e2e/20-basic-features/20-basic-features--use-tools.cy.ts

@@ -120,6 +120,7 @@ context('Page Accessories Modal', () => {
     });
 
     cy.visit('/');
+    cy.collapseSidebar(true, true);
 
     cy.waitUntil(() => {
       // do
@@ -141,7 +142,6 @@ context('Page Accessories Modal', () => {
 
     cy.getByTestid('page-history').should('be.visible');
 
-    cy.collapseSidebar(true, true);
     cy.waitUntilSpinnerDisappear();
     cy.screenshot(`${ssPrefix}-open-page-history-bootstrap4`);
   });
@@ -154,7 +154,6 @@ context('Page Accessories Modal', () => {
     cy.waitUntilSpinnerDisappear();
     cy.getByTestid('page-attachment').should('be.visible').contains('No attachments yet.');
 
-    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}-open-page-attachment-data-bootstrap4`);
   });
 
@@ -167,7 +166,6 @@ context('Page Accessories Modal', () => {
     cy.getByTestid('page-accessories-modal').should('be.visible');
     cy.getByTestid('share-link-management').should('be.visible');
 
-    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}-open-share-link-management-bootstrap4`);
   });
 });
@@ -186,6 +184,7 @@ context('Tag Oprations', { scrollBehavior: false }, () =>{
     const tag = 'we';
 
     cy.visit('/Sandbox/Bootstrap4');
+    cy.collapseSidebar(true);
 
     // Add tag
     cy.get('#edit-tags-btn-wrapper-for-tooltip').as('edit-tag-tooltip').should('be.visible');
@@ -198,7 +197,6 @@ context('Tag Oprations', { scrollBehavior: false }, () =>{
       return cy.get('#edit-tag-modal').then($elem => $elem.is(':visible'));
     });
 
-    cy.collapseSidebar(true);
     cy.get('#edit-tag-modal').should('be.visible').screenshot(`${ssPrefix}1-edit-tag-input`);
 
     cy.get('#edit-tag-modal').should('be.visible').within(() => {

+ 10 - 6
apps/app/test/cypress/e2e/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.cy.ts

@@ -11,23 +11,27 @@ context('Access to page by guest', () => {
 
   // TODO: https://redmine.weseek.co.jp/issues/109939
   it('/Sandbox with anchor hash is successfully loaded', () => {
-    cy.visit('/Sandbox#Headers');
-    cy.waitUntilSkeletonDisappear();
+    cy.visit('/Sandbox#headers');
+    cy.collapseSidebar(true);
 
     // hide fab
     cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
 
+    // assert the element is in viewport
+    cy.get('#headers').should('be.inViewport');
+
     // remove animation for screenshot
     // remove 'blink' class because ::after element cannot be operated
     // https://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin/21709814#21709814
     cy.get('#headers').invoke('removeClass', 'blink');
 
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-sandbox-headers`);
   });
 
   it('/Sandbox/Math is successfully loaded', () => {
     cy.visit('/Sandbox/Math');
-    cy.waitUntilSkeletonDisappear();
+    cy.collapseSidebar(true);
 
     // for check download toc data
     // https://redmine.weseek.co.jp/issues/111384
@@ -35,15 +39,15 @@ context('Access to page by guest', () => {
 
     cy.get('.math').should('be.visible');
 
-    cy.collapseSidebar(true);
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-sandbox-math`);
   });
 
   it('/Sandbox with edit is successfully loaded', () => {
     cy.visit('/Sandbox#edit');
-    cy.waitUntilSkeletonDisappear();
-
     cy.collapseSidebar(true);
+
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-sandbox-with-edit-hash`);
   })
 

+ 4 - 6
apps/app/test/cypress/e2e/22-sharelink/22-sharelink--access-to-sharelink.cy.ts

@@ -10,18 +10,16 @@ context('Access to sharelink by guest', () => {
     });
 
     cy.visit('/Sandbox/Bootstrap4');
+    cy.waitUntilSkeletonDisappear();
 
     // open dropdown
     cy.waitUntil(() => {
       // do
-      cy.getByTestid('grw-contextual-sub-nav').should('be.visible').within(() => {
-        cy.waitUntilSkeletonDisappear();
-        cy.getByTestid('open-page-item-control-btn').find('button').first().as('btn').click();
+      cy.get('#grw-subnav-container').within(() => {
+        cy.getByTestid('open-page-item-control-btn').find('button').click({force: true});
       });
       // wait until
-      return cy.get('body').within(() => {
-        return Cypress.$('.dropdown-menu.show').is(':visible');
-      });
+      return cy.getByTestid('page-item-control-menu').then($elem => $elem.is(':visible'))
     });
 
     // open modal

+ 6 - 5
apps/app/test/cypress/e2e/23-editor/23-editor--saving.cy.ts

@@ -11,6 +11,7 @@ context('PageCreateModal', () => {
 
   it("PageCreateModal is shown and closed successfully", () => {
     cy.visit('/');
+    cy.collapseSidebar(true, true);
 
     cy.waitUntil(() => {
       // do
@@ -24,13 +25,13 @@ context('PageCreateModal', () => {
       cy.get('button.close').click();
     });
 
-    cy.collapseSidebar(true, true);
     cy.screenshot(`${ssPrefix}page-create-modal-closed`);
   });
 
   it("Successfully Create Today's page", () => {
     const pageName = "Today's page";
     cy.visit('/');
+    cy.collapseSidebar(true);
 
     cy.waitUntil(() => {
       // do
@@ -55,7 +56,6 @@ context('PageCreateModal', () => {
     });
     cy.get('.layout-root').should('not.have.class', 'editing');
 
-    cy.collapseSidebar(true);
     cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}create-today-page`);
   });
@@ -64,6 +64,7 @@ context('PageCreateModal', () => {
     const pageName = 'child';
 
     cy.visit('/foo/bar');
+    cy.collapseSidebar(true);
 
     cy.waitUntil(() => {
       // do
@@ -94,12 +95,12 @@ context('PageCreateModal', () => {
     cy.getByTestid('grw-contextual-sub-nav').should('be.visible');
 
     cy.waitUntilSkeletonDisappear();
-    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}create-page-under-specific-page`);
   });
 
   it('Trying to create template page under the root page fail', () => {
     cy.visit('/');
+    cy.collapseSidebar(true);
 
     cy.waitUntil(() => {
       // do
@@ -116,8 +117,9 @@ context('PageCreateModal', () => {
       cy.getByTestid('grw-btn-edit-page').should('be.visible').click();
     });
     cy.get('.Toastify__toast').should('be.visible');
-    cy.collapseSidebar(true);
+
     cy.screenshot(`${ssPrefix}create-template-for-children-error`);
+
     cy.get('.Toastify__toast').should('be.visible').within(() => {
       cy.get('.Toastify__close-button').should('be.visible').click();
       cy.get('.Toastify__progress-bar').invoke('attr', 'style', 'display: none')
@@ -129,7 +131,6 @@ context('PageCreateModal', () => {
       cy.getByTestid('grw-btn-edit-page').should('be.visible').click();
     });
     cy.get('.Toastify__toast').should('be.visible');
-    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}create-template-for-descendants-error`);
   });
 

+ 21 - 0
apps/app/test/cypress/support/assertions.ts

@@ -0,0 +1,21 @@
+// from https://github.com/cypress-io/cypress/issues/877#issuecomment-538708750
+const isInViewport = (_chai) => {
+  function assertIsInViewport() {
+
+    const subject = this._obj;
+
+    const bottom = Cypress.config("viewportWidth");
+    const rect = subject[0].getBoundingClientRect();
+
+    this.assert(
+      rect.top < bottom && rect.bottom < bottom,
+      "expected #{this} to be in viewport",
+      "expected #{this} to not be in viewport",
+      this._obj
+    )
+  }
+
+  _chai.Assertion.addMethod('inViewport', assertIsInViewport)
+};
+
+chai.use(isInViewport);

+ 19 - 19
apps/app/test/cypress/support/commands.ts

@@ -70,31 +70,31 @@ Cypress.Commands.add('waitUntilSpinnerDisappear', () => {
 });
 
 Cypress.Commands.add('collapseSidebar', (isCollapsed: boolean, waitUntilSaving = false) => {
-  cy.getByTestid('grw-sidebar-wrapper', { timeout: 5000 }).within(() => {
+  cy.getByTestid('grw-sidebar').should('be.visible');
+
+  cy.getByTestid('grw-sidebar').within(($elem) => {
+
     // skip if .grw-sidebar-dock does not exist
-    if (isHidden(Cypress.$('.grw-sidebar-dock'))) {
+    if ($elem.hasClass('grw-sidebar-dock')) {
       return;
     }
 
-    // process only when Dock Mode
-    cy.get('.grw-sidebar-dock').within(() => {
-      const isSidebarContextualNavigationHidden = isHiddenByTestId('grw-contextual-navigation-sub');
-      if (isSidebarContextualNavigationHidden === isCollapsed) {
-        return;
-      }
+    const isSidebarContextualNavigationHidden = isHiddenByTestId('grw-contextual-navigation-sub');
+    if (isSidebarContextualNavigationHidden === isCollapsed) {
+      return;
+    }
 
-      cy.waitUntil(() => {
-        // do
-        cy.getByTestid("grw-navigation-resize-button").click({force: true});
-        // wait until saving UserUISettings
-        if (waitUntilSaving) {
-          // eslint-disable-next-line cypress/no-unnecessary-waiting
-          cy.wait(1500);
-        }
+    cy.waitUntil(() => {
+      // do
+      cy.getByTestid("grw-navigation-resize-button").click({force: true});
+      // wait until saving UserUISettings
+      if (waitUntilSaving) {
+        // eslint-disable-next-line cypress/no-unnecessary-waiting
+        cy.wait(1500);
+      }
 
-        // wait until
-        return cy.getByTestid('grw-contextual-navigation-sub').then($contents => isHidden($contents) === isCollapsed);
-      });
+      // wait until
+      return cy.getByTestid('grw-contextual-navigation-sub').then($contents => isHidden($contents) === isCollapsed);
     });
   });
 });

+ 1 - 0
apps/app/test/cypress/support/index.ts

@@ -14,6 +14,7 @@
 // ***********************************************************
 
 // Import commands.js using ES2015 syntax:
+import './assertions'
 import './commands'
 import './screenshot'