├── .npmignore
├── .gitignore
├── src
├── components
│ ├── tabs
│ │ ├── Tab.js
│ │ ├── Tabs.js
│ │ ├── RoutedTabs.js
│ │ └── ScrollableRoutedTabs.js
│ ├── animatedIcons
│ │ ├── index.js
│ │ └── MenuIcon.js
│ ├── utils
│ │ ├── Icon.js
│ │ ├── Error.js
│ │ └── Loading.js
│ ├── content
│ │ ├── ContentHeaderActions.js
│ │ ├── ContentBody.js
│ │ ├── ContentHeader.js
│ │ └── Content.js
│ ├── sidebar
│ │ ├── SidebarMenu.js
│ │ ├── SidebarToggler.js
│ │ ├── SidebarHeader.js
│ │ ├── Sidebar.js
│ │ └── SidebarItem.js
│ ├── Responsive.js
│ ├── card
│ │ ├── CardHeader.js
│ │ ├── Card.js
│ │ └── CardFooter.js
│ ├── Theme.js
│ ├── ThemeProvider.js
│ └── header
│ │ ├── HeaderBrand.js
│ │ └── Header.js
├── reducers
│ └── index.js
├── index.js
└── ducks
│ └── Sidebar.js
├── .babelrc
├── .flowconfig
├── storybook
├── head.html
├── reducers.js
├── store.js
├── stories
│ ├── Snackbar.story.js
│ ├── Utils.story.js
│ ├── Content.story.js
│ ├── Card.story.js
│ ├── Header.story.js
│ ├── AnimatedIcons.story.js
│ ├── Template.story.js
│ ├── Sidebar.story.js
│ └── Tabs.story.js
└── config.js
├── README.md
├── .eslintrc
├── LICENSE.md
└── package.json
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | npm-debug.log
3 | node_modules
4 | .idea
5 | storybook-static
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | npm-debug.log
3 | node_modules
4 | .idea
5 | storybook-static
6 | lib
7 |
--------------------------------------------------------------------------------
/src/components/tabs/Tab.js:
--------------------------------------------------------------------------------
1 | import { Tab } from 'material-ui/Tabs';
2 |
3 | export default Tab;
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["flow", "es2015", "react", "stage-0"],
3 | "plugins": ["material-ui"],
4 | }
5 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | [include]
4 |
5 | [libs]
6 |
7 | [options]
8 | sharedmemory.hash_table_pow=21
9 |
--------------------------------------------------------------------------------
/src/components/animatedIcons/index.js:
--------------------------------------------------------------------------------
1 | import MenuIcon from './MenuIcon';
2 |
3 | export default {
4 | Menu: MenuIcon,
5 | };
6 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import sidebarReducer from '../ducks/Sidebar';
2 |
3 | export default {
4 | sidebar: sidebarReducer,
5 | };
6 |
--------------------------------------------------------------------------------
/storybook/head.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/storybook/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import { reducers } from '../src';
4 |
5 | export default combineReducers(reducers);
6 |
--------------------------------------------------------------------------------
/src/components/utils/Icon.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import FontIcon from 'material-ui/FontIcon';
3 |
4 | const Icon = ({ children, ...props }) =>
5 | ;
6 |
7 | export default Icon;
8 |
--------------------------------------------------------------------------------
/storybook/store.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 |
3 | import reducers from './reducers';
4 |
5 | const store = createStore(reducers);
6 |
7 | if (module.hot) {
8 | module.hot.accept(() => {
9 | const nextRootReducer = require('./reducers').default;
10 | store.replaceReducer(nextRootReducer);
11 | });
12 | }
13 |
14 | export default store;
15 |
--------------------------------------------------------------------------------
/src/components/content/ContentHeaderActions.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ContentHeaderActions = ({ children }) =>
4 |
5 | {children}
6 |
;
7 |
8 | const styles = () => ({
9 | wrapper: {
10 | display: 'flex',
11 | alignItems: 'center',
12 | },
13 | });
14 |
15 | export default ContentHeaderActions;
16 |
--------------------------------------------------------------------------------
/src/components/utils/Error.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Error = ({ error }) =>
4 |
5 |
Error
6 |
{error.message}
7 |
;
8 |
9 | const styles = () => ({
10 | wrapper: {
11 | display: 'flex',
12 | flexDirection: 'column',
13 | width: '100%',
14 | height: '100%',
15 | alignItems: 'center',
16 | justifyContent: 'center',
17 | },
18 | });
19 |
20 | export default Error;
21 |
--------------------------------------------------------------------------------
/src/components/sidebar/SidebarMenu.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { List, makeSelectable } from 'material-ui/List';
3 |
4 | const SelectableList = makeSelectable(List);
5 |
6 | const SidebarMenu = ({ children }) =>
7 |
8 |
9 | {children}
10 |
11 |
;
12 |
13 | const styles = () => ({
14 | wrapper: {
15 | marginTop: 20,
16 | },
17 | });
18 |
19 | export default SidebarMenu;
20 |
--------------------------------------------------------------------------------
/src/components/Responsive.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { getTheme } from './Theme';
3 |
4 | export const VIEWPORT: Object = {
5 | SMALL: getTheme().responsive.small || 480,
6 | MEDIUM: getTheme().responsive.medium || 768,
7 | };
8 |
9 | export const isSmall = (): boolean => window.innerWidth <= VIEWPORT.SMALL;
10 |
11 | export const isMedium = (): boolean => !isSmall() && window.innerWidth <= VIEWPORT.MEDIUM;
12 |
13 | export const isLarge = (): boolean => !isMedium() && window.innerWidth > VIEWPORT.MEDIUM;
14 |
--------------------------------------------------------------------------------
/src/components/sidebar/SidebarToggler.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { connect } from 'react-redux';
4 | import { toggleSidebar } from '../../ducks/Sidebar';
5 |
6 | const SidebarToggler = ({ actions, children }) =>
7 | actions.toggleSidebar()}>
8 | {children}
9 | ;
10 |
11 | const mapDispatchToProps = dispatch => ({
12 | actions: {
13 | toggleSidebar: () => dispatch(toggleSidebar()),
14 | },
15 | });
16 |
17 | export default connect(null, mapDispatchToProps)(SidebarToggler);
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Entria Components
2 |
3 | ## Install
4 |
5 | ```
6 | npm i @entria/components --save
7 | yarn add @entria/components
8 | ```
9 |
10 | ## Config
11 |
12 | The Redux store should know how to handle actions coming from the components:
13 |
14 | ```js
15 | import { createStore, combineReducers } from 'redux';
16 | import { reducers as entriaComponentsReducers } from '@entria/components';
17 |
18 | const rootReducer = combineReducers({
19 | // ...your other reducers here
20 | ...entriaComponentsReducers,
21 | });
22 |
23 | const store = createStore(rootReducer);
24 | ```
25 |
--------------------------------------------------------------------------------
/src/components/sidebar/SidebarHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { getTheme } from '../Theme';
4 |
5 | const SidebarHeader = ({ children }) =>
6 |
7 | {children}
8 |
;
9 |
10 | const styles = () => ({
11 | wrapper: {
12 | display: 'flex',
13 | alignItems: 'center',
14 | justifyContent: 'center',
15 | width: '100%',
16 | height: 100,
17 | padding: 20,
18 | backgroundColor: getTheme().palette.primary1Color,
19 | color: 'white',
20 | boxSizing: 'border-box',
21 | },
22 | });
23 |
24 | export default SidebarHeader;
25 |
--------------------------------------------------------------------------------
/src/components/content/ContentBody.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import styled from 'styled-components';
4 |
5 | import { VIEWPORT } from '../Responsive';
6 |
7 | const Wrapper = styled.div`
8 | padding: 20px;
9 |
10 | @media(min-width: ${VIEWPORT.MEDIUM}px) {
11 | padding: 40px;
12 | }
13 | `;
14 |
15 | const ContentBody = ({ style, children }) =>
16 |
17 | {children}
18 | ;
19 |
20 | ContentBody.defaultProps = {
21 | style: {},
22 | };
23 |
24 | ContentBody.propTypes = {
25 | style: PropTypes.object,
26 | };
27 |
28 | export default ContentBody;
29 |
--------------------------------------------------------------------------------
/src/components/card/CardHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const CardHeader = ({ style, children }) =>
5 |
6 | {children}
7 |
;
8 |
9 | const styles = () => ({
10 | wrapper: {
11 | padding: '15px 40px',
12 | marginTop: -20,
13 | marginRight: -40,
14 | marginLeft: -40,
15 | marginBottom: 20,
16 | borderBottom: '1px solid #e5e5e5',
17 | },
18 | });
19 |
20 | CardHeader.defaultProps = {
21 | style: {},
22 | };
23 |
24 | CardHeader.propTypes = {
25 | style: PropTypes.object,
26 | };
27 |
28 | export default CardHeader;
29 |
--------------------------------------------------------------------------------
/storybook/stories/Snackbar.story.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { storiesOf } from '@kadira/storybook';
4 | import loremIpsum from 'lorem-ipsum';
5 |
6 | import FlatButton from 'material-ui/FlatButton';
7 | import { Content } from '../../src';
8 |
9 | const stories = storiesOf('Snackbar', module);
10 |
11 | const ComponentWithSnackbar = (props, context) => (
12 | context.showSnackbar({ message: loremIpsum() })}
15 | />
16 | );
17 |
18 | ComponentWithSnackbar.contextTypes = {
19 | showSnackbar: PropTypes.func,
20 | };
21 |
22 | stories.add('default', () =>
23 |
24 |
25 |
26 | );
27 |
--------------------------------------------------------------------------------
/storybook/stories/Utils.story.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 |
4 | import { Error, Icon, Loading } from '../../src';
5 |
6 | const stories = storiesOf('Utils', module);
7 |
8 | stories.add('Error', () => );
9 |
10 | stories.add('Icon', () =>
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 |
19 | stories.add('Loading', () => );
20 |
21 | const styles = {
22 | iconsWrapper: {
23 | padding: 20,
24 | display: 'flex',
25 | alignItems: 'center',
26 | justifyContent: 'center',
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/card/Card.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Card as MUICard } from 'material-ui/Card';
4 |
5 | import CardFooter from './CardFooter';
6 | import CardHeader from './CardHeader';
7 |
8 | const Card = ({ style, children }) =>
9 |
10 | {React.isValidElement(children) ? children : {children}
}
11 | ;
12 |
13 | const styles = () => ({
14 | wrapper: {
15 | position: 'relative',
16 | padding: '20px 40px',
17 | width: '100%',
18 | height: '100%',
19 | },
20 | });
21 |
22 | Card.Footer = CardFooter;
23 | Card.Header = CardHeader;
24 |
25 | Card.defaultProps = {
26 | style: {},
27 | };
28 |
29 | Card.propTypes = {
30 | style: PropTypes.object,
31 | };
32 |
33 | export default Card;
34 |
--------------------------------------------------------------------------------
/src/components/card/CardFooter.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { CardActions } from 'material-ui/Card';
4 |
5 | const CardFooter = ({ style, children }) =>
6 |
7 | {React.isValidElement(children) ? children : {children}
}
8 | ;
9 |
10 | const styles = () => ({
11 | wrapper: {
12 | display: 'flex',
13 | justifyContent: 'flex-end',
14 | padding: '15px 40px',
15 | marginBottom: -20,
16 | marginRight: -40,
17 | marginLeft: -40,
18 | marginTop: 20,
19 | borderTop: '1px solid #e5e5e5',
20 | },
21 | });
22 |
23 | CardFooter.defaultProps = {
24 | style: {},
25 | };
26 |
27 | CardFooter.propTypes = {
28 | style: PropTypes.object,
29 | };
30 |
31 | export default CardFooter;
32 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export AnimatedIcons from './components/animatedIcons';
2 | export Card from './components/card/Card';
3 | export Content from './components/content/Content';
4 | export Header from './components/header/Header';
5 | export Sidebar from './components/sidebar/Sidebar';
6 |
7 | export RoutedTabs from './components/tabs/RoutedTabs';
8 | export ScrollableRoutedTabs from './components/tabs/ScrollableRoutedTabs';
9 | export Tab from './components/tabs/Tab';
10 | export Tabs from './components/tabs/Tabs';
11 |
12 | export Error from './components/utils/Error';
13 | export Icon from './components/utils/Icon';
14 | export Loading from './components/utils/Loading';
15 |
16 | export * as Responsive from './components/Responsive';
17 | export { createTheme, getTheme } from './components/Theme';
18 | export ThemeProvider from './components/ThemeProvider';
19 |
20 | export reducers from './reducers';
21 |
--------------------------------------------------------------------------------
/src/components/tabs/Tabs.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import { Tabs } from 'material-ui/Tabs';
4 |
5 | import { VIEWPORT } from '../Responsive';
6 |
7 | const Wrapper = styled.div`
8 | > div {
9 | > div:nth-child(2) > div {
10 | transition: all 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms !important;
11 | }
12 |
13 | @media(max-width: ${VIEWPORT.MEDIUM}px) {
14 | > div:nth-child(1) {
15 | flex-direction: column;
16 |
17 | button {
18 | width: 100% !important;
19 | }
20 | }
21 |
22 | > div:nth-child(2) > div {
23 | left: 0 !important;
24 | width: 100% !important;
25 | top: -${props => (props.tabsCount - props.selectedIndex - 1) * 48}px
26 | }
27 | }
28 | }
29 | `;
30 |
31 | const CustomTabs = props =>
32 |
33 |
34 | ;
35 |
36 | export default CustomTabs;
37 |
--------------------------------------------------------------------------------
/src/ducks/Sidebar.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import { isLarge } from '../components/Responsive';
3 |
4 | const LOCAL_STORAGE_SIDEBAR = 'entria-components-v1-sidebarVisible';
5 |
6 | // Actions
7 | const TOGGLE: string = 'entria-components/Sidebar/TOGGLE';
8 |
9 | // Reducer
10 | type State = {
11 | visible: boolean,
12 | };
13 |
14 | type Action = {
15 | type: string,
16 | };
17 |
18 | const initialState: State = {
19 | visible: isLarge() ? localStorage.getItem(LOCAL_STORAGE_SIDEBAR) !== 'false' : false,
20 | };
21 |
22 | export default function reducer(state: State = initialState, action: Action) {
23 | switch (action.type) {
24 | case TOGGLE: {
25 | const visible = !state.visible;
26 |
27 | localStorage.setItem(LOCAL_STORAGE_SIDEBAR, visible);
28 |
29 | return {
30 | ...state,
31 | visible,
32 | };
33 | }
34 | default:
35 | return state;
36 | }
37 | }
38 |
39 | // Action Creators
40 | export function toggleSidebar(): Action {
41 | return { type: TOGGLE };
42 | }
43 |
--------------------------------------------------------------------------------
/storybook/config.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { addDecorator, configure } from '@kadira/storybook';
3 | import { Provider } from 'react-redux';
4 | import { MemoryRouter } from 'react-router';
5 | import injectTapEventPlugin from 'react-tap-event-plugin';
6 |
7 | import { ThemeProvider, createTheme } from '../src';
8 | import store from './store';
9 |
10 | injectTapEventPlugin();
11 |
12 | const theme = createTheme({
13 | palette: {
14 | primary1Color: '#8b2756',
15 | accent1Color: '#661f42',
16 | accent2Color: '#3d192f',
17 | },
18 | });
19 |
20 | addDecorator(story =>
21 |
22 |
23 |
24 | {story()}
25 |
26 |
27 | ,
28 | );
29 |
30 | const req = require.context('./stories', true, /\.story\.js$/);
31 |
32 | function loadStories() {
33 | req.keys().forEach(filename => req(filename));
34 | }
35 |
36 | configure(loadStories, module);
37 |
--------------------------------------------------------------------------------
/src/components/tabs/RoutedTabs.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { withRouter } from 'react-router-dom';
4 | import Tabs from './Tabs';
5 | import Tab from './Tab';
6 |
7 | const RoutedTabs = ({ tabs, location, history }) => {
8 | let initialSelectedIndex = 0;
9 | tabs.forEach((tab, tabIndex) => {
10 | if (tab.route === location.pathname) {
11 | initialSelectedIndex = tabIndex;
12 | }
13 | });
14 |
15 | return (
16 |
17 | {tabs.map((tab, tabIndex) =>
18 | history.replace(tab.route)}>
19 | {tabIndex === initialSelectedIndex ? tab.component : null}
20 | ,
21 | )}
22 |
23 | );
24 | };
25 |
26 | RoutedTabs.propTypes = {
27 | tabs: PropTypes.arrayOf(
28 | PropTypes.shape({
29 | route: PropTypes.string,
30 | label: PropTypes.string,
31 | component: PropTypes.node,
32 | }),
33 | ).isRequired,
34 | };
35 |
36 | export default withRouter(RoutedTabs);
37 |
--------------------------------------------------------------------------------
/src/components/utils/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { CircularProgress } from 'material-ui';
4 |
5 | const Loading = ({ visible, background, zIndex, size, thickness }) => {
6 | const styles = {
7 | wrapper: {
8 | position: 'absolute',
9 | top: 0,
10 | right: 0,
11 | bottom: 0,
12 | left: 0,
13 | zIndex: visible ? zIndex : -1,
14 | opacity: visible ? 1 : 0,
15 | transition: 'all 0.2s',
16 | background,
17 | display: 'flex',
18 | width: '100%',
19 | height: '100%',
20 | alignItems: 'center',
21 | justifyContent: 'center',
22 | },
23 | };
24 |
25 | return (
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | Loading.defaultProps = {
33 | visible: true,
34 | background: '#FFFFFF',
35 | zIndex: 9999,
36 | };
37 |
38 | Loading.propTypes = {
39 | visible: PropTypes.bool,
40 | background: PropTypes.string,
41 | zIndex: PropTypes.number,
42 | };
43 |
44 | export default Loading;
45 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "env": {
5 | "browser": true,
6 | "jest": true
7 | },
8 | "settings": {
9 | "import/resolver": "webpack"
10 | },
11 | "rules": {
12 | "arrow-parens": 0,
13 | "react/jsx-filename-extension": 0,
14 | "import/extensions": 0,
15 | "max-len": [1, 120, 4, {"ignoreRegExpLiterals": true}],
16 | "jsx-a11y/no-static-element-interactions": 0,
17 | "react/no-unescaped-entities": 0,
18 | "react/forbid-prop-types": 0,
19 | "react/jsx-wrap-multilines": 0,
20 | "no-use-before-define": 0,
21 | "react/prop-types": 0,
22 | "react/jsx-boolean-value": 0,
23 | "default-case": 0,
24 | "react/no-children-prop": 0,
25 | "jsx-a11y/img-has-alt": 0,
26 | "comma-dangle": 0,
27 | "no-return-assign": 0,
28 | "consistent-return": 0,
29 | "class-methods-use-this": 0,
30 | "import/prefer-default-export": 0,
31 | "react/no-did-mount-set-state": 0,
32 | "prefer-const": [ 2, { "destructuring": "all" }],
33 | "react/prefer-stateless-function": [2, { "ignorePureComponents": true }]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Entria Tech Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/components/tabs/ScrollableRoutedTabs.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { withRouter } from 'react-router-dom';
4 | import { Tabs, Tab } from 'material-ui-scrollable-tabs/Tabs';
5 |
6 | const ScrollableRoutedTabs = ({ tabs, location, history }) => {
7 | let initialSelectedIndex = 0;
8 | tabs.forEach((tab, tabIndex) => {
9 | if (tab.route === location.pathname) {
10 | initialSelectedIndex = tabIndex;
11 | }
12 | });
13 |
14 | return (
15 |
16 | {tabs.map((tab, tabIndex) => (
17 | history.replace(tab.route)}>
18 | {tabIndex === initialSelectedIndex ? tab.component : null}
19 |
20 | ))}
21 |
22 | );
23 | };
24 |
25 | ScrollableRoutedTabs.propTypes = {
26 | tabs: PropTypes.arrayOf(
27 | PropTypes.shape({
28 | route: PropTypes.string,
29 | label: PropTypes.string,
30 | component: PropTypes.node,
31 | }),
32 | ).isRequired,
33 | };
34 |
35 | export default withRouter(ScrollableRoutedTabs);
36 |
--------------------------------------------------------------------------------
/src/components/content/ContentHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { getTheme } from '../Theme';
5 | import ContentHeaderActions from './ContentHeaderActions';
6 |
7 | const ContentHeader = ({ title, actions }) =>
8 |
9 |
10 | {title}
11 |
12 |
13 | {actions &&
{actions}
}
14 |
;
15 |
16 | ContentHeader.Actions = ContentHeaderActions;
17 |
18 | const styles = () => ({
19 | container: {
20 | display: 'flex',
21 | alignItems: 'center',
22 | paddingTop: 40,
23 | paddingLeft: 40,
24 | paddingRight: 40,
25 | boxSizing: 'border-box',
26 | },
27 | title: {
28 | width: '100%',
29 | margin: 0,
30 | fontSize: 28,
31 | fontWeight: 400,
32 | letterSpacing: '-0.015em',
33 | color: getTheme().palette.primary1Color,
34 | },
35 | actions: {},
36 | });
37 |
38 | ContentHeader.defaultProps = {
39 | title: null,
40 | actions: null,
41 | };
42 |
43 | ContentHeader.propTypes = {
44 | title: PropTypes.string,
45 | actions: PropTypes.node,
46 | };
47 |
48 | export default ContentHeader;
49 |
--------------------------------------------------------------------------------
/storybook/stories/Content.story.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import loremIpsum from 'lorem-ipsum';
4 |
5 | import FlatButton from 'material-ui/FlatButton';
6 | import { Content } from '../../src';
7 |
8 | const stories = storiesOf('Content', module);
9 |
10 | const HeaderActions = (
11 |
12 |
13 |
14 |
15 | );
16 |
17 | stories.add('default', () =>
18 |
19 | {loremIpsum({ count: 3, units: 'paragraphs' })}
20 | ,
21 | );
22 |
23 | stories.add('with body', () =>
24 |
25 |
26 | {loremIpsum({ count: 3, units: 'paragraphs' })}
27 |
28 | ,
29 | );
30 |
31 | stories.add('with header', () =>
32 |
33 |
34 | ,
35 | );
36 |
37 | stories.add('with body and header', () =>
38 |
39 |
40 |
41 | {loremIpsum({ count: 3, units: 'paragraphs' })}
42 |
43 | ,
44 | );
45 |
--------------------------------------------------------------------------------
/storybook/stories/Card.story.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 | import loremIpsum from 'lorem-ipsum';
4 |
5 | import FlatButton from 'material-ui/FlatButton';
6 | import { Card } from '../../src';
7 |
8 | const stories = storiesOf('Card', module);
9 |
10 | stories.addDecorator(story => {story()}
);
11 |
12 | stories.add('default', () =>
13 |
14 | {loremIpsum({ count: 3, units: 'paragraphs' })}
15 | ,
16 | );
17 |
18 | stories.add('with header', () =>
19 |
20 | {loremIpsum()}
21 | {loremIpsum({ count: 3, units: 'paragraphs' })}
22 | ,
23 | );
24 |
25 | stories.add('with footer', () =>
26 |
27 | {loremIpsum({ count: 3, units: 'paragraphs' })}
28 |
29 |
30 |
31 | ,
32 | );
33 |
34 | stories.add('with header and footer', () =>
35 |
36 | {loremIpsum()}
37 | {loremIpsum({ count: 3, units: 'paragraphs' })}
38 |
39 |
40 |
41 | ,
42 | );
43 |
44 | const styles = {
45 | wrapper: {
46 | padding: 20,
47 | height: 'auto',
48 | boxSizing: 'border-box',
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/storybook/stories/Header.story.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@kadira/storybook';
3 |
4 | import { Header, getTheme } from '../../src';
5 |
6 | const stories = storiesOf('Header', module);
7 |
8 | const BrandWithoutLogo = ;
9 |
10 | const BrandWihLogo = (
11 |
16 | );
17 |
18 | stories.add('default', () => );
19 |
20 | stories.add('styled', () =>
21 |