Skip to content

Commit

Permalink
react-components: support external state for grid allowing for easier…
Browse files Browse the repository at this point in the history
… data mutations
  • Loading branch information
giannif committed Apr 22, 2024
1 parent 920cd4e commit 09fa137
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 3 deletions.
16 changes: 13 additions & 3 deletions packages/react-components/src/components/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Props = {
noLink?: boolean
noResultsMessage?: string | JSX.Element
initialGifs?: IGif[]
externalGifs?: IGif[]
useTransform?: boolean
columnOffsets?: number[]
backgroundColor?: string
Expand Down Expand Up @@ -73,14 +74,17 @@ class Grid extends PureComponent<Props, State> {
unmounted: boolean = false
paginator = gifPaginator(this.props.fetchGifs, this.state.gifs)
static getDerivedStateFromProps: GetDerivedStateFromProps<Props, State> = (
{ columns, gutter, width }: Props,
{ columns, gutter, width, externalGifs }: Props,
prevState: State
) => {
const gutterOffset = gutter * (columns - 1)
const gifWidth = Math.floor((width - gutterOffset) / columns)
if (prevState.gifWidth !== gifWidth) {
return { gifWidth }
}
if (externalGifs && externalGifs !== prevState.gifs) {
return { gifs: externalGifs }
}
return null
}

Expand All @@ -100,8 +104,13 @@ class Grid extends PureComponent<Props, State> {

onFetch = debounce(Grid.fetchDebounce, async () => {
if (this.unmounted) return
const { isFetching, isLoaderVisible, gifs } = this.state
const prefetchCount = gifs.length
const { isFetching, isLoaderVisible } = this.state
const { externalGifs, fetchGifs } = this.props
const prefetchCount = (externalGifs || this.state.gifs).length
if (externalGifs) {
// reinitialize the paginator every fetch with the new external gifs
this.paginator = gifPaginator(fetchGifs, externalGifs)
}
if (!isFetching && isLoaderVisible) {
this.setState({ isFetching: true, isError: false })
let gifs
Expand Down Expand Up @@ -156,6 +165,7 @@ class Grid extends PureComponent<Props, State> {
fetchPriority,
} = this.props
const { gifWidth, gifs, isError, isDoneFetching } = this.state
// const gifs = gifState || externalGifs
const showLoader = !isDoneFetching
const isFirstLoad = gifs.length === 0
// get the height of each grid item
Expand Down
117 changes: 117 additions & 0 deletions packages/react-components/stories/grid-editable.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import styled from '@emotion/styled'
import { GiphyFetch } from '@giphy/js-fetch-api'
import { Meta, StoryObj } from '@storybook/react'
import fetchMock from 'fetch-mock'
import React, { useEffect, useState } from 'react'
import { throttle } from 'throttle-debounce'
import { GifOverlayProps, Grid as GridComponent } from '../src'
import inTestsRunner from './in-tests-runner'
import mockGifsResult from './mock-data/gifs.json'
import { IGif } from '@giphy/js-types'
import { giphyPurple } from '@giphy/colors'

const apiKey = 'sXpGFDGZs0Dv1mmNFvYaGUvYwKX0PWIh'
const gf = new GiphyFetch(apiKey)

const OverlayContainer = styled.div`
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
display: flex;
color: white;
justify-content: center;
align-items: center;
`
const ButtonContainer = styled.div`
display: flex;
flex-direction: column;
gap: 2px;
align-items: center;
h4 {
text-align: center;
}
`
const Button = styled.div`
cursor: pointer;
background: ${giphyPurple};
padding: 6px;
border-radius: 4px;
max-width: fit-content;
`

type GridProps = Partial<React.ComponentProps<typeof GridComponent>>

const Grid = ({ loader, ...other }: GridProps) => {
const [width, setWidth] = useState(innerWidth)
const [externalGifs, setExternalGifs] = useState<IGif[] | undefined>()
const Overlay = ({ gif, isHovered }: GifOverlayProps) => (
<OverlayContainer>
{isHovered && (
<ButtonContainer>
<h4>{gif.title}</h4>
<Button
onClick={() => {
setExternalGifs(externalGifs?.filter((g) => g.id !== gif.id))
}}
>
Delete Me
</Button>
<Button
onClick={() => {
const result = prompt('Edit Title', gif.title) || 'New Title'
const editGif = externalGifs?.find((g) => g.id === gif.id)
if (editGif) {
editGif.title = result
setExternalGifs(externalGifs)
}
}}
>
Edit Title
</Button>
</ButtonContainer>
)}
</OverlayContainer>
)
const onResize = throttle(500, () => setWidth(innerWidth))
useEffect(() => {
window.addEventListener('resize', onResize, false)
return () => window.removeEventListener('resize', onResize, false)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const fetchGifs = async (offset: number) => {
if (inTestsRunner()) {
fetchMock.restore().getOnce(`begin:https://api.giphy.com/v1/gifs/search`, { body: mockGifsResult })
}
const result = await gf.trending({ offset, limit: 10 })
fetchMock.restore()
setExternalGifs(result.data)
return result
}
return (
<GridComponent
width={width}
noLink
columns={3}
loader={loader}
externalGifs={externalGifs}
fetchGifs={fetchGifs}
overlay={Overlay}
onGifsFetched={(gifs) => setExternalGifs(gifs)}
{...other}
/>
)
}

const meta: Meta<typeof Grid> = {
component: Grid,
title: 'React Components/Grid/Editable',
}

export default meta

type Story = StoryObj<typeof meta>

export const EditableGrid: Story = {}

0 comments on commit 09fa137

Please sign in to comment.