Skip to content

Commit

Permalink
update(Sheet): Add header bar (#20)
Browse files Browse the repository at this point in the history
* Adding a header bar to sheets

* Updating test

* Making TS happy

* Removing unused includes

* Adding compact spacing & headerShadow

* Updates per @stefhatcher's comments

* Rebasing and merging to new docs

* Removing duplicated checkboxes from botched rebase

* contentCompact -> content_compact

* Update packages/core/src/components/Sheet.story.tsx

* Apply suggestions from code review

* header bar -> header

* Alpha sorting sheet props
  • Loading branch information
Mark Kahn authored and milesj committed May 3, 2019
1 parent 161fdcd commit 138f690
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 28 deletions.
47 changes: 46 additions & 1 deletion packages/core/src/components/Sheet.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ class SheetDemo extends React.Component<
{},
{
animated: boolean;
compact: boolean;
gap: boolean;
header: boolean;
headerShadow: boolean;
portal: boolean;
visible: boolean;
}
> {
state = {
animated: true,
compact: false,
gap: false,
header: false,
headerShadow: false,
portal: false,
visible: false,
};
Expand All @@ -30,16 +36,28 @@ class SheetDemo extends React.Component<
this.setState(({ animated }) => ({ animated: !animated }));
};

handleCompactChange = () => {
this.setState(({ compact }) => ({ compact: !compact }));
};

handleGapChange = () => {
this.setState(({ gap }) => ({ gap: !gap }));
};

handleHeaderChange = () => {
this.setState(({ header }) => ({ header: !header }));
};

handleHeaderShadowChange = () => {
this.setState(({ headerShadow }) => ({ headerShadow: !headerShadow }));
};

handlePortalChange = () => {
this.setState(({ portal }) => ({ portal: !portal }));
};

render() {
const { animated, gap, portal, visible } = this.state;
const { animated, compact, gap, header, headerShadow, portal, visible } = this.state;

return (
<SheetArea>
Expand All @@ -61,18 +79,45 @@ class SheetDemo extends React.Component<
/>

<CheckBox
noSpacing
name="animated"
label="Render with animation"
checked={animated}
onChange={this.handleAnimatedChange}
/>

<CheckBox
noSpacing
name="header"
label="Show header"
checked={header}
onChange={this.handleHeaderChange}
/>

<CheckBox
noSpacing
name="headerShadow"
label="Show shadow on header"
checked={headerShadow}
onChange={this.handleHeaderShadowChange}
/>

<CheckBox
name="compact"
label="Render with compact spacing"
checked={compact}
onChange={this.handleCompactChange}
/>

<Sheet
gap={gap}
noAnimation={!animated}
portal={portal}
visible={visible}
onClose={this.handleClick}
header={header && <Text>This is the header!</Text>}
headerShadow={headerShadow}
compact={compact}
>
<Spacing inner vertical={12}>
<Text>This is in a sheet!</Text>
Expand Down
72 changes: 48 additions & 24 deletions packages/core/src/components/Sheet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
import React from 'react';
import IconClose from '@airbnb/lunar-icons/lib/interface/IconClose';
import withStyles, { css, WithStylesProps } from '../../composers/withStyles';
import { Z_INDEX_PORTAL } from '../../constants';
import { ESCAPE } from '../../keys';
import focusableSelector from '../../utils/focusableSelector';
import IconButton from '../IconButton';
import toRGBA from '../../utils/toRGBA';
import FocusTrap from '../FocusTrap';
import IconButton from '../IconButton';
import Portal from '../Portal';
import Row from '../Row';
import Spacing from '../Spacing';
import T from '../Translate';
import SheetArea from './SheetArea';
import SheetContext, { Context } from './SheetContext';
import toRGBA from '../../utils/toRGBA';
import { Z_INDEX_PORTAL } from '../../constants';

export { SheetArea, SheetContext };

export type Props = {
/** Invoked when the sheet close button is pressed, or when escape is pressed when displaying a portal sheet. This function should set the `visible` prop to false. */
onClose: () => void;
/** The contents of the sheet. */
children: NonNullable<React.ReactNode>;
/** Determines if the sheet is currently visible or not. */
visible?: boolean;
/** Determines if the sheet is displayed as a full-page view that covers the entire application. */
portal?: boolean;
/** Render with reduced padding */
compact?: boolean;
/** Determines if the sheet has a side gap. */
gap?: boolean;
/** Content of the header bar */
header?: React.ReactNode;
/** Render the header area with a drop-shadow */
headerShadow?: boolean;
/** Determines if the sheet animates in/out. */
noAnimation?: boolean;
/** Invoked when the sheet close button is pressed, or when escape is pressed when displaying a portal sheet. This function should set the `visible` prop to false. */
onClose: () => void;
/** Determines if the sheet is displayed as a full-page view that covers the entire application. */
portal?: boolean;
/** Determines if the sheet is currently visible or not. */
visible?: boolean;
};

export type PrivateProps = {
Expand Down Expand Up @@ -159,13 +167,28 @@ class BaseSheet extends React.Component<Props & PrivateProps & WithStylesProps,

render() {
const { animating } = this.state;
const { gap, theme, styles, portal, visible, children } = this.props;
const {
gap,
theme,
styles,
portal,
visible,
children,
header,
compact,
headerShadow,
} = this.props;

if (!visible && !animating) {
return null;
}

const closeText = T.phrase('Close', {}, 'Close a sheet popup');
const closeIcon = (
<IconButton onClick={this.handleClose}>
<IconClose accessibilityLabel={closeText} size={3 * theme!.unit} />
</IconButton>
);

const sheetContent = (
<div
Expand Down Expand Up @@ -209,13 +232,15 @@ class BaseSheet extends React.Component<Props & PrivateProps & WithStylesProps,
gap && animating && visible && styles.sheet_slide_in,
)}
>
<div {...css(styles.closeButton, gap && styles.closeButton_rightAlign)}>
<IconButton onClick={this.handleClose}>
<IconClose accessibilityLabel={closeText} size={3 * theme!.unit} />
</IconButton>
<div {...css(headerShadow && styles.headerShadow)}>
<Spacing all={compact ? 1 : 4} bottom={0}>
<Row middleAlign before={!gap && closeIcon} after={gap && closeIcon}>
{header || ''}
</Row>
</Spacing>
</div>

<div {...css(styles.content)}>{children}</div>
<div {...css(styles.content, compact && styles.content_compact)}>{children}</div>
</div>
</div>
</FocusTrap>
Expand Down Expand Up @@ -364,21 +389,20 @@ const InternalSheet = withStyles(
height: 'auto',
},

closeButton: {
padding: 4 * unit,
paddingBottom: 0,
},

closeButton_rightAlign: {
textAlign: 'right',
},

content: {
marginTop: 2 * unit,
padding: 4 * unit,
paddingTop: 0,
flex: 1,
},

content_compact: {
padding: unit,
},

headerShadow: {
boxShadow: ui.boxShadow,
},
}),
{
passThemeProp: true,
Expand Down
6 changes: 3 additions & 3 deletions packages/core/test/components/Sheet.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Enzyme, { mount, shallow } from 'enzyme';
import React from 'react';
import ReactDOM from 'react-dom';
import Enzyme, { shallow, mount } from 'enzyme';
import { mockContextConsumer, unwrapHOCs } from '@airbnb/lunar-test-utils';
import IconButton from '../../src/components/IconButton';
import Row from '../../src/components/Row';
import Sheet from '../../src/components/Sheet';
import SheetContext from '../../src/components/Sheet/SheetContext';
import { ESCAPE } from '../../src/keys';
Expand Down Expand Up @@ -49,7 +49,7 @@ describe('<Sheet />', () => {
</Sheet>,
);

wrapper.find(IconButton).simulate('click');
shallow(wrapper.find(Row).prop('before') as React.ReactElement).simulate('click');

expect(close).toHaveBeenCalled();
});
Expand Down

0 comments on commit 138f690

Please sign in to comment.