-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
set up sheet to delegate rendering to frame
- Loading branch information
1 parent
b760eaf
commit d4a627d
Showing
13 changed files
with
424 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
$sheet-desktop-width: rem(380px); | ||
|
||
.Sheet { | ||
position: fixed; | ||
bottom: 0; | ||
width: 100vw; | ||
height: 100%; | ||
background: color('white'); | ||
box-shadow: shadow('layer'); | ||
|
||
&.desktop { | ||
right: 0; | ||
width: $sheet-desktop-width; | ||
} | ||
|
||
&:focus { | ||
outline: 0; | ||
} | ||
} | ||
|
||
.Container { | ||
position: fixed; | ||
z-index: z-index('modal', $fixed-element-stacking-order); | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
left: 0; | ||
|
||
&.desktop { | ||
left: auto; | ||
width: $sheet-desktop-width; | ||
} | ||
} | ||
|
||
// Bottom | ||
.Bottom { | ||
will-change: transform; | ||
transition: transform duration('slow') easing('base'); | ||
transform-origin: bottom; | ||
} | ||
.enterBottom { | ||
transform: translateY(100%); | ||
} | ||
.enterBottomActive { | ||
transform: translateY(0%); | ||
} | ||
.exitBottom { | ||
transform: translateY(0%); | ||
} | ||
.exitBottomActive { | ||
transform: translateY(100%); | ||
} | ||
|
||
// Right | ||
.Right { | ||
will-change: transform; | ||
transition: transform duration('slow') easing('base'); | ||
transform-origin: right; | ||
} | ||
.enterRight { | ||
transform: translateX(100%); | ||
} | ||
.enterRightActive { | ||
transform: translateX(0%); | ||
} | ||
.exitRight { | ||
transform: translateX(0%); | ||
} | ||
.exitRightActive { | ||
transform: translateX(100%); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import * as React from 'react'; | ||
|
||
import {CSSTransition} from 'react-transition-group'; | ||
import debounce from 'lodash/debounce'; | ||
import {classNames} from '@shopify/react-utilities/styles'; | ||
|
||
import {navigationBarCollapsed} from '../../../../utilities/breakpoints'; | ||
import {Key} from '../../../../types'; | ||
import {layer, overlay, Duration} from '../../../shared'; | ||
|
||
import TrapFocus from '../../../TrapFocus'; | ||
import Portal from '../../../Portal'; | ||
import KeypressListener from '../../../KeypressListener'; | ||
import EventListener from '../../../EventListener'; | ||
|
||
import {SheetProps as Props} from '../../types'; | ||
|
||
import styles from './Sheet.scss'; | ||
|
||
export const BOTTOM_CLASS_NAMES = { | ||
enter: classNames(styles.Bottom, styles.enterBottom), | ||
enterActive: classNames(styles.Bottom, styles.enterBottomActive), | ||
exit: classNames(styles.Bottom, styles.exitBottom), | ||
exitActive: classNames(styles.Bottom, styles.exitBottomActive), | ||
}; | ||
|
||
export const RIGHT_CLASS_NAMES = { | ||
enter: classNames(styles.Right, styles.enterRight), | ||
enterActive: classNames(styles.Right, styles.enterRightActive), | ||
exit: classNames(styles.Right, styles.exitRight), | ||
exitActive: classNames(styles.Right, styles.exitRightActive), | ||
}; | ||
|
||
export interface State { | ||
mobile: boolean; | ||
} | ||
|
||
export default class Sheet extends React.PureComponent<Props, State> { | ||
state: State = { | ||
mobile: false, | ||
}; | ||
|
||
private handleResize = debounce( | ||
() => { | ||
const {mobile} = this.state; | ||
if (mobile !== isMobile()) { | ||
this.setState({mobile: !mobile}); | ||
} | ||
}, | ||
40, | ||
{leading: true, trailing: true, maxWait: 40}, | ||
); | ||
|
||
componentDidMount() { | ||
const {mobile} = this.state; | ||
if (mobile !== isMobile()) { | ||
this.setState({mobile: !mobile}); | ||
} | ||
} | ||
|
||
render() { | ||
const {children, open, onClose} = this.props; | ||
const {mobile} = this.state; | ||
|
||
const sheetClassName = classNames(styles.Sheet, !mobile && styles.desktop); | ||
|
||
const containerClassName = classNames( | ||
styles.Container, | ||
!mobile && styles.desktop, | ||
); | ||
|
||
function Container() { | ||
return ( | ||
<div className={containerClassName} {...layer.props} {...overlay.props}> | ||
<TrapFocus trapping={open}> | ||
<div role="dialog" tabIndex={-1} className={sheetClassName}> | ||
{children} | ||
</div> | ||
</TrapFocus> | ||
</div> | ||
); | ||
} | ||
|
||
const sharedTransitionProps = { | ||
timeout: Duration.Slow, | ||
in: open, | ||
mountOnEnter: true, | ||
unmountOnExit: true, | ||
}; | ||
|
||
const finalTransitionProps = { | ||
classNames: mobile ? BOTTOM_CLASS_NAMES : RIGHT_CLASS_NAMES, | ||
...sharedTransitionProps, | ||
}; | ||
|
||
return ( | ||
<Portal idPrefix="sheet"> | ||
<CSSTransition {...finalTransitionProps}> | ||
<Container /> | ||
</CSSTransition> | ||
<KeypressListener keyCode={Key.Escape} handler={onClose} /> | ||
<EventListener event="resize" handler={this.handleResize} /> | ||
</Portal> | ||
); | ||
} | ||
} | ||
|
||
export function isMobile(): boolean { | ||
return navigationBarCollapsed().matches; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import Sheet from './Sheet'; | ||
|
||
export {isMobile} from './Sheet'; | ||
export default Sheet; |
70 changes: 70 additions & 0 deletions
70
src/components/Frame/components/Sheet/tests/Sheet.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import * as React from 'react'; | ||
|
||
import {CSSTransition} from 'react-transition-group'; | ||
import {noop} from '@shopify/javascript-utilities/other'; | ||
import {matchMedia} from '@shopify/jest-dom-mocks'; | ||
import {mountWithAppProvider} from 'test-utilities'; | ||
|
||
import Sheet, {isMobile, BOTTOM_CLASS_NAMES, RIGHT_CLASS_NAMES} from '../Sheet'; | ||
|
||
describe('<Sheet />', () => { | ||
beforeEach(() => { | ||
matchMedia.mock(); | ||
}); | ||
|
||
afterEach(() => { | ||
matchMedia.restore(); | ||
}); | ||
|
||
const mockProps = { | ||
open: true, | ||
onClose: noop, | ||
}; | ||
|
||
it('renders its children', () => { | ||
const children = <div>Content</div>; | ||
|
||
const sheet = mountWithAppProvider( | ||
<Sheet {...mockProps}>{children}</Sheet>, | ||
); | ||
|
||
expect(sheet.find(children)).not.toBeNull(); | ||
}); | ||
|
||
it('renders a css transition component with bottom class names at mobile sizes', () => { | ||
matchMedia.setMedia(() => ({matches: true})); | ||
|
||
const sheet = mountWithAppProvider( | ||
<Sheet {...mockProps}> | ||
<div>Content</div> | ||
</Sheet>, | ||
); | ||
|
||
expect(sheet.find(CSSTransition).props().classNames).toEqual( | ||
BOTTOM_CLASS_NAMES, | ||
); | ||
}); | ||
|
||
it('renders a css transition component with right class names at desktop sizes', () => { | ||
const sheet = mountWithAppProvider( | ||
<Sheet {...mockProps}> | ||
<div>Content</div> | ||
</Sheet>, | ||
); | ||
|
||
expect(sheet.find(CSSTransition).props().classNames).toEqual( | ||
RIGHT_CLASS_NAMES, | ||
); | ||
}); | ||
|
||
describe('isMobile', () => { | ||
it('returns false by default', () => { | ||
expect(isMobile()).toBe(false); | ||
}); | ||
|
||
it('returns true at mobile sizes', () => { | ||
matchMedia.setMedia(() => ({matches: true})); | ||
expect(isMobile()).toBe(true); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.