mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-23 05:50:37 +03:00
finish svelte version
This commit is contained in:
Generated
+10
@@ -29556,6 +29556,7 @@
|
|||||||
"@sveltejs/package": "next",
|
"@sveltejs/package": "next",
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
||||||
"@testing-library/svelte": "^3.2.2",
|
"@testing-library/svelte": "^3.2.2",
|
||||||
|
"overlayscrollbars": "file:./../overlayscrollbars/dist",
|
||||||
"svelte": "^3.44.0",
|
"svelte": "^3.44.0",
|
||||||
"svelte-check": "^2.7.1",
|
"svelte-check": "^2.7.1",
|
||||||
"svelte-preprocess": "^4.10.6",
|
"svelte-preprocess": "^4.10.6",
|
||||||
@@ -29563,6 +29564,7 @@
|
|||||||
"typescript": "^4.7.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"overlayscrollbars": "^2.0.0",
|
||||||
"svelte": "^3.44.0"
|
"svelte": "^3.44.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -29623,6 +29625,10 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"packages/overlayscrollbars-svelte/node_modules/overlayscrollbars": {
|
||||||
|
"resolved": "packages/overlayscrollbars/dist",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"packages/overlayscrollbars-svelte/node_modules/semver": {
|
"packages/overlayscrollbars-svelte/node_modules/semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
@@ -45348,6 +45354,7 @@
|
|||||||
"@sveltejs/package": "next",
|
"@sveltejs/package": "next",
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
||||||
"@testing-library/svelte": "^3.2.2",
|
"@testing-library/svelte": "^3.2.2",
|
||||||
|
"overlayscrollbars": "file:../overlayscrollbars/dist",
|
||||||
"svelte": "^3.44.0",
|
"svelte": "^3.44.0",
|
||||||
"svelte-check": "^2.7.1",
|
"svelte-check": "^2.7.1",
|
||||||
"svelte-preprocess": "^4.10.6",
|
"svelte-preprocess": "^4.10.6",
|
||||||
@@ -45405,6 +45412,9 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"overlayscrollbars": {
|
||||||
|
"version": "file:packages/overlayscrollbars/dist"
|
||||||
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
|||||||
@@ -113,11 +113,13 @@ Additionally to the `events` property the `OverlayScrollbarsComponent` emits "na
|
|||||||
></div>
|
></div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
All events are typed, but you can use the `EventListenerArgs` type as utility in case its needed:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import type { EventListenerArgs } from 'overlayscrollbars';
|
import type { EventListenerArgs } from 'overlayscrollbars';
|
||||||
|
|
||||||
// example listener
|
// example listener
|
||||||
onUpdated([instance, onUpdatedArgs]: EventListenerArgs['updated']) {}
|
const onUpdated = ([instance, onUpdatedArgs]: EventListenerArgs['updated']) => {}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ref
|
### Ref
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
[](https://github.com/KingSora/OverlayScrollbars)
|
[](https://github.com/KingSora/OverlayScrollbars)
|
||||||
[](https://github.com/sveltejs/svelte)
|
[](https://github.com/sveltejs/svelte)
|
||||||
|
[](https://www.npmjs.com/package/overlayscrollbars-svelte)
|
||||||
|
[](https://www.npmjs.com/package/overlayscrollbars-svelte)
|
||||||
[](#)
|
[](#)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -15,4 +17,103 @@
|
|||||||
|
|
||||||
This is the official OverlayScrollbars Svelte wrapper.
|
This is the official OverlayScrollbars Svelte wrapper.
|
||||||
|
|
||||||
# This project is a WIP
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install overlayscrollbars-svelte
|
||||||
|
```
|
||||||
|
|
||||||
|
## Peer Dependencies
|
||||||
|
|
||||||
|
OverlayScrollbars for Vue has the following **peer dependencies**:
|
||||||
|
|
||||||
|
- The vanilla JavaScript library: [overlayscrollbars](https://www.npmjs.com/package/overlayscrollbars)
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install overlayscrollbars
|
||||||
|
```
|
||||||
|
|
||||||
|
- The Vue framework: [svelte](https://www.npmjs.com/package/svelte)
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install svelte
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The first step is to import the CSS file into your app:
|
||||||
|
```ts
|
||||||
|
import 'overlayscrollbars/overlayscrollbars.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
> __Note__: In older node versions use `'overlayscrollbars/styles/overlayscrollbars.css'` as the import path for the CSS file.
|
||||||
|
|
||||||
|
## Component
|
||||||
|
|
||||||
|
The main entry point is the `OverlayScrollbarsComponent` which can be used in your application as a component:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
<OverlayScrollbarsComponent>
|
||||||
|
example content
|
||||||
|
</OverlayScrollbarsComponent>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
It has three optional properties: `element`, `options` and `events`.
|
||||||
|
|
||||||
|
- `element`: accepts a `string` which represents the tag of the root element.
|
||||||
|
- `options`: accepts an `object` which represents the OverlayScrollbars options.
|
||||||
|
- `events`: accepts an `object` which represents the OverlayScrollbars events.
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// example usage
|
||||||
|
<OverlayScrollbarsComponent
|
||||||
|
element="span"
|
||||||
|
options={{ scrollbars: { autoHide: 'scroll' } }}
|
||||||
|
events={{ scroll: () => { /* ... */ } }}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
Additionally to the `events` property the `OverlayScrollbarsComponent` emits "native" Svelte events. To prevent name collisions with DOM events the events have a `os` prefix.
|
||||||
|
|
||||||
|
> __Note__: It doesn't matter whether you use the `events` property or the Svelte events or both.
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// example usage
|
||||||
|
<OverlayScrollbarsComponent
|
||||||
|
on:osInitialized={onInitialized}
|
||||||
|
on:osUpdated={onUpdated}
|
||||||
|
on:osDestroyed={onDestroyed}
|
||||||
|
on:osScroll={onScroll}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
All events are typed, but you can use the `EventListenerArgs` type as utility in case its needed:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { EventListenerArgs } from 'overlayscrollbars';
|
||||||
|
|
||||||
|
// example listener
|
||||||
|
const onUpdated = (event) => {
|
||||||
|
const [instance, onUpdatedArgs] = event.detail as EventListenerArgs['updated'];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ref
|
||||||
|
|
||||||
|
The `ref` of the `OverlayScrollbarsComponent` will give you an object with which you can access the OverlayScrollbars `instance` and the root `element` of the component.
|
||||||
|
The ref object has two properties:
|
||||||
|
|
||||||
|
- `osInstance`: a function which returns the OverlayScrollbars instance.
|
||||||
|
- `getElement`: a function which returns the root element.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^3.44.0"
|
"svelte": "^3.44.0",
|
||||||
|
"overlayscrollbars": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "next",
|
"@sveltejs/adapter-auto": "next",
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
"@sveltejs/package": "next",
|
"@sveltejs/package": "next",
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
||||||
"@testing-library/svelte": "^3.2.2",
|
"@testing-library/svelte": "^3.2.2",
|
||||||
|
"overlayscrollbars": "file:./../overlayscrollbars/dist",
|
||||||
"svelte": "^3.44.0",
|
"svelte": "^3.44.0",
|
||||||
"svelte-check": "^2.7.1",
|
"svelte-check": "^2.7.1",
|
||||||
"svelte-preprocess": "^4.10.6",
|
"svelte-preprocess": "^4.10.6",
|
||||||
|
|||||||
@@ -1,3 +1,101 @@
|
|||||||
<h1>Welcome to your library project</h1>
|
<script lang="ts">
|
||||||
<p>Create your package using @sveltejs/package and preview/showcase your work with SvelteKit</p>
|
import { onMount, afterUpdate, createEventDispatcher } from 'svelte';
|
||||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
import { OverlayScrollbars } from 'overlayscrollbars';
|
||||||
|
import type { EventListeners, EventListenerArgs } from 'overlayscrollbars';
|
||||||
|
import type { OverlayScrollbarsComponentProps, OverlayScrollbarsComponentRef } from './OverlayScrollbarsComponent.types';
|
||||||
|
|
||||||
|
type EmitEventsMap = {
|
||||||
|
[N in keyof EventListenerArgs]: `os${Capitalize<N>}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export let element: OverlayScrollbarsComponentProps["element"] = 'div';
|
||||||
|
export let options: OverlayScrollbarsComponentProps["options"] = undefined;
|
||||||
|
export let events: OverlayScrollbarsComponentProps["events"] = undefined;
|
||||||
|
|
||||||
|
let instance: OverlayScrollbars | null = null;
|
||||||
|
let elementRef: HTMLElement | null = null;
|
||||||
|
let slotRef: HTMLElement | null = null;
|
||||||
|
let combinedEvents: OverlayScrollbarsComponentProps["events"] = undefined;
|
||||||
|
let prevElement: string | undefined;
|
||||||
|
|
||||||
|
const initialize = () => {
|
||||||
|
instance?.destroy();
|
||||||
|
instance = OverlayScrollbars(
|
||||||
|
{
|
||||||
|
target: elementRef!,
|
||||||
|
elements: {
|
||||||
|
viewport: slotRef,
|
||||||
|
content: slotRef
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options || {},
|
||||||
|
combinedEvents || {}
|
||||||
|
);
|
||||||
|
prevElement = element;
|
||||||
|
return () => instance?.destroy();
|
||||||
|
};
|
||||||
|
const dispatchEvents: EmitEventsMap = {
|
||||||
|
initialized: 'osInitialized',
|
||||||
|
updated: 'osUpdated',
|
||||||
|
destroyed: 'osDestroyed',
|
||||||
|
scroll: 'osScroll',
|
||||||
|
};
|
||||||
|
const dispatchEvent = createEventDispatcher<{
|
||||||
|
osInitialized: EventListenerArgs["initialized"];
|
||||||
|
osUpdated: EventListenerArgs["updated"];
|
||||||
|
osDestroyed: EventListenerArgs["destroyed"];
|
||||||
|
osScroll: EventListenerArgs["scroll"];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
export const osInstance: OverlayScrollbarsComponentRef["osInstance"] = () => instance;
|
||||||
|
export const getElement: OverlayScrollbarsComponentRef["getElement"] = () => elementRef;
|
||||||
|
|
||||||
|
onMount(initialize);
|
||||||
|
|
||||||
|
afterUpdate(() => {
|
||||||
|
if (prevElement !== element) {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$: {
|
||||||
|
const currEvents = events || {};
|
||||||
|
combinedEvents = (
|
||||||
|
Object.keys(dispatchEvents) as (keyof EventListeners)[]
|
||||||
|
).reduce<EventListeners>(<N extends keyof EventListeners>(obj: EventListeners, name: N) => {
|
||||||
|
const eventListener = currEvents[name];
|
||||||
|
obj[name] = [
|
||||||
|
(...args: EventListenerArgs[N]) =>
|
||||||
|
dispatchEvent(
|
||||||
|
dispatchEvents[name],
|
||||||
|
// @ts-ignore
|
||||||
|
args
|
||||||
|
),
|
||||||
|
...(Array.isArray(eventListener) ? eventListener : [eventListener]).filter(Boolean),
|
||||||
|
];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (OverlayScrollbars.valid(instance)) {
|
||||||
|
instance.options(options || {}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (OverlayScrollbars.valid(instance)) {
|
||||||
|
instance.on(
|
||||||
|
/* c8 ignore next */
|
||||||
|
combinedEvents || {},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element data-overlayscrollbars-initialize="" this={element} bind:this={elementRef} {...$$restProps}>
|
||||||
|
<div bind:this={slotRef}>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</svelte:element>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import type { OverlayScrollbars, PartialOptions, EventListeners } from 'overlayscrollbars';
|
||||||
|
|
||||||
|
export interface OverlayScrollbarsComponentProps {
|
||||||
|
element?: string;
|
||||||
|
options?: PartialOptions | false | null;
|
||||||
|
events?: EventListeners | false | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OverlayScrollbarsComponentRef {
|
||||||
|
/** Returns the OverlayScrollbars instance or null if not initialized. */
|
||||||
|
osInstance(): OverlayScrollbars | null;
|
||||||
|
/** Returns the root element. */
|
||||||
|
getElement(): HTMLElement | null;
|
||||||
|
}
|
||||||
@@ -1,14 +1,368 @@
|
|||||||
import { describe, it, expect, afterEach } from 'vitest';
|
import { describe, test, expect, afterEach, vitest } from 'vitest';
|
||||||
import { cleanup, render } from '@testing-library/svelte';
|
import { cleanup, render, screen, fireEvent } from '@testing-library/svelte';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { OverlayScrollbars } from 'overlayscrollbars';
|
||||||
import { OverlayScrollbarsComponent } from '~/index'; // eslint-disable-line import/named
|
import { OverlayScrollbarsComponent } from '~/index'; // eslint-disable-line import/named
|
||||||
|
import type { OverlayScrollbarsComponentRef } from '~/index'; // eslint-disable-line import/named
|
||||||
|
import Test from './Test.svelte';
|
||||||
|
|
||||||
describe('Hello.svelte', () => {
|
/**
|
||||||
// TODO: @testing-library/svelte claims to add this automatically but it doesn't work without explicit afterEach
|
* rerender would unmount and re-mount component... so I am faking it with custom event...
|
||||||
|
*/
|
||||||
|
describe('OverlayScrollbarsComponent', () => {
|
||||||
afterEach(() => cleanup());
|
afterEach(() => cleanup());
|
||||||
|
|
||||||
it('mounts', () => {
|
describe('correct rendering', () => {
|
||||||
const { container } = render(OverlayScrollbarsComponent);
|
test('correct root element with instance', async () => {
|
||||||
expect(container).toBeTruthy();
|
const elementA = 'code';
|
||||||
expect(container.innerHTML).toContain('Welcome to your library project');
|
const elementB = 'span';
|
||||||
|
let osInstance;
|
||||||
|
const { container } = render(Test);
|
||||||
|
const realContainer = container.firstElementChild!;
|
||||||
|
|
||||||
|
expect(realContainer).not.toBeEmptyDOMElement();
|
||||||
|
expect(realContainer.querySelector('div')).toBe(realContainer.firstElementChild); // default is div
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
||||||
|
osInstance = OverlayScrollbars(realContainer.firstElementChild as HTMLElement);
|
||||||
|
|
||||||
|
expect(osInstance).toBeDefined();
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: { element: elementA },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(realContainer.querySelector(elementA)).toBe(realContainer.firstElementChild);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false); // prev instance is destroyed
|
||||||
|
osInstance = OverlayScrollbars(realContainer.firstElementChild as HTMLElement);
|
||||||
|
expect(osInstance).toBeDefined();
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: { element: elementB },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(realContainer.querySelector(elementB)).toBe(realContainer.firstElementChild);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false); // prev instance is destroyed
|
||||||
|
osInstance = OverlayScrollbars(realContainer.firstElementChild as HTMLElement);
|
||||||
|
expect(osInstance).toBeDefined();
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('data-overlayscrollbars-initialize', async () => {
|
||||||
|
const { container } = render(OverlayScrollbarsComponent);
|
||||||
|
|
||||||
|
expect(container.querySelector('[data-overlayscrollbars-initialize]')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('children', () => {
|
||||||
|
const { container } = render(Test);
|
||||||
|
expect(screen.getByText(/hello/)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/svelte/)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/svelte/).parentElement).not.toBe(container.firstElementChild);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dynamic children', async () => {
|
||||||
|
render(Test);
|
||||||
|
const addBtn = screen.getByText('add');
|
||||||
|
const removeBtn = screen.getByText('remove');
|
||||||
|
const initialElement = screen.getByText('0');
|
||||||
|
expect(initialElement).toBeInTheDocument();
|
||||||
|
|
||||||
|
const initialElementParent = initialElement.parentElement;
|
||||||
|
expect(initialElementParent).toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.click(addBtn);
|
||||||
|
expect((await screen.findByText('1')).parentElement).toBe(initialElementParent);
|
||||||
|
|
||||||
|
userEvent.click(removeBtn);
|
||||||
|
userEvent.click(removeBtn);
|
||||||
|
expect((await screen.findByText('empty')).parentElement).toBe(initialElementParent);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('className', async () => {
|
||||||
|
const { container } = render(Test, {
|
||||||
|
props: {
|
||||||
|
className: 'overlay scrollbars',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const realContainer = container.firstElementChild!;
|
||||||
|
|
||||||
|
expect(realContainer.firstElementChild).toHaveClass('overlay', 'scrollbars');
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: { className: 'overlay scrollbars svelte' },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(realContainer.firstElementChild).toHaveClass('overlay', 'scrollbars', 'svelte');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('style', async () => {
|
||||||
|
const { container } = render(Test, {
|
||||||
|
props: {
|
||||||
|
style: 'width: 22px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const realContainer = container.firstElementChild!;
|
||||||
|
|
||||||
|
expect(realContainer.firstElementChild).toHaveStyle({ width: '22px' });
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: { style: 'height: 33px' },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(realContainer.firstElementChild).toHaveStyle({ height: '33px' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ref', () => {
|
||||||
|
let osRef: OverlayScrollbarsComponentRef | undefined;
|
||||||
|
const { container } = render(Test, {
|
||||||
|
props: {
|
||||||
|
getRef: (ref: any) => {
|
||||||
|
osRef = ref;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const realContainer = container.firstElementChild!;
|
||||||
|
|
||||||
|
expect(osRef).toBeTruthy();
|
||||||
|
|
||||||
|
const { osInstance, getElement } = osRef!;
|
||||||
|
expect(osInstance).toBeTypeOf('function');
|
||||||
|
expect(getElement).toBeTypeOf('function');
|
||||||
|
expect(OverlayScrollbars.valid(osInstance())).toBe(true);
|
||||||
|
expect(getElement()).toBe(realContainer.firstElementChild);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('options', async () => {
|
||||||
|
let osRef: OverlayScrollbarsComponentRef | undefined;
|
||||||
|
render(Test, {
|
||||||
|
props: {
|
||||||
|
options: { paddingAbsolute: true, overflow: { y: 'hidden' } },
|
||||||
|
getRef: (ref: any) => {
|
||||||
|
osRef = ref;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = osRef!.osInstance()!;
|
||||||
|
|
||||||
|
const opts = instance.options();
|
||||||
|
expect(opts.paddingAbsolute).toBe(true);
|
||||||
|
expect(opts.overflow.y).toBe('hidden');
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: {
|
||||||
|
options: { overflow: { x: 'hidden' } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const newOpts = instance.options();
|
||||||
|
expect(newOpts.paddingAbsolute).toBe(false); //switches back to default because its not specified in the new options
|
||||||
|
expect(newOpts.overflow.x).toBe('hidden');
|
||||||
|
expect(newOpts.overflow.y).toBe('scroll'); //switches back to default because its not specified in the new options
|
||||||
|
|
||||||
|
// instance didn't change
|
||||||
|
expect(instance).toBe(osRef!.osInstance());
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: {
|
||||||
|
element: 'span',
|
||||||
|
options: { overflow: { x: 'hidden', y: 'hidden' } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const newElementInstance = osRef!.osInstance()!;
|
||||||
|
const newElementNewOpts = newElementInstance.options();
|
||||||
|
expect(newElementInstance).not.toBe(instance);
|
||||||
|
expect(newElementNewOpts.paddingAbsolute).toBe(false);
|
||||||
|
expect(newElementNewOpts.overflow.x).toBe('hidden');
|
||||||
|
expect(newElementNewOpts.overflow.y).toBe('hidden');
|
||||||
|
|
||||||
|
// reset options with `undefined`, `null`, `false` or `{}`
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: {
|
||||||
|
options: undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const clearedOpts = newElementInstance.options();
|
||||||
|
expect(osRef!.osInstance()).toBe(newElementInstance);
|
||||||
|
expect(clearedOpts.paddingAbsolute).toBe(false);
|
||||||
|
expect(clearedOpts.overflow.x).toBe('scroll');
|
||||||
|
expect(clearedOpts.overflow.y).toBe('scroll');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('events', async () => {
|
||||||
|
const onUpdatedInitial = vitest.fn();
|
||||||
|
const onUpdated = vitest.fn();
|
||||||
|
let osRef: OverlayScrollbarsComponentRef | undefined;
|
||||||
|
render(Test, {
|
||||||
|
props: {
|
||||||
|
events: { updated: onUpdatedInitial },
|
||||||
|
getRef: (ref: any) => {
|
||||||
|
osRef = ref;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = osRef!.osInstance()!;
|
||||||
|
|
||||||
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: {
|
||||||
|
events: { updated: onUpdated },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(onUpdated).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
instance.update(true);
|
||||||
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: {
|
||||||
|
events: { updated: [onUpdated, onUpdatedInitial] },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.update(true);
|
||||||
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(2);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
// unregister with `[]`, `null` or `undefined`
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: {
|
||||||
|
events: { updated: null },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.update(true);
|
||||||
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(2);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
// instance didn't change
|
||||||
|
expect(instance).toBe(osRef!.osInstance());
|
||||||
|
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: { element: 'span', events: { updated: [onUpdated, onUpdatedInitial] } },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const newElementInstance = osRef!.osInstance()!;
|
||||||
|
expect(newElementInstance).not.toBe(instance);
|
||||||
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(3);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
// reset events with `undefined`, `null`, `false` or `{}`
|
||||||
|
await fireEvent(
|
||||||
|
screen.getByText('props'),
|
||||||
|
new CustomEvent('osProps', {
|
||||||
|
detail: { element: 'span', events: undefined },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
newElementInstance.update(true);
|
||||||
|
expect(newElementInstance).toBe(osRef!.osInstance());
|
||||||
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(3);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('destroy', () => {
|
||||||
|
let osRef: OverlayScrollbarsComponentRef | undefined;
|
||||||
|
const { unmount } = render(Test, {
|
||||||
|
props: {
|
||||||
|
getRef(ref: any) {
|
||||||
|
osRef = ref;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { osInstance } = osRef!;
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance())).toBe(true);
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
|
||||||
|
expect(osInstance()).toBeDefined();
|
||||||
|
expect(OverlayScrollbars.valid(osInstance())).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dispatch events', async () => {
|
||||||
|
const initialized = vitest.fn((e: any) => {
|
||||||
|
const args = e.detail;
|
||||||
|
expect(args).toEqual([expect.any(Object)]);
|
||||||
|
});
|
||||||
|
const updated = vitest.fn((e: any) => {
|
||||||
|
const args = e.detail;
|
||||||
|
expect(args).toEqual([expect.any(Object), expect.any(Object)]);
|
||||||
|
});
|
||||||
|
const destroyed = vitest.fn((e: any) => {
|
||||||
|
const args = e.detail;
|
||||||
|
expect(args).toEqual([expect.any(Object), expect.any(Boolean)]);
|
||||||
|
});
|
||||||
|
const scroll = vitest.fn((e: any) => {
|
||||||
|
const args = e.detail;
|
||||||
|
expect(args).toEqual([expect.any(Object), expect.any(Event)]);
|
||||||
|
});
|
||||||
|
const { container, unmount } = render(Test, {
|
||||||
|
props: {
|
||||||
|
initialized,
|
||||||
|
updated,
|
||||||
|
destroyed,
|
||||||
|
scroll,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(initialized).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(destroyed).not.toHaveBeenCalled();
|
||||||
|
expect(scroll).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
container.querySelectorAll('*').forEach((e) => {
|
||||||
|
fireEvent.scroll(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(destroyed).not.toHaveBeenCalled();
|
||||||
|
expect(scroll).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
|
||||||
|
expect(destroyed).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { OverlayScrollbarsComponent } from '~/index';
|
||||||
|
let children = 1;
|
||||||
|
let ref: any;
|
||||||
|
|
||||||
|
export let element: any = 'div';
|
||||||
|
export let options: any = undefined;
|
||||||
|
export let events: any = undefined;
|
||||||
|
export let getRef: any = undefined;
|
||||||
|
export let initialized: any = undefined;
|
||||||
|
export let updated: any = undefined;
|
||||||
|
export let destroyed: any = undefined;
|
||||||
|
export let scroll: any = undefined;
|
||||||
|
export let className: any = undefined;
|
||||||
|
export let style: any = undefined;
|
||||||
|
|
||||||
|
const propsChange = (e: any) => {
|
||||||
|
const optionsChanged = Object.prototype.hasOwnProperty.call(e.detail, 'options');
|
||||||
|
const eventsChanged = Object.prototype.hasOwnProperty.call(e.detail, 'events');
|
||||||
|
const elementChanged = Object.prototype.hasOwnProperty.call(e.detail, 'element');
|
||||||
|
const classChanged = Object.prototype.hasOwnProperty.call(e.detail, 'className');
|
||||||
|
const styleChanged = Object.prototype.hasOwnProperty.call(e.detail, 'style');
|
||||||
|
if (optionsChanged) {
|
||||||
|
options = e.detail.options;
|
||||||
|
}
|
||||||
|
if (eventsChanged) {
|
||||||
|
events = e.detail.events;
|
||||||
|
}
|
||||||
|
if (elementChanged) {
|
||||||
|
element = e.detail.element;
|
||||||
|
}
|
||||||
|
if (classChanged) {
|
||||||
|
className = e.detail.className;
|
||||||
|
}
|
||||||
|
if (styleChanged) {
|
||||||
|
style = e.detail.style;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$: {
|
||||||
|
getRef?.(ref);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<OverlayScrollbarsComponent
|
||||||
|
bind:this={ref}
|
||||||
|
element={element}
|
||||||
|
options={options}
|
||||||
|
events={events}
|
||||||
|
class={className}
|
||||||
|
style={style}
|
||||||
|
on:osInitialized={initialized}
|
||||||
|
on:osUpdated={updated}
|
||||||
|
on:osDestroyed={destroyed}
|
||||||
|
on:osScroll={scroll}>
|
||||||
|
hello <span>svelte</span>
|
||||||
|
{#if children === 0}<div>empty</div>{/if}
|
||||||
|
{#each [...Array(children).keys()] as child}
|
||||||
|
<section>{child}</section>
|
||||||
|
{/each}
|
||||||
|
</OverlayScrollbarsComponent>
|
||||||
|
<button id="add" on:click={() => children++}>add</button>
|
||||||
|
<button id="remove" on:click={() => children--}>remove</button>
|
||||||
|
<optionsChange on:osProps={propsChange}>
|
||||||
|
props
|
||||||
|
</optionsChange>
|
||||||
@@ -2,4 +2,12 @@ import { mergeConfig } from 'vite';
|
|||||||
import vitestConfig from '@~local/config/vitest';
|
import vitestConfig from '@~local/config/vitest';
|
||||||
import viteConfig from './vite.config';
|
import viteConfig from './vite.config';
|
||||||
|
|
||||||
export default mergeConfig(viteConfig, vitestConfig);
|
export default mergeConfig(viteConfig, {
|
||||||
|
test: {
|
||||||
|
...vitestConfig.test,
|
||||||
|
coverage: {
|
||||||
|
...vitestConfig.test.coverage,
|
||||||
|
exclude: [...vitestConfig.test.coverage.exclude, '**/Test.svelte'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user