Skip to content

Commit

Permalink
Merge pull request #1026 from primer/VanAnderson/migrate-details-to-TSX
Browse files Browse the repository at this point in the history
Migrate Details component to TSX
  • Loading branch information
colebemis authored Feb 11, 2021
2 parents b8b60e7 + c272660 commit c976411
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/hip-ads-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/components": patch
---

Migrate `Details` to TypeScript
13 changes: 10 additions & 3 deletions src/Details.js → src/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import styled from 'styled-components'
import {COMMON} from './constants'
import theme from './theme'
import sx from './sx'
import {COMMON, SystemCommonProps} from './constants'
import {ComponentProps} from './utils/types'
import sx, {SxProp} from './sx'

const Details = styled.details`
type StyledDetailsProps = {
open?: boolean
} & SystemCommonProps &
SxProp

const Details = styled.details<StyledDetailsProps>`
& > summary {
list-style: none;
}
Expand All @@ -26,4 +32,5 @@ Details.propTypes = {
...sx.propTypes
}

export type DetailsProps = ComponentProps<typeof Details>
export default Details
13 changes: 7 additions & 6 deletions src/__tests__/Details.js → src/__tests__/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import {Details, useDetails, Button, ButtonPrimary, Box} from '..'
import {ButtonProps} from '../Button/Button'
import {mount, behavesAsComponent, checkExports} from '../utils/testing'
import {COMMON} from '../constants'
import {render as HTMLRender, cleanup} from '@testing-library/react'
Expand All @@ -25,7 +26,7 @@ describe('Details', () => {
const Component = () => {
const {getDetailsProps} = useDetails({closeOnOutsideClick: true})
return (
<Details {...getDetailsProps}>
<Details {...getDetailsProps()}>
<summary>hi</summary>
</Details>
)
Expand All @@ -46,7 +47,7 @@ describe('Details', () => {
const Component = () => {
const {getDetailsProps, open} = useDetails({closeOnOutsideClick: true})
return (
<Details {...getDetailsProps}>
<Details {...getDetailsProps()}>
<Button as="summary">{open ? 'Open' : 'Closed'}</Button>
</Details>
)
Expand All @@ -66,11 +67,11 @@ describe('Details', () => {
})

it('Can manipulate state with setOpen', () => {
const CloseButton = props => <Button {...props} />
const CloseButton = (props: ButtonProps) => <Button {...props} />
const Component = () => {
const {getDetailsProps, setOpen, open} = useDetails({closeOnOutsideClick: true, defaultOpen: true})
return (
<Details {...getDetailsProps}>
<Details {...getDetailsProps()}>
<Button as="summary">{open ? 'Open' : 'Closed'}</Button>
<CloseButton onClick={() => setOpen(false)} />
</Details>
Expand All @@ -92,9 +93,9 @@ describe('Details', () => {

it('Does not toggle when you click inside', () => {
const Component = () => {
const {getDetailsProps} = useDetails({closeOnOutsideClick: true, defaultOpen: true})
const {getDetailsProps, open} = useDetails({closeOnOutsideClick: true, defaultOpen: true})
return (
<Details {...getDetailsProps}>
<Details {...getDetailsProps()}>
<Button as="summary">{open ? 'Open' : 'Closed'}</Button>
<Box>
<ButtonPrimary>hi</ButtonPrimary>
Expand Down
File renamed without changes.
21 changes: 16 additions & 5 deletions src/hooks/useDetails.js → src/hooks/useDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import {useCallback, useEffect, useState, useRef} from 'react'

function useDetails({ref, closeOnOutsideClick, defaultOpen, onClickOutside} = {}) {
type UseDetailsParameters = {
ref?: React.RefObject<HTMLElement>
closeOnOutsideClick?: boolean
defaultOpen?: boolean
onClickOutside?: (event: MouseEvent) => void
}

function useDetails({ref, closeOnOutsideClick, defaultOpen, onClickOutside}: UseDetailsParameters) {
const [open, setOpen] = useState(defaultOpen)
const backupRef = useRef(null)
const customRef = ref ?? backupRef

const onClickOutsideInternal = useCallback(
event => {
if (event.target.closest('details') !== customRef.current) {
(event: MouseEvent) => {
const {current} = customRef
const eventTarget = event.target as HTMLElement
const closest = eventTarget.closest('details') as HTMLDetailsElement
if (closest !== current) {
onClickOutside && onClickOutside(event)
if (!event.defaultPrevented) {
setOpen(false)
Expand All @@ -27,9 +37,10 @@ function useDetails({ref, closeOnOutsideClick, defaultOpen, onClickOutside} = {}
}
}, [open, closeOnOutsideClick, onClickOutsideInternal])

const handleToggle = e => {
const handleToggle = (e: React.SyntheticEvent<HTMLElement, Event>) => {
if (!e.defaultPrevented) {
setOpen(e.target.open)
const eventTarget = e.target as HTMLDetailsElement
setOpen(eventTarget.open)
}
}

Expand Down

1 comment on commit c976411

@vercel
Copy link

@vercel vercel bot commented on c976411 Feb 11, 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.