Skip to content

Commit

Permalink
refactor(image): Use inline clip-path to do circular masking
Browse files Browse the repository at this point in the history
  • Loading branch information
codedavinci committed Nov 29, 2017
1 parent ee574fa commit 9b8e2fc
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 87 deletions.
4 changes: 4 additions & 0 deletions src/components/Borders.modules.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@
.rounded {
border-radius: 4px;
}

.circular {
border-radius: 50%;
}
70 changes: 19 additions & 51 deletions src/components/Image/Image.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,69 +6,42 @@ import joinClasses from '../../utils/joinClassNames'
import safeRest from '../../utils/safeRest'

import styles from './Image.modules.scss'
import borderStyles from '../Borders.modules.scss'

/**
* An image is a graphic representation of something.
*/

const ImageCicularlyMasked = ({ y, x, children }) => {
const smaller = Math.min(x, y)
// const perfectRadius = Math.sqrt(smaller / 100) * 90

const perfectRadius = smaller / 2
const getClipPathStyles = (width, height) => {
const radius = Math.min(width, height) / 2

const getStyle = () => {
const css = {}
const clipPath = `circle(${radius}px at center)`

if (x === y) {
css.borderRadius = '50%'
}

if (x > y || x < y) {
css.clipPath = `circle(${perfectRadius}px at center)`
}
return { clipPath }
}

return {
style: { ...css },
}
}
/**
* An image is a graphic representation of something.
*/
const Image = ({ src, width, height, alt, rounded, ...rest }) => {
const isCircle = rounded === 'circle'
const isSquare = width === height

return (
<div width={smaller} height={smaller} className={styles.wrapper}>
{React.cloneElement(children, getStyle())}
</div>
const classes = joinClasses(
styles.fluid,
rounded === 'corners' && borderStyles.rounded,
isCircle && isSquare && borderStyles.circular
)
}

ImageCicularlyMasked.propTypes = {
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
children: PropTypes.node.isRequired,
}

const Image = ({ src, width, height, alt, rounded, fluid, ...rest }) => {
const isCircle = rounded === 'circle'
const classes = joinClasses(fluid && styles.fluid, isCircle ? styles.circle : styles.corners)
const style = isCircle && !isSquare ? getClipPathStyles(width, height) : undefined

const hidratedImage = (
return (
<img
{...safeRest(rest)}
src={src}
width={width}
height={height}
alt={alt}
className={classes}
style={style}
/>
)

if (isCircle) {
return (
<ImageCicularlyMasked y={height} x={width}>
{hidratedImage}
</ImageCicularlyMasked>
)
}
return hidratedImage
}

Image.propTypes = {
Expand All @@ -92,14 +65,9 @@ Image.propTypes = {
* Makes the border of the image rounded (4pxs)
*/
rounded: PropTypes.oneOf(['circle', 'corners']),
/**
* Makes the image be fluid
*/
fluid: PropTypes.bool,
}

Image.defaultProps = {
fluid: false,
rounded: undefined,
}

Expand Down
15 changes: 0 additions & 15 deletions src/components/Image/Image.modules.scss
Original file line number Diff line number Diff line change
@@ -1,19 +1,4 @@
.corners {
border-radius: 4px;
}

.fluid {
height: auto;
max-width: 100%;
}

.circle {
clip-path: circle(100px at center);
}

.wrapper {
display: inline-block;
position: relative;
overflow: hidden;
border-radius: 50%;
}
37 changes: 16 additions & 21 deletions src/components/Image/__tests__/Image.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,34 @@ describe('Image', () => {
height: 200,
}

const dummyClass = 'dummy-class'
const doShallow = (newProps = {}) => shallow(<Image {...defaultProps} {...newProps} />)

it.skip('renders', () => {
it('renders', () => {
const image = doShallow({})

expect(image).toMatchSnapshot()
})

it('returns the image rounded', () => {
it('rounds 4 corners', () => {
const image = doShallow({ rounded: 'corners' })
expect(image).toHaveClassName('corners')
})

it.skip('returns the circular image', () => {
const image = doShallow({ rounded: 'circle' })
expect(image).toHaveClassName('circle')
expect(image).toHaveClassName('rounded')
})

it('should set width and height', () => {
const image = doShallow({ width: 300, height: 300 })
expect(image).toHaveProp('width', 300)
expect(image).toHaveProp('height', 300)
})
describe('circular masking', () => {
it('applies 50% border radius when the image is square', () => {
const image = doShallow({ rounded: 'circle', width: 50, height: 50 })

it('should not be able to pass custom className', () => {
const image = doShallow({ className: dummyClass })
expect(image).not.toHaveClassName(dummyClass)
})
expect(image).toHaveClassName('circular')
})

it('applies a circular clip when the image is not a square', () => {
let image = doShallow({ rounded: 'circle', width: 50, height: 60 })
expect(image).toHaveStyle('clipPath', 'circle(25px at center)')

it('must render fluidly, it must be boolean', () => {
const image = doShallow({ fluid: true })
expect(image).toHaveClassName('fluid')
image = doShallow({ rounded: 'circle', width: 60, height: 50 })
expect(image).toHaveStyle('clipPath', 'circle(25px at center)')
})
})

it('passed additional attributes to image element', () => {
Expand All @@ -55,6 +50,6 @@ describe('Image', () => {
it('does not allow custom CSS', () => {
const image = doShallow({ className: 'my-custom-class', style: { color: 'purple' } })
expect(image).not.toHaveProp('className', 'my-custom-class')
expect(image).not.toHaveProp('style')
expect(image).not.toHaveStyle('color', 'purple')
})
})

0 comments on commit 9b8e2fc

Please sign in to comment.