Added Ng2FittextDirective class unit tests #32
7 changed files with 294 additions and 63 deletions
20
.vscode/launch.json
vendored
Normal file
20
.vscode/launch.json
vendored
Normal 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}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"prettier.configPath": "./src/.prettierrc",
|
||||
"editor.formatOnSave": true
|
||||
"editor.formatOnSave": true,
|
||||
"javascript.implicitProjectConfig.experimentalDecorators": true
|
||||
}
|
||||
59
README.md
59
README.md
|
|
@ -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:
|
||||
|
||||
Input Parameters:
|
||||
| Parameter | Description | Values |
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------- |
|
||||
| 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 |
|
||||
|
||||
| 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
|
||||
Output Parameters:
|
||||
|
||||
|
||||
Output Parameters:
|
||||
|
||||
| Parameter | Description | Values |
|
||||
| --- | --- | --- |
|
||||
| fontSizeChanged | current font size | string
|
||||
| Parameter | Description | Values |
|
||||
| --------------- | ----------------- | ------ |
|
||||
| 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
|
||||
- Write tests
|
||||
- Find a better algorithm to find the font-size who fits better the container
|
||||
|
||||
License
|
||||
----
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
|
||||
**Lorenzo I.**
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
|
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
197
src/lib/directives/specs/ng2-fittext.directive.spec.ts
Normal file
197
src/lib/directives/specs/ng2-fittext.directive.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue