Skip to content

Commit

Permalink
feat(top-app-bar): add children components for composition
Browse files Browse the repository at this point in the history
  • Loading branch information
mgr34 authored and Matt Goo committed Mar 18, 2019
1 parent 95a593f commit f3454b6
Show file tree
Hide file tree
Showing 32 changed files with 1,234 additions and 254 deletions.
54 changes: 54 additions & 0 deletions packages/top-app-bar/Icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// The MIT License
//
// Copyright (c) 2019 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import * as React from 'react';
import * as classnames from 'classnames';
import {cssClasses} from './constants';


export interface IconProps<T> extends React.HTMLProps<T> {
actionItem?: boolean;
className?: string;
children: React.ReactElement<any>;
navIcon?: boolean;
}


const Icon: <T extends Element = HTMLElement>(props: IconProps<T> ) =>
React.ReactElement<HTMLElement> = ({
/* eslint-disable react/prop-types */
actionItem = false,
navIcon = false,
className,
children,
...otherProps
/* eslint-enable react/prop-types */
}) =>
React.cloneElement(children, {
...otherProps,
className: classnames(className, children.props.className, {
[cssClasses.ACTION_ITEM]: actionItem,
[cssClasses.NAV_ICON]: navIcon,
}),
});

export default Icon;
93 changes: 75 additions & 18 deletions packages/top-app-bar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,38 @@ import '@material/react-material-icon/dist/material-icon.css';

### Javascript Instantiation
```js
import TopAppBar, {TopAppBarFixedAdjust} from '@material/react-top-app-bar';
import TopAppBar, {
TopAppBarFixedAdjust,
TopAppBarIcon,
TopAppBarRow,
TopAppBarSection,
TopAppBarTitle,
} from '@material/react-top-app-bar';
import MaterialIcon from '@material/react-material-icon';

const MyComponent = () => {
return (
<div>
<TopAppBar
title='Miami, FL'
navigationIcon={<MaterialIcon
icon='menu'
onClick={() => console.log('click')}
/>}
actionItems={[<MaterialIcon key='item' icon='bookmark' />]}
/>
<TopAppBar>
<TopAppBarRow>
<TopAppBarSection align='start'>
<TopAppBarIcon navIcon tabIndex={0}>
<MaterialIcon hasRipple icon='menu' onClick={() => console.log('click')}/>
</TopAppBarIcon>
<TopAppBarTitle>Miami, FL</TopAppBarTitle>
</TopAppBarSection>
<TopAppBarSection align='end' role='toolbar'>
<TopAppBarIcon actionItem tabIndex={0}>
<MaterialIcon
aria-label="print page"
hasRipple
icon='print'
onClick={() => console.log('print')}
/>
</TopAppBarIcon>
</TopAppBarSection>
</TopAppBarRow>
</TopAppBar>
<TopAppBarFixedAdjust>
My exciting content!
</TopAppBarFixedAdjust>
Expand All @@ -56,17 +74,55 @@ Use the `<TopAppBarFixedAdjust />` component to give your content top-padding, s

Prop Name | Type | Description
--- | --- | ---
actionItems | Array | Accepts an array of elements that should be rendered to the opposite side of the title. Note that a single action item should also be passed as an array.
className | String | Classes to be applied to the root element.
title | String | The title of the Top App Bar.
navigationIcon | Element | Appears adjacent to the title. This acts as the main action of the Top App Bar.
short | Boolean | Enables short variant.
shortCollapsed | Boolean | Enables short collapsed variant.
prominent | Boolean | Enables prominent variant.
fixed | Boolean | Enables fixed variant.
dense | Boolean | Enables dense variant.
scrollTarget | React.RefObject | Sets scroll target to different DOM node (default is `window`)
tag | String | Customizes the `TopAppBar` HTML tag. (default: `<header>`)
> NOTES: As per design guidelines, prominent and dense variants should not be used with short or short collapsed. Additionally, dense variants should only be used on desktop. Additionally short top-app-bars should be used with no more than 1 action item.
#### Deprecated TopAppBar Props

The following props are deprecated since v0.11.0 and are scheduled for removal in v0.13.0.
They will still render as expected until v0.13.0, but will output a warning to the console.

Prop Name | Type | Description
--- | --- | ---
actionItems | Array | Accepts an array of elements that should be rendered to the opposite side of the title. Note that a single action item should also be passed as an array.
navigationIcon | Element | Appears adjacent to the title. This acts as the main action of the Top App Bar.
title | String | The Title of the Top App Bar.

### TopAppBarRow
Prop Name | Type | Description
--- | --- | ---
className | String | Classes to be applied to the root element.
tag | String | Customizes the `TopAppBarRow` tag. (default: `<div>`)

### TopAppBarSection
Prop Name | Type | Description
--- | --- | ---
align | Sring ('start' or 'end') | optional property tha aligns section content to either start or end of section
className | String | Classes to be applied to the root element.
tag | String | Customizes the `TopAppBarSection` tag. (default: `<section>`)
> Note: if section contains action items it is reccomended to add property role='toolbar' for a11y purposes
### TopAppBarTitle
Prop Name | Type | Description
--- | --- | ---
className | String | Classes to be applied to the root element.
tag | String | Customizes the `TopAppBarTitle` tag. (default: `<span>`)

> NOTES: As per design guidelines, prominent and dense variants should not be used with short or short collapsed. Additionally, dense variants should only be used on desktop.
### TopAppBarIcon
Prop Name | Type | Description
--- | --- | ---
className | String | Classes to be applied to the root element.
actionItem | Boolean | applies action-item class to icon
navIcon | Boolean | applies nav-icon class to icon
children | React.ReactElement<any> | can be any icon. Material Icons are reccomended
> Notes: (1) consider adding `aria-label` to actionItem's. (2) you may need to manually add ripple or tabindex to icon. (3) Short top-app-bars shold be used with no more than 1 action item.
### TopAppBarFixedAdjust

Expand All @@ -89,8 +145,9 @@ Use of [Material Icon's](../material-icon/README.md) for Action Items and Naviga
The navigation icon can be a `<a>`, `<i>`, `<svg>`, `<image>`, `<span>`, etc., but again must be wrapped with the `withRipple HOC`.

```js
<TopAppBar
navigationIcon={<i className='material-icons'>menu</i>} />
<TopAppBarIcon navIcon>
<i className='material-icons'>menu</i>
</TopAppBarIcon>
```

If you decide to use a React Component please see [Integrating with Components](./../../docs/guidelines.md#integrating-with-components).
Expand All @@ -100,13 +157,13 @@ If you decide to use a React Component please see [Integrating with Components](
Similar to the [navigation icon](#navigation-icon), it can be `<a>`, `<i>`, `<svg>`, `<image>`, `<span>`, etc., and must be wrapped with the `withRipple HOC`.

```js
<TopAppBar
actionItems={[<i className='material-icons'>bookmark</i>]} />
<TopAppBarIcon actionItem>
<i className='material-icons'>bookmark</i
</TopAppBarIcon>
```

If you decide to use a React Component please see [Integrating with Components](./../../docs/guidelines.md#integrating-with-components).

> NOTE: `actionItems` prop is expecting an array of elements.

## Sass Mixins

Expand Down
47 changes: 47 additions & 0 deletions packages/top-app-bar/Row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// The MIT License
//
// Copyright (c) 2019 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import * as React from 'react';
import * as classnames from 'classnames';
import {cssClasses} from './constants';

export interface RowProps<T> extends React.HTMLProps<T> {
className?: string;
tag?: string;
};

const Row: <T extends HTMLElement = HTMLDivElement>(props: RowProps<T>) =>
React.ReactElement<T> = ({
/* eslint-disable react/prop-types */
children,
className,
tag: Tag = 'div',
...otherProps
/* eslint-enable react/prop-types */
}) => (
// @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892
<Tag className={classnames(className, cssClasses.ROW)} {...otherProps}>
{children}
</Tag>
);

export default Row;
55 changes: 55 additions & 0 deletions packages/top-app-bar/Section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// The MIT License
//
// Copyright (c) 2019 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import * as React from 'react';
import * as classnames from 'classnames';
import {cssClasses} from './constants';

export interface SectionProps<T> extends React.HTMLProps<T> {
align?: 'start' | 'end';
className?: string;
tag?: string;
};

const Section: <T extends HTMLElement = HTMLElement>(props: SectionProps<T>) =>
React.ReactElement<T> = ({
/* eslint-disable react/prop-types */
align,
className,
children,
tag: Tag = 'section',
...otherProps
/* eslint-enable react/prop-types */
}) => (
// @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892
<Tag
className={classnames(className, cssClasses.SECTION, {
[cssClasses.SECTION_START]: align === 'start',
[cssClasses.SECTION_END]: align === 'end',
})}
{...otherProps}
>
{children}
</Tag>
);

export default Section;
47 changes: 47 additions & 0 deletions packages/top-app-bar/Title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// The MIT License
//
// Copyright (c) 2019 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import * as React from 'react';
import * as classnames from 'classnames';
import {cssClasses} from './constants';

export interface TitleProps<T> extends React.HTMLProps<T> {
className?: string;
tag?: string;
};

const Title: <T extends HTMLElement = HTMLSpanElement>(props: TitleProps<T>) =>
React.ReactElement<T> = ({
/* eslint-disable react/prop-types */
children,
className,
tag: Tag = 'span',
...otherProps
/* eslint-enable react/prop-types */
}) => (
// @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892
<Tag className={classnames(className, cssClasses.TITLE)} {...otherProps} >
{children}
</Tag>
);

export default Title;
39 changes: 39 additions & 0 deletions packages/top-app-bar/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// The MIT License
//
// Copyright (c) 2019 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
const BASE = 'mdc-top-app-bar';
const SECTION = `${BASE}__section`;
const cssClasses = {
BASE,
ROW: `${BASE}__row`,
SECTION,
SECTION_START: `${SECTION}--align-start`,
SECTION_END: `${SECTION}--align-end`,
FIXED: `${BASE}--fixed`,
SHORT: `${BASE}--short`,
SHORT_COLLAPSED: `${BASE}--short-collapsed`,
PROMINENT: `${BASE}--prominent`,
DENSE: `${BASE}--dense`,
TITLE: `${BASE}__title`,
ACTION_ITEM: `${BASE}__action-item`,
NAV_ICON: `${BASE}__navigation-icon`,
};

export {cssClasses};

0 comments on commit f3454b6

Please sign in to comment.