Migrate from a handrolled interface for building examples to using React Components directly. (#10259)

* Migrate from a handrolled interface for building examples to using React Components directly.
* Wrap components with containers instead of passing down state and actions via context.
This commit is contained in:
CJ Cenizal 2017-02-17 16:13:39 -08:00 committed by GitHub
parent 2fb2b4ca6c
commit e570afcca8
58 changed files with 1957 additions and 930 deletions

View file

@ -5,9 +5,10 @@ export default keyMirror({
// Source code viewer actions
OPEN_CODE_VIEWER: null,
UPDATE_CODE_VIEWER: null,
CLOSE_CODE_VIEWER: null,
REGISTER_CODE: null,
UNREGISTER_CODE: null,
// Example nav actions
REGISTER_SECTION: null,
UNREGISTER_SECTION: null,
});

View file

@ -1,30 +0,0 @@
import ActionTypes from '../action_types';
export default {
openCodeViewer: slug => ({
type: ActionTypes.OPEN_CODE_VIEWER,
slug,
}),
updateCodeViewer: slug => ({
type: ActionTypes.UPDATE_CODE_VIEWER,
slug,
}),
closeCodeViewer: () => ({
type: ActionTypes.CLOSE_CODE_VIEWER,
}),
registerCode: code => ({
type: ActionTypes.REGISTER_CODE,
code,
}),
unregisterCode: code => ({
type: ActionTypes.UNREGISTER_CODE,
code
}),
};

View file

@ -0,0 +1,10 @@
import ActionTypes from './action_types';
export const openCodeViewer = source => ({
type: ActionTypes.OPEN_CODE_VIEWER,
source,
});
export const closeCodeViewer = () => ({
type: ActionTypes.CLOSE_CODE_VIEWER,
});

View file

@ -0,0 +1,12 @@
import ActionTypes from './action_types';
export const registerSection = (id, name) => ({
type: ActionTypes.REGISTER_SECTION,
id,
name,
});
export const unregisterSection = id => ({
type: ActionTypes.UNREGISTER_SECTION,
id,
});

View file

@ -1,4 +1,9 @@
export {
openCodeViewer,
closeCodeViewer,
} from './code_viewer_actions';
export {
default as CodeViewerActions,
} from './code_viewer/code_viewer_actions';
registerSection,
unregisterSection,
} from './example_nav_actions';

View file

@ -0,0 +1,6 @@
.guideCode {
padding: 2px 4px;
font-family: 'Ubuntu Mono', monospace;
background-color: #e8e8e8;
color: #565656;
}

View file

@ -0,0 +1,5 @@
import React from 'react';
export const GuideCode = props => (
<code className="guideCode">{props.children}</code>
);

View file

@ -1,10 +1,3 @@
.guideCode {
padding: 2px 4px;
font-family: 'Ubuntu Mono', monospace;
background-color: #e8e8e8;
color: #565656;
}
.guideCodeViewer {
position: fixed;
top: $guideNavHeight;

View file

@ -1,4 +1,3 @@
import React, {
Component,
PropTypes,
@ -7,8 +6,7 @@ import React, {
import classNames from 'classnames';
import hljs from 'highlight.js';
export default class GuideCodeViewer extends Component {
export class GuideCodeViewer extends Component {
constructor(props) {
super(props);
}
@ -23,19 +21,26 @@ export default class GuideCodeViewer extends Component {
}
}
renderSection(title, content, codeClass) {
if (content) {
renderSection(type, code) {
const typeToCodeClassMap = {
JavaScript: 'javascript',
HTML: 'html',
};
const codeClass = typeToCodeClassMap[type];
if (code) {
return (
<div className="guideCodeViewer__section">
<div className="guideCodeViewer__section" key={type}>
<div className="guideCodeViewer__title">
{title}
{type}
</div>
<pre className="guideCodeViewer__content">
<code
ref={codeClass}
className={codeClass}
>
{content}
{code}
</code>
</pre>
</div>
@ -48,6 +53,10 @@ export default class GuideCodeViewer extends Component {
'is-code-viewer-open': this.props.isOpen,
});
const codeSections = this.props.source.map(sourceObject => (
this.renderSection(sourceObject.type, sourceObject.code)
));
return (
<div className={classes}>
<div className="guideCodeViewer__header">
@ -59,18 +68,15 @@ export default class GuideCodeViewer extends Component {
onClick={this.props.onClose}
/>
{this.renderSection('HTML', this.props.html, 'html')}
{this.renderSection('JavaScript', this.props.js, 'javascript')}
{codeSections}
</div>
);
}
}
GuideCodeViewer.propTypes = {
isOpen: PropTypes.bool,
onClose: PropTypes.func,
title: PropTypes.string,
html: PropTypes.string,
js: PropTypes.string,
source: PropTypes.array,
};

View file

@ -0,0 +1,11 @@
$guideVerticalRhythm: 20px;
@import "guide_code/guide_code";
@import "guide_code_viewer/guide_code_viewer";
@import "guide_demo/guide_demo";
@import "guide_link/guide_link";
@import "guide_nav/guide_nav";
@import "guide_page/guide_page";
@import "guide_page_side_nav/guide_page_side_nav";
@import "guide_section/guide_section";
@import "guide_text/guide_text";

View file

@ -0,0 +1,3 @@
.guideDemo {
margin-top: $guideVerticalRhythm;
}

View file

@ -0,0 +1,60 @@
import React, {
Component,
PropTypes,
} from 'react';
import classNames from 'classnames';
export class GuideDemo extends Component {
componentDidMount() {
this.update();
}
componentDidUpdate() {
this.update();
}
update() {
// Inject HTML
this.content.innerHTML = this.props.html;
// Inject JS
const js = document.createElement('script');
js.type = 'text/javascript';
js.innerHTML = this.props.js;
this.content.appendChild(js);
// Inject CSS
const css = document.createElement('style');
css.innerHTML = this.props.css;
this.content.appendChild(css);
}
render() {
const classes = classNames('guideDemo', {
'theme-dark': this.props.isDarkTheme,
});
return (
<div className={classes}>
<div
ref={c => (this.content = c)}
/>
</div>
);
}
}
GuideDemo.propTypes = {
js: PropTypes.string.isRequired,
html: PropTypes.string.isRequired,
css: PropTypes.string.isRequired,
isDarkTheme: PropTypes.bool.isRequired,
};
GuideDemo.defaultProps = {
js: '',
html: '',
css: '',
isDarkTheme: false,
};

View file

@ -1,3 +0,0 @@
.guideExample {
}

View file

@ -1,78 +0,0 @@
import React, {
Component,
PropTypes,
} from 'react';
import {
Slugify,
} from '../../services';
import {
GuidePage,
GuidePageSection,
} from '../';
export default class GuideExample extends Component {
constructor(props, sections) {
super(props);
this.sections = sections.map(section => Object.assign({}, section, {
slug: Slugify.one(section.title),
}));
}
componentWillMount() {
this.sections.forEach(section => {
this.context.registerCode(section);
});
}
componentWillUnmount() {
this.sections.forEach(section => {
this.context.unregisterCode(section);
});
}
renderSections() {
return this.sections.map((section, index) => (
<GuidePageSection
key={index}
title={section.title}
slug={section.slug}
html={section.html}
js={section.js}
hasDarkTheme={section.hasDarkTheme}
>
{section.description}
</GuidePageSection>
));
}
render() {
return (
<GuidePage
title={this.props.route.name}
>
{this.renderSections()}
</GuidePage>
);
}
}
GuideExample.contextTypes = {
registerCode: PropTypes.func,
unregisterCode: PropTypes.func,
};
GuideExample.propTypes = {
route: PropTypes.object.isRequired,
sections: PropTypes.arrayOf(React.PropTypes.shape({
title: React.PropTypes.string.isRequired,
description: React.PropTypes.any,
html: React.PropTypes.string.isRequired,
js: React.PropTypes.string,
hasDarkTheme: React.PropTypes.bool,
})),
};

View file

@ -0,0 +1,11 @@
import React from 'react';
export const GuideLink = props => (
<a
href={props.href}
target={props.target}
className="guideLink"
>
{props.children}
</a>
);

View file

@ -1,4 +1,3 @@
import React, {
PropTypes,
} from 'react';
@ -9,7 +8,7 @@ import {
import classNames from 'classnames';
const GuideNav = props => {
export const GuideNav = props => {
const classes = classNames('guideNav', {
'is-guide-nav-open': props.isNavOpen,
});
@ -64,4 +63,3 @@ GuideNav.propTypes = {
items: PropTypes.array,
};
export default GuideNav;

View file

@ -1,4 +1,3 @@
import React, {
Component,
PropTypes,
@ -13,34 +12,30 @@ import {
GuidePageSideNavItem,
} from '../';
export default class GuidePage extends Component {
export class GuidePage extends Component {
constructor(props) {
super(props);
this.onClickLink = this.onClickLink.bind(this);
}
onClickLink(slug) {
onClickLink(id) {
// Scroll to element.
$('html, body').animate({
scrollTop: $(`#${slug}`).offset().top - 100
scrollTop: $(`#${id}`).offset().top - 100
}, 250);
// Load in code viewer.
this.context.updateCodeViewer(slug);
}
renderSideNavMenu() {
// Traverse children and build side nav from it.
return this.props.children.map((section, index) => {
// Traverse sections and build side nav from it.
return this.props.sections.map((section, index) => {
return (
<GuidePageSideNavItem
key={index}
slug={Slugify.one(section.props.title)}
id={section.id}
onClick={this.onClickLink}
>
{section.props.title}
{section.name}
</GuidePageSideNavItem>
);
});
@ -59,14 +54,10 @@ export default class GuidePage extends Component {
</div>
);
}
}
GuidePage.contextTypes = {
updateCodeViewer: PropTypes.func,
};
GuidePage.propTypes = {
children: PropTypes.any,
title: PropTypes.string,
sections: PropTypes.array,
};

View file

@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import { getSections } from '../../store';
import { GuidePage } from './guide_page.jsx';
const mapStateToProps = state => ({
sections: getSections(state),
});
export const GuidePageContainer = connect(mapStateToProps)(GuidePage);

View file

@ -1,139 +0,0 @@
import React, {
Component,
PropTypes,
} from 'react';
import classNames from 'classnames';
import {
JsInjector,
} from '../../services';
export default class GuidePageSection extends Component {
constructor(props) {
super(props);
this.onClickSource = this.onClickSource.bind(this);
}
componentDidMount() {
// NOTE: This will cause a race condition if a GuidePage adds and removes
// GuidePageSection instances during its lifetime (e.g. if a user is allowed
// to click "add" and "delete" buttons to add and remove GuidePageSections).
//
// In such a race condition, we could end up with GuidePageSections with
// identical id values.
//
// As long as all GuidePageSection instances are added when a GuidePage
// is instantiated, and then they're all removed when a GuidePage is
// removed, we won't encounter this race condition.
if (this.props.js) {
this.scriptId = `${GuidePageSection.SCRIPT_ID}${GuidePageSection.count}`;
GuidePageSection.count++;
// JS injection must occur _after_ the component has been mounted, so
// the component DOM is available for the JS to manipulate.
JsInjector.inject(this.props.js, this.scriptId);
}
function trimChildren(node) {
if (node.children.length > 0) {
[...node.children].forEach(trimChildren);
return;
}
node.textContent = node.textContent.trim();
}
trimChildren(this.refs.html);
if (this.refs.htmlDarkTheme) {
trimChildren(this.refs.htmlDarkTheme);
}
}
componentWillUnmount() {
JsInjector.remove(this.scriptId);
GuidePageSection.count--;
}
onClickSource() {
this.context.openCodeViewer(this.props.slug);
}
render() {
const exampleClasses = classNames('guidePageSection__example', {
'guidePageSection__example--standalone': !this.props.children,
});
let description;
if (this.props.children) {
description = (
<div className="guidePageSection__description">
{this.props.children}
</div>
);
}
let darkThemeExample;
if (this.props.hasDarkTheme) {
darkThemeExample = (
<div
ref="htmlDarkTheme"
className={`${exampleClasses} theme-dark`}
dangerouslySetInnerHTML={{ __html: this.props.html }}
/>
);
} else {
darkThemeExample = (
<div className="guideWarning">This component is missing Dark Theme variations.</div>
);
}
return (
<div
id={this.props.slug}
className="guidePageSection"
>
<div className="guidePageSection__header">
<div className="guidePageSection__title">
{this.props.title}
</div>
<div
className="guidePageSection__sourceButton fa fa-code"
onClick={this.onClickSource}
/>
</div>
{description}
<div
ref="html"
className={exampleClasses}
dangerouslySetInnerHTML={{ __html: this.props.html }}
/>
{darkThemeExample}
</div>
);
}
}
GuidePageSection.count = 0;
GuidePageSection.SCRIPT_ID = 'EXAMPLE_SCRIPT';
GuidePageSection.contextTypes = {
openCodeViewer: PropTypes.func,
};
GuidePageSection.propTypes = {
title: PropTypes.string,
slug: PropTypes.string,
html: PropTypes.string,
js: PropTypes.string,
children: PropTypes.any,
hasDarkTheme: PropTypes.bool,
};

View file

@ -1,10 +1,9 @@
import React, {
Component,
PropTypes,
} from 'react';
export default class GuidePageSideNav extends Component {
export class GuidePageSideNav extends Component {
constructor(props) {
super(props);

View file

@ -1,10 +1,9 @@
import React, {
Component,
PropTypes,
} from 'react';
export default class GuidePageSideNavItem extends Component {
export class GuidePageSideNavItem extends Component {
constructor(props) {
super(props);
@ -13,7 +12,7 @@ export default class GuidePageSideNavItem extends Component {
}
onClick() {
this.props.onClick(this.props.slug);
this.props.onClick(this.props.id);
}
render() {
@ -32,7 +31,7 @@ export default class GuidePageSideNavItem extends Component {
}
GuidePageSideNavItem.propTypes = {
slug: PropTypes.string,
id: PropTypes.string,
children: PropTypes.any,
onClick: PropTypes.func,
};

View file

@ -1,11 +1,11 @@
@import "../../variables";
.guidePageSection {
.guideSection {
margin-bottom: 40px;
}
.guidePageSection__header {
.guideSection__header {
display: flex;
justify-content: space-between;
align-items: center;
@ -14,12 +14,12 @@
border-bottom: 1px solid #d6d6d6;
}
.guidePageSection__title {
.guideSection__title {
font-size: 18px;
font-weight: 700;
}
.guidePageSection__sourceButton {
.guideSection__sourceButton {
line-height: 10px;
padding: 4px 10px;
background-color: #19a8e0;
@ -39,18 +39,3 @@
inset 0 2px 8px rgba(black, 0.2);
}
}
.guidePageSection__description {
font-size: 14px;
line-height: 21px;
}
.guidePageSection__example {
& + & {
margin-top: 20px;
}
}
.guidePageSection__example--standalone {
margin-top: 10px;
}

View file

@ -0,0 +1,59 @@
import React, {
Component,
PropTypes,
} from 'react';
import Slugify from '../../services/string/slugify';
export class GuideSection extends Component {
constructor(props) {
super(props);
this.onClickSource = this.onClickSource.bind(this);
}
getId() {
return Slugify.one(this.props.title);
}
onClickSource() {
this.props.openCodeViewer(this.props.source);
}
componentWillMount() {
this.props.registerSection(this.getId(), this.props.title);
}
componentWillUnmount() {
this.props.unregisterSection(this.getId());
}
render() {
return (
<div
id={this.getId()}
className="guideSection"
>
<div className="guideSection__header">
<div className="guideSection__title">
{this.props.title}
</div>
<div
className="guideSection__sourceButton fa fa-code"
onClick={this.onClickSource}
/>
</div>
{this.props.children}
</div>
);
}
}
GuideSection.propTypes = {
title: PropTypes.string,
source: PropTypes.array,
children: PropTypes.any,
openCodeViewer: PropTypes.func,
registerSection: PropTypes.func,
unregisterSection: PropTypes.func,
};

View file

@ -0,0 +1,17 @@
import { connect } from 'react-redux';
import { GuideSection } from './guide_section.jsx';
import {
openCodeViewer,
registerSection,
unregisterSection,
} from '../../actions';
export const GuideSectionContainer = connect(
null,
{
openCodeViewer,
registerSection,
unregisterSection,
},
)(GuideSection);

View file

@ -0,0 +1,4 @@
export const GuideSectionTypes = {
JS: 'JavaScript',
HTML: 'HTML',
};

View file

@ -0,0 +1,5 @@
.guideText {
font-size: 14px;
line-height: 21px;
margin-top: $guideVerticalRhythm;
}

View file

@ -0,0 +1,5 @@
import React from 'react';
export const GuideText = props => (
<div className="guideText">{props.children}</div>
);

View file

@ -1,21 +1,11 @@
export * from './guide_code_viewer/guide_code_viewer.jsx';
export { default as GuideCodeViewer } from './guide_code_viewer/guide_code_viewer.jsx';
export * from './guide_example/guide_example.jsx';
export { default as GuideExample } from './guide_example/guide_example.jsx';
export * from './guide_nav/guide_nav.jsx';
export { default as GuideNav } from './guide_nav/guide_nav.jsx';
export * from './guide_page/guide_page.jsx';
export { default as GuidePage } from './guide_page/guide_page.jsx';
export * from './guide_page_section/guide_page_section.jsx';
export { default as GuidePageSection } from './guide_page_section/guide_page_section.jsx';
export * from './guide_page_side_nav/guide_page_side_nav.jsx';
export { default as GuidePageSideNav } from './guide_page_side_nav/guide_page_side_nav.jsx';
export * from './guide_page_side_nav/guide_page_side_nav_item.jsx';
export { default as GuidePageSideNavItem } from './guide_page_side_nav/guide_page_side_nav_item.jsx';
export { GuideCode } from './guide_code/guide_code.jsx';
export { GuideCodeViewer } from './guide_code_viewer/guide_code_viewer.jsx';
export { GuideDemo } from './guide_demo/guide_demo.jsx';
export { GuideLink } from './guide_link/guide_link.jsx';
export { GuideNav } from './guide_nav/guide_nav.jsx';
export { GuidePageContainer as GuidePage } from './guide_page/guide_page_container';
export { GuidePageSideNav } from './guide_page_side_nav/guide_page_side_nav.jsx';
export { GuidePageSideNavItem } from './guide_page_side_nav/guide_page_side_nav_item.jsx';
export { GuideSectionContainer as GuideSection } from './guide_section/guide_section_container';
export { GuideSectionTypes } from './guide_section/guide_section_types';
export { GuideText } from './guide_text/guide_text.jsx';

View file

@ -1,10 +1,6 @@
@import "../../dist/ui_framework.css";
@import "./views/app";
@import "./components/guide_code_viewer/guide_code_viewer";
@import "./components/guide_nav/guide_nav";
@import "./components/guide_page/guide_page";
@import "./components/guide_page_section/guide_page_section";
@import "./components/guide_page_side_nav/guide_page_side_nav";
@import "./components/guide_components";
* {
box-sizing: border-box;

View file

@ -1,4 +1,3 @@
import Slugify from '../string/slugify';
import ActionItemExample

View file

@ -11,6 +11,7 @@ import {
} from 'react-router-redux';
import codeViewerReducer from './reducers/code_viewer_reducer';
import sectionsReducer from './reducers/sections_reducer';
/**
* @param {Object} initialState An object defining the application's initial
@ -21,6 +22,7 @@ export default function configureStore(initialState) {
return {
routing: routerReducer(state.routing, action),
codeViewer: codeViewerReducer(state.codeViewer, action),
sections: sectionsReducer(state.sections, action),
};
}

View file

@ -0,0 +1,11 @@
export function getIsCodeViewerOpen(state) {
return state.codeViewer.isOpen
}
export function getSections(state) {
return state.sections.sections;
}
export function getSource(state) {
return state.codeViewer.source;
}

View file

@ -1,35 +1,34 @@
import ActionTypes from '../../actions/action_types';
const defaultState = {
isOpen: false,
codesBySlug: {},
code: undefined,
source: undefined,
};
export default function codeViewerReducer(state = defaultState, action) {
switch (action.type) {
case ActionTypes.OPEN_CODE_VIEWER: {
const newCode = state.codesBySlug[action.slug];
const source = action.source;
if (state.code === newCode) {
if (state.code === source) {
// If we are opening the existing code, then close the viewer.
return Object.assign({}, state, {
isOpen: false,
code: undefined,
source: undefined,
});
}
return Object.assign({}, state, {
isOpen: true,
code: newCode,
source: source,
});
}
case ActionTypes.UPDATE_CODE_VIEWER: {
if (state.isOpen) {
return Object.assign({}, state, {
code: state.codesBySlug[action.slug],
source: state.codesBySlug[action.slug],
});
}
return state;
@ -38,7 +37,7 @@ export default function codeViewerReducer(state = defaultState, action) {
case ActionTypes.CLOSE_CODE_VIEWER: {
return Object.assign({}, state, {
isOpen: false,
code: undefined,
source: undefined,
});
}

View file

@ -0,0 +1,36 @@
import ActionTypes from '../../actions/action_types';
const defaultState = {
sections: [],
};
export default function sectionsReducer(state = defaultState, action) {
switch (action.type) {
case ActionTypes.REGISTER_SECTION: {
const sections = state.sections.slice();
sections.push({
id: action.id,
name: action.name,
});
return Object.assign({}, state, {
sections,
});
}
case ActionTypes.UNREGISTER_SECTION: {
const sections = state.sections.slice();
const index = sections.findIndex(section => section.id === action.id);
sections.splice(index, 1);
return Object.assign({}, state, {
sections,
});
}
default:
break;
}
return state;
}

View file

@ -1,15 +1,53 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'ActionItem',
html: require('./action_item.html'),
hasDarkTheme: false,
}, {
title: 'ActionItems in Menu',
html: require('./action_items_in_menu.html'),
hasDarkTheme: false,
}]);
const actionItemHtml = require('./action_item.html');
const inMenuHtml = require('./action_items_in_menu.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="ActionItem"
source={[{
type: GuideSectionTypes.HTML,
code: actionItemHtml,
}]}
>
<GuideText>
Events can represent updates, logs, notifications, and status changes.
</GuideText>
<GuideDemo
html={actionItemHtml}
/>
</GuideSection>
<GuideSection
title="ActionItems in Menu"
source={[{
type: GuideSectionTypes.HTML,
code: inMenuHtml,
}]}
>
<GuideText>
You&rsquo;ll typically want to present them within a Menu.
</GuideText>
<GuideDemo
html={inMenuHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,27 +1,34 @@
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import AppView from './app_view.jsx';
import {
CodeViewerActions,
getIsCodeViewerOpen,
getSections,
getSource,
} from '../store';
import { AppView } from './app_view.jsx';
import {
openCodeViewer,
closeCodeViewer,
registerSection,
unregisterSection,
} from '../actions';
function mapStateToProps(state, ownProps) {
return {
routes: ownProps.routes,
isCodeViewerOpen: state.codeViewer.isOpen,
code: state.codeViewer.code,
isCodeViewerOpen: getIsCodeViewerOpen(state),
source: getSource(state),
sections: getSections(state),
};
}
function mapDispatchToProps(dispatch) {
const actions = {
openCodeViewer: CodeViewerActions.openCodeViewer,
updateCodeViewer: CodeViewerActions.updateCodeViewer,
closeCodeViewer: CodeViewerActions.closeCodeViewer,
registerCode: CodeViewerActions.registerCode,
unregisterCode: CodeViewerActions.unregisterCode,
openCodeViewer,
closeCodeViewer,
registerSection,
unregisterSection,
};
return bindActionCreators(actions, dispatch);

View file

@ -1,4 +1,3 @@
import React, {
Component,
PropTypes,
@ -18,8 +17,7 @@ import {
// Inject version into header.
const pkg = require('json!../../../../package.json');
export default class AppView extends Component {
export class AppView extends Component {
constructor(props) {
super(props);
@ -32,15 +30,6 @@ export default class AppView extends Component {
this.onCloseCodeViewer = this.onCloseCodeViewer.bind(this);
}
getChildContext() {
return {
openCodeViewer: this.props.openCodeViewer,
updateCodeViewer: this.props.updateCodeViewer,
registerCode: this.props.registerCode,
unregisterCode: this.props.unregisterCode,
};
}
onClickNavItem() {
this.setState({
isNavOpen: false,
@ -79,35 +68,26 @@ export default class AppView extends Component {
<GuideCodeViewer
isOpen={this.props.isCodeViewerOpen}
onClose={this.onCloseCodeViewer}
title={this.props.code.title}
html={this.props.code.html}
js={this.props.code.js}
title={'No title'}
source={this.props.source}
/>
</div>
);
}
}
AppView.childContextTypes = {
openCodeViewer: PropTypes.func,
updateCodeViewer: PropTypes.func,
registerCode: PropTypes.func,
unregisterCode: PropTypes.func,
};
AppView.propTypes = {
children: PropTypes.any,
routes: PropTypes.array.isRequired,
openCodeViewer: PropTypes.func,
updateCodeViewer: PropTypes.func,
closeCodeViewer: PropTypes.func,
registerCode: PropTypes.func,
unregisterCode: PropTypes.func,
isCodeViewerOpen: PropTypes.bool,
code: PropTypes.object,
registerSection: PropTypes.func,
unregisterSection: PropTypes.func,
sections: PropTypes.array,
source: PropTypes.array,
};
AppView.defaultProps = {
code: {},
source: [],
};

View file

@ -1,31 +1,78 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Bar',
description: (
<div>
<p>Use the Bar to organize controls in a horizontal layout. This is especially useful for surfacing controls in the corners of a view.</p>
<p><strong>Note:</strong> Instead of using this component with a Table, try using the ControlledTable, ToolBar, and ToolBarFooter components.</p>
</div>
),
html: require('./bar.html'),
hasDarkTheme: false,
}, {
title: 'One section',
description: (
<p>A Bar with one section will align it to the right, by default. To align it to the left, just add another section and leave it empty, or don't use a Bar at all.</p>
),
html: require('./bar_one_section.html'),
hasDarkTheme: false,
}, {
title: 'Three sections',
description: (
<p>Technically the Bar can contain three or more sections, but there's no established use-case for this.</p>
),
html: require('./bar_three_sections.html'),
hasDarkTheme: false,
}]);
const barHtml = require('./bar.html');
const oneSectionHtml = require('./bar_one_section.html');
const threeSectionsHtml = require('./bar_three_sections.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Bar"
source={[{
type: GuideSectionTypes.HTML,
code: barHtml,
}]}
>
<GuideText>
Use the Bar to organize controls in a horizontal layout. This is especially useful for
surfacing controls in the corners of a view.
</GuideText>
<GuideText>
<strong>Note:</strong> Instead of using this component with a Table, try using the
ControlledTable, ToolBar, and ToolBarFooter components.
</GuideText>
<GuideDemo
html={barHtml}
/>
</GuideSection>
<GuideSection
title="One section"
source={[{
type: GuideSectionTypes.HTML,
code: oneSectionHtml,
}]}
>
<GuideText>
A Bar with one section will align it to the right, by default. To align it to the left,
just add another section and leave it empty, or don't use a Bar at all.
</GuideText>
<GuideDemo
html={oneSectionHtml}
/>
</GuideSection>
<GuideSection
title="Three sections"
source={[{
type: GuideSectionTypes.HTML,
code: threeSectionsHtml,
}]}
>
<GuideText>
Technically the Bar can contain three or more sections, but there's no established use-case
for this.
</GuideText>
<GuideDemo
html={threeSectionsHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,70 +1,178 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Basic Button',
description: (
<p>Use the basic Button in most situations.</p>
),
html: require('./button_basic.html'),
hasDarkTheme: false,
}, {
title: 'Hollow Button',
description: (
<p>Use the hollow Button when presenting a neutral action, e.g. a "Cancel" button.</p>
),
html: require('./button_hollow.html'),
hasDarkTheme: false,
}, {
title: 'Primary Button',
description: (
<p>Use the primary Button to represent the most common action. Generally, there won't be a need to present more than one of these at a time.</p>
),
html: require('./button_primary.html'),
hasDarkTheme: false,
}, {
title: 'Danger Button',
description: (
<p>Danger Buttons represent irreversible, potentially regrettable actions.</p>
),
html: require('./button_danger.html'),
hasDarkTheme: false,
}, {
title: 'Button with icon',
description: (
<p>You can toss an icon into a Button, with or without text.</p>
),
html: require('./button_with_icon.html'),
hasDarkTheme: false,
}, {
title: 'ButtonGroup',
html: require('./button_group.html'),
hasDarkTheme: false,
}, {
title: 'United ButtonGroup',
description: (
<div>
<p>Use the united version of the ButtonGroup to emphasize the close relationship within a set of Buttons, and differentiate them from Buttons outside of the set.</p>
<p>They support containing a single Button, so that Buttons can be dynamically added and removed.</p>
</div>
),
html: require('./button_group_united.html'),
hasDarkTheme: false,
}, {
title: 'In ToolBar',
description: (
<p>This example verifies that Buttons are legible against the ToolBar's background.</p>
),
html: require('./buttons_in_tool_bar.html'),
hasDarkTheme: false,
}, {
title: 'Element variations',
description: (
<p>You can create a Button using a button element, link, or input[type="submit"].</p>
),
html: require('./button_elements.html'),
hasDarkTheme: false,
}]);
const basicHtml = require('./button_basic.html');
const hollowHtml = require('./button_hollow.html');
const primaryHtml = require('./button_primary.html');
const dangerHtml = require('./button_danger.html');
const withIconHtml = require('./button_with_icon.html');
const groupHtml = require('./button_group.html');
const groupUnitedHtml = require('./button_group_united.html');
const inToolBarHtml = require('./buttons_in_tool_bar.html');
const elementsHtml = require('./button_elements.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Basic Button"
source={[{
type: GuideSectionTypes.HTML,
code: basicHtml,
}]}
>
<GuideText>
Use the basic Button in most situations.
</GuideText>
<GuideDemo
html={basicHtml}
/>
</GuideSection>
<GuideSection
title="Hollow Button"
source={[{
type: GuideSectionTypes.HTML,
code: hollowHtml,
}]}
>
<GuideText>
Use the hollow Button when presenting a neutral action, e.g. a "Cancel" button.
</GuideText>
<GuideDemo
html={hollowHtml}
/>
</GuideSection>
<GuideSection
title="Primary Button"
source={[{
type: GuideSectionTypes.HTML,
code: primaryHtml,
}]}
>
<GuideText>
Use the primary Button to represent the most common action. Generally, there won't be a
need to present more than one of these at a time.
</GuideText>
<GuideDemo
html={primaryHtml}
/>
</GuideSection>
<GuideSection
title="Danger Button"
source={[{
type: GuideSectionTypes.HTML,
code: dangerHtml,
}]}
>
<GuideText>
Danger Buttons represent irreversible, potentially regrettable actions.
</GuideText>
<GuideDemo
html={dangerHtml}
/>
</GuideSection>
<GuideSection
title="Button with icon"
source={[{
type: GuideSectionTypes.HTML,
code: withIconHtml,
}]}
>
<GuideText>
You can toss an icon into a Button, with or without text.
</GuideText>
<GuideDemo
html={withIconHtml}
/>
</GuideSection>
<GuideSection
title="ButtonGroup"
source={[{
type: GuideSectionTypes.HTML,
code: groupHtml,
}]}
>
<GuideText>
</GuideText>
<GuideDemo
html={groupHtml}
/>
</GuideSection>
<GuideSection
title="United ButtonGroup"
source={[{
type: GuideSectionTypes.HTML,
code: groupUnitedHtml,
}]}
>
<GuideText>
Use the united version of the ButtonGroup to emphasize the close relationship within a set
of Buttons, and differentiate them from Buttons outside of the set.
</GuideText>
<GuideText>
They support containing a single Button, so that Buttons can be dynamically added and
removed.
</GuideText>
<GuideDemo
html={groupUnitedHtml}
/>
</GuideSection>
<GuideSection
title="In ToolBar"
source={[{
type: GuideSectionTypes.HTML,
code: inToolBarHtml,
}]}
>
<GuideText>
This example verifies that Buttons are legible against the ToolBar's background.
</GuideText>
<GuideDemo
html={inToolBarHtml}
/>
</GuideSection>
<GuideSection
title="Element variations"
source={[{
type: GuideSectionTypes.HTML,
code: elementsHtml,
}]}
>
<GuideText>
You can create a Button using a button element, link, or input[type="submit"].
</GuideText>
<GuideDemo
html={elementsHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,21 +1,53 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Event',
description: (
<p>Events can represent updates, logs, notifications, and status changes.</p>
),
html: require('./event.html'),
hasDarkTheme: false,
}, {
title: 'Event Menu',
description: (
<p>You&rsquo;ll typically want to present them within a Menu.</p>
),
html: require('./event_menu.html'),
hasDarkTheme: false,
}]);
const eventHtml = require('./event.html');
const eventMenuHtml = require('./event_menu.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Event"
source={[{
type: GuideSectionTypes.HTML,
code: eventHtml,
}]}
>
<GuideText>
Events can represent updates, logs, notifications, and status changes.
</GuideText>
<GuideDemo
html={eventHtml}
/>
</GuideSection>
<GuideSection
title="Event Menu"
source={[{
type: GuideSectionTypes.HTML,
code: eventMenuHtml,
}]}
>
<GuideText>
You&rsquo;ll typically want to present them within a Menu.
</GuideText>
<GuideDemo
html={eventMenuHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,34 +1,100 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'TextInput',
html: require('./text_input.html'),
hasDarkTheme: false,
}, {
title: 'StaticInput',
description: (
<p>Use StaticInput to display dynamic content in a form which the user isn&rsquo;t allowed to edit.</p>
),
html: require('./static_input.html'),
hasDarkTheme: false,
}, {
title: 'TextArea',
html: require('./text_area.html'),
hasDarkTheme: false,
}, {
title: 'TextArea, non-resizable',
html: require('./text_area_non_resizable.html'),
hasDarkTheme: false,
}, {
title: 'CheckBox',
html: require('./check_box.html'),
hasDarkTheme: false,
}, {
title: 'Select',
html: require('./select.html'),
hasDarkTheme: false,
}]);
const textInputHtml = require('./text_input.html');
const staticInputHtml = require('./static_input.html');
const textAreaHtml = require('./text_area.html');
const textAreaNonResizableHtml = require('./text_area_non_resizable.html');
const checkBoxHtml = require('./check_box.html');
const selectHtml = require('./select.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="TextInput"
source={[{
type: GuideSectionTypes.HTML,
code: textInputHtml,
}]}
>
<GuideDemo
html={textInputHtml}
/>
</GuideSection>
<GuideSection
title="StaticInput"
source={[{
type: GuideSectionTypes.HTML,
code: staticInputHtml,
}]}
>
<GuideText>
Use StaticInput to display dynamic content in a form which the user isn&rsquo;t allowed to edit.
</GuideText>
<GuideDemo
html={staticInputHtml}
/>
</GuideSection>
<GuideSection
title="TextArea"
source={[{
type: GuideSectionTypes.HTML,
code: textAreaHtml,
}]}
>
<GuideDemo
html={textAreaHtml}
/>
</GuideSection>
<GuideSection
title="TextArea, non-resizable"
source={[{
type: GuideSectionTypes.HTML,
code: textAreaNonResizableHtml,
}]}
>
<GuideDemo
html={textAreaNonResizableHtml}
/>
</GuideSection>
<GuideSection
title="CheckBox"
source={[{
type: GuideSectionTypes.HTML,
code: checkBoxHtml,
}]}
>
<GuideDemo
html={checkBoxHtml}
/>
</GuideSection>
<GuideSection
title="Select"
source={[{
type: GuideSectionTypes.HTML,
code: selectHtml,
}]}
>
<GuideDemo
html={selectHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,15 +1,44 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
} from '../../components';
export default createExample([{
title: 'Header Bar',
html: require('./header_bar.html'),
hasDarkTheme: false,
}, {
title: 'Two sections',
html: require('./header_bar_two_sections.html'),
hasDarkTheme: false,
}]);
const headerBarHtml = require('./header_bar.html');
const twoSectionsHtml = require('./header_bar_two_sections.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Header Bar"
source={[{
type: GuideSectionTypes.HTML,
code: headerBarHtml,
}]}
>
<GuideDemo
html={headerBarHtml}
/>
</GuideSection>
<GuideSection
title="Two sections"
source={[{
type: GuideSectionTypes.HTML,
code: twoSectionsHtml,
}]}
>
<GuideDemo
html={twoSectionsHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,55 +1,142 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Icon',
description: (
<p>Use the <code className="guideCode">icon</code> class instead of the <code className="guideCode">fa</code> class for FontAwesome icons. This will make it easier for us to migrate away from FontAwesome.</p>
),
html: require('./icon.html'),
hasDarkTheme: false,
}, {
title: 'Info',
description: (
<p>Use this Icon to denote useful information.</p>
),
html: require('./icon_info.html'),
}, {
title: 'Basic',
description: (
<p>Use this Icon when you don't want to communicate any particular meaning with the icon's color.</p>
),
html: require('./icon_basic.html'),
hasDarkTheme: false,
}, {
title: 'Success',
description: (
<p>Use this Icon to denote the successful completion of an action, e.g. filling out a form field correctly or a successful API request.</p>
),
html: require('./icon_success.html'),
hasDarkTheme: false,
}, {
title: 'Warning',
description: (
<p>Use this Icon to denote an irregularity or potential problems.</p>
),
html: require('./icon_warning.html'),
hasDarkTheme: false,
}, {
title: 'Error',
description: (
<p>Use this Icon to denote a failed attempt at an action, e.g. an invalid form field or an API error.</p>
),
html: require('./icon_error.html'),
hasDarkTheme: false,
}, {
title: 'Inactive',
description: (
<p>Use this Icon to denote a disabled, inactive, off, offline, or asleep status.</p>
),
html: require('./icon_inactive.html'),
hasDarkTheme: false,
}, ]);
const iconHtml = require('./icon.html');
const infoHtml = require('./icon_info.html');
const basicHtml = require('./icon_basic.html');
const successHtml = require('./icon_success.html');
const warningHtml = require('./icon_warning.html');
const errorHtml = require('./icon_error.html');
const inactiveHtml = require('./icon_inactive.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Icon"
source={[{
type: GuideSectionTypes.HTML,
code: iconHtml,
}]}
>
<GuideText>
Use the <GuideCode>icon</GuideCode> class instead of the <GuideCode>fa</GuideCode> class for
FontAwesome icons. This will make it easier for us to migrate away from FontAwesome.
</GuideText>
<GuideDemo
html={iconHtml}
/>
</GuideSection>
<GuideSection
title="Info"
source={[{
type: GuideSectionTypes.HTML,
code: infoHtml,
}]}
>
<GuideText>
Use this Icon to denote useful information.
</GuideText>
<GuideDemo
html={infoHtml}
/>
</GuideSection>
<GuideSection
title="Basic"
source={[{
type: GuideSectionTypes.HTML,
code: basicHtml,
}]}
>
<GuideText>
Use this Icon when you don't want to communicate any particular meaning with the icon's
color.
</GuideText>
<GuideDemo
html={basicHtml}
/>
</GuideSection>
<GuideSection
title="Success"
source={[{
type: GuideSectionTypes.HTML,
code: successHtml,
}]}
>
<GuideText>
Use this Icon to denote the successful completion of an action, e.g. filling out a form
field correctly or a successful API request.
</GuideText>
<GuideDemo
html={successHtml}
/>
</GuideSection>
<GuideSection
title="Warning"
source={[{
type: GuideSectionTypes.HTML,
code: warningHtml,
}]}
>
<GuideText>
Use this Icon to denote an irregularity or potential problems.
</GuideText>
<GuideDemo
html={warningHtml}
/>
</GuideSection>
<GuideSection
title="Error"
source={[{
type: GuideSectionTypes.HTML,
code: errorHtml,
}]}
>
<GuideText>
Use this Icon to denote a failed attempt at an action, e.g. an invalid form field or an API
error.
</GuideText>
<GuideDemo
html={errorHtml}
/>
</GuideSection>
<GuideSection
title="Inactive"
source={[{
type: GuideSectionTypes.HTML,
code: inactiveHtml,
}]}
>
<GuideText>
Use this Icon to denote a disabled, inactive, off, offline, or asleep status.
</GuideText>
<GuideDemo
html={inactiveHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,35 +1,86 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Info',
description: (
<p>Use this InfoPanel to generally inform the user.</p>
),
html: require('./info_panel_info.html'),
hasDarkTheme: false,
}, {
title: 'Success',
description: (
<p>Use this InfoPanel to notify the user of an action successfully completing.</p>
),
html: require('./info_panel_success.html'),
hasDarkTheme: false,
}, {
title: 'Warning',
description: (
<p>Use this InfoPanel to warn the user against decisions they might regret.</p>
),
html: require('./info_panel_warning.html'),
hasDarkTheme: false,
}, {
title: 'Error',
description: (
<p>Use this InfoPanel to let the user know something went wrong.</p>
),
html: require('./info_panel_error.html'),
hasDarkTheme: false,
}]);
const infoHtml = require('./info_panel_info.html');
const successHtml = require('./info_panel_success.html');
const warningHtml = require('./info_panel_warning.html');
const errorHtml = require('./info_panel_error.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Info"
source={[{
type: GuideSectionTypes.HTML,
code: infoHtml,
}]}
>
<GuideText>
Use this InfoPanel to generally inform the user.
</GuideText>
<GuideDemo
html={infoHtml}
/>
</GuideSection>
<GuideSection
title="Success"
source={[{
type: GuideSectionTypes.HTML,
code: successHtml,
}]}
>
<GuideText>
Use this InfoPanel to notify the user of an action successfully completing.
</GuideText>
<GuideDemo
html={successHtml}
/>
</GuideSection>
<GuideSection
title="Warning"
source={[{
type: GuideSectionTypes.HTML,
code: warningHtml,
}]}
>
<GuideText>
Use this InfoPanel to warn the user against decisions they might regret.
</GuideText>
<GuideDemo
html={warningHtml}
/>
</GuideSection>
<GuideSection
title="Error"
source={[{
type: GuideSectionTypes.HTML,
code: errorHtml,
}]}
>
<GuideText>
Use this InfoPanel to let the user know something went wrong.
</GuideText>
<GuideDemo
html={errorHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,11 +1,31 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Link',
html: require('./link.html'),
hasDarkTheme: false,
}]);
const linkHtml = require('./link.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Link"
source={[{
type: GuideSectionTypes.HTML,
code: linkHtml,
}]}
>
<GuideDemo
html={linkHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,63 +1,196 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
const simpleHtml = require('./local_nav_simple.html');
const breadcrumbsHtml = require('./local_nav_breadcrumbs.html');
const searchHtml = require('./local_nav_search.html');
const searchErrorHtml = require('./local_nav_search_error.html');
const menuItemStatesHtml = require('./local_nav_menu_item_states.html');
const dropdownHtml = require('./local_nav_dropdown.html');
const dropdownPanelsHtml = require('./local_nav_dropdown_panels.html');
const tabsHtml = require('./local_nav_tabs.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Simple"
source={[{
type: GuideSectionTypes.HTML,
code: simpleHtml,
}]}
>
<GuideText>
Here's a simple LocalNav with a Title in the top left corner and Menu in the top right.
</GuideText>
<GuideDemo
html={simpleHtml}
/>
<GuideDemo
html={simpleHtml}
isDarkTheme={true}
/>
</GuideSection>
<GuideSection
title="Breadcrumbs"
source={[{
type: GuideSectionTypes.HTML,
code: breadcrumbsHtml,
}]}
>
<GuideText>
You can replace the Title with Breadcrumbs.
</GuideText>
<GuideDemo
html={breadcrumbsHtml}
/>
<GuideDemo
html={breadcrumbsHtml}
isDarkTheme={true}
/>
</GuideSection>
<GuideSection
title="Search"
source={[{
type: GuideSectionTypes.HTML,
code: searchHtml,
}]}
>
<GuideText>
You can add a Search component for filtering results.
</GuideText>
<GuideDemo
html={searchHtml}
/>
<GuideDemo
html={searchHtml}
isDarkTheme={true}
/>
</GuideSection>
<GuideSection
title="Invalid Search"
source={[{
type: GuideSectionTypes.HTML,
code: searchErrorHtml,
}]}
>
<GuideDemo
html={searchErrorHtml}
/>
<GuideDemo
html={searchErrorHtml}
isDarkTheme={true}
/>
</GuideSection>
<GuideSection
title="Selected and disabled Menu Item states"
source={[{
type: GuideSectionTypes.HTML,
code: menuItemStatesHtml,
}]}
>
<GuideText>
When the user selects a Menu Item, additional content can be displayed inside of a Dropdown.
</GuideText>
<GuideText>
Menu Items can also be disabled, in which case they become non-interactive.
</GuideText>
<GuideDemo
html={menuItemStatesHtml}
/>
<GuideDemo
html={menuItemStatesHtml}
isDarkTheme={true}
/>
</GuideSection>
<GuideSection
title="Dropdown"
source={[{
type: GuideSectionTypes.HTML,
code: dropdownHtml,
}]}
>
<GuideText>
Selecting a Menu Item will commonly result in an open Dropdown.
</GuideText>
<GuideDemo
html={dropdownHtml}
/>
<GuideDemo
html={dropdownHtml}
isDarkTheme={true}
/>
</GuideSection>
<GuideSection
title="Dropdown panels"
source={[{
type: GuideSectionTypes.HTML,
code: dropdownPanelsHtml,
}]}
>
<GuideText>
You can split the Dropdown into side-by-side Panels.
</GuideText>
<GuideDemo
html={dropdownPanelsHtml}
/>
<GuideDemo
html={dropdownPanelsHtml}
isDarkTheme={true}
/>
</GuideSection>
<GuideSection
title="Tabs"
source={[{
type: GuideSectionTypes.HTML,
code: tabsHtml,
}]}
>
<GuideText>
You can display Tabs for navigating local content.
</GuideText>
<GuideDemo
html={tabsHtml}
/>
<GuideDemo
html={tabsHtml}
isDarkTheme={true}
/>
</GuideSection>
</GuidePage>
);
export default createExample([{
title: 'Simple',
description: (
<p>Here's a simple LocalNav with a Title in the top left corner and Menu in the top right.</p>
),
html: require('./local_nav_simple.html'),
hasDarkTheme: true,
}, {
title: 'Breadcrumbs',
description: (
<p>You can replace the Title with Breadcrumbs.</p>
),
html: require('./local_nav_breadcrumbs.html'),
hasDarkTheme: true,
}, {
title: 'Search',
description: (
<p>You can add a Search component for filtering results.</p>
),
html: require('./local_nav_search.html'),
hasDarkTheme: true,
}, {
title: 'Invalid Search',
html: require('./local_nav_search_error.html'),
hasDarkTheme: true,
}, {
title: 'Selected and disabled Menu Item states',
description: (
<div>
<p>When the user selects a Menu Item, additional content can be displayed inside of a Dropdown.</p>
<p>Menu Items can also be disabled, in which case they become non-interactive.</p>
</div>
),
html: require('./local_nav_menu_item_states.html'),
hasDarkTheme: true,
}, {
title: 'Dropdown',
description: (
<p>Selecting a Menu Item will commonly result in an open Dropdown.</p>
),
html: require('./local_nav_dropdown.html'),
hasDarkTheme: true,
}, {
title: 'Dropdown panels',
description: (
<p>You can split the Dropdown into side-by-side Panels.</p>
),
html: require('./local_nav_dropdown_panels.html'),
hasDarkTheme: true,
}, {
title: 'Tabs',
description: (
<p>You can display Tabs for navigating local content.</p>
),
html: require('./local_nav_tabs.html'),
hasDarkTheme: true,
}]);

View file

@ -1,15 +1,44 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
} from '../../components';
export default createExample([{
title: 'Menu',
html: require('./menu.html'),
hasDarkTheme: false,
}, {
title: 'Menu, contained',
html: require('./menu_contained.html'),
hasDarkTheme: false,
}]);
const menuHtml = require('./menu.html');
const menuContainedHtml = require('./menu_contained.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Menu"
source={[{
type: GuideSectionTypes.HTML,
code: menuHtml,
}]}
>
<GuideDemo
html={menuHtml}
/>
</GuideSection>
<GuideSection
title="Menu, contained"
source={[{
type: GuideSectionTypes.HTML,
code: menuContainedHtml,
}]}
>
<GuideDemo
html={menuContainedHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,26 +1,74 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Basic MenuButton',
html: require('./menu_button_basic.html'),
hasDarkTheme: false,
}, {
title: 'Danger MenuButton',
html: require('./menu_button_danger.html'),
hasDarkTheme: false,
}, {
title: 'MenuButton with Icon',
description: (
<p>You can use a MenuButton with an Icon, with or without text.</p>
),
html: require('./menu_button_with_icon.html'),
hasDarkTheme: false,
}, {
title: 'MenuButtonGroup',
html: require('./menu_button_group.html'),
hasDarkTheme: false,
}]);
const basicHtml = require('./menu_button_basic.html');
const dangerHtml = require('./menu_button_danger.html');
const withIconHtml = require('./menu_button_with_icon.html');
const groupHtml = require('./menu_button_group.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Basic MenuButton"
source={[{
type: GuideSectionTypes.HTML,
code: basicHtml,
}]}
>
<GuideDemo
html={basicHtml}
/>
</GuideSection>
<GuideSection
title="Danger MenuButton"
source={[{
type: GuideSectionTypes.HTML,
code: dangerHtml,
}]}
>
<GuideDemo
html={dangerHtml}
/>
</GuideSection>
<GuideSection
title="MenuButton with Icon"
source={[{
type: GuideSectionTypes.HTML,
code: withIconHtml,
}]}
>
<GuideText>
You can use a MenuButton with an Icon, with or without text.
</GuideText>
<GuideDemo
html={withIconHtml}
/>
</GuideSection>
<GuideSection
title="MenuButtonGroup"
source={[{
type: GuideSectionTypes.HTML,
code: groupHtml,
}]}
>
<GuideDemo
html={groupHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,28 +1,70 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'MicroButton',
description: (
<p>Use MicroButtons for inline actions inside of Table rows.</p>
),
html: require('./micro_button.html'),
hasDarkTheme: false,
}, {
title: 'MicroButtonGroup',
description: (
<p>Use the MicroButtonGroup to emphasize the relationships between a set of MicroButtons, and differentiate them from MicroButtons outside of the set.</p>
),
html: require('./micro_button_group.html'),
hasDarkTheme: false,
}, {
title: 'Element variations',
description: (
<p>You can create a MicroButton using a button element or a link.</p>
),
html: require('./micro_button_elements.html'),
hasDarkTheme: false,
}]);
const microButtonHtml = require('./micro_button.html');
const microButtonGroupHtml = require('./micro_button_group.html');
const microButtonElementsHtml = require('./micro_button_elements.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="MicroButton"
source={[{
type: GuideSectionTypes.HTML,
code: microButtonHtml,
}]}
>
<GuideText>
Use MicroButtons for inline actions inside of Table rows.
</GuideText>
<GuideDemo
html={microButtonHtml}
/>
</GuideSection>
<GuideSection
title="MicroButtonGroup"
source={[{
type: GuideSectionTypes.HTML,
code: microButtonGroupHtml,
}]}
>
<GuideText>
se the MicroButtonGroup to emphasize the relationships between a set of MicroButtons, and
differentiate them from MicroButtons outside of the set.
</GuideText>
<GuideDemo
html={microButtonGroupHtml}
/>
</GuideSection>
<GuideSection
title="Element variations"
source={[{
type: GuideSectionTypes.HTML,
code: microButtonElementsHtml,
}]}
>
<GuideText>
You can create a MicroButton using a button element or a link.
</GuideText>
<GuideDemo
html={microButtonElementsHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,16 +1,49 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Modal',
html: require('./modal.html'),
hasDarkTheme: false,
}, {
title: 'ModalOverlay',
html: require('./modal_overlay.html'),
js: require('raw!./modal_overlay.js'),
hasDarkTheme: false,
}]);
const modalHtml = require('./modal.html');
const modalOverlayHtml = require('./modal_overlay.html');
const modalOverlayJs = require('raw!./modal_overlay.js');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Modal"
source={[{
type: GuideSectionTypes.HTML,
code: modalHtml,
}]}
>
<GuideDemo
html={modalHtml}
/>
</GuideSection>
<GuideSection
title="ModalOverlay"
source={[{
type: GuideSectionTypes.HTML,
code: modalOverlayHtml,
}, {
type: GuideSectionTypes.JS,
code: modalOverlayJs,
}]}
>
<GuideDemo
html={modalOverlayHtml}
js={modalOverlayJs}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,18 +1,48 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Panel',
html: require('./panel.html'),
hasDarkTheme: false,
}, {
title: 'Panel with PanelHeader',
description: (
<p>The Panel requires a special class when used with a PanelHeader.</p>
),
html: require('./panel_with_header.html'),
hasDarkTheme: false,
}]);
const panelHtml = require('./panel.html');
const panelWithHeaderHtml = require('./panel_with_header.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Panel"
source={[{
type: GuideSectionTypes.HTML,
code: panelHtml,
}]}
>
<GuideDemo
html={panelHtml}
/>
</GuideSection>
<GuideSection
title="Panel with PanelHeader"
source={[{
type: GuideSectionTypes.HTML,
code: panelWithHeaderHtml,
}]}
>
<GuideText>
The Panel requires a special class when used with a PanelHeader.
</GuideText>
<GuideDemo
html={panelWithHeaderHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,23 +1,69 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
} from '../../components';
export default createExample([{
title: 'Info',
html: require('./status_text_info.html'),
hasDarkTheme: false,
}, {
title: 'Success',
html: require('./status_text_success.html'),
hasDarkTheme: false,
}, {
title: 'Warning',
html: require('./status_text_warning.html'),
hasDarkTheme: false,
}, {
title: 'Error',
html: require('./status_text_error.html'),
hasDarkTheme: false,
}]);
const infoHtml = require('./status_text_info.html');
const successHtml = require('./status_text_success.html');
const warningHtml = require('./status_text_warning.html');
const errorHtml = require('./status_text_error.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Info"
source={[{
type: GuideSectionTypes.HTML,
code: infoHtml,
}]}
>
<GuideDemo
html={infoHtml}
/>
</GuideSection>
<GuideSection
title="Success"
source={[{
type: GuideSectionTypes.HTML,
code: successHtml,
}]}
>
<GuideDemo
html={successHtml}
/>
</GuideSection>
<GuideSection
title="Warning"
source={[{
type: GuideSectionTypes.HTML,
code: warningHtml,
}]}
>
<GuideDemo
html={warningHtml}
/>
</GuideSection>
<GuideSection
title="Error"
source={[{
type: GuideSectionTypes.HTML,
code: errorHtml,
}]}
>
<GuideDemo
html={errorHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,28 +1,87 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
} from '../../components';
export default createExample([{
title: 'Table',
html: require('./table.html'),
js: require('raw!./table.js'),
hasDarkTheme: false,
}, {
title: 'ControlledTable',
html: require('./controlled_table.html'),
hasDarkTheme: false,
}, {
title: 'ControlledTable with LoadingItems',
html: require('./controlled_table_loading_items.html'),
hasDarkTheme: false,
}, {
title: 'ControlledTable with NoItems',
html: require('./controlled_table_no_items.html'),
hasDarkTheme: false,
}, {
title: 'ControlledTable with PromptForItems',
html: require('./controlled_table_prompt_for_items.html'),
hasDarkTheme: false,
}]);
const tableHtml = require('./table.html');
const tableJs = require('raw!./table.js');
const controlledTableHtml = require('./controlled_table.html');
const controlledTableWithLoadingItemsHtml = require('./controlled_table_loading_items.html');
const controlledTableWithNoItemsHtml = require('./controlled_table_no_items.html');
const controlledTableWithPromptForItemsHtml = require('./controlled_table_prompt_for_items.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Table"
source={[{
type: GuideSectionTypes.HTML,
code: tableHtml,
}, {
type: GuideSectionTypes.JS,
code: tableJs,
}]}
>
<GuideDemo
html={tableHtml}
js={tableJs}
/>
</GuideSection>
<GuideSection
title="ControlledTable"
source={[{
type: GuideSectionTypes.HTML,
code: controlledTableHtml,
}]}
>
<GuideDemo
html={controlledTableHtml}
/>
</GuideSection>
<GuideSection
title="ControlledTable with LoadingItems"
source={[{
type: GuideSectionTypes.HTML,
code: controlledTableWithLoadingItemsHtml,
}]}
>
<GuideDemo
html={controlledTableWithLoadingItemsHtml}
/>
</GuideSection>
<GuideSection
title="ControlledTable with NoItems"
source={[{
type: GuideSectionTypes.HTML,
code: controlledTableWithNoItemsHtml,
}]}
>
<GuideDemo
html={controlledTableWithNoItemsHtml}
/>
</GuideSection>
<GuideSection
title="ControlledTable with PromptForItems"
source={[{
type: GuideSectionTypes.HTML,
code: controlledTableWithPromptForItemsHtml,
}]}
>
<GuideDemo
html={controlledTableWithPromptForItemsHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,12 +1,49 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Tabs',
html: require('./tabs.html'),
js: require('raw!./tabs.js'),
hasDarkTheme: false,
}]);
const html = require('./tabs.html');
const js = require('raw!./tabs.js');
export default class TabsExample extends Component {
render() {
return (
<GuidePage title={this.props.route.name}>
<GuideSection
title="Tabs"
source={[{
type: GuideSectionTypes.HTML,
code: html,
}, {
type: GuideSectionTypes.JS,
code: js,
}]}
>
<GuideText>
Wrap any series of components, e.g. Panel, in the VerticalRhythm component to space
them apart.
</GuideText>
<GuideDemo
html={html}
js={js}
/>
</GuideSection>
</GuidePage>
);
}
}
TabsExample.propTypes = {
route: PropTypes.object.isRequired,
};

View file

@ -1,25 +1,67 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'ToolBar',
description: (
<p>Use the ToolBar to surface controls for manipulating and filtering content, e.g. in a list, table, or menu.</p>
),
html: require('./tool_bar.html'),
hasDarkTheme: false,
}, {
title: 'ToolBar with Search only',
html: require('./tool_bar_search_only.html'),
hasDarkTheme: false,
}, {
title: 'ToolBarFooter',
description: (
<p>Use the ToolBarFooter in conjunction with the ToolBar. It can surface secondary controls or a subset of the primary controls.</p>
),
html: require('./tool_bar_footer.html'),
hasDarkTheme: false,
}]);
const toolBarHtml = require('./tool_bar.html');
const toolBarSearchOnlyHtml = require('./tool_bar_search_only.html');
const toolBarFooterHtml = require('./tool_bar_footer.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="ToolBar"
source={[{
type: GuideSectionTypes.HTML,
code: toolBarHtml,
}]}
>
<GuideText>
Use the ToolBar to surface controls for manipulating and filtering content, e.g. in a
list, table, or menu.
</GuideText>
<GuideDemo
html={toolBarHtml}
/>
</GuideSection>
<GuideSection
title="ToolBar with Search only"
source={[{
type: GuideSectionTypes.HTML,
code: toolBarSearchOnlyHtml,
}]}
>
<GuideDemo
html={toolBarSearchOnlyHtml}
/>
</GuideSection>
<GuideSection
title="ToolBarFooter"
source={[{
type: GuideSectionTypes.HTML,
code: toolBarFooterHtml,
}]}
>
<GuideText>
Use the ToolBarFooter in conjunction with the ToolBar. It can surface secondary
controls or a subset of the primary controls.
</GuideText>
<GuideDemo
html={toolBarFooterHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,28 +1,70 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideCode,
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'Title',
description: (
<p>Works well with an <code className="guideCode">h1</code>.</p>
),
html: require('./title.html'),
hasDarkTheme: false,
}, {
title: 'SubTitle',
description: (
<p>Works well with an <code className="guideCode">h2</code>.</p>
),
html: require('./sub_title.html'),
hasDarkTheme: false,
}, {
title: 'Text',
description: (
<p>Works well with a <code className="guideCode">p</code>.</p>
),
html: require('./text.html'),
hasDarkTheme: false,
}]);
const titleHtml = require('./title.html');
const subTitleHtml = require('./sub_title.html');
const textHtml = require('./text.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Title"
source={[{
type: GuideSectionTypes.HTML,
code: titleHtml,
}]}
>
<GuideText>
Works well with an <GuideCode>h1</GuideCode>.
</GuideText>
<GuideDemo
html={titleHtml}
/>
</GuideSection>
<GuideSection
title="SubTitle"
source={[{
type: GuideSectionTypes.HTML,
code: subTitleHtml,
}]}
>
<GuideText>
Works well with an <GuideCode>h2</GuideCode>.
</GuideText>
<GuideDemo
html={subTitleHtml}
/>
</GuideSection>
<GuideSection
title="Text"
source={[{
type: GuideSectionTypes.HTML,
code: textHtml,
}]}
>
<GuideText>
Works well with a <GuideCode>p</GuideCode>.
</GuideText>
<GuideDemo
html={textHtml}
/>
</GuideSection>
</GuidePage>
);

View file

@ -1,31 +1,75 @@
import React from 'react';
import React, {
Component,
PropTypes,
} from 'react';
import {
createExample,
} from '../../services';
GuideDemo,
GuideLink,
GuidePage,
GuideSection,
GuideSectionTypes,
GuideText,
} from '../../components';
export default createExample([{
title: 'VerticalRhythm',
description: (
<div>
<p>VerticalRhythm creates regular vertical spacing between elements.</p>
<p><strong>Note:</strong> It only works if two adjacent elements have this class applied, in which case it will create space between them.</p>
</div>
),
html: require('./vertical_rhythm.html'),
hasDarkTheme: false,
}, {
title: 'VerticalRhythm as wrapper',
description: (
<p>Wrap any series of components, e.g. Panel, in the VerticalRhythm component to space them apart.</p>
),
html: require('./vertical_rhythm_as_wrapper.html'),
hasDarkTheme: false,
}, {
title: 'VerticalRhythm on component',
description: (
<p>You can also apply the VerticalRhythm class directly to components.</p>
),
html: require('./vertical_rhythm_on_component.html'),
hasDarkTheme: false,
}]);
const verticalRhythmHtml = require('./vertical_rhythm.html');
const verticalRhythmAsWrapperHtml = require('./vertical_rhythm_as_wrapper.html');
const verticalRhythmOnComponentHtml = require('./vertical_rhythm_on_component.html');
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="VerticalRhythm"
source={[{
type: GuideSectionTypes.HTML,
code: verticalRhythmHtml,
}]}
>
<GuideText>
VerticalRhythm creates regular vertical spacing between elements.
</GuideText>
<GuideText>
<strong>Note:</strong> It only works if two adjacent elements have this class applied, in
which case it will create space between them.
</GuideText>
<GuideDemo
html={verticalRhythmHtml}
/>
</GuideSection>
<GuideSection
title="VerticalRhythm as wrapper"
source={[{
type: GuideSectionTypes.HTML,
code: verticalRhythmAsWrapperHtml,
}]}
>
<GuideText>
Wrap any series of components, e.g. Panel, in the VerticalRhythm component to space them
apart.
</GuideText>
<GuideDemo
html={verticalRhythmAsWrapperHtml}
/>
</GuideSection>
<GuideSection
title="VerticalRhythm on component"
source={[{
type: GuideSectionTypes.HTML,
code: verticalRhythmOnComponentHtml,
}]}
>
<GuideText>
You can also apply the VerticalRhythm class directly to components.
</GuideText>
<GuideDemo
html={verticalRhythmOnComponentHtml}
/>
</GuideSection>
</GuidePage>
);