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

Migrate Overlay component with scss #1949

Merged
merged 10 commits into from
Jan 29, 2024
9 changes: 9 additions & 0 deletions .changeset/curly-poets-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@channel.io/bezier-react": major
---

**Breaking Changes: Property updates in `Overlay` component**

- No longer support `as` and `interpolation` property. Replace any usage of `interpolation` property with appropriate `style` or `className` implementations.
- No longer support `containerInterpolation` property. Replace any usage of `containerInterpolation` property with appropriate `containerStyle` or `containerClassName` implementations.
- No longer support `wrapperTestId` property.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('Select', () => {

await user.click(body)

expect(dropdown).not.toBeVisible()
expect(dropdown).toHaveClass('hidden')
})
})
})
32 changes: 32 additions & 0 deletions packages/bezier-react/src/components/Overlay/Overlay.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.OverlayContainer {
position: fixed;
z-index: var(--z-index-overlay);
inset: 0;

width: 100%;
height: 100%;

&:where(.hidden) {
pointer-events: none;
}
}

.OverlayWrapper {
position: relative;
z-index: var(--z-index-base);
width: 100%;
height: 100%;
}

.Overlay {
position: absolute;
z-index: var(--z-index-overlay);

&:where(.hidden) {
opacity: 0;
}

&:where(.transition) {
transition: top var(--transition-s), opacity var(--transition-s);
}
}
287 changes: 54 additions & 233 deletions packages/bezier-react/src/components/Overlay/Overlay.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,18 @@ import React, {
import {
type Meta,
type StoryFn,
type StoryObj,
} from '@storybook/react'

import { styled } from '~/src/foundation'
import { isBoolean } from '~/src/utils/type'

import Overlay from './Overlay'
import type OverlayProps from './Overlay.types'
import { Button } from '~/src/components/Button'

import { Overlay } from './Overlay'
import type { OverlayProps } from './Overlay.types'
import { OverlayPosition } from './Overlay.types'

const meta: Meta<OverlayProps & {
containerWidth: number
containerHeight: number
} & {
targetTop: number
targetLeft: number
}> = {
const meta: Meta<OverlayProps> = {
component: Overlay,
argTypes: {
position: {
Expand Down Expand Up @@ -63,246 +60,70 @@ const meta: Meta<OverlayProps & {
step: 1,
},
},
containerWidth: {
control: {
type: 'range',
min: 100,
max: 1000,
step: 20,
},
},
containerHeight: {
control: {
type: 'range',
min: 100,
max: 1000,
step: 20,
},
},
targetTop: {
control: {
type: 'range',
min: 100,
max: 1000,
step: 20,
},
},
targetLeft: {
control: {
type: 'range',
min: 100,
max: 1000,
step: 20,
},
},
},
}
export default meta

interface ContainerProps {
containerWidth?: number
containerHeight?: number
}

interface TargetProps {
targetTop?: number
targetLeft?: number
}

const Container = styled.div<ContainerProps>`
position: relative;
width: ${({ containerWidth }) => containerWidth ?? 600}px;
height: ${({ containerHeight }) => containerHeight ?? 500}px;
overflow: hidden;
border: 1px solid ${props => props.foundation?.theme?.['bg-black-dark']};
`

const Wrapper = styled.div`
display: flex;
width: 100%;
height: 100%;
`

const Target = styled.div<TargetProps>`
position: absolute;
top: ${({ targetTop }) => targetTop ?? 200}px;
left: ${({ targetLeft }) => targetLeft ?? 200}px;
display: flex;
align-items: center;
justify-content: center;
width: 70px;
height: 40px;
background-color: ${props => props.foundation?.theme?.['bg-black-dark']};
border-radius: 4px;
`

const Children = styled.div`
width: 250px;
height: 150px;
overflow-y: scroll;
background-color: ${props => props.foundation?.theme?.['bg-black-dark']};
border-radius: 4px;
`

const ScrollContent = styled.div`
box-sizing: border-box;
width: 250px;
height: 350px;
padding: 5px;
color: white;
`

const OverlayTemplate: React.FC<OverlayProps & ContainerProps & TargetProps> = ({
const Template: StoryFn<OverlayProps> = ({
show: showProp,
children,
containerWidth,
containerHeight,
targetTop,
targetLeft,
...rests
...rest
}) => {
const containerRef = useRef<any>(null)
const targetRef = useRef<any>(null)
const [show, setShow] = useState(false)
const containerRef = useRef<HTMLDivElement | null>(null)
const targetRef = useRef<HTMLButtonElement | null>(null)

useEffect(function syncShow() {
if (isBoolean(showProp)) {
setShow(showProp)
}
}, [showProp])

return (
<Container
containerWidth={containerWidth}
containerHeight={containerHeight}
<div
ref={containerRef}
style={{
position: 'relative',
width: 300,
height: 300,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Wrapper>
<Target
targetTop={targetTop}
targetLeft={targetLeft}
ref={targetRef}
>
target
</Target>
<Overlay
{...rests}
target={targetRef.current}
container={containerRef.current}
>
{ children }
</Overlay>
</Wrapper>
</Container>
<Button
ref={targetRef}
text="Click me!"
onClick={() => setShow(true)}
/>

<Overlay
show={show}
style={{
width: 200,
height: 200,
backgroundColor: 'var(--bg-white-high)',
borderRadius: 'var(--radius-8)',
boxShadow: 'var(--ev-3)',
}}
target={targetRef.current}
container={containerRef.current}
onHide={() => setShow(false)}
{...rest}
>
{ children }
</Overlay>
</div>
)
}

const Template: StoryFn = (props) => (
<OverlayTemplate {...props}>
<Children>
<ScrollContent>
{
`Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industry's standard dummy text
ever since the 1500s, when an unknown printer took a galley of type
and scrambled it to make a type specimen book. It has survived not
only five centuries, but also the leap into electronic typesetting,
remaining essentially unchanged. It was popularised in the 1960s
with the release of Letraset sheets containing Lorem Ipsum passages,
and more recently with desktop publishing software like Aldus PageMaker
including versions of Lorem Ipsum.`
}
</ScrollContent>
</Children>
</OverlayTemplate>
)

export const Primary = {
export const Primary: StoryObj<OverlayProps> = {
render: Template,

args: {
show: false,
position: OverlayPosition.BottomCenter,
marginX: 0,
marginY: 0,
keepInContainer: false,
withTransition: false,
marginY: 6,
},
}

const StressTestTemplate: StoryFn<OverlayProps> = (props) => {
const targetRef = useRef<any>()
const containerRef = useRef<any>()
const [, reload] = useState(0)

useEffect(() => {
setInterval(() => {
reload((prev) => prev + 1)
}, 100)
}, [])

return (
<OverlayTemplate
{...props}
// 실제 값은 변하지 않지만, 100ms의 매 렌더링마다 참조가 변하고 있다.
containerStyle={{ opacity: 1 }}
target={targetRef.current}
container={containerRef.current}
>
<Children>
<ScrollContent>
{
`Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industry's standard dummy text
ever since the 1500s, when an unknown printer took a galley of type
and scrambled it to make a type specimen book. It has survived not
only five centuries, but also the leap into electronic typesetting,
remaining essentially unchanged. It was popularised in the 1960s
with the release of Letraset sheets containing Lorem Ipsum passages,
and more recently with desktop publishing software like Aldus PageMaker
including versions of Lorem Ipsum.`
}
</ScrollContent>
</Children>
</OverlayTemplate>
)
}

export const StressTest = {
render: StressTestTemplate,

args: {
enableClickOutside: false,
},
}

const ChangeableChildrenTemplate: StoryFn<OverlayProps> = (props) => {
const [items, setItems] = useState<number[]>([])

const addItem = React.useCallback(() => {
setItems([...items, Math.random()])
}, [items])

return (
<>
<button type="button" onClick={addItem}>Add</button>
<div>
<OverlayTemplate {...props}>
<Children>
{ items.map((item) => (
<div key={item}>
{ item }
</div>
)) }
</Children>
</OverlayTemplate>
</div>
</>
)
}

export const ChangeableChildren = {
render: ChangeableChildrenTemplate,
export default meta

args: {
show: false,
position: OverlayPosition.BottomCenter,
marginX: 0,
marginY: 0,
keepInContainer: false,
withTransition: false,
enableClickOutside: true,
},
}
Loading
Loading