finish up angular component and improve vue docs

This commit is contained in:
Rene Haas
2022-11-08 18:05:13 +01:00
parent d7aa1051eb
commit f36ffb91f1
10 changed files with 90 additions and 38 deletions
@@ -10,8 +10,8 @@ import {
AfterViewInit,
} from '@angular/core';
import { OverlayScrollbars } from 'overlayscrollbars';
import { OverlayScrollbarsDirective } from '~/overlayscrollbars.directive';
import type { PartialOptions, EventListeners, EventListenerMap } from 'overlayscrollbars';
import { OverlayScrollbarsDirective } from './overlayscrollbars.directive';
const mergeEventListeners = (emits: EventListeners, events: EventListeners) =>
(Object.keys(emits) as (keyof EventListeners)[]).reduce<EventListeners>(
@@ -29,17 +29,12 @@ const mergeEventListeners = (emits: EventListeners, events: EventListeners) =>
);
@Component({
selector: '[overlay-scrollbars-component]', // https://angular.io/guide/styleguide#component-selectors
exportAs: 'overlayScrollbars',
selector: 'overlay-scrollbars, [overlay-scrollbars]', // https://angular.io/guide/styleguide#component-selectors
host: { 'data-overlayscrollbars': '' },
template: `<div
#content
[overlayScrollbarsDirective]
[options]="options"
[events]="mergeEvents(events)"
>
template: `<div overlayScrollbars [options]="options" [events]="mergeEvents(events)" #content>
<ng-content></ng-content>
</div>`,
styles: [':host { display: block; }'],
})
export class OverlayScrollbarsComponent implements OnDestroy, AfterViewInit {
@Input('options')
@@ -91,7 +86,7 @@ export class OverlayScrollbarsComponent implements OnDestroy, AfterViewInit {
this.osDirective?.instance()!.destroy();
}
private mergeEvents(originalEvents: OverlayScrollbarsComponent['events']) {
mergeEvents(originalEvents: OverlayScrollbarsComponent['events']) {
return mergeEventListeners(
{
initialized: (...args) => this.onInitialized.emit(args),
@@ -5,8 +5,7 @@ import type { InitializationTarget } from 'overlayscrollbars';
import type { OverlayScrollbarsComponent } from '~/overlayscrollbars.component';
@Directive({
selector: '[overlayScrollbarsDirective]', // https://angular.io/guide/styleguide#component-selectors
exportAs: 'overlayScrollbars',
selector: '[overlayScrollbars]', // https://angular.io/guide/styleguide#directive-selectors
})
export class OverlayScrollbarsDirective implements OnChanges {
private instanceRef: OverlayScrollbars | null = null;
@@ -12,7 +12,7 @@ import type { EventListenerMap } from 'overlayscrollbars';
@Component({
template: `
<div
[overlay-scrollbars-component]
overlay-scrollbars
[options]="options"
[events]="events"
(osInitialized)="onInitialized($event)"
@@ -70,6 +70,19 @@ class Test {
}
}
@Component({
template: `
<span overlay-scrollbars #span>span</span>
<overlay-scrollbars #os>span</overlay-scrollbars>
`,
})
class TestTag {
@ViewChild('span', { read: OverlayScrollbarsComponent })
spanRef?: OverlayScrollbarsComponent;
@ViewChild('os', { read: OverlayScrollbarsComponent })
osRef?: OverlayScrollbarsComponent;
}
describe('OverlayscrollbarsNgxComponent', () => {
let component: OverlayScrollbarsComponent;
let fixture: ComponentFixture<OverlayScrollbarsComponent>;
@@ -77,7 +90,7 @@ describe('OverlayscrollbarsNgxComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
...new OverlayscrollbarsModule(),
declarations: [OverlayScrollbarsComponent, OverlayScrollbarsDirective, Test],
declarations: [OverlayScrollbarsComponent, OverlayScrollbarsDirective, Test, TestTag],
}).compileComponents();
fixture = TestBed.createComponent(OverlayScrollbarsComponent);
@@ -349,4 +362,25 @@ describe('OverlayscrollbarsNgxComponent', () => {
expect(onDestroyed).toHaveBeenCalledTimes(1);
expect(onDestroyed).toHaveBeenCalledWith([jasmine.any(Object), jasmine.any(Boolean)]);
});
it('has correct tags', async () => {
const testFixture = TestBed.createComponent(TestTag);
const testInstance = testFixture.componentInstance;
testFixture.detectChanges();
const osRef = testInstance.osRef!;
const spanRef = testInstance.spanRef!;
expect(osRef).toBeDefined();
expect(spanRef).toBeDefined();
expect(OverlayScrollbars.valid(osRef.instance())).toBe(true);
expect(OverlayScrollbars.valid(spanRef.instance())).toBe(true);
testFixture.destroy();
expect(OverlayScrollbars.valid(osRef.instance())).toBe(false);
expect(OverlayScrollbars.valid(spanRef.instance())).toBe(false);
});
});
+16
View File
@@ -90,6 +90,22 @@ It has three optional properties: `element`, `options` and `events`.
/>
```
### Events
Additionally to the `events` property the `OverlayScrollbarsComponent` emits "native" Vue events. To prevent name collisions with DOM events, the events are prefixed with `os`. It doesn't matter whether you use the `events` property, the Vue events or both.
```jsx
// example usage
<template>
<OverlayScrollbarsComponent
@os-initialized="onInitialized"
@os-updated="onUpdated"
@os-destroyed="onDestroyed"
@os-scroll="onScroll"
/>
</template>
```
### 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.
@@ -8,9 +8,8 @@ import type {
import type { PropType } from 'vue';
import type { EventListeners, EventListenerMap } from 'overlayscrollbars';
type EmitName<N extends keyof EventListenerMap = keyof EventListenerMap> = `os${Capitalize<N>}`;
type EmitEventsMap = {
[N in keyof EventListenerMap]: EmitName<N>;
[N in keyof EventListenerMap]: `os${Capitalize<N>}`;
};
const emitEvents: EmitEventsMap = {
@@ -27,12 +26,12 @@ const props = defineProps({
options: { type: Object as PropType<OverlayScrollbarsComponentProps['options']> },
events: { type: Object as PropType<OverlayScrollbarsComponentProps['events']> },
});
const emits = defineEmits({
osInitialized: (...args: EventListenerMap['initialized']) => true,
osUpdated: (...args: EventListenerMap['updated']) => true,
osDestroyed: (...args: EventListenerMap['destroyed']) => true,
osScroll: (...args: EventListenerMap['scroll']) => true,
} as Record<EmitName, any>);
const emits = defineEmits<{
(name: 'osInitialized', ...args: EventListenerMap['initialized']): void;
(name: 'osUpdated', ...args: EventListenerMap['updated']): void;
(name: 'osDestroyed', ...args: EventListenerMap['destroyed']): void;
(name: 'osScroll', ...args: EventListenerMap['scroll']): void;
}>();
const elementRef = shallowRef<HTMLElement | null>(null);
const slotRef = shallowRef<HTMLElement | null>(null);
@@ -73,8 +72,12 @@ watch(
).reduce<EventListeners>(<N extends keyof EventListeners>(obj: EventListeners, name: N) => {
const eventListener = currEvents[name];
obj[name] = [
// @ts-ignore
(...args: EventListenerMap[N]) => emits(emitEvents[name], ...args),
(...args: EventListenerMap[N]) =>
emits(
emitEvents[name],
// @ts-ignore
...args
),
...(Array.isArray(eventListener) ? eventListener : [eventListener]).filter(Boolean),
];
return obj;