From 44409ccbadfa7e80705bfe27a735b39edd1dd0cb Mon Sep 17 00:00:00 2001 From: Roman Kuzhelev Date: Mon, 9 Jul 2018 14:35:35 +0300 Subject: [PATCH 1/2] prevent content of variables panel to overflow horizontal boundaries --- .../ComponentExample/ComponentExample.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx b/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx index 16647aa80b..3696aec68a 100644 --- a/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx +++ b/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx @@ -39,6 +39,17 @@ const controlsWrapperStyle = { minHeight: pxToRem(30), } +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. @@ -238,6 +249,8 @@ class ComponentExample extends PureComponent { renderSourceCode = _.debounce(() => { try { const Example = evalTypeScript(this.state.sourceCode) + + console.log(Example) const exampleElement = _.isFunction(Example) ? this.renderWithProvider(Example) : Example if (!isValidElement(exampleElement)) { @@ -297,6 +310,9 @@ class ComponentExample extends PureComponent { getComponentName = () => this.props.examplePath.split('/')[1] renderWithProvider(ExampleComponent) { + console.log('renderWithProvider:state', this.state) + console.log('renderWithProivder:componentVariables', this.state.componentVariables) + return ( @@ -439,9 +455,10 @@ class ComponentExample extends PureComponent { return (
- + {_.map(defaultVariables, (val, key) => ( { componentVariables: { ...state.componentVariables, [component]: { - ...(state.componentVariables && state.componentVariables[component]), [variable]: value, }, }, From 5a68d1f9338e036ddacb755cb4970bd2f070dc23 Mon Sep 17 00:00:00 2001 From: Roman Kuzhelev Date: Thu, 12 Jul 2018 22:00:22 +0200 Subject: [PATCH 2/2] original version --- .../ComponentExample/ComponentExample.tsx | 26 ++++-- src/components/Button/buttonRules.ts | 39 +++------ src/components/Button/buttonVariables.ts | 2 +- src/components/Provider/Provider.tsx | 7 +- src/lib/getClasses.tsx | 9 ++- src/lib/index.ts | 7 ++ src/lib/renderComponent.tsx | 2 +- src/lib/variablesTracking.ts | 81 +++++++++++++++++++ 8 files changed, 134 insertions(+), 39 deletions(-) create mode 100644 src/lib/variablesTracking.ts diff --git a/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx b/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx index 3696aec68a..09437c1a31 100644 --- a/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx +++ b/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx @@ -6,7 +6,7 @@ import { renderToStaticMarkup } from 'react-dom/server' import { html } from 'js-beautify' import copyToClipboard from 'copy-to-clipboard' import { Divider, Form, Grid, Menu, Segment, Visibility } from 'semantic-ui-react' -import { pxToRem } from 'src/lib' +import { pxToRem, createSpy, ActiveVariablesTracker } from 'src/lib' import evalTypeScript from 'docs/src/utils/evalTypeScript' import { Provider } from 'stardust' @@ -42,6 +42,7 @@ const controlsWrapperStyle = { const variablesPanelStyle = { display: 'flex', flexWrap: 'wrap', + justifyContent: 'space-between', maxHeight: pxToRem(250), overflowY: 'auto', } @@ -62,6 +63,7 @@ class ComponentExample extends PureComponent { KnobsComponent: any ghBugHref: any ghEditHref: any + variablesTracker = new ActiveVariablesTracker() static contextTypes = { onPassed: PropTypes.func, @@ -249,8 +251,6 @@ class ComponentExample extends PureComponent { renderSourceCode = _.debounce(() => { try { const Example = evalTypeScript(this.state.sourceCode) - - console.log(Example) const exampleElement = _.isFunction(Example) ? this.renderWithProvider(Example) : Example if (!isValidElement(exampleElement)) { @@ -310,11 +310,21 @@ class ComponentExample extends PureComponent { getComponentName = () => this.props.examplePath.split('/')[1] renderWithProvider(ExampleComponent) { - console.log('renderWithProvider:state', this.state) - console.log('renderWithProivder:componentVariables', this.state.componentVariables) + const variableSpies = createSpy({ + componentDisplayName: this.getComponentName(), + onVariableTouched: variableName => this.variablesTracker.registerAsActive(variableName), + whenApplied: () => { + this.variablesTracker.isEnabled = true + }, + }) + this.variablesTracker.resetAndDisable() return ( - + ) @@ -458,6 +468,9 @@ class ComponentExample extends PureComponent { {_.map(defaultVariables, (val, key) => ( { componentVariables: { ...state.componentVariables, [component]: { + ...(state.componentVariables && state.componentVariables[component]), [variable]: value, }, }, diff --git a/src/components/Button/buttonRules.ts b/src/components/Button/buttonRules.ts index 43b41c261c..dfaa1de8f5 100644 --- a/src/components/Button/buttonRules.ts +++ b/src/components/Button/buttonRules.ts @@ -1,51 +1,38 @@ import { IButtonVariables } from './buttonVariables' export default { - root: ({ props, theme, variables }) => { - const { - backgroundColor, - backgroundColorHover, - circularRadius, - circularWidth, - typePrimaryColor, - typePrimaryBackgroundColor, - typePrimaryBackgroundColorHover, - typePrimaryBorderColor, - typeSecondaryColor, - typeSecondaryBackgroundColor, - typeSecondaryBackgroundColorHover, - typeSecondaryBorderColor, - }: IButtonVariables = variables + root: ({ props, theme, trackVariables }) => { + const v = trackVariables() return { - backgroundColor, + backgroundColor: v.backgroundColor, display: 'inline-block', verticalAlign: 'middle', cursor: 'pointer', borderWidth: 0, ':hover': { - backgroundColor: backgroundColorHover, + backgroundColor: v.backgroundColorHover, }, - ...(props.circular && { borderRadius: circularRadius, width: circularWidth }), + ...(props.circular && { borderRadius: v.circularRadius, width: v.circularWidth }), ...(props.type === 'primary' && { - color: typePrimaryColor, - backgroundColor: typePrimaryBackgroundColor, - borderColor: typePrimaryBorderColor, + color: v.typePrimaryColor, + backgroundColor: v.typePrimaryBackgroundColor, + borderColor: v.typePrimaryBorderColor, ':hover': { - backgroundColor: typePrimaryBackgroundColorHover, + backgroundColor: v.typePrimaryBackgroundColorHover, }, }), ...(props.type === 'secondary' && { - color: typeSecondaryColor, - backgroundColor: typeSecondaryBackgroundColor, - borderColor: typeSecondaryBorderColor, + color: v.typeSecondaryColor, + backgroundColor: v.typeSecondaryBackgroundColor, + borderColor: v.typeSecondaryBorderColor, borderWidth: '2px', ':hover': { borderColor: 'transparent', - backgroundColor: typeSecondaryBackgroundColorHover, + backgroundColor: v.typeSecondaryBackgroundColorHover, }, }), } diff --git a/src/components/Button/buttonVariables.ts b/src/components/Button/buttonVariables.ts index 39c76404bf..9b89f9b8a3 100644 --- a/src/components/Button/buttonVariables.ts +++ b/src/components/Button/buttonVariables.ts @@ -20,7 +20,7 @@ export default (siteVars: any): IButtonVariables => { backgroundColor: siteVars.gray08, backgroundColorHover: siteVars.gray06, circularRadius: pxToRem(999), - circularWidth: '32px', + circularWidth: pxToRem(32), typePrimaryColor: siteVars.white, typePrimaryBackgroundColor: siteVars.brand, typePrimaryBackgroundColorHover: siteVars.brand04, diff --git a/src/components/Provider/Provider.tsx b/src/components/Provider/Provider.tsx index 7085a968e2..a6dba58535 100644 --- a/src/components/Provider/Provider.tsx +++ b/src/components/Provider/Provider.tsx @@ -90,10 +90,8 @@ class Provider extends Component { } render() { - const { componentVariables, siteVariables, children } = this.props + const { componentVariables, siteVariables, children, variableSpies } = this.props - // ensure we don't assign `undefined` values to the theme context - // they will override values down stream const theme: any = {} if (siteVariables) { theme.siteVariables = siteVariables @@ -101,6 +99,9 @@ class Provider extends Component { if (componentVariables) { theme.componentVariables = componentVariables } + if (variableSpies) { + theme.spies = variableSpies + } return ( diff --git a/src/lib/getClasses.tsx b/src/lib/getClasses.tsx index c4a78d2811..fd5d1a625c 100644 --- a/src/lib/getClasses.tsx +++ b/src/lib/getClasses.tsx @@ -1,22 +1,27 @@ import renderer from './felaRenderer' +import { connect } from './variablesTracking' export interface IClasses { [key: string]: string } /** + * @param displayName * @param rules * @param props * @param variables * @param theme * @returns {{}} */ -const getClasses = (props, rules, variables: any = () => {}, theme: any = {}): IClasses => { +const getClasses = (displayName: string, props, rules, getVariables: any = () => {}, theme: any = {}): IClasses => { const { renderRule } = renderer + const variables = getVariables(theme.siteVariables) + const ruleProps = { props, theme, - variables: variables(theme.siteVariables), + variables: variables, + trackVariables: connect(theme.spies || {}, displayName, variables) } return Object.keys(rules).reduce((acc, ruleName) => { diff --git a/src/lib/index.ts b/src/lib/index.ts index 2af176621c..7119e64874 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -36,3 +36,10 @@ export { default as leven } from './leven' export { pxToRem, setHTMLFontSize } from './fontSizeUtility' export { customPropTypes, SUI } + +export { + createSpy, + connect, + ActiveVariablesTracker, + WithTrackedVariables, +} from './variablesTracking' diff --git a/src/lib/renderComponent.tsx b/src/lib/renderComponent.tsx index aa98caf579..e1c11812a8 100644 --- a/src/lib/renderComponent.tsx +++ b/src/lib/renderComponent.tsx @@ -45,7 +45,7 @@ const renderComponent =

( const mergedVariables = () => Object.assign({}, variablesFromFile, variablesFromTheme, variablesFromProp) - const classes = getClasses(props, rules, mergedVariables, theme) + const classes = getClasses(displayName, props, rules, mergedVariables, theme) classes.root = cx(className, classes.root, props.className) const config: IRenderResultConfig

= { ElementType, rest, classes } diff --git a/src/lib/variablesTracking.ts b/src/lib/variablesTracking.ts new file mode 100644 index 0000000000..3453f19af8 --- /dev/null +++ b/src/lib/variablesTracking.ts @@ -0,0 +1,81 @@ +export type WithTrackedVariables = TProps extends { variables: infer TVariables } + ? TProps & { trackVariables: () => TVariables } + : never + +type VariableTouchedCallback = (variableName: string | number | symbol) => void + +interface VariableSpy { + onVariableTouched: VariableTouchedCallback + whenApplied: () => void +} + +interface PerComponentVariableSpies { + [componentDisplayName: string]: VariableSpy +} + +interface VariableSpySpec { + componentDisplayName: string + onVariableTouched: VariableTouchedCallback + whenApplied?: () => void +} + +const DoNothing = () => {} + +export function createSpy(spec: VariableSpySpec): PerComponentVariableSpies { + const { componentDisplayName, onVariableTouched, whenApplied } = spec + + return { + [componentDisplayName]: { + onVariableTouched, + whenApplied: whenApplied || DoNothing, + }, + } +} + +function createTrackedVariablesProvider( + spy: VariableSpy, + variables: TVariables, +): () => TVariables { + return () => { + spy.whenApplied() + + return new Proxy(variables, { + get: (it, variableName) => { + spy.onVariableTouched(variableName) + return it[variableName] + }, + }) + } +} + +export function connect( + spies: PerComponentVariableSpies, + componentDisplayName: string, + variables: TVariables, +): () => TVariables { + if (!spies || !spies[componentDisplayName]) { + return () => variables + } + + return createTrackedVariablesProvider(spies[componentDisplayName], variables) +} + +export class ActiveVariablesTracker { + isEnabled = false + activeVariables: (string | number | symbol)[] = [] + + registerAsActive(variableName: string | number | symbol): void { + if (!this.activeVariables.some(v => v === variableName)) { + this.activeVariables.push(variableName) + } + } + + isActive(variableName: string | number | symbol): boolean { + return this.activeVariables.some(v => v === variableName) + } + + resetAndDisable(): void { + this.isEnabled = false + this.activeVariables = [] + } +}