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

new(Modal): Add topBar and topBarCentered support. #359

Merged
merged 2 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/core/src/components/Modal/private/Inner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export class ModalInner extends React.Component<ModalInnerProps & WithStylesProp
styles,
subtitle,
title,
topBar,
topBarCentered,
} = this.props;

const showLargeContent = large || !!image;
Expand All @@ -101,6 +103,8 @@ export class ModalInner extends React.Component<ModalInnerProps & WithStylesProp
scrollable={scrollable}
subtitle={subtitle}
title={title}
topBar={topBar}
topBarCentered={topBarCentered}
onClose={this.handleClose}
>
{children}
Expand Down
44 changes: 33 additions & 11 deletions packages/core/src/components/Modal/private/InnerContent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import IconClose from '@airbnb/lunar-icons/lib/interface/IconClose';
import Row from '../../Row';
import Text from '../../Text';
import Title from '../../Title';
import IconButton from '../../IconButton';
Expand All @@ -23,6 +24,10 @@ export type ModalInnerContentProps = {
subtitle?: React.ReactNode;
/** Dialog header title. */
title?: React.ReactNode;
/** Top bar, above dialog header. */
topBar?: React.ReactNode;
/** Whether the top bar content is centered. */
topBarCentered?: boolean;
stefhatcher marked this conversation as resolved.
Show resolved Hide resolved
/** Callback for when the Dialog should be closed. */
onClose: (event: React.MouseEvent | React.KeyboardEvent) => void;
/** Custom style sheet. */
Expand All @@ -39,38 +44,55 @@ export default function ModalInnerContent({
scrollable,
subtitle,
title,
topBar,
topBarCentered,
styleSheet,
}: ModalInnerContentProps) {
const [styles, cx] = useStyles(styleSheet ?? styleSheetInnerContent);
const theme = useTheme();
const withHeader = Boolean(title || subtitle);
const withFooter = Boolean(footer);

const closeButton = (
<IconButton onClick={onClose}>
<IconClose
accessibilityLabel={T.phrase('lunar.common.close', 'Close')}
color={theme.color.muted}
size={theme.unit * 3}
/>
</IconButton>
);

return (
<div className={cx(styles.wrapper)}>
{topBar && (
<div className={cx(styles.header, styles.topBar, topBarCentered && styles.topBar_centered)}>
<Row middleAlign after={closeButton}>
{topBar}
</Row>
</div>
)}

{withHeader && (
<header className={cx(styles.header, scrollable && styles.header_scrollable)}>
{title && <Title level={3}>{title}</Title>}
{subtitle && <Text muted>{subtitle}</Text>}
<Row after={topBar ? null : closeButton}>
<div className={cx(styles.headerInner)}>
{title && <Title level={3}>{title}</Title>}
{subtitle && <Text muted>{subtitle}</Text>}
</div>
</Row>
</header>
)}

<div className={cx(styles.close, !withHeader && styles.close_float)}>
<IconButton onClick={onClose}>
<IconClose
accessibilityLabel={T.phrase('lunar.common.close', 'Close')}
color={theme.color.muted}
size={theme.unit * 3}
/>
</IconButton>
</div>
{!withHeader && !topBar && <div className={cx(styles.close_float)}>{closeButton}</div>}

<div
className={cx(
styles.body,
!withHeader && styles.body_paddingTop,
!withFooter && styles.body_paddingBottom,
scrollable && styles.body_scrollable,
scrollable && !withHeader && styles.body_scrollable_noHeader,
small && scrollable && styles.body_scrollableSmall,
large && scrollable && styles.body_scrollableLarge,
)}
Expand Down
38 changes: 38 additions & 0 deletions packages/core/src/components/Modal/story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class ModalDemo extends React.Component<
showScrollable?: boolean;
showSubtitle?: boolean;
showTitle?: boolean;
showTopBar?: boolean;
showTopBarCentered?: boolean;
},
{ visible: boolean }
> {
Expand All @@ -36,6 +38,8 @@ class ModalDemo extends React.Component<
showScrollable,
showSubtitle,
showTitle,
showTopBar,
showTopBarCentered,
} = this.props;

return (
Expand Down Expand Up @@ -71,6 +75,8 @@ class ModalDemo extends React.Component<
small={showSmall}
subtitle={showSubtitle ? 'Modal Sub-Title' : undefined}
title={showTitle ? <LoremIpsum short /> : undefined}
topBar={showTopBar ? <Text bold>Top bar</Text> : undefined}
topBarCentered={showTopBarCentered}
onClose={this.handleClose}
>
<Text>
Expand Down Expand Up @@ -151,6 +157,22 @@ withTitleAndFooter.story = {
name: 'With title and footer',
};

export function withTopBar() {
return <ModalDemo showTopBar />;
}

withTopBar.story = {
name: 'With top bar, not centered',
};

export function withTopBarCentered() {
return <ModalDemo showTopBar showTopBarCentered />;
}

withTopBarCentered.story = {
name: 'With top bar, centered',
};

export function scrollableContent() {
return <ModalDemo showFooter showTitle showScrollable />;
}
Expand All @@ -159,6 +181,22 @@ scrollableContent.story = {
name: 'Scrollable content',
};

export function withTopBarAndFooter() {
return <ModalDemo showFooter showTopBar showTopBarCentered showScrollable />;
}

withTopBarAndFooter.story = {
name: 'With top bar, topBarCentered, footer, and scrollable',
};

export function withTitleTopBarAndFooter() {
return <ModalDemo showTitle showFooter showTopBar showScrollable />;
}

withTitleTopBarAndFooter.story = {
name: 'With top bar, title, footer, and scrollable',
};

export function scrollableContentWithoutFooter() {
return <ModalDemo showTitle showScrollable />;
}
Expand Down
28 changes: 19 additions & 9 deletions packages/core/src/components/Modal/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@ export const styleSheetInnerContent: StyleSheet = ({ color, ui, unit }) => ({
},

header: {
padding: `${unit * 3}px ${unit * 4}px ${unit * 3}px ${unit * 3}px`,
padding: `${unit * 2}px ${unit * 2}px ${unit * 3}px ${unit * 3}px`,
},

headerInner: {
paddingTop: unit,
},

header_scrollable: {
Expand All @@ -127,18 +131,18 @@ export const styleSheetInnerContent: StyleSheet = ({ color, ui, unit }) => ({
},
},

close: {
position: 'absolute',
top: unit * 2,
right: unit * 2,
zIndex: Z_INDEX_MODAL,
topBar: {
borderBottom: ui.border,
paddingBottom: unit * 2,
},

topBar_centered: {
textAlign: 'center',
paddingLeft: unit * 8, // width of close button + padding around to offset top bar content
},

close_float: {
float: 'right',
position: 'relative',
top: 0,
right: 0,
margin: `${unit * 2}px ${unit * 2}px ${unit / 2}px ${unit / 2}px`,
},

Expand Down Expand Up @@ -170,6 +174,12 @@ export const styleSheetInnerContent: StyleSheet = ({ color, ui, unit }) => ({
},
},

body_scrollable_noHeader: {
':before': {
display: 'none',
},
},

body_scrollableSmall: {
maxHeight: 160,
},
Expand Down
15 changes: 15 additions & 0 deletions packages/core/test/components/Modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,21 @@ describe('<Modal />', () => {
expect(wrapper.find('header').find(Text).prop('children')).toBe('Subtitle');
});

it('renders a top bar', () => {
const { wrapper } = setup(
{
title: null,
topBar: 'Top bar',
},
false /* isShallow */,
);

expect(wrapper.find('header')).toHaveLength(0);

const div = wrapper.find(ModalInnerContent).find('div').at(1);
expect(div.text()).toContain('Top bar');
});

it('no header if no title is provided', () => {
const { wrapper } = setup(
{
Expand Down