mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-05-17 05:09:40 +03:00
finish svelte version
This commit is contained in:
Generated
+10
@@ -29556,6 +29556,7 @@
|
||||
"@sveltejs/package": "next",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
||||
"@testing-library/svelte": "^3.2.2",
|
||||
"overlayscrollbars": "file:./../overlayscrollbars/dist",
|
||||
"svelte": "^3.44.0",
|
||||
"svelte-check": "^2.7.1",
|
||||
"svelte-preprocess": "^4.10.6",
|
||||
@@ -29563,6 +29564,7 @@
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"overlayscrollbars": "^2.0.0",
|
||||
"svelte": "^3.44.0"
|
||||
}
|
||||
},
|
||||
@@ -29623,6 +29625,10 @@
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"packages/overlayscrollbars-svelte/node_modules/overlayscrollbars": {
|
||||
"resolved": "packages/overlayscrollbars/dist",
|
||||
"link": true
|
||||
},
|
||||
"packages/overlayscrollbars-svelte/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
@@ -45348,6 +45354,7 @@
|
||||
"@sveltejs/package": "next",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
||||
"@testing-library/svelte": "^3.2.2",
|
||||
"overlayscrollbars": "file:../overlayscrollbars/dist",
|
||||
"svelte": "^3.44.0",
|
||||
"svelte-check": "^2.7.1",
|
||||
"svelte-preprocess": "^4.10.6",
|
||||
@@ -45405,6 +45412,9 @@
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"overlayscrollbars": {
|
||||
"version": "file:packages/overlayscrollbars/dist"
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"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>
|
||||
```
|
||||
|
||||
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
|
||||
onUpdated([instance, onUpdatedArgs]: EventListenerArgs['updated']) {}
|
||||
const onUpdated = ([instance, onUpdatedArgs]: EventListenerArgs['updated']) => {}
|
||||
```
|
||||
|
||||
### Ref
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
[](https://github.com/KingSora/OverlayScrollbars)
|
||||
[](https://github.com/sveltejs/svelte)
|
||||
[](https://www.npmjs.com/package/overlayscrollbars-svelte)
|
||||
[](https://www.npmjs.com/package/overlayscrollbars-svelte)
|
||||
[](#)
|
||||
|
||||
</div>
|
||||
@@ -15,4 +17,103 @@
|
||||
|
||||
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",
|
||||
"type": "module",
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.44.0"
|
||||
"svelte": "^3.44.0",
|
||||
"overlayscrollbars": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "next",
|
||||
@@ -12,6 +13,7 @@
|
||||
"@sveltejs/package": "next",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.9",
|
||||
"@testing-library/svelte": "^3.2.2",
|
||||
"overlayscrollbars": "file:./../overlayscrollbars/dist",
|
||||
"svelte": "^3.44.0",
|
||||
"svelte-check": "^2.7.1",
|
||||
"svelte-preprocess": "^4.10.6",
|
||||
|
||||
@@ -1,3 +1,101 @@
|
||||
<h1>Welcome to your library project</h1>
|
||||
<p>Create your package using @sveltejs/package and preview/showcase your work with SvelteKit</p>
|
||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
||||
<script lang="ts">
|
||||
import { onMount, afterUpdate, createEventDispatcher } from 'svelte';
|
||||
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 { cleanup, render } from '@testing-library/svelte';
|
||||
import { describe, test, expect, afterEach, vitest } from 'vitest';
|
||||
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 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());
|
||||
|
||||
it('mounts', () => {
|
||||
const { container } = render(OverlayScrollbarsComponent);
|
||||
expect(container).toBeTruthy();
|
||||
expect(container.innerHTML).toContain('Welcome to your library project');
|
||||
describe('correct rendering', () => {
|
||||
test('correct root element with instance', async () => {
|
||||
const elementA = 'code';
|
||||
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 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