Skip to content

Commit

Permalink
feat: validate configuration input
Browse files Browse the repository at this point in the history
Throw an error if an invalid configuration is passed. Also move range function to the utils. Add
unit tests for input validation. Better document all the configurations.
  • Loading branch information
omnibrain committed Sep 25, 2019
1 parent b30c774 commit 7acc8f1
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 11 deletions.
87 changes: 76 additions & 11 deletions src/svguitar.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { Container, QuerySelector, Element, SVG } from '@svgdotjs/svg.js'
import { isNode } from './utils'

function range(length: number, from: number = 0): number[] {
return Array.from({ length }, (_, i) => i + from)
}
import { isNode, range } from './utils'

// Chart input types (compatible with Vexchords input, see https://github.com/0xfe/vexchords)
export type SilentString = 'x'
Expand All @@ -12,22 +8,42 @@ export type Finger = [number, number | OpenString | SilentString, string?]
export type Barre = { fromString: number; toString: number; fret: number }
export type Chord = { fingers: Finger[]; barres: Barre[] }

/**
* Value for an open string (O)
*/
const OPEN: OpenString = 0

/**
* Value for a silent string (X)
*/
const SILENT: SilentString = 'x'

/**
* Possible positions of the fret label (eg. "3fr").
*/
export enum FretLabelPosition {
LEFT = 'left',
RIGHT = 'right'
}

export interface ChordSettings {
/**
* The number of strings
*/
strings: number

/**
* The number of frets
*/
frets: number
/**
* The starting fret (first fret is 1)
*/
position: number

/**
* These are the labels under the strings. Can be any string.
*/
tuning: string[]

/**
Expand Down Expand Up @@ -71,11 +87,28 @@ export interface ChordSettings {
*/
fontFamily: string

/**
* The title of the chart
*/
title?: string

/**
* Font size of the title. This is only the initial font size. If the title doesn't fit, the title
* is automatically scaled so that it fits.
*/
titleFontSize: number

/**
* Space between the title and the chart
*/
titleBottomMargin: number

/**
* Global color of the whole chart. Can be overridden with more specifig color settings such as
* @link titleColor or @link stringColor etc.
*/
color: string

titleColor?: string
stringColor?: string
fretLabelColor?: string
Expand All @@ -92,6 +125,9 @@ export interface ChordSettings {
*/
emptyStringIndicatorSize: number

/**
* Global stroke width
*/
strokeWidth: number

/**
Expand Down Expand Up @@ -163,10 +199,13 @@ export class SVGuitarChord {
this.svg.attr('preserveAspectRatio', 'xMidYMid meet').viewbox(0, 0, width, height)

// initialize settings
this.settings = { ...defaultChordSettings, ...settings }
this.settings = defaultChordSettings
this.configure(settings)
}

configure(settings: Partial<ChordSettings> = {}) {
this.sanityCheckSettings(settings)

this.settings = { ...this.settings, ...settings }

return this
Expand Down Expand Up @@ -202,6 +241,32 @@ export class SVGuitarChord {
}
}

private sanityCheckSettings(settings: Partial<ChordSettings>): void {
if (typeof settings.strings !== 'undefined' && settings.strings <= 1) {
throw new Error('Must have at least 2 strings')
}

if (typeof settings.frets !== 'undefined' && settings.frets < 0) {
throw new Error('Cannot have less than 0 frets')
}

if (typeof settings.position !== 'undefined' && settings.position < 1) {
throw new Error('Position cannot be less than 1')
}

if (typeof settings.fretSize !== 'undefined' && settings.fretSize < 0) {
throw new Error('Fret size cannot be smaller than 0')
}

if (typeof settings.nutSize !== 'undefined' && settings.nutSize < 0) {
throw new Error('Nut size cannot be smaller than 0')
}

if (typeof settings.strokeWidth !== 'undefined' && settings.strokeWidth < 0) {
throw new Error('Stroke width cannot be smaller than 0')
}
}

private drawTunings(y: number) {
// add some padding relative to the fret spacing
const padding = this.fretSpacing() / 5
Expand Down Expand Up @@ -247,9 +312,11 @@ export class SVGuitarChord {
const text = `${this.settings.position}fr`
const size = this.settings.fretLabelFontSize
const color = this.settings.fretLabelColor || this.settings.color
const nutSize = this.stringSpacing() * this.settings.nutSize

// add some padding relative to the streing spacing
const padding = this.stringSpacing() / 5
// add some padding relative to the string spacing. Also make sure the padding is at least
// 1/2 nutSize plus some padding to prevent the nut overlapping the position label.
const padding = Math.max(this.stringSpacing() / 5, nutSize / 2 + 5)

if (this.settings.fretLabelPosition === FretLabelPosition.RIGHT) {
this.svg
Expand Down Expand Up @@ -427,7 +494,7 @@ export class SVGuitarChord {
.rect(Math.abs(toString - fromString) * stringSpacing + stringSpacing / 2, nutSize)
.move(
stringXPositions[this.toArrayIndex(fromString)] - stringSpacing / 4,
fretYPositions[fret - 1] - fretSpacing + nutSize / 2
fretYPositions[fret - 1] - fretSpacing / 2 - nutSize / 2
)
.fill(nutColor)
.radius(nutSize * this.settings.barreChordRadius)
Expand Down Expand Up @@ -466,5 +533,3 @@ export class SVGuitarChord {
}
}
}

export default SVGuitarChord
4 changes: 4 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ export function isNode(): boolean {
// tslint:disable-next-line:strict-type-predicates
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null
}

export function range(length: number, from: number = 0): number[] {
return Array.from({ length }, (_, i) => i + from)
}
20 changes: 20 additions & 0 deletions test/svguitar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,24 @@ describe('SVGuitarChord', () => {

saveSvg('red', container.outerHTML)
})

test.each`
setting | value | valid
${'strings'} | ${1} | ${false}
${'strings'} | ${2} | ${true}
${'frets'} | ${0} | ${true}
${'frets'} | ${-1} | ${false}
${'position'} | ${1} | ${true}
${'position'} | ${0} | ${false}
${'fretSize'} | ${-1} | ${false}
${'nutSize'} | ${-1} | ${false}
${'strokeWidth'} | ${-1} | ${false}
`('Should correctly sanity check the settings', ({ setting, value, valid }) => {
// console.log(`Should ${valid ? 'not' : ''} thrown if ${setting} is ${value}`)
if (valid) {
expect(() => svguitar.configure({ [setting]: value })).not.toThrow()
} else {
expect(() => svguitar.configure({ [setting]: value })).toThrow()
}
})
})

0 comments on commit 7acc8f1

Please sign in to comment.