Skip to content

Commit

Permalink
refactor(*-slider)!: refactor featured-slider & case-study-slider (#303)
Browse files Browse the repository at this point in the history
* refactor(case-study-slider)!: convert to CSS modules

* refactor(featured-slider)!: convert to CSS modules

* refactor(case-study-slider): DRY up by using featured-slider

* fix(featured-slider): fix named export causing issues in swingset

* tests(*-slider): fix up tests

* chore: add changesets

* Update packages/featured-slider/package.json

Co-authored-by: Jeff Escalante <jescalan@users.noreply.github.com>

Co-authored-by: Jeff Escalante <jescalan@users.noreply.github.com>
  • Loading branch information
zchsh and jescalan committed Sep 7, 2021
1 parent b46f4e4 commit d8f4d4a
Show file tree
Hide file tree
Showing 22 changed files with 13,034 additions and 561 deletions.
11 changes: 11 additions & 0 deletions .changeset/pretty-mayflies-serve.md
@@ -0,0 +1,11 @@
---
'@hashicorp/react-featured-slider': major
---

- 💥 BREAKING CHANGE: Converts to CSS modules.
- Consumers will need to remove any `@hashicorp/react-featured-slider/style.css` imports.
- To support overrides in projects, consumers can use the `className` prop.
- For example, you can pass `className="g-featured-slider` to retain existing overrides.
- ✨ Adds named `FeaturedSlideInner` export
- Allows component to be used without external margin
- Supports reconciling duplicative code in `case-study-slider`
10 changes: 10 additions & 0 deletions .changeset/real-shirts-juggle.md
@@ -0,0 +1,10 @@
---
'@hashicorp/react-case-study-slider': major
---

- 💥 BREAKING CHANGE: Converts to CSS modules.
- Consumers will need to remove any `@hashicorp/react-case-study-slider/style.css` imports.
- To support overrides in projects, consumers can use the `className` prop.
- For example, you can pass `className="g-case-study-slider` to retain existing overrides.
- 🔨 Refactors to re-use work in `featured-slider`
- Props interface remains unchanged
10 changes: 6 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions packages/case-study-slider/Logo.js

This file was deleted.

252 changes: 31 additions & 221 deletions packages/case-study-slider/index.js
@@ -1,229 +1,39 @@
import React, { Component } from 'react'
import Logo from './Logo'
import StatusBar from './StatusBar'
import Button from '@hashicorp/react-button'
import React from 'react'
import Image from '@hashicorp/react-image'
import fragment from './fragment.graphql'

class CaseStudySlider extends Component {
constructor(props) {
super(props)
this.data = this.props.data
const timing = this.props.timing ? parseInt(this.props.timing) : 10
this.state = {
active: 0,
timing: timing,
numFrames: this.data.caseStudies.length,
measure: true,
containerWidth: 0,
import { FeaturedSliderInner } from '@hashicorp/react-featured-slider'

function CaseStudySlider({ data, dark, className }) {
const features = data.caseStudies.map((caseStudy) => {
const heading = caseStudy.headline
const content = caseStudy.description
const logoImage = caseStudy.company[dark ? 'whiteLogo' : 'monochromeLogo']
const logo = {
url: logoImage.url,
alt: logoImage.alt,
}

this.frames = []

this.handleClick = this.handleClick.bind(this)
this.throttledResize = this.throttledResize.bind(this)
this.measureFrameSize = this.measureFrameSize.bind(this)
this.resetTimer = this.resetTimer.bind(this)
this.resizeTimeout = null
}

componentDidMount() {
if (this.state.numFrames > 1) {
this.timer = setInterval(() => this.tick(), this.state.timing * 1000)
this.measureFrameSize()
const caseStudyLink =
caseStudy.caseStudyLink ||
`/resources/${caseStudy.caseStudyResource.slug}`
const link = {
text: caseStudy.buttonLabel || 'Read Case Study',
url: caseStudyLink.url,
}
window.addEventListener('resize', this.throttledResize, false)
}

componentWillUnmount() {
clearInterval(this.timer)
window.removeEventListener('resize', this.throttledResize)
}

componentDidUpdate(prevProps, prevState) {
if (this.props.data !== prevProps.data) {
this.data = this.props.data
if (this.data.caseStudies.length != prevState.numFrames) {
this.setState(
{
numFrames: this.data.caseStudies.length,
measure: true,
},
() => {
if (this.data.caseStudies.length === 1) {
clearInterval(this.timer)
window.removeEventListener('resize', this.throttledResize)
}
}
)
}
if (prevState.active > this.data.caseStudies.length - 1) {
this.setState({ active: 0 })
}
const caseStudyImage =
caseStudy.caseStudyImage || caseStudy.caseStudyResource.image
const image = {
url: caseStudyImage.url,
alt: caseStudyImage.alt || '',
}

if (this.props.timing && parseInt(this.props.timing) != prevState.timing) {
this.setState(
{
timing: parseInt(this.props.timing),
active: 0,
},
this.resetTimer
)
}
// If we're measuring on this update get the width
if (!prevState.measure && this.state.measure && this.state.numFrames > 1) {
this.measureFrameSize()
}
}

resetTimer() {
clearInterval(this.timer)
this.timer = setInterval(() => this.tick(), this.state.timing * 1000)
}

throttledResize() {
this.resizeTimeout && clearTimeout(this.resizeTimeout)
this.resizeTimeout = setTimeout(() => {
this.resizeTimeout = null
this.setState({ measure: true })
}, 250)
}

tick() {
const nextSlide =
this.state.active === this.state.numFrames - 1 ? 0 : this.state.active + 1
this.setState({ active: nextSlide })
}

handleClick(i) {
if (i === this.state.active) return
this.setState({ active: i }, this.resetTimer)
}

measureFrameSize() {
// All frames are the same size, so we measure the first one
if (this.frames[0]) {
const { width } = this.frames[0].getBoundingClientRect()
this.setState({
frameSize: width,
containerWidth: width * this.state.numFrames,
measure: false,
})
}
}

render() {
// Clear our frames array so we don't keep old refs around
this.frames = []
const { caseStudies } = this.data

const { measure, active, timing, numFrames, containerWidth } = this.state
const { dark } = this.props

const single = numFrames === 1

// Create inline styling for slide container
// If we're measuring, or have a single slide then no inline styles should be applied
const containerStyle =
measure || single
? {}
: {
width: `${containerWidth}px`,
transform: `translateX(-${(containerWidth / numFrames) * active}px`,
}

// Create inline styling to apply to each frame
// If we're measuring or have a single slide then no inline styles should be applied
const frameStyle =
measure || single ? {} : { float: 'left', width: `${100 / numFrames}%` }

return (
<div className="g-case-study-slider">
{!single && (
<div
className={`logo-bar-container${numFrames === 2 ? ' double' : ''}`}
>
{caseStudies.map(({ company }, i) => (
<div
className="logo-bar"
onClick={() => this.handleClick(i)}
key={company.monochromeLogo.url}
>
<div className="logo-container">
<Logo dark={dark} image={company} />
</div>
<StatusBar dark={dark} active={active === i} timing={timing} />
</div>
))}
</div>
)}
<div className="case-study-container">
<div className="slider-container" style={containerStyle}>
{/* React pushes a null ref the first time, so we're filtering those out. */}
{/* see https://reactjs.org/docs/refs-and-the-dom.html#caveats-with-callback-refs */}
{caseStudies.map((caseStudy) => {
const caseStudyLink =
caseStudy.caseStudyLink ||
`/resources/${caseStudy.caseStudyResource.slug}`
return (
<div
className={`slider-frame${single ? ' single' : ''}`}
style={frameStyle}
ref={(el) => el && this.frames.push(el)}
key={caseStudy.headline}
>
<div className="case-study">
<div className="feature-image">
<a href={caseStudyLink}>
<Image
{...(caseStudy.caseStudyImage ||
caseStudy.caseStudyResource.image)}
aspectRatio={single ? [16, 10, 500] : [16, 9, 500]}
/>
</a>
</div>
<div
className={`feature-content g-type-body ${
dark ? 'dark' : 'light'
}`}
>
{single && (
<div className="single-logo">
<Logo dark={dark} image={caseStudy.company} />
</div>
)}
<h3
className="g-type-display-4"
dangerouslySetInnerHTML={{
__html: caseStudy.headline,
}}
/>
<p
className="g-type-body"
dangerouslySetInnerHTML={{
__html: caseStudy.description,
}}
/>
<Button
className="g-btn"
theme={{
variant: 'secondary',
background: dark ? 'dark' : 'light',
}}
title={caseStudy.buttonLabel || 'Read Case Study'}
url={caseStudyLink}
/>
</div>
</div>
</div>
)
})}
</div>
</div>
</div>
)
}
return { heading, content, image, link, logo }
})
return (
<FeaturedSliderInner
className={className}
theme={dark === true ? 'dark' : 'light'}
features={features}
/>
)
}

CaseStudySlider.fragmentSpec = { fragment, dependencies: [Image] }
Expand Down

1 comment on commit d8f4d4a

@vercel
Copy link

@vercel vercel bot commented on d8f4d4a Sep 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.