mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-24 18:36:51 +08:00
feat(Sticky): support sticky to bottom (#7979)
This commit is contained in:
parent
0f3610bafe
commit
6c5315f0b1
@ -38,6 +38,14 @@ app.use(Sticky);
|
|||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Offset Bottom
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-sticky :offset-bottom="50" position="bottom">
|
||||||
|
<van-button type="primary">Offset Bottom</van-button>
|
||||||
|
</van-sticky>
|
||||||
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
@ -54,6 +62,8 @@ export default {
|
|||||||
| Attribute | Description | Type | Default |
|
| Attribute | Description | Type | Default |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| offset-top | Offset top, supports `px` `vw` `vh` `rem` unit, default `px` | _number \| string_ | `0` |
|
| offset-top | Offset top, supports `px` `vw` `vh` `rem` unit, default `px` | _number \| string_ | `0` |
|
||||||
|
| offset-bottom | Offset bottom, supports `px` `vw` `vh` `rem` unit, default `px` | _number \| string_ | `0` |
|
||||||
|
| position | Offset position, supports `top` `bottom` | _string_ | `top` |
|
||||||
| z-index | z-index when sticky | _number \| string_ | `99` |
|
| z-index | z-index when sticky | _number \| string_ | `99` |
|
||||||
| container | Container DOM | _Element_ | - |
|
| container | Container DOM | _Element_ | - |
|
||||||
|
|
||||||
|
@ -48,6 +48,16 @@ app.use(Sticky);
|
|||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 吸底距离
|
||||||
|
|
||||||
|
通过 `offset-bottom` 属性可以设置组件在吸底时与底部的距离。通过 `position` 属性控制吸附位置,默认值为 `top`。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-sticky :offset-bottom="50" position="bottom">
|
||||||
|
<van-button type="primary">吸底距离</van-button>
|
||||||
|
</van-sticky>
|
||||||
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
@ -64,6 +74,8 @@ export default {
|
|||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| offset-top | 吸顶时与顶部的距离,支持 `px` `vw` `vh` `rem` 单位,默认 `px` | _number \| string_ | `0` |
|
| offset-top | 吸顶时与顶部的距离,支持 `px` `vw` `vh` `rem` 单位,默认 `px` | _number \| string_ | `0` |
|
||||||
|
| offset-bottom | 吸底时与底部的距离,支持 `px` `vw` `vh` `rem` 单位,默认 `px` | _number \| string_ | `0` |
|
||||||
|
| position | 吸附位置,支持 `top` `bottom` | _string_ | `top` |
|
||||||
| z-index | 吸顶时的 z-index | _number \| string_ | `99` |
|
| z-index | 吸顶时的 z-index | _number \| string_ | `99` |
|
||||||
| container | 容器对应的 HTML 节点 | _Element_ | - |
|
| container | 容器对应的 HTML 节点 | _Element_ | - |
|
||||||
|
|
||||||
|
@ -24,6 +24,15 @@
|
|||||||
</van-sticky>
|
</van-sticky>
|
||||||
</div>
|
</div>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block :title="t('offsetBottom')">
|
||||||
|
<div style="height: 200px"></div>
|
||||||
|
<van-sticky :offset-bottom="50" position="bottom">
|
||||||
|
<van-button type="primary" style="margin-left: 15px">
|
||||||
|
{{ t('offsetBottom') }}
|
||||||
|
</van-button>
|
||||||
|
</van-sticky>
|
||||||
|
</demo-block>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@ -33,10 +42,12 @@ import { useTranslate } from '@demo/use-translate';
|
|||||||
const i18n = {
|
const i18n = {
|
||||||
'zh-CN': {
|
'zh-CN': {
|
||||||
offsetTop: '吸顶距离',
|
offsetTop: '吸顶距离',
|
||||||
|
offsetBottom: '吸底距离',
|
||||||
setContainer: '指定容器',
|
setContainer: '指定容器',
|
||||||
},
|
},
|
||||||
'en-US': {
|
'en-US': {
|
||||||
offsetTop: 'Offset Top',
|
offsetTop: 'Offset Top',
|
||||||
|
offsetBottom: 'Offset Bottom',
|
||||||
setContainer: 'Set Container',
|
setContainer: 'Set Container',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
.van-sticky {
|
.van-sticky {
|
||||||
&--fixed {
|
&--fixed {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: @sticky-z-index;
|
z-index: @sticky-z-index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
import { ref, reactive, computed, CSSProperties } from 'vue';
|
import { computed, CSSProperties, PropType, reactive, ref } from 'vue';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import {
|
import { createNamespace, getScrollTop, isHidden, unitToPx } from '../utils';
|
||||||
isHidden,
|
|
||||||
unitToPx,
|
|
||||||
getScrollTop,
|
|
||||||
getElementTop,
|
|
||||||
createNamespace,
|
|
||||||
} from '../utils';
|
|
||||||
|
|
||||||
// Composition
|
// Composition
|
||||||
import { useScrollParent, useEventListener } from '@vant/use';
|
import { useEventListener, useScrollParent } from '@vant/use';
|
||||||
import { useVisibilityChange } from '../composables/use-visibility-change';
|
import { useVisibilityChange } from '../composables/use-visibility-change';
|
||||||
|
|
||||||
const [createComponent, bem] = createNamespace('sticky');
|
const [createComponent, bem] = createNamespace('sticky');
|
||||||
|
|
||||||
|
export type Position = 'top' | 'bottom';
|
||||||
|
|
||||||
export default createComponent({
|
export default createComponent({
|
||||||
props: {
|
props: {
|
||||||
zIndex: [Number, String],
|
zIndex: [Number, String],
|
||||||
@ -23,6 +19,14 @@ export default createComponent({
|
|||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
|
offsetBottom: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
type: String as PropType<Position>,
|
||||||
|
default: 'top',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['scroll'],
|
emits: ['scroll'],
|
||||||
@ -33,24 +37,31 @@ export default createComponent({
|
|||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
fixed: false,
|
fixed: false,
|
||||||
height: 0,
|
height: 0, // root 高度
|
||||||
|
width: 0, // root 宽度
|
||||||
|
clientHeight: 0, // documentElement clientHeight
|
||||||
transform: 0,
|
transform: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const offsetTop = computed(() => unitToPx(props.offsetTop));
|
const offsetTop = computed(() => unitToPx(props.offsetTop));
|
||||||
|
const offsetBottom = computed(() => unitToPx(props.offsetBottom));
|
||||||
|
|
||||||
const style = computed<CSSProperties | undefined>(() => {
|
const style = computed<CSSProperties | undefined>(() => {
|
||||||
if (!state.fixed) {
|
if (!state.fixed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const top = offsetTop.value ? `${offsetTop.value}px` : undefined;
|
const top = offsetTop.value ? `${offsetTop.value}px` : 0;
|
||||||
|
const bottom = offsetBottom.value ? `${offsetBottom.value}px` : 0;
|
||||||
const transform = state.transform
|
const transform = state.transform
|
||||||
? `translate3d(0, ${state.transform}px, 0)`
|
? `translate3d(0, ${state.transform}px, 0)`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
top,
|
height: `${state.height}px`,
|
||||||
|
width: `${state.width}px`,
|
||||||
|
top: props.position === 'top' ? top : undefined,
|
||||||
|
bottom: props.position === 'bottom' ? bottom : undefined,
|
||||||
zIndex: props.zIndex !== undefined ? +props.zIndex : undefined,
|
zIndex: props.zIndex !== undefined ? +props.zIndex : undefined,
|
||||||
transform,
|
transform,
|
||||||
};
|
};
|
||||||
@ -68,38 +79,43 @@ export default createComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.height = root.value.offsetHeight;
|
|
||||||
|
|
||||||
const { container } = props;
|
const { container } = props;
|
||||||
|
const rootRect = root.value.getBoundingClientRect();
|
||||||
|
const containerRect = container?.getBoundingClientRect();
|
||||||
|
state.height = rootRect.height;
|
||||||
|
state.width = rootRect.width;
|
||||||
|
state.clientHeight = document.documentElement.clientHeight;
|
||||||
|
|
||||||
const scrollTop = getScrollTop(window);
|
const scrollTop = getScrollTop(window);
|
||||||
const topToPageTop = getElementTop(root.value);
|
|
||||||
|
|
||||||
// The sticky component should be kept inside the container element
|
// The sticky component should be kept inside the container element
|
||||||
|
|
||||||
|
if (props.position === 'top') {
|
||||||
if (container) {
|
if (container) {
|
||||||
const bottomToPageTop = topToPageTop + container.offsetHeight;
|
const difference =
|
||||||
|
containerRect.bottom - offsetTop.value - state.height;
|
||||||
if (scrollTop + offsetTop.value + state.height > bottomToPageTop) {
|
state.fixed =
|
||||||
const distanceToBottom = state.height + scrollTop - bottomToPageTop;
|
offsetTop.value > rootRect.top && containerRect.bottom > 0;
|
||||||
|
state.transform = difference < 0 ? difference : 0;
|
||||||
if (distanceToBottom < state.height) {
|
|
||||||
state.fixed = true;
|
|
||||||
state.transform = -(distanceToBottom + offsetTop.value);
|
|
||||||
} else {
|
} else {
|
||||||
state.fixed = false;
|
state.fixed = offsetTop.value > rootRect.top;
|
||||||
}
|
}
|
||||||
|
} else if (props.position === 'bottom') {
|
||||||
emitScrollEvent(scrollTop);
|
if (container) {
|
||||||
return;
|
const difference =
|
||||||
}
|
state.clientHeight -
|
||||||
}
|
containerRect.top -
|
||||||
|
offsetBottom.value -
|
||||||
if (scrollTop + offsetTop.value > topToPageTop) {
|
state.height;
|
||||||
state.fixed = true;
|
state.fixed =
|
||||||
state.transform = 0;
|
state.clientHeight - offsetBottom.value < rootRect.bottom &&
|
||||||
|
state.clientHeight > containerRect.top;
|
||||||
|
state.transform = difference < 0 ? -difference : 0;
|
||||||
} else {
|
} else {
|
||||||
state.fixed = false;
|
state.fixed =
|
||||||
|
state.clientHeight - offsetBottom.value < rootRect.bottom;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitScrollEvent(scrollTop);
|
emitScrollEvent(scrollTop);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,9 +123,10 @@ export default createComponent({
|
|||||||
useVisibilityChange(root, onScroll);
|
useVisibilityChange(root, onScroll);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const { fixed, height } = state;
|
const { fixed, height, width } = state;
|
||||||
const rootStyle: CSSProperties = {
|
const rootStyle: CSSProperties = {
|
||||||
height: fixed ? `${height}px` : undefined,
|
height: fixed ? `${height}px` : undefined,
|
||||||
|
width: fixed ? `${width}px` : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -51,4 +51,22 @@ exports[`should render demo and match snapshot 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div style="height: 200px;">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="van-sticky">
|
||||||
|
<button type="button"
|
||||||
|
class="van-button van-button--primary van-button--normal"
|
||||||
|
style="margin-left: 15px;"
|
||||||
|
>
|
||||||
|
<div class="van-button__content">
|
||||||
|
<span class="van-button__text">
|
||||||
|
Offset Bottom
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -30,11 +30,39 @@ exports[`should allow to using offset-top prop with vw unit 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`should sticky inside container when using container prop 1`] = `
|
exports[`should sticky inside container bottom when using container prop 1`] = `
|
||||||
<div style="height: 20px;">
|
<div style="margin-top: 640px;">
|
||||||
<div style="height: 10px;">
|
<div style="height: 150px;">
|
||||||
|
</div>
|
||||||
|
<div style="height: 44px; width: 88px;">
|
||||||
<div class="van-sticky van-sticky--fixed"
|
<div class="van-sticky van-sticky--fixed"
|
||||||
style="transform: translate3d(0, -5px, 0);"
|
style="height: 44px; width: 88px; bottom: 0px;"
|
||||||
|
>
|
||||||
|
Content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`should sticky inside container bottom when using container prop 2`] = `
|
||||||
|
<div style="margin-top: 640px;">
|
||||||
|
<div style="height: 150px;">
|
||||||
|
</div>
|
||||||
|
<div style="height: 44px; width: 88px;">
|
||||||
|
<div class="van-sticky van-sticky--fixed"
|
||||||
|
style="height: 44px; width: 88px; bottom: 0px; transform: translate3d(0, 24px, 0);"
|
||||||
|
>
|
||||||
|
Content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`should sticky inside container when using container prop 1`] = `
|
||||||
|
<div style="height: 150px;">
|
||||||
|
<div style="height: 44px; width: 88px;">
|
||||||
|
<div class="van-sticky van-sticky--fixed"
|
||||||
|
style="height: 44px; width: 88px; top: 0px;"
|
||||||
>
|
>
|
||||||
Content
|
Content
|
||||||
</div>
|
</div>
|
||||||
@ -43,26 +71,32 @@ exports[`should sticky inside container when using container prop 1`] = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`should sticky inside container when using container prop 2`] = `
|
exports[`should sticky inside container when using container prop 2`] = `
|
||||||
<div style="height: 20px;">
|
<div style="height: 150px;">
|
||||||
<div style="height: 10px;">
|
<div style="height: 44px; width: 88px;">
|
||||||
<div class="van-sticky">
|
<div class="van-sticky van-sticky--fixed"
|
||||||
|
style="height: 44px; width: 88px; top: 0px; transform: translate3d(0, -14px, 0);"
|
||||||
|
>
|
||||||
Content
|
Content
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`should sticky to bottom after scrolling 1`] = `
|
||||||
|
<div style="height: 10px;">
|
||||||
|
<div class="van-sticky van-sticky--fixed"
|
||||||
|
style="bottom: 10px;"
|
||||||
|
>
|
||||||
|
Content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`should sticky to top after scrolling 1`] = `
|
exports[`should sticky to top after scrolling 1`] = `
|
||||||
<div style="height: 10px;">
|
<div style="height: 10px;">
|
||||||
<div class="van-sticky">
|
<div class="van-sticky van-sticky--fixed"
|
||||||
Content
|
style="top: 0px;"
|
||||||
</div>
|
>
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`should sticky to top after scrolling 2`] = `
|
|
||||||
<div style="height: 10px;">
|
|
||||||
<div class="van-sticky van-sticky--fixed">
|
|
||||||
Content
|
Content
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -71,7 +105,7 @@ exports[`should sticky to top after scrolling 2`] = `
|
|||||||
exports[`should update z-index when using z-index prop 1`] = `
|
exports[`should update z-index when using z-index prop 1`] = `
|
||||||
<div style="height: 10px;">
|
<div style="height: 10px;">
|
||||||
<div class="van-sticky van-sticky--fixed"
|
<div class="van-sticky van-sticky--fixed"
|
||||||
style="z-index: 0;"
|
style="top: 0px; z-index: 0;"
|
||||||
>
|
>
|
||||||
Content
|
Content
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,19 +1,51 @@
|
|||||||
import { nextTick, ref } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
import { mount, mockScrollTop } from '../../../test';
|
import { mockScrollTop, mount } from '../../../test';
|
||||||
import Sticky from '..';
|
import Sticky from '..';
|
||||||
|
|
||||||
|
Object.defineProperty(window.HTMLElement.prototype, 'clientHeight', {
|
||||||
|
value: 640,
|
||||||
|
});
|
||||||
|
|
||||||
test('should sticky to top after scrolling', async () => {
|
test('should sticky to top after scrolling', async () => {
|
||||||
const wrapper = mount({
|
const wrapper = mount({
|
||||||
render() {
|
render() {
|
||||||
return <Sticky style="height: 10px;">Content</Sticky>;
|
return <Sticky style="height: 10px;">Content</Sticky>;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const mockStickyRect = jest
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: -100,
|
||||||
|
bottom: -90,
|
||||||
|
});
|
||||||
|
|
||||||
await mockScrollTop(100);
|
await mockScrollTop(100);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
|
||||||
|
mockStickyRect.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should sticky to bottom after scrolling', async () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Sticky style="height: 10px;" offsetBottom={10} position="bottom">
|
||||||
|
Content
|
||||||
|
</Sticky>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const mockStickyRect = jest
|
||||||
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: 640,
|
||||||
|
bottom: 650,
|
||||||
|
});
|
||||||
|
|
||||||
await mockScrollTop(0);
|
await mockScrollTop(0);
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
|
||||||
|
mockStickyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should update z-index when using z-index prop', async () => {
|
test('should update z-index when using z-index prop', async () => {
|
||||||
@ -27,25 +59,41 @@ test('should update z-index when using z-index prop', async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockStickyRect = jest
|
||||||
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: -100,
|
||||||
|
bottom: -90,
|
||||||
|
});
|
||||||
|
|
||||||
await mockScrollTop(100);
|
await mockScrollTop(100);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
await mockScrollTop(0);
|
|
||||||
|
mockStickyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should add offset top when using offset-top prop', async () => {
|
test('should add offset top when using offset-top prop', async () => {
|
||||||
const wrapper = mount({
|
const wrapper = mount({
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Sticky style="height: 10px;" offsetTop={10}>
|
<Sticky style="height: 10px;" offsetTop={10} position="top">
|
||||||
Content
|
Content
|
||||||
</Sticky>
|
</Sticky>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockStickyRect = jest
|
||||||
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: -100,
|
||||||
|
bottom: -90,
|
||||||
|
});
|
||||||
|
|
||||||
await mockScrollTop(100);
|
await mockScrollTop(100);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
await mockScrollTop(0);
|
|
||||||
|
mockStickyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should allow to using offset-top prop with rem unit', async () => {
|
test('should allow to using offset-top prop with rem unit', async () => {
|
||||||
@ -63,10 +111,17 @@ test('should allow to using offset-top prop with rem unit', async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockStickyRect = jest
|
||||||
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: -100,
|
||||||
|
bottom: -90,
|
||||||
|
});
|
||||||
|
|
||||||
await mockScrollTop(100);
|
await mockScrollTop(100);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
|
||||||
await mockScrollTop(0);
|
mockStickyRect.mockRestore();
|
||||||
window.getComputedStyle = originGetComputedStyle;
|
window.getComputedStyle = originGetComputedStyle;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,9 +138,17 @@ test('should allow to using offset-top prop with vw unit', async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockStickyRect = jest
|
||||||
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: -100,
|
||||||
|
bottom: -90,
|
||||||
|
});
|
||||||
|
|
||||||
await mockScrollTop(100);
|
await mockScrollTop(100);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
await mockScrollTop(0);
|
|
||||||
|
mockStickyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not trigger scroll event when hidden', () => {
|
test('should not trigger scroll event when hidden', () => {
|
||||||
@ -114,8 +177,8 @@ test('should sticky inside container when using container prop', async () => {
|
|||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div ref="container" style="height: 20px;">
|
<div ref="container" style="height: 150px;">
|
||||||
<Sticky ref="sticky" style="height: 10px;" container={this.container}>
|
<Sticky ref="sticky" style="height: 44px;" container={this.container}>
|
||||||
Content
|
Content
|
||||||
</Sticky>
|
</Sticky>
|
||||||
</div>
|
</div>
|
||||||
@ -123,12 +186,97 @@ test('should sticky inside container when using container prop', async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await nextTick();
|
const mockStickyRect = jest
|
||||||
await mockScrollTop(15);
|
.spyOn(wrapper.element.firstElementChild, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
height: 44,
|
||||||
|
width: 88,
|
||||||
|
top: -100,
|
||||||
|
bottom: -56,
|
||||||
|
});
|
||||||
|
const mockContainerRect = jest
|
||||||
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: -100,
|
||||||
|
bottom: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
await mockScrollTop(100);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
await mockScrollTop(25);
|
|
||||||
|
mockStickyRect.mockReturnValue({
|
||||||
|
height: 44,
|
||||||
|
width: 88,
|
||||||
|
top: -120,
|
||||||
|
bottom: -76,
|
||||||
|
});
|
||||||
|
mockContainerRect.mockReturnValue({
|
||||||
|
top: -120,
|
||||||
|
bottom: 30,
|
||||||
|
});
|
||||||
|
await mockScrollTop(120);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
await mockScrollTop(0);
|
mockContainerRect.mockRestore();
|
||||||
|
mockStickyRect.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should sticky inside container bottom when using container prop', async () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
setup() {
|
||||||
|
const container = ref();
|
||||||
|
return {
|
||||||
|
container,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div ref="container" style="margin-top: 640px">
|
||||||
|
<div style="height: 150px" />
|
||||||
|
<Sticky
|
||||||
|
ref="sticky"
|
||||||
|
style="height: 44px;"
|
||||||
|
container={this.container}
|
||||||
|
position="bottom"
|
||||||
|
>
|
||||||
|
Content
|
||||||
|
</Sticky>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockStickyRect = jest
|
||||||
|
.spyOn(wrapper.element.children[1], 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
height: 44,
|
||||||
|
width: 88,
|
||||||
|
top: 690,
|
||||||
|
bottom: 734,
|
||||||
|
});
|
||||||
|
const mockContainerRect = jest
|
||||||
|
.spyOn(wrapper.element, 'getBoundingClientRect')
|
||||||
|
.mockReturnValue({
|
||||||
|
top: 540,
|
||||||
|
bottom: 734,
|
||||||
|
});
|
||||||
|
|
||||||
|
await mockScrollTop(100);
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
|
||||||
|
mockStickyRect.mockReturnValue({
|
||||||
|
height: 44,
|
||||||
|
width: 88,
|
||||||
|
top: 770,
|
||||||
|
bottom: 814,
|
||||||
|
});
|
||||||
|
mockContainerRect.mockReturnValue({
|
||||||
|
top: 620,
|
||||||
|
bottom: 814,
|
||||||
|
});
|
||||||
|
await mockScrollTop(20);
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
mockContainerRect.mockRestore();
|
||||||
|
mockStickyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should emit scroll event when visibility changed', async () => {
|
test('should emit scroll event when visibility changed', async () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user