浏览代码

feat(tooltip): leftTopOver supports autoAdjustOverflow #294 (#551)

走鹃 3 年之前
父节点
当前提交
19d7accbee

+ 4 - 4
.github/workflows/cypress.yml

@@ -66,8 +66,8 @@ jobs:
         with:
           # we have already installed all dependencies above
           install: true
-          start: npx http-server -p 6009 storybook-static
-          wait-on: 'http://localhost:6009'
+          start: npx http-server -p 6006 storybook-static
+          wait-on: 'http://localhost:6006'
           wait-on-timeout: 120
           browser: chrome
           record: true
@@ -103,8 +103,8 @@ jobs:
         with:
           # we have already installed all dependencies above
           install: true
-          start: npx http-server -p 6009 storybook-static
-          wait-on: 'http://localhost:6009'
+          start: npx http-server -p 6006 storybook-static
+          wait-on: 'http://localhost:6006'
           wait-on-timeout: 120
           browser: firefox
           record: true

+ 2 - 1
cypress.json

@@ -1,3 +1,4 @@
 {
-  "projectId": "k83u7j"
+  "projectId": "k83u7j",
+  "scrollBehavior": false
 }

+ 6 - 6
cypress/integration/datePicker.spec.js

@@ -11,7 +11,7 @@
  */
 describe('DatePicker', () => {
     it('dateTime needConfirm cancel', () => {
-        cy.visit('http://127.0.0.1:6009/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
         cy.get('[data-cy=1] .semi-input-wrapper').click();
         cy.get('.semi-datepicker-footer > .semi-button-borderless')
             .then(($btn) => {
@@ -22,7 +22,7 @@ describe('DatePicker', () => {
     });
 
     it('dateTime needConfirm confirm', () => {
-        cy.visit('http://127.0.0.1:6009/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
         cy.get('[data-cy=1] .semi-input-wrapper').click();
         cy.get('.semi-datepicker-day').contains('15')
             .then($day => {
@@ -36,7 +36,7 @@ describe('DatePicker', () => {
     });
 
     it('dateTime needConfirm select+cancel', () => {
-        cy.visit('http://127.0.0.1:6009/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
         cy.get('[data-cy=1] .semi-input-wrapper').click();
         cy.get('.semi-datepicker-day').contains('15')
             .then($day => {
@@ -51,7 +51,7 @@ describe('DatePicker', () => {
     });
 
     it('dateTimeRange needConfirm cancel', () => {
-        cy.visit('http://127.0.0.1:6009/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
         cy.get('[data-cy=3] .semi-datepicker-range-input-wrapper-start .semi-input-wrapper').click();
         cy.get('.semi-datepicker-footer > .semi-button-borderless')
             .then($btn => {
@@ -61,7 +61,7 @@ describe('DatePicker', () => {
     });
 
     it('dateTimeRange needConfirm select+cancel', () => {
-        cy.visit('http://127.0.0.1:6009/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
         cy.get('[data-cy=3] .semi-datepicker-range-input-wrapper-start .semi-input-wrapper').click();
         cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day').contains('15')
             .then($day => {
@@ -80,7 +80,7 @@ describe('DatePicker', () => {
     });
 
     it('dateTimeRange needConfirm confirm', () => {
-        cy.visit('http://127.0.0.1:6009/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=datepicker--fix-need-confirm&args=&viewMode=story');
         cy.get('[data-cy=3] .semi-datepicker-range-input-wrapper-start .semi-input-wrapper').click();
         cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day').contains('15')
             .then($day => {

+ 2 - 2
cypress/integration/table.spec.js

@@ -7,7 +7,7 @@
 
 describe('table', () => {
     it('row selection', () => {
-        cy.visit('http://127.0.0.1:6009/iframe.html?id=table--selection-table&args=&viewMode=story');
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=table--selection-table&args=&viewMode=story');
         cy.get('.semi-table-row-head .semi-checkbox-inner-display').click();
         cy.get('.semi-checkbox-checked').should('have.length', 4);
     });
@@ -17,7 +17,7 @@ describe('table', () => {
      * 即更新 columns 不影响 currentPage
      */
     it('columns change ', () => {
-        cy.visit('http://localhost:6009/iframe.html?id=table--fixed-columns-change&viewMode=story');
+        cy.visit('http://localhost:6006/iframe.html?id=table--fixed-columns-change&viewMode=story');
         cy.get('.semi-page-item').contains('2').click();
         cy.get('.semi-table-tbody .semi-checkbox').eq(1).click()
             .then(() => {

+ 29 - 0
cypress/integration/tooltip.spec.js

@@ -0,0 +1,29 @@
+// tooltip.spec.js created with Cypress
+//
+// Start writing your Cypress tests below!
+// If you're unfamiliar with how Cypress works,
+// check out the link below and learn how to write your first test:
+// https://on.cypress.io/writing-first-test
+
+/**
+ * Cypress will default scroll element into view
+ * @see https://docs.cypress.io/guides/core-concepts/interacting-with-elements#Scrolling
+ */
+describe('tooltip', () => {
+    it('leftTopOver autoAdjustOverflow', () => {
+        const viewportWidth = 1200;
+        const viewportHeight = 660;
+        const triggerWidth = 200;
+        const triggerHeight = 32;
+        const leftTopPosition = { offset: { top: 0, left: 0 }}; 
+        const rightBottomPosition = { offset: { top: -viewportHeight + triggerHeight, left: -viewportWidth + triggerWidth }};
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=tooltip--left-top-over-auto-adjust-overflow&args=&viewMode=story');
+        cy.viewport(viewportWidth, viewportHeight);
+        const dataSelector = `[data-cy=leftTopOver]`;
+        cy.get(dataSelector).scrollIntoView(leftTopPosition);
+        cy.get(dataSelector).click({force: true});
+        cy.get('[x-placement="leftTopOver"]').should('have.length', 1);
+        cy.get(dataSelector).scrollIntoView(rightBottomPosition);
+        cy.get('[x-placement="rightBottomOver"]').should('have.length', 1);
+    });
+});

+ 1 - 1
package.json

@@ -29,7 +29,7 @@
     "test:story": "cross-env NODE_ENV=test type=story ./node_modules/.bin/jest --silent --notify --maxWorkers=4",
     "test:coverage": "cross-env TZ=Asia/Shanghai NODE_ENV=test type=unit ./node_modules/.bin/jest --silent --coverage --notify",
     "test:storyUpdate": "cross-env NODE_ENV=test type=story ./node_modules/.bin/jest --silent -u --notify --maxWorkers=4",
-    "test:cy": "npx http-server storybook-static -p 6009 && npx wait-on http://127.0.0.1:6009 && cypress open",
+    "test:cy": "npx wait-on http://127.0.0.1:6006 && ./node_modules/.bin/cypress open",
     "build:lib": "lerna run build:lib",
     "build:js": "lerna run build:js",
     "build:css": "lerna run build:css",

+ 2 - 0
packages/semi-foundation/tooltip/constants.ts

@@ -20,6 +20,8 @@ const strings = {
         'bottomRight',
         'leftTopOver',
         'rightTopOver',
+        'leftBottomOver',
+        'rightBottomOver',
     ],
     TRIGGER_SET: ['hover', 'focus', 'click', 'custom'],
     STATUS_DISABLED: 'disabled',

+ 52 - 4
packages/semi-foundation/tooltip/foundation.ts

@@ -434,6 +434,17 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                 top = triggerRect.top;
                 translateX = -1;
                 break;
+            case 'leftBottomOver':
+                left = triggerRect.left;
+                top = triggerRect.bottom;
+                translateY = -1;
+                break;
+            case 'rightBottomOver':
+                left = triggerRect.right;
+                top = triggerRect.bottom;
+                translateX = -1;
+                translateY = -1;
+                break;
             default:
                 break;
         }
@@ -602,14 +613,19 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
 
             const shouldReverseTop = clientTop < wrapperRect.height + spacing && restClientBottom > wrapperRect.height + spacing;
             const shouldReverseLeft = clientLeft < wrapperRect.width + spacing && restClientRight > wrapperRect.width + spacing;
-            const sholdReverseBottom = restClientBottom < wrapperRect.height + spacing && clientTop > wrapperRect.height + spacing;
+            const shouldReverseBottom = restClientBottom < wrapperRect.height + spacing && clientTop > wrapperRect.height + spacing;
             const shouldReverseRight = restClientRight < wrapperRect.width + spacing && clientLeft > wrapperRect.width + spacing;
+            const shouldReverseTopOver = restClientTop < wrapperRect.height + spacing && clientBottom > wrapperRect.height + spacing;
+            const shouldReverseBottomOver = clientBottom < wrapperRect.height + spacing && restClientTop > wrapperRect.height + spacing;
 
             const shouldReverseTopSide = restClientTop < wrapperRect.height && clientBottom > wrapperRect.height;
             const shouldReverseBottomSide = clientBottom < wrapperRect.height && restClientTop > wrapperRect.height;
             const shouldReverseLeftSide = restClientLeft < wrapperRect.width && clientRight > wrapperRect.width;
             const shouldReverseRightSide = clientRight < wrapperRect.width && restClientLeft > wrapperRect.width;
 
+            const shouldReverseLeftOver = restClientLeft < wrapperRect.width && clientRight > wrapperRect.width;
+            const shouldReverseRightOver = clientRight < wrapperRect.width && restClientLeft > wrapperRect.width;
+
             switch (position) {
                 case 'top':
                     if (shouldReverseTop) {
@@ -654,12 +670,12 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                     }
                     break;
                 case 'bottom':
-                    if (sholdReverseBottom) {
+                    if (shouldReverseBottom) {
                         position = this._reversePos(position, true);
                     }
                     break;
                 case 'bottomLeft':
-                    if (sholdReverseBottom) {
+                    if (shouldReverseBottom) {
                         position = this._reversePos(position, true);
                     }
                     if (shouldReverseLeftSide && widthIsBigger) {
@@ -667,7 +683,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                     }
                     break;
                 case 'bottomRight':
-                    if (sholdReverseBottom) {
+                    if (shouldReverseBottom) {
                         position = this._reversePos(position, true);
                     }
                     if (shouldReverseRightSide && widthIsBigger) {
@@ -695,6 +711,38 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                         position = this._reversePos(position, true);
                     }
                     break;
+                case 'leftTopOver':
+                    if (shouldReverseTopOver) {
+                        position = this._reversePos(position, true);
+                    }
+                    if (shouldReverseLeftOver) {
+                        position = this._reversePos(position);
+                    }
+                    break;
+                case 'leftBottomOver':
+                    if (shouldReverseBottomOver) {
+                        position = this._reversePos(position, true);
+                    }
+                    if (shouldReverseLeftOver) {
+                        position = this._reversePos(position);
+                    }
+                    break;
+                case 'rightTopOver':
+                    if (shouldReverseTopOver) {
+                        position = this._reversePos(position, true);
+                    }
+                    if (shouldReverseRightOver) {
+                        position = this._reversePos(position);
+                    }
+                    break;
+                case 'rightBottomOver':
+                    if (shouldReverseBottomOver) {
+                        position = this._reversePos(position, true);
+                    }
+                    if (shouldReverseRightOver) {
+                        position = this._reversePos(position);
+                    }
+                    break;
                 default:
                     break;
             }

+ 83 - 1
packages/semi-ui/tooltip/_story/tooltip.stories.js

@@ -1,7 +1,7 @@
 import React, { useState, useMemo } from 'react';
 import Tooltip from '../index';
 import './story.scss';
-import { Tag, Icon, IconButton, Switch, Checkbox, Radio, Button, Select, InputNumber } from '@douyinfe/semi-ui';
+import { Tag, Icon, IconButton, Switch, Checkbox, Radio, Button, Select, InputNumber, Space } from '@douyinfe/semi-ui';
 
 import InTableDemo from './InTable';
 import EdgeDemo from './Edge';
@@ -736,3 +736,85 @@ export const AutoAdjustWithSpacing = () => {
 AutoAdjustWithSpacing.story = {
   name: 'AutoAdjustWithSpacing',
 };
+
+/**
+ * Chromatic UI test
+ */
+export const leftTopOverDemo = () => {
+    const [visible, setVisible] = useState(true);
+    const content = (
+        <div style={{ height: 200, width: 200 }}>
+            Semi Design
+        </div>
+    );
+
+    const commonProps = {
+        content,
+        trigger: 'click',
+        showArrow: false,
+        visible,
+        trigger: 'custom',
+        motion: false,
+    };
+    const buttonStyle = {
+        width: 200,
+    };
+
+    return (
+        <div data-cy='wrapper'>
+            <Button onClick={() => setVisible(!visible)}>toggle visible</Button>
+            <div style={{ paddingTop: 200 }}>
+                <Space spacing={80}>
+                    <Tooltip {...commonProps} position='leftTopOver' >
+                        <Button data-cy='leftTopOver' style={buttonStyle}>leftTopOver</Button>
+                    </Tooltip>
+                    <Tooltip {...commonProps} position='leftBottomOver'>
+                        <Button data-cy='leftBottomOver' style={buttonStyle}>leftBottomOver</Button>
+                    </Tooltip>
+                    <Tooltip {...commonProps} position='rightTopOver'>
+                        <Button data-cy='rightTopOver' style={buttonStyle}>rightTopOver</Button>
+                    </Tooltip>
+                    <Tooltip {...commonProps} position='rightBottomOver'>
+                        <Button data-cy='rightBottomOver' style={buttonStyle}>rightBottomOver</Button>
+                    </Tooltip>
+                </Space>
+            </div>
+        </div>
+    )
+};
+leftTopOverDemo.storyName = `leftTopOver visible`;
+leftTopOverDemo.parameters = { 
+    chromatic: {
+        disableSnapshot: false,
+        delay: 3000,
+        viewports: [1200]
+    }
+};
+
+/**
+ * Cypress test
+ */
+export const leftTopOverAutoAdjustOverflow = () => {
+    const content = (
+        <div style={{ height: 200, width: 200 }}>
+            Semi Design
+        </div>
+    );
+
+    const commonProps = {
+        content,
+        trigger: 'click',
+        showArrow: false,
+    };
+
+    return (
+        <div data-cy='wrapper' style={{ width: '200vw', height: '200vw', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
+            <div>
+                <Tooltip {...commonProps} position='leftTopOver' >
+                    <Button data-cy='leftTopOver' style={{ width: 200 }}>leftTopOver</Button>
+                </Tooltip>
+            </div>
+        </div>
+    )
+};
+leftTopOverAutoAdjustOverflow.storyName = `leftTopOver autoAdjustOverflow`;