From b54bc6599a25c064d66c684a6bfe95bba0c883c8 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Thu, 11 Jul 2019 08:18:45 +0200 Subject: [PATCH 1/2] Adds integration test for combined up/down responsive props --- .../integration/responsive-props/down.test.js | 31 +++++++++++++++++++ cypress/integration/responsive-props/index.js | 1 + .../ResponsiveProps/BreakpointSpecific.jsx | 5 +-- examples/Core/ResponsiveProps/Down.jsx | 31 +++++++++++++++++++ .../Core/ResponsiveProps/InclusiveNotch.jsx | 5 +-- examples/Core/ResponsiveProps/MobileFirst.jsx | 5 +-- examples/index.js | 2 ++ 7 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 cypress/integration/responsive-props/down.test.js create mode 100644 examples/Core/ResponsiveProps/Down.jsx diff --git a/cypress/integration/responsive-props/down.test.js b/cypress/integration/responsive-props/down.test.js new file mode 100644 index 00000000..9372e8e7 --- /dev/null +++ b/cypress/integration/responsive-props/down.test.js @@ -0,0 +1,31 @@ +describe('Behavior: "down"', () => { + before(() => { + cy.loadStory(['core'], ['responsive-props', 'down']) + }) + + it('with multiple "down" props', () => { + const assertPadding = (values) => { + return cy.get('#composition-down').should('have.css', 'padding', values) + } + + assertPadding('10px') + cy.setBreakpoint('sm').then(() => assertPadding('20px')) + cy.setBreakpoint('md').then(() => assertPadding('30px')) + cy.setBreakpoint('lg').then(() => assertPadding('40px')) + cy.setBreakpoint('xl').then(() => assertPadding('50px')) + }) + + it('when combined with "up" sibling', () => { + const assertTemplateCols = (value) => { + return cy + .get('#composition-combination') + .should('have.css', 'grid-template-columns', value) + } + + assertTemplateCols('200px') + cy.setBreakpoint('sm').then(() => assertTemplateCols('100px')) + cy.setBreakpoint('md').then(() => assertTemplateCols('200px')) + cy.setBreakpoint('lg').then(() => assertTemplateCols('200px')) + cy.setBreakpoint('xl').then(() => assertTemplateCols('200px')) + }) +}) diff --git a/cypress/integration/responsive-props/index.js b/cypress/integration/responsive-props/index.js index d7d76517..c9539578 100644 --- a/cypress/integration/responsive-props/index.js +++ b/cypress/integration/responsive-props/index.js @@ -2,4 +2,5 @@ describe('Responsive props', () => { require('./mobile-first.test') require('./breakpoint-specific.test') require('./bell-notch.test') + require('./down.test') }) diff --git a/examples/Core/ResponsiveProps/BreakpointSpecific.jsx b/examples/Core/ResponsiveProps/BreakpointSpecific.jsx index 4a0fdda5..fde77191 100644 --- a/examples/Core/ResponsiveProps/BreakpointSpecific.jsx +++ b/examples/Core/ResponsiveProps/BreakpointSpecific.jsx @@ -1,10 +1,7 @@ import React from 'react' -import Layout, { Composition } from 'atomic-layout' -// import resetLayout from '../resetLayout' +import { Composition } from 'atomic-layout' import Square from '@stories/Square' -// resetLayout(Layout) - const BreakpointSpecific = () => ( ( + <> + {/* Multiple "down" behaviors */} + + First + + + {/* Combination of "up" and "down" */} + + First + Second + + +) +export default DownResponsiveProps diff --git a/examples/Core/ResponsiveProps/InclusiveNotch.jsx b/examples/Core/ResponsiveProps/InclusiveNotch.jsx index d1a8b351..493013a8 100644 --- a/examples/Core/ResponsiveProps/InclusiveNotch.jsx +++ b/examples/Core/ResponsiveProps/InclusiveNotch.jsx @@ -1,10 +1,7 @@ import React from 'react' -import Layout, { Composition } from 'atomic-layout' -// import resetLayout from '../resetLayout' +import { Composition } from 'atomic-layout' import Square from '@stories/Square' -// resetLayout(Layout) - const template = 'first second' const InclusiveNotch = () => ( diff --git a/examples/Core/ResponsiveProps/MobileFirst.jsx b/examples/Core/ResponsiveProps/MobileFirst.jsx index 12834907..0b55e1ed 100644 --- a/examples/Core/ResponsiveProps/MobileFirst.jsx +++ b/examples/Core/ResponsiveProps/MobileFirst.jsx @@ -1,10 +1,7 @@ import React from 'react' -import Layout, { Composition } from 'atomic-layout' -// import resetLayout from '../resetLayout' +import { Composition } from 'atomic-layout' import Square from '@stories/Square' -// resetLayout(Layout) - const Foo = () => ( {({ First, Second }) => ( diff --git a/examples/index.js b/examples/index.js index 6c1c2055..b75c8bb2 100644 --- a/examples/index.js +++ b/examples/index.js @@ -22,11 +22,13 @@ storiesOf('Core|Rendering', module) import MobileFirstResponsiveProps from './Core/ResponsiveProps/MobileFirst' import BreakpointSpecificResponsiveProps from './Core/ResponsiveProps/BreakpointSpecific' import InclusiveNotchResponsiveProps from './Core/ResponsiveProps/InclusiveNotch' +import DownResponsiveProps from './Core/ResponsiveProps/Down' storiesOf('Core|Responsive props', module) .add('Mobile-first', () => ) .add('Breakpoint-specific', () => ) .add('Inclusive-notch', () => ) + .add('Down', () => ) /** * Configuration From e1a9dd00829e799e75ef6b9175010026f4124d4d Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Fri, 12 Jul 2019 15:37:22 +0200 Subject: [PATCH 2/2] WIP: Tests multiple "down" responsive props --- examples/Core/ResponsiveProps/Down.jsx | 4 +- .../closeBreakpoint/closeBreakpoint.ts | 4 +- src/utils/styles/applyStyles/applyStyles.ts | 157 ++++++++++++++---- 3 files changed, 126 insertions(+), 39 deletions(-) diff --git a/examples/Core/ResponsiveProps/Down.jsx b/examples/Core/ResponsiveProps/Down.jsx index fb1c6378..8b24dab1 100644 --- a/examples/Core/ResponsiveProps/Down.jsx +++ b/examples/Core/ResponsiveProps/Down.jsx @@ -17,7 +17,7 @@ const DownResponsiveProps = () => ( {/* Combination of "up" and "down" */} - ( > First Second - + */} ) export default DownResponsiveProps diff --git a/src/utils/breakpoints/closeBreakpoint/closeBreakpoint.ts b/src/utils/breakpoints/closeBreakpoint/closeBreakpoint.ts index 99981a3c..7d2bf8bb 100644 --- a/src/utils/breakpoints/closeBreakpoint/closeBreakpoint.ts +++ b/src/utils/breakpoints/closeBreakpoint/closeBreakpoint.ts @@ -8,10 +8,10 @@ import getPrefix from '@utils/strings/getPrefix' * of the original breakpoint are omitted. * * @example - * flipBreakpoint({ minWidth: 500, maxWidth: 600 }) + * closeBreakpoint({ minWidth: 500, maxWidth: 600 }) * // { maxWidth: 499 } */ -export default function flipBreakpoint(breakpoint: Breakpoint): Breakpoint { +export default function closeBreakpoint(breakpoint: Breakpoint): Breakpoint { return Object.entries(breakpoint) .map(([propName, propValue]) => [getPrefix(propName), propName, propValue]) .filter(([prefix]) => prefix !== 'max') diff --git a/src/utils/styles/applyStyles/applyStyles.ts b/src/utils/styles/applyStyles/applyStyles.ts index bacc88e9..d9dd2270 100644 --- a/src/utils/styles/applyStyles/applyStyles.ts +++ b/src/utils/styles/applyStyles/applyStyles.ts @@ -3,57 +3,144 @@ import { BreakpointBehavior } from '@const/defaultOptions' import propAliases from '@const/propAliases' import parsePropName, { Props } from '@utils/strings/parsePropName' import isset from '@utils/functions/isset' +import mergeBreakpoints from '@utils/breakpoints/mergeBreakpoints' import createMediaQuery from '../createMediaQuery' +import { AreaBreakpoint } from '@src/utils/breakpoints/getAreaBreakpoints' const createStyleString = ( propsList: string[], propValue: any, - breakpoint: any, - behavior: BreakpointBehavior, + areaBreakpoint: { + isDefault: boolean + values: AreaBreakpoint + }, + // breakpoint: any, + // behavior: BreakpointBehavior, ) => { + const { isDefault, values } = areaBreakpoint + const { behavior, ...breakpoint } = values const styleProps = propsList .map((propName) => `${propName}:${String(propValue)};`) .join('') - const breakpointOptions = Layout.breakpoints[breakpoint.name] + console.warn('createStyleString') + console.log({ areaBreakpoint }) + console.log({ styleProps }) - /** - * Wrap CSS rule in a media query only if its prop includes - * a breakpoint and behavior different than the default ones. - */ + // const breakpointOptions = Layout.breakpoints[breakpoint.name] + + // Wrap CSS rule in a media query only if its prop includes + // a breakpoint and behavior different than the default ones. const shouldWrapInMediaQuery = - breakpointOptions && - !(breakpoint.isDefault && behavior === Layout.defaultBehavior) + !!areaBreakpoint && !(isDefault && behavior === Layout.defaultBehavior) + + console.log('has areaBreakpoint', !!areaBreakpoint) + console.log('isDefault?', isDefault) + console.log('has default beheavior', behavior === Layout.defaultBehavior) + console.log({ shouldWrapInMediaQuery }) + + if (shouldWrapInMediaQuery) { + console.log('SHOULD wrap in media query...') + const queryString = createMediaQuery(breakpoint, behavior) + console.log('given this breakpoint:', breakpoint) + console.log('given behavior:', behavior) + console.log({ queryString }) + return `@media ${queryString} {${styleProps}}` + } - return shouldWrapInMediaQuery - ? `@media ${createMediaQuery(breakpointOptions, behavior)} {${styleProps}}` - : styleProps + return styleProps } export default function applyStyles(pristineProps: Props): string { - return ( - Object.keys(pristineProps) - /* Parse each prop to include "breakpoint" and "behavior" */ - .map(parsePropName) - /* Filter out props that are not included in prop aliases */ - .filter(({ purePropName }) => propAliases.hasOwnProperty(purePropName)) - /* Filter out props with "undefined" or "null" as value */ - .filter(({ originPropName }) => isset(pristineProps[originPropName])) - /* Map each prop to a CSS string */ - .map(({ purePropName, originPropName, breakpoint, behavior }) => { - const { props, transformValue } = propAliases[purePropName] - const propValue = pristineProps[originPropName] - const transformedPropValue = transformValue - ? transformValue(propValue) - : propValue - - return createStyleString( - props, - transformedPropValue, - breakpoint, - behavior, + console.log('applyStyles for', pristineProps) + + const propsGroups = Object.keys(pristineProps) + /* Filter out props with "undefined" or "null" as value */ + .filter((propName) => isset(pristineProps[propName])) + /* Parse each prop to include "breakpoint" and "behavior" */ + .map(parsePropName) + /* Filter out props that are not included in prop aliases */ + .filter(({ purePropName }) => propAliases.hasOwnProperty(purePropName)) + /* Map each prop to a CSS string */ + .reduce((groups, parsedPropName) => { + const { purePropName } = parsedPropName + const nextGroupEntry = groups[purePropName] + ? groups[purePropName].concat(parsedPropName) + : [parsedPropName] + + return { + ...groups, + [purePropName]: nextGroupEntry, + } + }, {}) + + console.log(propsGroups) + + // This can be moved outside + const createStyle = (parsedPropName) => { + const { originPropName, purePropName, breakpoint } = parsedPropName + const { props, transformValue } = propAliases[purePropName] + const propValue = pristineProps[originPropName] + const transformedPropValue = transformValue + ? transformValue(propValue) + : propValue + + return createStyleString(props, transformedPropValue, breakpoint) + } + + const styles = Object.keys(propsGroups).reduce((acc, propName) => { + const declarations = propsGroups[propName] + const nextStyles = declarations.map((parsedPropName, index) => { + const { behavior, breakpoint } = parsedPropName + const goesDown = behavior === 'down' + const prevDeclaration = declarations[index - 1] + + // Prevent multiple "down" responsive props + // from overlapping. + if (goesDown && prevDeclaration && prevDeclaration.behavior === 'down') { + const actualBreakpoint = mergeBreakpoints( + { + ...Layout.breakpoints[breakpoint.name], + behavior, + }, + { + ...Layout.breakpoints[prevDeclaration.breakpoint.name], + behavior: 'up', + }, + true, ) + + // console.warn('combined down props!') + // console.log( + // `prop ${parsedPropName.originPropName} has a predcessor ${prevDeclaration.originPropName}`, + // ) + // console.log({ actualBreakpoint }) + + return createStyle({ + ...parsedPropName, + breakpoint: { + ...breakpoint, + behavior, + values: actualBreakpoint, + }, + }) + } + + return createStyle({ + ...parsedPropName, + breakpoint: { + ...breakpoint, + behavior, + values: Layout.breakpoints[breakpoint.name], + }, }) - .join(' ') - ) + }) + + return acc.concat(nextStyles) + }, []) + // .join(' ') + + console.log({ styles }) + + return styles }