Added Ng2FittextDirective class unit tests #32

Merged
giacomoferlaino merged 11 commits from master into master 2020-03-16 11:28:52 +00:00
7 changed files with 294 additions and 63 deletions

20
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,20 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "attach",
"name": "Unit tests",
"address": "localhost",
"port": 9333,
"sourceMaps": true,
"webRoot": "${workspaceFolder}",
"pathMapping": {
"/_karma_webpack_": "${workspaceFolder}"
}
}
]
}

View file

@ -1,4 +1,5 @@
{
"prettier.configPath": "./src/.prettierrc",
"editor.formatOnSave": true
"editor.formatOnSave": true,
"javascript.implicitProjectConfig.experimentalDecorators": true
}

View file

@ -1,19 +1,22 @@
# ng2-fittext
An Angular2 directive written in pure typescript (and without jquery!), for autoscale the font size of an element to fit an upper level container.
An Angular2 directive written in pure typescript (and without jquery!), to autoscale the font size of an element so that it fits an upper level container.
### Demo
http://plnkr.co/edit/v0TQaYepV4E2Heur02j5?p=preview
### Installation
Install the library
```sh
$ npm install --save ng2-fittext
```
### Usage
1) Declare it in your module
1. Declare it in your module
```sh
import {Ng2FittextModule} from "ng2-fittext";
@NgModule({
@ -22,7 +25,7 @@ $ npm install --save ng2-fittext
]
})
```
2) Use it in your components
2. Use it in your components
```sh
import {Component} from '@angular/core';
@Component({
@ -35,7 +38,8 @@ $ npm install --save ng2-fittext
```
### Examples
Fit with the parent element (this works if you have a variable number of element between element and parent)
Fit to the parent element (this works if you have a variable number of elements between your element and its parent)
```sh
import {Component} from '@angular/core';
@ -61,6 +65,7 @@ import {Component} from '@angular/core';
export class InputBoxComponent {}
```
**NEW! Support for maxFontSize!**
```sh
@ -74,46 +79,40 @@ import {Component} from '@angular/core';
export class InputBoxComponent {}
```
Input Parameters:
| Parameter | Description | Values |
| --- | --- | --- |
| fittext | is the selector of the directive | true/false
| container | the container to fit (if not present it fit default to parent container)| ElementRef
| activateOnResize | enable/disable the autofit in case of window resize | true or false (default false)
| activateOnInputEvents | enbale/disable the autofit in case of input box events (keydown, keyup etc..) | true or false (default false)
| maxFontSize | maximal font size | number, default is 1000
| **!deprecated!** useMaxFontSize | use max font size if is true | deprecated!
| minFontSize | minimal font size | number, default is 7
| modelToWatch | pass model to watch, when this model changes -> font size is automatically recalculated | any type of model
| ------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------- |
| fittext | the directive selector | true/false |
| container | the container to fit (if not present it fits to the parent container by default) | ElementRef |
| activateOnResize | enable/disable the autofit in case of window resize | true or false (default false) |
| activateOnInputEvents | enable/disable the autofit in case of input box events (keydown, keyup etc..) | true or false (default false) |
| maxFontSize | maximum font size | number, default is 1000 |
| **!deprecated!** useMaxFontSize | use max font size if is true | deprecated! |
| minFontSize | minimum font size | number, default is 7 |
| modelToWatch | pass model to watch, when this model changes -> font size is automatically recalculated | any type of model |
Output Parameters:
| Parameter | Description | Values |
| --- | --- | --- |
| fontSizeChanged | current font size | string
| --------------- | ----------------- | ------ |
| fontSizeChanged | current font size | string |
### Development
Want to contribute? Great!
Simply, clone the repository!
I created this library because I always spended too much time for solve this problem and because i didn't find nothing on the web (13/03/2017) that do this without jquery and easily integrable in angular2.
For sure is not a good implementation, maybe is not the best way to do it, but, it do the job.
I created this library because I always spent too much time to solve this problem and didn't find anything on the web (13/03/2017) that does this without jquery and that is also easily integrable in angular2.
For sure it is not the best implementation, maybe is not the best way to do it, but, it gets the job done.
### Todos
- Write tests
- Find a better algorithm to find the font-size who fits better the container
License
----
## License
MIT
**Lorenzo I.**

View file

@ -10,22 +10,28 @@ module.exports = function (config) {
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
browsers: ['Chrome', 'ChromeHeadlessDebug'],
customLaunchers: {
ChromeHeadlessDebug: {
base: 'ChromeHeadless',
flags: ['--remote-debugging-port=9333'],
},
},
singleRun: false,
});
};

View file

@ -6,6 +6,7 @@
"start": "./node_modules/.bin/ng serve",
"build": "./node_modules/.bin/ng build --aot --prod",
"test": "./node_modules/.bin/ng test",
"test:headless": "./node_modules/.bin/ng test --browsers=ChromeHeadlessDebug",
"lint": "./node_modules/.bin/ng lint",
"e2e": "./node_modules/.bin/ng e2e",
"pack": "./node_modules/.bin/ng-packagr -p ./src/lib/package.json"

View file

@ -29,7 +29,7 @@ export class Ng2FittextDirective
@Input('modelToWatch') modelToWatch: any;
@Output() fontSizeChanged = new EventEmitter();
@Output() fontSizeChanged: EventEmitter<any> = new EventEmitter();
private fontSize = 1000;
private speed = 1.05;
@ -37,29 +37,32 @@ export class Ng2FittextDirective
constructor(public el: ElementRef, public renderer: Renderer2) {}
setFontSize(fontSize: number) {
if (this.isVisible() && !this.done) {
setFontSize(fontSize: number): void {
if (this.isVisible() && !this.isDone()) {
if (fontSize < this.minFontSize) {
fontSize = this.minFontSize;
}
if (fontSize > this.maxFontSize) {
fontSize = this.maxFontSize;
}
this.fontSize = fontSize;
this.fontSizeChanged.emit(fontSize);
return this.el.nativeElement.style.setProperty(
this.el.nativeElement.style.setProperty(
'font-size',
fontSize.toString() + 'px'
);
}
}
calculateFontSize(fontSize: number, speed: number) {
getFontSize(): number {
return this.fontSize;
}
calculateFontSize(fontSize: number, speed: number): number {
return Math.floor(fontSize / speed);
}
checkOverflow(parent: any, children: any) {
checkOverflow(parent: any, children: any): boolean {
const overflowX = children.scrollWidth - parent.clientWidth;
const overflowY = children.clientHeight - parent.clientHeight;
return overflowX > 1 || overflowY > 1;
@ -95,7 +98,7 @@ export class Ng2FittextDirective
}
ngAfterViewInit() {
if (this.isVisible() && !this.done) {
if (this.isVisible() && !this.isDone()) {
if (this.fittext) {
const overflow = this.container
? this.checkOverflow(this.container, this.el.nativeElement)
@ -134,7 +137,7 @@ export class Ng2FittextDirective
}
}
private getStartFontSizeFromHeight(): number {
getStartFontSizeFromHeight(): number {
return this.container
? this.container.clientHeight
: this.el.nativeElement.parentElement.clientHeight;
@ -146,7 +149,11 @@ export class Ng2FittextDirective
: this.el.nativeElement.parentElement.clientWidth;
}
private isVisible(): boolean {
isDone(): boolean {
return this.done;
}
isVisible(): boolean {
return this.getStartFontSizeFromHeight() > 0;
}
}

View file

@ -0,0 +1,197 @@
import { Ng2FittextDirective } from '../ng2-fittext.directive';
import { Renderer2, ElementRef } from '@angular/core';
describe('Class: Ng2FittextDirective', () => {
let ng2FittextDirective: Ng2FittextDirective;
let elMock: ElementRef;
let rendererMock: Renderer2;
beforeEach(() => {
elMock = {} as ElementRef;
rendererMock = {} as Renderer2;
ng2FittextDirective = new Ng2FittextDirective(elMock, rendererMock);
});
describe('Method: setFontSize', () => {
let newFontSize: number;
let isVisibleSpy: jasmine.Spy;
let isDoneSpy: jasmine.Spy;
beforeEach(() => {
newFontSize = 100;
isVisibleSpy = spyOn(ng2FittextDirective, 'isVisible').and.returnValue(
true
);
isDoneSpy = spyOn(ng2FittextDirective, 'isDone').and.returnValue(false);
elMock.nativeElement = {
style: {
setProperty: () => {},
},
};
});
it('Should not change the font size if the element is not visible', () => {
isVisibleSpy.and.returnValue(false);
const previousFontSize: number = ng2FittextDirective.getFontSize();
ng2FittextDirective.setFontSize(newFontSize);
expect(ng2FittextDirective.getFontSize()).toEqual(previousFontSize);
});
it('Should not change the font size if the fitting operation is done', () => {
isDoneSpy.and.returnValue(true);
const previousFontSize: number = ng2FittextDirective.getFontSize();
ng2FittextDirective.setFontSize(newFontSize);
expect(ng2FittextDirective.getFontSize()).toEqual(previousFontSize);
});
it('Should use the minFontSize property value if the specified font size is smaller', () => {
const minFontSize: number = ng2FittextDirective.minFontSize;
newFontSize = 5;
ng2FittextDirective.setFontSize(newFontSize);
const currentFontSize: number = ng2FittextDirective.getFontSize();
expect(currentFontSize).toEqual(minFontSize);
});
it('Should use the maxFontSize property value if the specified font size is bigger', () => {
const maxFontSize: number = ng2FittextDirective.maxFontSize;
newFontSize = 1001;
ng2FittextDirective.setFontSize(newFontSize);
const currentFontSize: number = ng2FittextDirective.getFontSize();
expect(currentFontSize).toEqual(maxFontSize);
});
it('Should set a new fontSize value', () => {
newFontSize = 500;
ng2FittextDirective.setFontSize(newFontSize);
const currentFontSize: number = ng2FittextDirective.getFontSize();
expect(currentFontSize).toEqual(newFontSize);
});
it('Should emit the font size change', () => {
newFontSize = 500;
spyOn(ng2FittextDirective.fontSizeChanged, 'emit');
ng2FittextDirective.setFontSize(newFontSize);
expect(ng2FittextDirective.fontSizeChanged.emit).toHaveBeenCalledWith(
newFontSize
);
});
it('Should update the nativeElement with the new font size', () => {
newFontSize = 500;
spyOn(ng2FittextDirective.el.nativeElement.style, 'setProperty');
ng2FittextDirective.setFontSize(newFontSize);
expect(
ng2FittextDirective.el.nativeElement.style.setProperty
).toHaveBeenCalledWith('font-size', `${newFontSize}px`);
});
});
describe('Method: getFontSize', () => {
it('Should return the current font size', () => {
expect(ng2FittextDirective.getFontSize()).toEqual(1000);
});
});
describe('Method: calculateFontSize', () => {
it('Should return the font size rounded down', () => {
expect(ng2FittextDirective.calculateFontSize(10, 3)).toEqual(3);
expect(ng2FittextDirective.calculateFontSize(9, 3)).toEqual(3);
expect(ng2FittextDirective.calculateFontSize(8, 3)).toEqual(2);
});
});
describe('Method: checkOverflow', () => {
let parentElementMock: any;
let childrenElementMock: any;
beforeEach(() => {
parentElementMock = {
clientWidth: 0,
clientHeight: 0,
};
childrenElementMock = {
scrollWidth: 0,
clientHeight: 0,
};
});
it('Should return false if no overflow is present', () => {
expect(
ng2FittextDirective.checkOverflow(
parentElementMock,
childrenElementMock
)
).toBe(false);
});
it('Should return true if x axis has overflow', () => {
childrenElementMock.scrollWidth = 2;
expect(
ng2FittextDirective.checkOverflow(
parentElementMock,
childrenElementMock
)
).toBe(true);
});
it('Should return true if y axis has overflow', () => {
childrenElementMock.clientHeight = 2;
expect(
ng2FittextDirective.checkOverflow(
parentElementMock,
childrenElementMock
)
).toBe(true);
});
});
describe('Method: getStartFontSizeFromHeight', () => {
it('Should return the container clientHeight value if the container is present', () => {
const containerClientHeight = 10;
ng2FittextDirective.container = {
clientHeight: containerClientHeight,
} as HTMLElement;
expect(ng2FittextDirective.getStartFontSizeFromHeight()).toEqual(
containerClientHeight
);
});
it('Should return the parentElement clientHeight value if no container is present', () => {
const parentlientHeight = 11;
elMock.nativeElement = {
parentElement: {
clientHeight: parentlientHeight,
},
} as HTMLElement;
expect(ng2FittextDirective.getStartFontSizeFromHeight()).toEqual(
parentlientHeight
);
});
});
describe('Method: isDone', () => {
it('Should return the done property value', () => {
const defaultDoneValue = false;
expect(ng2FittextDirective.isDone()).toBe(defaultDoneValue);
});
});
describe('Method: isVisible', () => {
it('Should return the true if getStartFontSizeFromHeight() is greater than zero', () => {
spyOn(ng2FittextDirective, 'getStartFontSizeFromHeight').and.returnValue(
1
);
expect(ng2FittextDirective.isVisible()).toBe(true);
});
it('Should return the false if getStartFontSizeFromHeight() is smaller or equal to zero', () => {
const spy = spyOn(
ng2FittextDirective,
'getStartFontSizeFromHeight'
).and.returnValue(0);
expect(ng2FittextDirective.isVisible()).toBe(false);
spy.and.returnValue(-1);
expect(ng2FittextDirective.isVisible()).toBe(false);
});
});
});