diff --git a/docs/app/Examples/elements/Header/States/HeaderDisabledExample.js b/docs/app/Examples/elements/Header/States/HeaderDisabledExample.js index 0fcda5e543..d579c2d540 100644 --- a/docs/app/Examples/elements/Header/States/HeaderDisabledExample.js +++ b/docs/app/Examples/elements/Header/States/HeaderDisabledExample.js @@ -4,7 +4,7 @@ import { Header } from 'stardust' export default class HeaderDisabledExample extends Component { render() { return ( - + Disabled Header ) diff --git a/docs/app/Examples/elements/Header/Variations/HeaderAttachedExample.js b/docs/app/Examples/elements/Header/Variations/HeaderAttachedExample.js index a85a4a181a..0941d72fdb 100644 --- a/docs/app/Examples/elements/Header/Variations/HeaderAttachedExample.js +++ b/docs/app/Examples/elements/Header/Variations/HeaderAttachedExample.js @@ -5,7 +5,7 @@ export default class HeaderAttachedExample extends Component { render() { return (
- + Attached Header diff --git a/docs/app/Examples/elements/Header/Variations/HeaderBlockExample.js b/docs/app/Examples/elements/Header/Variations/HeaderBlockExample.js index 43a39f21fc..a7ce7f9178 100644 --- a/docs/app/Examples/elements/Header/Variations/HeaderBlockExample.js +++ b/docs/app/Examples/elements/Header/Variations/HeaderBlockExample.js @@ -4,7 +4,7 @@ import { Header } from 'stardust' export default class HeaderBlockExample extends Component { render() { return ( - + Block Header ) diff --git a/docs/app/Examples/elements/Header/Variations/HeaderColoredExample.js b/docs/app/Examples/elements/Header/Variations/HeaderColoredExample.js index 2421d6eb45..88809dd7c3 100644 --- a/docs/app/Examples/elements/Header/Variations/HeaderColoredExample.js +++ b/docs/app/Examples/elements/Header/Variations/HeaderColoredExample.js @@ -5,18 +5,18 @@ export default class HeaderColoredExample extends Component { render() { return (
- Red - Orange - Yellow - Olive - Green - Teal - Blue - Purple - Violent - Pink - Brown - Grey + Red + Orange + Yellow + Olive + Green + Teal + Blue + Purple + Violent + Pink + Brown + Grey
) } diff --git a/docs/app/Examples/elements/Header/Variations/HeaderDividingExample.js b/docs/app/Examples/elements/Header/Variations/HeaderDividingExample.js index 17eb8a62b8..0a755b0502 100644 --- a/docs/app/Examples/elements/Header/Variations/HeaderDividingExample.js +++ b/docs/app/Examples/elements/Header/Variations/HeaderDividingExample.js @@ -4,7 +4,7 @@ import { Header } from 'stardust' export default class HeaderDividingExample extends Component { render() { return ( - + Dividing Header ) diff --git a/docs/app/Examples/elements/Header/Variations/HeaderFloatingExample.js b/docs/app/Examples/elements/Header/Variations/HeaderFloatingExample.js index 0f400e2350..148a935054 100644 --- a/docs/app/Examples/elements/Header/Variations/HeaderFloatingExample.js +++ b/docs/app/Examples/elements/Header/Variations/HeaderFloatingExample.js @@ -5,10 +5,10 @@ export default class HeaderFloatingExample extends Component { render() { return ( - + Float Right - + Float Left diff --git a/docs/app/Examples/elements/Header/Variations/HeaderInvertedExample.js b/docs/app/Examples/elements/Header/Variations/HeaderInvertedExample.js index dc05b5f88d..001c8552b9 100644 --- a/docs/app/Examples/elements/Header/Variations/HeaderInvertedExample.js +++ b/docs/app/Examples/elements/Header/Variations/HeaderInvertedExample.js @@ -5,18 +5,18 @@ export default class HeaderInvertedExample extends Component { render() { return ( - Red - Orange - Yellow - Olive - Green - Teal - Blue - Purple - Violent - Pink - Brown - Grey + Red + Orange + Yellow + Olive + Green + Teal + Blue + Purple + Violent + Pink + Brown + Grey ) } diff --git a/docs/app/Examples/elements/Header/Variations/HeaderTextAlignmentExample.js b/docs/app/Examples/elements/Header/Variations/HeaderTextAlignmentExample.js index 80fd5fe3ef..d00e51ed8c 100644 --- a/docs/app/Examples/elements/Header/Variations/HeaderTextAlignmentExample.js +++ b/docs/app/Examples/elements/Header/Variations/HeaderTextAlignmentExample.js @@ -5,16 +5,16 @@ export default class HeaderTextAlignmentExample extends Component { render() { return ( - + Float Right - + Float Left - + This text takes up the full width of the container - + Centered diff --git a/src/elements/Header/Header.js b/src/elements/Header/Header.js index 7b67a107c1..e49f3fa209 100644 --- a/src/elements/Header/Header.js +++ b/src/elements/Header/Header.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React from 'react' import META from '../../utils/Meta' import _Header from './_Header' import HeaderH1 from './HeaderH1' @@ -9,24 +9,24 @@ import HeaderH5 from './HeaderH5' import HeaderH6 from './HeaderH6' import HeaderSubheader from './HeaderSubheader' -export default class Header extends Component { - static _meta = { - library: META.library.semanticUI, - name: 'Header', - type: META.type.element, - } - - static H1 = HeaderH1 - static H2 = HeaderH2 - static H3 = HeaderH3 - static H4 = HeaderH4 - static H5 = HeaderH5 - static H6 = HeaderH6 - static Subheader = HeaderSubheader +function Header(props) { + return ( + <_Header {...props} /> + ) +} - render() { - return ( - <_Header {...this.props} /> - ) - } +Header._meta = { + library: META.library.semanticUI, + name: 'Header', + type: META.type.element, } + +Header.H1 = HeaderH1 +Header.H2 = HeaderH2 +Header.H3 = HeaderH3 +Header.H4 = HeaderH4 +Header.H5 = HeaderH5 +Header.H6 = HeaderH6 +Header.Subheader = HeaderSubheader + +export default Header diff --git a/src/elements/Header/HeaderH1.js b/src/elements/Header/HeaderH1.js index 5a7cf545c0..1d5e3e0e2a 100644 --- a/src/elements/Header/HeaderH1.js +++ b/src/elements/Header/HeaderH1.js @@ -1,18 +1,18 @@ -import React, { Component } from 'react' +import React from 'react' import META from '../../utils/Meta' import _Header from './_Header' -export default class HeaderH1 extends Component { - static _meta = { - library: META.library.semanticUI, - name: 'HeaderH1', - parent: 'Header', - type: META.type.element, - } +function HeaderH1(props) { + return ( + <_Header {...props} _sdClass='sd-header-h1' _headerElement='h1' /> + ) +} - render() { - return ( - <_Header {...this.props} _sdClass='sd-header-h1' _headerElement='h1' /> - ) - } +HeaderH1._meta = { + library: META.library.semanticUI, + name: 'HeaderH1', + parent: 'Header', + type: META.type.element, } + +export default HeaderH1 diff --git a/src/elements/Header/HeaderH2.js b/src/elements/Header/HeaderH2.js index 0cd00512d2..e088e95b82 100644 --- a/src/elements/Header/HeaderH2.js +++ b/src/elements/Header/HeaderH2.js @@ -1,19 +1,18 @@ -import React, { Component } from 'react' +import React from 'react' import META from '../../utils/Meta' import _Header from './_Header' -export default class HeaderH2 extends Component { - - static _meta = { - library: META.library.semanticUI, - name: 'HeaderH2', - parent: 'Header', - type: META.type.element, - } +function HeaderH2(props) { + return ( + <_Header {...props} _sdClass='sd-header-h2' _headerElement='h2' /> + ) +} - render() { - return ( - <_Header {...this.props} _sdClass='sd-header-h2' _headerElement='h2' /> - ) - } +HeaderH2._meta = { + library: META.library.semanticUI, + name: 'HeaderH2', + parent: 'Header', + type: META.type.element, } + +export default HeaderH2 diff --git a/src/elements/Header/HeaderH3.js b/src/elements/Header/HeaderH3.js index ac0304ee28..b47a366d91 100644 --- a/src/elements/Header/HeaderH3.js +++ b/src/elements/Header/HeaderH3.js @@ -1,18 +1,18 @@ -import React, { Component } from 'react' +import React from 'react' import META from '../../utils/Meta' import _Header from './_Header' -export default class HeaderH3 extends Component { - static _meta = { - library: META.library.semanticUI, - name: 'HeaderH3', - parent: 'Header', - type: META.type.element, - } +function HeaderH3(props) { + return ( + <_Header {...props} _sdClass='sd-header-h3' _headerElement='h3' /> + ) +} - render() { - return ( - <_Header {...this.props} _sdClass='sd-header-h3' _headerElement='h3' /> - ) - } +HeaderH3._meta = { + library: META.library.semanticUI, + name: 'HeaderH3', + parent: 'Header', + type: META.type.element, } + +export default HeaderH3 diff --git a/src/elements/Header/HeaderH4.js b/src/elements/Header/HeaderH4.js index e30c2a7023..8339f066bc 100644 --- a/src/elements/Header/HeaderH4.js +++ b/src/elements/Header/HeaderH4.js @@ -1,18 +1,18 @@ -import React, { Component } from 'react' +import React from 'react' import META from '../../utils/Meta' import _Header from './_Header' -export default class HeaderH4 extends Component { - static _meta = { - library: META.library.semanticUI, - name: 'HeaderH4', - parent: 'Header', - type: META.type.element, - } +function HeaderH4(props) { + return ( + <_Header {...props} _sdClass='sd-header-h4' _headerElement='h4' /> + ) +} - render() { - return ( - <_Header {...this.props} _sdClass='sd-header-h4' _headerElement='h4' /> - ) - } +HeaderH4._meta = { + library: META.library.semanticUI, + name: 'HeaderH4', + parent: 'Header', + type: META.type.element, } + +export default HeaderH4 diff --git a/src/elements/Header/HeaderH5.js b/src/elements/Header/HeaderH5.js index 0419266d74..3bd2ab0b67 100644 --- a/src/elements/Header/HeaderH5.js +++ b/src/elements/Header/HeaderH5.js @@ -1,18 +1,18 @@ -import React, { Component } from 'react' +import React from 'react' import META from '../../utils/Meta' import _Header from './_Header' -export default class HeaderH5 extends Component { - static _meta = { - library: META.library.semanticUI, - name: 'HeaderH5', - parent: 'Header', - type: META.type.element, - } +function HeaderH5(props) { + return ( + <_Header {...props} _sdClass='sd-header-h5' _headerElement='h5' /> + ) +} - render() { - return ( - <_Header {...this.props} _sdClass='sd-header-h5' _headerElement='h5' /> - ) - } +HeaderH5._meta = { + library: META.library.semanticUI, + name: 'HeaderH5', + parent: 'Header', + type: META.type.element, } + +export default HeaderH5 diff --git a/src/elements/Header/HeaderH6.js b/src/elements/Header/HeaderH6.js index f911da65b5..ee67420e9d 100644 --- a/src/elements/Header/HeaderH6.js +++ b/src/elements/Header/HeaderH6.js @@ -1,18 +1,18 @@ -import React, { Component } from 'react' +import React from 'react' import META from '../../utils/Meta' import _Header from './_Header' -export default class HeaderH6 extends Component { - static _meta = { - library: META.library.semanticUI, - name: 'HeaderH6', - parent: 'Header', - type: META.type.element, - } +function HeaderH6(props) { + return ( + <_Header {...props} _sdClass='sd-header-h6' _headerElement='h6' /> + ) +} - render() { - return ( - <_Header {...this.props} _sdClass='sd-header-h6' _headerElement='h6' /> - ) - } +HeaderH6._meta = { + library: META.library.semanticUI, + name: 'HeaderH6', + parent: 'Header', + type: META.type.element, } + +export default HeaderH6 diff --git a/src/elements/Header/HeaderSubheader.js b/src/elements/Header/HeaderSubheader.js index 50640a8f91..02cc8a68ae 100644 --- a/src/elements/Header/HeaderSubheader.js +++ b/src/elements/Header/HeaderSubheader.js @@ -1,33 +1,42 @@ -import React, { Component, PropTypes } from 'react' -import classNames from 'classnames' +import React, { PropTypes } from 'react' +import cx from 'classnames' import META from '../../utils/Meta' -import { getUnhandledProps } from '../../utils/propUtils' - -export default class HeaderSubheader extends Component { - static propTypes = { - children: PropTypes.node, - className: PropTypes.string, - } - static _meta = { - library: META.library.semanticUI, - name: 'HeaderSubheader', - parent: 'Header', - type: META.type.element, - } - render() { - const classes = classNames( - 'sd-header-subheader', - 'sub', - this.props.className, - 'header', - ) - - const props = getUnhandledProps(HeaderSubheader, this.props) - - return ( -
- {this.props.children} -
- ) - } +import { + getUnhandledProps, +} from '../../utils/propUtils' + +function HeaderSubheader(props) { + const { + children, className, + } = props + + const classes = cx('sd-header-subheader', 'sub', + className, + 'header' + ) + + const rest = getUnhandledProps(HeaderSubheader, props) + + return ( +
+ {children} +
+ ) +} + +HeaderSubheader._meta = { + library: META.library.semanticUI, + name: 'HeaderSubheader', + parent: 'Header', + type: META.type.element, } + +HeaderSubheader.propTypes = { + /** Primary content of the HeaderSubheader */ + children: PropTypes.node, + + /** Classes to add to the subheader className. */ + className: PropTypes.string, +} + +export default HeaderSubheader diff --git a/src/elements/Header/_Header.js b/src/elements/Header/_Header.js index 1236704d80..05da4269bd 100644 --- a/src/elements/Header/_Header.js +++ b/src/elements/Header/_Header.js @@ -1,42 +1,110 @@ import cx from 'classnames' -import React, { createElement, Component, PropTypes } from 'react' +import React, { PropTypes } from 'react' import META from '../../utils/Meta' +import * as sui from '../../utils/semanticUtils' +import { + getUnhandledProps, + iconPropRenderer, + imagePropRenderer, + useValueAndKey, + useAlignedProp, + useKeyOrValueAndKey, + useKeyOnly, +} from '../../utils/propUtils' -export default class _Header extends Component { - static propTypes = { - _headerElement: PropTypes.string, - _sdClass: PropTypes.string, - children: PropTypes.node, - className: PropTypes.string, - icon: PropTypes.node, - image: PropTypes.node, - } - - static defaultProps = { - _headerElement: 'div', - _sdClass: 'sd-header', - } - - static _meta = { - library: META.library.stardust, - name: '_Header', - type: META.type.element, - } - - render() { - const content = this.props.image || this.props.icon - ?
{this.props.children}
- : this.props.children - - return createElement(this.props._headerElement, { - ...this.props, - className: cx( - this.props._sdClass, - 'ui', - this.props.className, - 'header' - ), - }, content) - } +function _Header(props) { + const { + _sdClass, _headerElement, + color, aligned, dividing, block, attached, floated, inverted, disabled, + icon, image, children, className, + } = props + + const classes = cx( + _sdClass, 'ui', + icon && 'icon', + color, + useAlignedProp(aligned), + useKeyOnly(dividing, 'dividing'), + useKeyOnly(block, 'block'), + useKeyOrValueAndKey(attached, 'attached'), + useValueAndKey(floated, 'floated'), + useKeyOnly(inverted, 'inverted'), + useKeyOnly(disabled, 'disabled'), + className, + 'header', + ) + + const _HeaderComponent = _headerElement + const rest = getUnhandledProps(_Header, props) + + return ( + <_HeaderComponent className={classes} {...rest}> + {iconPropRenderer(icon)} + {imagePropRenderer(image)} + {children} + + ) +} + +_Header._meta = { + library: META.library.semanticUI, + name: '_Header', + type: META.type.element, + props: { + aligned: ['left', 'center', 'right', 'justified'], + floated: ['left', 'right'], + attached: ['top', 'bottom'], + color: sui.colors, + }, +} + +_Header.propTypes = { + _headerElement: PropTypes.string, + _sdClass: PropTypes.string, + className: PropTypes.string, + children: PropTypes.node, + + /** Add an icon by icon className or pass an */ + icon: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element, + ]), + + /** Add an image by img src or pass an . */ + image: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element, + ]), + + /** Color of the header. */ + color: PropTypes.oneOf(_Header._meta.props.colors), + + /** Align header content */ + aligned: PropTypes.oneOf(_Header._meta.props.aligned), + + /** Divide header from the content below it */ + dividing: PropTypes.bool, + + /** Format header to appear inside a content block */ + block: PropTypes.bool, + + /** Attach header to other content, like a segment */ + attached: PropTypes.oneOf(_Header._meta.props.attached), + + /** Header can sit to the left or right of other content */ + floated: PropTypes.oneOf(_Header._meta.props.floated), + + /** Inverts the color of the header for dark backgrounds */ + inverted: PropTypes.bool, + + /** Show that the header is inactive */ + disabled: PropTypes.bool, } + +_Header.defaultProps = { + _headerElement: 'div', + _sdClass: 'sd-header', +} + +export default _Header diff --git a/test/specs/commonTests.js b/test/specs/commonTests.js index 42ae3ba1cc..0599a850de 100644 --- a/test/specs/commonTests.js +++ b/test/specs/commonTests.js @@ -9,6 +9,9 @@ import * as consoleUtil from 'test/utils/consoleUtil' import sandbox from 'test/utils/Sandbox-util' import * as syntheticEvent from 'test/utils/syntheticEvent' +import Icon from 'src/elements/Icon/Icon' +import Image from 'src/elements/Image/Image' + const componentCtx = require.context('../../src/', true, /(addons|collections|elements|modules|views).*\.js$/) const componentInfo = componentCtx.keys().map(key => { @@ -385,6 +388,9 @@ export const implementsAlignedProp = (Component, requiredProps = {}) => { it('adds "justified" without "aligned" to className', () => { shallow() .should.have.className('justified') + + shallow() + .should.not.have.className('aligned') }) } else { it(`adds "${propVal} aligned" to className`, () => { @@ -396,6 +402,72 @@ export const implementsAlignedProp = (Component, requiredProps = {}) => { }) } +export const implementsIconProp = (Component, requiredProps = {}) => { + const iconClass = faker.hacker.noun() + const assertValid = (wrapper) => { + wrapper.should.have.className('icon') + wrapper.should.have.descendants('Icon') + wrapper.find('Icon') + .should.have.className(iconClass) + } + + describe('icon (common)', () => { + _noDefaultClassNameFromProp(Component, 'icon') + + it('has no i when not defined', () => { + shallow() + .should.not.have.descendants('i') + }) + + it('adds a i as first child', () => { + shallow() + .childAt(0) + .should.match('i') + }) + + it('accepts an Icon instance', () => { + const icon = + assertValid(shallow()) + }) + + it('accepts an icon className string', () => { + assertValid(shallow()) + }) + }) +} + +export const implementsImageProp = (Component, requiredProps = {}) => { + const imageSrc = faker.internet.avatar() + const assertValid = (wrapper) => { + wrapper.should.have.descendants('Image') + wrapper.find('Image') + .should.have.prop('src', imageSrc) + } + describe('image (common)', () => { + _noDefaultClassNameFromProp(Component, 'image') + + it('has no img when prop is not defined', () => { + shallow() + .should.not.have.descendants('img') + }) + + it('adds a img as first child', () => { + shallow() + .childAt(0) + .should.match('img') + }) + + it('accepts an Image instance', () => { + const image = + assertValid(shallow()) + }) + + it('accepts an image src string', () => { + assertValid(shallow()) + }) + }) +} + /** * Assert that only a Component prop's name is converted to className. * @param {React.Component|Function} Component The component to test. diff --git a/test/specs/elements/Header/Header-test.js b/test/specs/elements/Header/Header-test.js new file mode 100644 index 0000000000..428b548386 --- /dev/null +++ b/test/specs/elements/Header/Header-test.js @@ -0,0 +1,16 @@ +import Header from 'src/elements/Header/Header' +import H1 from 'src/elements/Header/HeaderH1' +import H2 from 'src/elements/Header/HeaderH2' +import H3 from 'src/elements/Header/HeaderH3' +import H4 from 'src/elements/Header/HeaderH4' +import H5 from 'src/elements/Header/HeaderH5' +import H6 from 'src/elements/Header/HeaderH6' +import Subheader from 'src/elements/Header/HeaderSubheader' +import * as common from 'test/specs/commonTests' + +describe('Header', () => { + common.isConformant(Header) + common.hasSubComponents(Header, [ + H1, H2, H3, H4, H5, H6, Subheader, + ]) +}) diff --git a/test/specs/elements/Header/HeaderHeadings-test.js b/test/specs/elements/Header/HeaderHeadings-test.js new file mode 100644 index 0000000000..f8583051ec --- /dev/null +++ b/test/specs/elements/Header/HeaderHeadings-test.js @@ -0,0 +1,25 @@ +import H1 from 'src/elements/Header/HeaderH1' +import H2 from 'src/elements/Header/HeaderH2' +import H3 from 'src/elements/Header/HeaderH3' +import H4 from 'src/elements/Header/HeaderH4' +import H5 from 'src/elements/Header/HeaderH5' +import H6 from 'src/elements/Header/HeaderH6' +import * as common from 'test/specs/commonTests' + +const headers = { + h1: H1, + h2: H2, + h3: H3, + h4: H4, + h5: H5, + h6: H6, +} + +describe('HeaderHeadings', () => { + Object.keys(headers).forEach((key) => { + describe(`Header.${key.toUpperCase()}`, () => { + const Component = headers[key] + common.isConformant(Component) + }) + }) +}) diff --git a/test/specs/elements/Header/HeaderSubheader-test.js b/test/specs/elements/Header/HeaderSubheader-test.js new file mode 100644 index 0000000000..21313d7553 --- /dev/null +++ b/test/specs/elements/Header/HeaderSubheader-test.js @@ -0,0 +1,7 @@ +import HeaderSubheader from 'src/elements/Header/HeaderSubheader' +import * as common from 'test/specs/commonTests' + +describe('HeaderSubheader', () => { + common.isConformant(HeaderSubheader) + common.rendersChildren(HeaderSubheader) +}) diff --git a/test/specs/elements/Header/_Header-test.js b/test/specs/elements/Header/_Header-test.js new file mode 100644 index 0000000000..09de29eb3a --- /dev/null +++ b/test/specs/elements/Header/_Header-test.js @@ -0,0 +1,21 @@ +import _Header from 'src/elements/Header/_Header' +import * as common from 'test/specs/commonTests' + +describe('_Header', () => { + common.hasUIClassName(_Header) + common.rendersChildren(_Header) + + common.propKeyOnlyToClassName(_Header, 'dividing') + common.propKeyOnlyToClassName(_Header, 'block') + common.propKeyOnlyToClassName(_Header, 'inverted') + common.propKeyOnlyToClassName(_Header, 'disabled') + + common.propKeyAndValueToClassName(_Header, 'floated') + common.propKeyOrValueToClassName(_Header, 'attached') + + common.propValueOnlyToClassName(_Header, 'color') + + common.implementsAlignedProp(_Header) + common.implementsIconProp(_Header) + common.implementsImageProp(_Header) +})