Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: add color type helper #625

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
37 changes: 17 additions & 20 deletions src/color/hslToColorString.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import hsl from './hsl'
import hsla from './hsla'
import PolishedError from '../internalHelpers/_errors'
import colorObjectType from '../internalHelpers/_colorObjectType'

import type { HslColor, HslaColor } from '../types/color'

Expand Down Expand Up @@ -29,28 +30,24 @@ import type { HslColor, HslaColor } from '../types/color'
* background: "rgba(179,25,25,0.72)";
* }
*/
export default function hslToColorString(color: HslColor | HslaColor | number): string {
if (
typeof color === 'object'
&& typeof color.hue === 'number'
&& typeof color.saturation === 'number'
&& typeof color.lightness === 'number'
) {
if (color.alpha && typeof color.alpha === 'number') {
return hsla({
hue: color.hue,
saturation: color.saturation,
lightness: color.lightness,
alpha: color.alpha,
})
}
export default function hslToColorString(color: HslColor | HslaColor): string {
const colorType = colorObjectType({
color,
expectedTypes: ['hsl', 'hsla'],
typeError: new PolishedError(45),
})

return hsl({
hue: color.hue,
saturation: color.saturation,
lightness: color.lightness,
if (colorType === 'hsla') {
const hslaColor: HslaColor = (color: any)
return hsla({
hue: hslaColor.hue,
saturation: hslaColor.saturation,
lightness: hslaColor.lightness,
alpha: hslaColor.alpha,
})
}

throw new PolishedError(45)
return hsl({
hue: color.hue, saturation: color.saturation, lightness: color.lightness,
})
}
31 changes: 15 additions & 16 deletions src/color/rgbToColorString.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import rgb from './rgb'
import rgba from './rgba'
import PolishedError from '../internalHelpers/_errors'
import colorObjectType from '../internalHelpers/_colorObjectType'

import type { RgbColor, RgbaColor } from '../types/color'

Expand Down Expand Up @@ -30,23 +31,21 @@ import type { RgbColor, RgbaColor } from '../types/color'
* }
*/
export default function rgbToColorString(color: RgbColor | RgbaColor): string {
if (
typeof color === 'object'
&& typeof color.red === 'number'
&& typeof color.green === 'number'
&& typeof color.blue === 'number'
) {
if (typeof color.alpha === 'number') {
return rgba({
red: color.red,
green: color.green,
blue: color.blue,
alpha: color.alpha,
})
}
const colorType = colorObjectType({
color,
expectedTypes: ['rgb', 'rgba'],
typeError: new PolishedError(46),
})

return rgb({ red: color.red, green: color.green, blue: color.blue })
if (colorType === 'rgba') {
const rgbaColor: RgbaColor = (color: any)
return rgba({
red: rgbaColor.red,
green: rgbaColor.green,
blue: rgbaColor.blue,
alpha: rgbaColor.alpha,
})
}

throw new PolishedError(46)
return rgb({ red: color.red, green: color.green, blue: color.blue })
}
37 changes: 11 additions & 26 deletions src/color/toColorString.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,7 @@ import hsla from './hsla'
import rgb from './rgb'
import rgba from './rgba'
import PolishedError from '../internalHelpers/_errors'

const isRgb = (color: Object): boolean => typeof color.red === 'number'
&& typeof color.green === 'number'
&& typeof color.blue === 'number'
&& (typeof color.alpha !== 'number' || typeof color.alpha === 'undefined')

const isRgba = (color: Object): boolean => typeof color.red === 'number'
&& typeof color.green === 'number'
&& typeof color.blue === 'number'
&& typeof color.alpha === 'number'

const isHsl = (color: Object): boolean => typeof color.hue === 'number'
&& typeof color.saturation === 'number'
&& typeof color.lightness === 'number'
&& (typeof color.alpha !== 'number' || typeof color.alpha === 'undefined')

const isHsla = (color: Object): boolean => typeof color.hue === 'number'
&& typeof color.saturation === 'number'
&& typeof color.lightness === 'number'
&& typeof color.alpha === 'number'
import colorObjectType from '../internalHelpers/_colorObjectType'

/**
* Converts a RgbColor, RgbaColor, HslColor or HslaColor object to a color string.
Expand Down Expand Up @@ -57,11 +38,15 @@ const isHsla = (color: Object): boolean => typeof color.hue === 'number'
*/

export default function toColorString(color: Object): string {
if (typeof color !== 'object') throw new PolishedError(8)
if (isRgba(color)) return rgba(color)
if (isRgb(color)) return rgb(color)
if (isHsla(color)) return hsla(color)
if (isHsl(color)) return hsl(color)
const colorType = colorObjectType({
color,
expectedTypes: ['rgb', 'rgba', 'hsl', 'hsla'],
typeError: new PolishedError(8),
})

if (colorType === 'rgba') return rgba(color)
if (colorType === 'rgb') return rgb(color)
if (colorType === 'hsla') return hsla(color)

throw new PolishedError(8)
return hsl(color)
}
32 changes: 32 additions & 0 deletions src/internalHelpers/_colorObjectType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @flow
import PolishedError from './_errors'

const TYPE_KEYS = {
rgb: ['red', 'green', 'blue'],
rgba: ['red', 'green', 'blue', 'alpha'],
hsl: ['hue', 'saturation', 'lightness'],
hsla: ['hue', 'saturation', 'lightness', 'alpha'],
}

const matchType = (color: Object, type: string) => {
const keys = TYPE_KEYS[type]
return keys.every(key => typeof color[key] === 'number')
&& (typeof color.alpha === 'number' ? keys.indexOf('alpha') > -1 : true)
}

/**
* Check color object type
* @private
*/
export default function colorObjectType({
color,
expectedTypes = ['rgb', 'rgba', 'hsl', 'hsla'],
typeError = new PolishedError(8),
}:{ color: Object, expectedTypes?: string[], typeError?: PolishedError }): string {
if (typeof color !== 'object') throw typeError

const matchedType = expectedTypes.find(type => matchType(color, type))
if (matchedType) return matchedType

throw typeError
}
95 changes: 95 additions & 0 deletions src/internalHelpers/test/_colorObjectType.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// @flow
import colorObjectType from '../_colorObjectType'
import MockedError from '../_errors'

jest.mock('../_errors', () => jest.fn().mockImplementation((errorNumber = 0) => new Error(`Error ${errorNumber}`)))

const DEFAULT_TYPE_ERROR = 'Error 8'

describe('colorObjectType', () => {
it('identifies color object type', () => {
expect(colorObjectType({
color: { red: 255, green: 205, blue: 100 },
})).toEqual('rgb')

expect(colorObjectType({
color: {
red: 255, green: 205, blue: 100, alpha: 0.5,
},
})).toEqual('rgba')

expect(colorObjectType({
color: { hue: 100, saturation: 0.5, lightness: 0.5 },
})).toEqual('hsl')

expect(colorObjectType({
color: {
hue: 100, saturation: 0.5, lightness: 0.5, alpha: 0.5,
},
})).toEqual('hsla')
})

it('throws error if color object is invalid', () => {
expect(() => colorObjectType({
color: {
not: 123, a: 123, color: 123,
},
})).toThrowError(DEFAULT_TYPE_ERROR)

expect(() => colorObjectType({
color: {
red: 255, green: 205, notBlue: 123,
},
})).toThrowError(DEFAULT_TYPE_ERROR)

expect(() => colorObjectType({
color: {},
})).toThrowError(DEFAULT_TYPE_ERROR)
})

it('throws error if color object is missing', () => {
expect(() => colorObjectType({ color: undefined })).toThrowError(DEFAULT_TYPE_ERROR)
})

it('throws error if color object is not an object', () => {
expect(() => colorObjectType({ color: 'not an object' })).toThrowError(DEFAULT_TYPE_ERROR)
})

it('throws error for unexpected color types', () => {
expect(() => colorObjectType({
color: { red: 255, green: 205, blue: 100 },
expectedTypes: ['hsl', 'hsla'],
})).toThrowError(DEFAULT_TYPE_ERROR)

expect(() => colorObjectType({
color: { hue: 100, saturation: 0.5, lightness: 0.5 },
expectedTypes: ['rgb', 'rgba'],
})).toThrowError(DEFAULT_TYPE_ERROR)

expect(() => colorObjectType({
color: { hue: 100, saturation: 0.5, lightness: 0.5 },
expectedTypes: ['hsla'],
})).toThrowError(DEFAULT_TYPE_ERROR)

expect(() => colorObjectType({
color: { red: 255, green: 205, blue: 100 },
expectedTypes: ['rgba'],
})).toThrowError(DEFAULT_TYPE_ERROR)

expect(() => colorObjectType({
color: {
red: 255, green: 205, blue: 100, alpha: 0.5,
},
expectedTypes: ['rgb'],
})).toThrowError(DEFAULT_TYPE_ERROR)
})

it('throws custom errors', () => {
expect(() => colorObjectType({
color: {
not: 123, a: 123, color: 123,
},
typeError: new MockedError(99),
})).toThrowError('Error 99')
})
})