Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

Commit

Permalink
feat: support theming and styling (#16)
Browse files Browse the repository at this point in the history
* move rules and vars to themes, add typings

* wip

* test: add mergeTheme tests

* fix: mv listVariables to listItemVariables

* fix lint error, add changelog entry

* docs: restore default imports in doc examples

* docs: cleanup cruft component-anatomy page

* fix: Provier theme propTypes

* refactor: use toCompactArray where possible

* refactor: renderComponentStyles to getClasses

* add todo for avatar icon work

* revert minor changes

* rename getClasses

* rename getClasses with git mv

* uncomment icon styles

* refactor: avatar styles, fix export name

* refactor: add perf link to callable

* feat: expose resolved variables/styles in renderComponent

* feat: pass resolved variables and styles to components
  • Loading branch information
levithomason authored Aug 9, 2018
1 parent f14208b commit e345edf
Show file tree
Hide file tree
Showing 76 changed files with 654 additions and 552 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add `fluid` variant and size variables to Image @kuzhelov ([#54](https://github.com/stardust-ui/react/pull/54))
- Add SVG icons support @kuzhelov ([#50](https://github.com/stardust-ui/react/pull/50))
- Add `fluid` prop and variation and width variables to Input @alinais ([#59](https://github.com/stardust-ui/react/pull/59))

- Support `styles` prop and nested theme Providers @levithomason ([#16](https://github.com/stardust-ui/react/pull/16))

<!--------------------------------[ v0.2.5 ]------------------------------- -->
## [v0.2.5](https://github.com/stardust-ui/react/tree/v0.2.5) (2018-08-03)
[Compare changes](https://github.com/stardust-ui/react/compare/v0.2.4...v0.2.5)
Expand Down
109 changes: 57 additions & 52 deletions docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import {
knobsContext,
repoURL,
scrollToAnchor,
variablesContext,
} from 'docs/src/utils'
import evalTypeScript from 'docs/src/utils/evalTypeScript'
import { pxToRem, doesNodeContainClick } from 'src/lib'
import { callable, doesNodeContainClick, mergeThemes, pxToRem } from 'src/lib'
import Editor from 'docs/src/components/Editor'
import ComponentControls from '../ComponentControls'
import ComponentExampleTitle from './ComponentExampleTitle'
import ContributionPrompt from '../ContributionPrompt'
import getSourceCodeManager, { ISourceCodeManager, SourceCodeType } from './SourceCodeManager'
import { IThemeInput, IThemePrepared } from 'types/theme'
import { theme as teamsTheme } from '../../../../../src/themes/teams'

export interface IComponentExampleProps extends RouteComponentProps<any, any> {
title: string
Expand All @@ -33,19 +34,19 @@ export interface IComponentExampleProps extends RouteComponentProps<any, any> {

interface IComponentExampleState {
knobs: Object
componentVariables: { [key: string]: Object }
exampleElement: JSX.Element
handleMouseLeave: () => void
handleMouseMove: () => void
sourceCode: string
markup: string
error: string
showCode: boolean
showHTML: boolean
showRtl: boolean
showVariables: boolean
isHovering: boolean
copiedCode: boolean
theme: IThemeInput
exampleElement?: JSX.Element
handleMouseLeave?: () => void
handleMouseMove?: () => void
sourceCode?: string
markup?: string
error?: string
showCode?: boolean
showHTML?: boolean
showRtl?: boolean
showVariables?: boolean
isHovering?: boolean
copiedCode?: boolean
}

const EDITOR_BACKGROUND_COLOR = '#1D1F21'
Expand All @@ -61,17 +62,6 @@ const codeTypeApiButtonLabels: { [key in SourceCodeType]: string } = {
shorthand: 'Shorthand API',
}

const variablesPanelStyle = {
display: 'flex',
flexWrap: 'wrap',
maxHeight: pxToRem(250),
overflowY: 'auto',
}

const variableInputStyle = {
paddingBottom: pxToRem(10),
}

/**
* Renders a `component` and the raw `code` that produced it.
* Allows toggling the the raw `code` code block.
Expand All @@ -83,7 +73,10 @@ class ComponentExample extends React.PureComponent<IComponentExampleProps, IComp
private kebabExamplePath: string
private KnobsComponent: any

public state = { knobs: {} } as IComponentExampleState
public state: IComponentExampleState = {
knobs: {},
theme: teamsTheme,
}

public static contextTypes = {
onPassed: PropTypes.func,
Expand Down Expand Up @@ -349,8 +342,15 @@ class ComponentExample extends React.PureComponent<IComponentExampleProps, IComp
private getDisplayName = () => this.props.examplePath.split('/')[1]

private renderWithProvider(ExampleComponent) {
const { showRtl, theme } = this.state

const newTheme: IThemeInput = {
componentVariables: theme.componentVariables,
rtl: showRtl,
}

return (
<Provider componentVariables={this.state.componentVariables} rtl={this.state.showRtl}>
<Provider theme={mergeThemes(teamsTheme, newTheme)}>
<ExampleComponent knobs={this.getKnobsValue()} />
</Provider>
)
Expand Down Expand Up @@ -534,34 +534,36 @@ class ComponentExample extends React.PureComponent<IComponentExampleProps, IComp
<span style={{ opacity: 0.5 }}>Theme</span>
</Divider>
<Provider.Consumer
render={({ siteVariables }) => {
const variablesFilename = `./${displayName}/${_.camelCase(displayName)}Variables.ts`
const hasVariablesFile = _.includes(variablesContext.keys(), variablesFilename)
render={({ siteVariables, componentVariables }: IThemePrepared) => {
const variables = componentVariables[displayName]

if (!hasVariablesFile) {
if (!variables) {
return (
<Segment size="small" inverted padded basic>
This component has no variables to edit.
{displayName} has no variables to edit.
</Segment>
)
}

const variables = variablesContext(variablesFilename).default
const defaultVariables = variables(siteVariables)
const variablesObject = callable(variables)(siteVariables)

return (
<Form inverted style={{ padding: '1rem' }}>
<Form.Group inline style={variablesPanelStyle}>
{_.map(defaultVariables, (val, key) => (
<Form.Input
style={variableInputStyle}
key={key}
label={key}
defaultValue={val}
onChange={this.handleVariableChange(displayName, key)}
/>
))}
</Form.Group>
<Form inverted widths="equal" style={{ padding: '1rem' }}>
{_.chunk(_.toPairs(variablesObject), 2).map(fields => {
return (
<Form.Group widths="equal" key={fields.map(([key]) => key).join('-')}>
{fields.map(([key, val]) => (
<Form.Input
fluid
key={key}
label={key}
defaultValue={val}
onChange={this.handleVariableChange(displayName, key)}
/>
))}
</Form.Group>
)
})}
</Form>
)
}}
Expand All @@ -573,11 +575,14 @@ class ComponentExample extends React.PureComponent<IComponentExampleProps, IComp
private handleVariableChange = (component, variable) => (e, { value }) => {
this.setState(
state => ({
componentVariables: {
...state.componentVariables,
[component]: {
...(state.componentVariables && state.componentVariables[component]),
[variable]: value,
theme: {
...state.theme,
componentVariables: {
...state.theme.componentVariables,
[component]: {
...(state.theme.componentVariables && state.theme.componentVariables[component]),
[variable]: value,
},
},
},
}),
Expand Down
4 changes: 2 additions & 2 deletions docs/src/components/IconSearch/IconSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as _ from 'lodash/fp'
import leven from 'leven'
import * as React from 'react'

import { SUI } from 'src/lib'
import { SUI, toCompactArray } from 'src/lib'
import { Form, Grid, Header, Icon, Message, Popup } from 'semantic-ui-react'

const gridStyle = {
Expand Down Expand Up @@ -107,7 +107,7 @@ export default class IconSearch extends React.Component<any, any> {

renderIconColumn = (name, section?: string) => (
<Popup
key={[name, section].filter(Boolean).join('_')}
key={toCompactArray(name, section).join('_')}
mouseEnterDelay={1000}
inverted
closeOnTriggerClick={false}
Expand Down
40 changes: 2 additions & 38 deletions docs/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'

// TODO make themes a monorepo of packages
import { siteVariables, normalizeCSS } from 'src/themes/teams'
import { fontFaces, staticStyles, theme } from 'src/themes/teams'
import { Provider } from '@stardust-ui/react'

import Router from './routes'
Expand All @@ -15,46 +15,10 @@ import Router from './routes'
const mountNode = document.createElement('div')
document.body.appendChild(mountNode)

const globalRules = siteVars => ({
html: {
fontSize: siteVars.htmlFontSize,
},
body: {
padding: siteVars.bodyPadding,
margin: siteVars.bodyMargin,
fontFamily: siteVars.bodyFontFamily,
fontSize: siteVars.bodyFontSize,
lineHeight: siteVars.lineHeightBase,
},
})

// TODO: formalize fonts into siteVariables/theme contract. Leaving hard coded for now.
const fontFaces = [
{
name: 'Segoe UI',
paths: ['public/fonts/segoe-ui-regular.woff2'],
style: { fontWeight: 400 },
},
{
name: 'Segoe UI',
paths: ['public/fonts/segoe-ui-semibold.woff2'],
style: { fontWeight: 600 },
},
{
name: 'Segoe UI',
paths: ['public/fonts/segoe-ui-bold.woff2'],
style: { fontWeight: 700 },
},
]

const render = NewApp =>
ReactDOM.render(
<AppContainer>
<Provider
siteVariables={siteVariables}
staticStyles={[normalizeCSS, globalRules]}
fontFaces={fontFaces}
>
<Provider theme={theme} staticStyles={staticStyles} fontFaces={fontFaces}>
<NewApp />
</Provider>
</AppContainer>,
Expand Down
1 change: 0 additions & 1 deletion docs/src/utils/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ export { default as getFormattedHash } from './getFormattedHash'
export { default as getInfoForSeeTags } from './getInfoForSeeTags'
export { default as knobsContext } from './knobsContext'
export { default as parseExamplePath } from './parseExamplePath'
export { default as variablesContext } from './variablesContext'
export { default as scrollToAnchor } from './scrollToAnchor'
6 changes: 0 additions & 6 deletions docs/src/utils/variablesContext.ts

This file was deleted.

4 changes: 2 additions & 2 deletions specifications/creating-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const Image = (props) => {
}

export default createComponent(Image, {
rules: imageStyles,
styles: imageStyles,
variables: imageVariables,
})
```
Expand All @@ -73,7 +73,7 @@ class Image extends React.Component {
handledProps={[ ... ]}
state={StateMachine}
props={this.props}
rules={...}
styles={...}
variables={...}
render={({ ElementType, rest, classes }) => (
<ElementType {...rest} className={classes.root} />
Expand Down
3 changes: 0 additions & 3 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as PropTypes from 'prop-types'
import * as React from 'react'

import { AutoControlledComponent, customPropTypes, childrenExist } from '../../lib'
import accordionStyles from '../../themes/teams/components/Accordion/accordionStyles'
import AccordionTitle from './AccordionTitle'
import AccordionContent from './AccordionContent'
import { DefaultBehavior } from '../../lib/accessibility'
Expand Down Expand Up @@ -66,8 +65,6 @@ class Accordion extends AutoControlledComponent<any, any> {
accessibility: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
}

static styles = accordionStyles

static handledProps = [
'accessibility',
'activeIndex',
Expand Down
6 changes: 0 additions & 6 deletions src/components/Accordion/AccordionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import * as PropTypes from 'prop-types'
import * as React from 'react'

import { childrenExist, createShorthandFactory, customPropTypes, UIComponent } from '../../lib'
import accordionContentStyles from '../../themes/teams/components/Accordion/accordionContentStyles'
import accordionContentVariables from '../../themes/teams/components/Accordion/accordionContentVariables'

/**
* A standard AccordionContent.
Expand Down Expand Up @@ -42,10 +40,6 @@ class AccordionContent extends UIComponent<any, any> {

static handledProps = ['as', 'active', 'children', 'className', 'content', 'onClick']

static styles = accordionContentStyles

static variables = accordionContentVariables

renderComponent({ ElementType, classes, rest }) {
const { children, content } = this.props

Expand Down
3 changes: 0 additions & 3 deletions src/components/Accordion/AccordionTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as PropTypes from 'prop-types'
import * as React from 'react'

import { childrenExist, createShorthandFactory, customPropTypes, UIComponent } from '../../lib'
import accordionTitleStyles from '../../themes/teams/components/Accordion/accordionTitleStyles'

/**
* A standard AccordionTitle.
Expand Down Expand Up @@ -45,8 +44,6 @@ class AccordionTitle extends UIComponent<any, any> {

static handledProps = ['as', 'active', 'children', 'className', 'content', 'index', 'onClick']

static styles = accordionTitleStyles

handleClick = e => {
_.invoke(this.props, 'onClick', e, this.props)
}
Expand Down
6 changes: 0 additions & 6 deletions src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import * as React from 'react'
import { Image, Label, Icon } from '../../'

import { customPropTypes, UIComponent } from '../../lib'
import avatarStyles from '../../themes/teams/components/Avatar/avatarStyles'
import avatarVariables from '../../themes/teams/components/Avatar/avatarVariables'

/**
* An avatar is a graphic representation of user alongside with a presence icon.
Expand All @@ -18,10 +16,6 @@ class Avatar extends UIComponent<any, any> {

static handledProps = ['alt', 'as', 'className', 'getInitials', 'name', 'size', 'src', 'status']

static styles = avatarStyles

static variables = avatarVariables

static propTypes = {
/** The alternative text for the image used in the Avatar. */
alt: PropTypes.string,
Expand Down
6 changes: 0 additions & 6 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import * as PropTypes from 'prop-types'
import * as React from 'react'

import { UIComponent, childrenExist, customPropTypes } from '../../lib'
import buttonStyles from '../../themes/teams/components/Button/buttonStyles'
import buttonVariables from '../../themes/teams/components/Button/buttonVariables'
import Icon from '../Icon'
import Text from '../Text'
import { ButtonBehavior } from '../../lib/accessibility'
Expand All @@ -18,10 +16,6 @@ class Button extends UIComponent<any, any> {

public static className = 'ui-button'

public static styles = buttonStyles

public static variables = buttonVariables

public static propTypes = {
/** An element type to render as (string or function). */
as: customPropTypes.as,
Expand Down
Loading

0 comments on commit e345edf

Please sign in to comment.