diff --git a/packages/atomic-layout-core/src/index.ts b/packages/atomic-layout-core/src/index.ts index 4c6e868d..e830f1fe 100644 --- a/packages/atomic-layout-core/src/index.ts +++ b/packages/atomic-layout-core/src/index.ts @@ -52,6 +52,8 @@ export { /* Utilities */ export { default as warn } from './utils/functions/warn' export { default as compose } from './utils/functions/compose' +export { default as omit } from './utils/functions/omit' +export { default as omitProps } from './utils/functions/omit/omitProps' export { default as throttle } from './utils/functions/throttle' export { default as transformNumeric } from './utils/math/transformNumeric' export { default as memoizeWith } from './utils/functions/memoizeWith' diff --git a/packages/atomic-layout-core/src/utils/functions/memoizeWith/memoizeWith.ts b/packages/atomic-layout-core/src/utils/functions/memoizeWith/memoizeWith.ts index bc2ed341..fa30447b 100644 --- a/packages/atomic-layout-core/src/utils/functions/memoizeWith/memoizeWith.ts +++ b/packages/atomic-layout-core/src/utils/functions/memoizeWith/memoizeWith.ts @@ -1,7 +1,11 @@ -const memoizeWith = any>( +interface Cache { + [key: string]: any +} + +export default function memoizeWith any>( saltGenerator: (...args: Parameters) => string, -) => { - const cache: Record = {} +) { + const cache: Cache = {} return (func: F) => function(...args: Parameters): ReturnType { @@ -14,5 +18,3 @@ const memoizeWith = any>( return cache[key] } } - -export default memoizeWith diff --git a/packages/atomic-layout-core/src/utils/functions/omit/index.ts b/packages/atomic-layout-core/src/utils/functions/omit/index.ts new file mode 100644 index 00000000..5b997ae8 --- /dev/null +++ b/packages/atomic-layout-core/src/utils/functions/omit/index.ts @@ -0,0 +1,2 @@ +export { default } from './omit' +export * from './omit' diff --git a/packages/atomic-layout-core/src/utils/functions/omit/omit.spec.ts b/packages/atomic-layout-core/src/utils/functions/omit/omit.spec.ts new file mode 100644 index 00000000..b73db0bd --- /dev/null +++ b/packages/atomic-layout-core/src/utils/functions/omit/omit.spec.ts @@ -0,0 +1,26 @@ +import omit from './omit' + +describe('omit', () => { + describe('given an object and a set of keys to omit', () => { + let result: ReturnType + const obj = { + a: 1, + b: 2, + c: 3, + d: 4, + } + + beforeAll(() => { + result = omit(['b', 'd'], obj) + }) + + it('should not include omitted keys in the result', () => { + expect(result).not.toContain(['b', 'c']) + }) + + it('should preserve unaffected keys', () => { + expect(result).toHaveProperty('a', 1) + expect(result).toHaveProperty('c', 3) + }) + }) +}) diff --git a/packages/atomic-layout-core/src/utils/functions/omit/omit.ts b/packages/atomic-layout-core/src/utils/functions/omit/omit.ts new file mode 100644 index 00000000..3d3b818a --- /dev/null +++ b/packages/atomic-layout-core/src/utils/functions/omit/omit.ts @@ -0,0 +1,12 @@ +export default function omit>( + keys: string[], + obj: R, +): Omit { + return Object.keys(obj).reduce((acc, key) => { + if (!keys.includes(key)) { + acc[key] = (obj as any)[key] + } + + return acc + }, {}) +} diff --git a/packages/atomic-layout-core/src/utils/functions/omit/omitProps.ts b/packages/atomic-layout-core/src/utils/functions/omit/omitProps.ts new file mode 100644 index 00000000..b22f25e2 --- /dev/null +++ b/packages/atomic-layout-core/src/utils/functions/omit/omitProps.ts @@ -0,0 +1,37 @@ +import capitalize from '../../strings/capitalize' +import Layout from '../../../Layout' +import propAliases from '../../../const/propAliases' +import memoizeWith from '../memoizeWith' + +const breakpoints = Object.keys(Layout.breakpoints) +const responsiveProps = Object.keys(propAliases) +const allResponsiveProps = responsiveProps.reduce((acc, prop) => { + return acc.concat( + prop, + `${prop}Down`, + `${prop}Only`, + ...breakpoints.map((breakpointName) => { + const responsivePropName = `${prop}${capitalize(breakpointName)}` + return [`${responsivePropName}Down`, `${responsivePropName}Only`] + }), + ) +}, []) +const regExp = new RegExp(allResponsiveProps.join('|')) + +function omitProps

>(props: P) { + return Object.keys(props).reduce((acc, key) => { + if (!regExp.test(key)) { + acc[key] = props[key] + } + + return acc + }, {} as Record) +} + +const memoizeWithPairs = memoizeWith((props) => + Object.keys(props) + .map((key) => [key, props[key]]) + .join(), +) + +export default memoizeWithPairs(omitProps) diff --git a/packages/atomic-layout/src/components/Box.tsx b/packages/atomic-layout/src/components/Box.tsx index 7cbc8804..db8b1ab4 100644 --- a/packages/atomic-layout/src/components/Box.tsx +++ b/packages/atomic-layout/src/components/Box.tsx @@ -1,8 +1,10 @@ import * as React from 'react' import styled from 'styled-components' -import { BoxProps, applyStyles } from '@atomic-layout/core' +import { BoxProps, omitProps, applyStyles } from '@atomic-layout/core' -const Box: React.FC = styled.div` +const Box: React.FC = styled( + ({ as: As = 'div', ...rest }: BoxProps) => , +)` display: ${({ flex, inline }) => flex ? inline @@ -19,4 +21,12 @@ const Box: React.FC = styled.div` Box.displayName = 'Box' +/** + * @todo Export a regular Box by default to be used by Emotion, + * which does attributes clean up by default. + * Export a Box with responsive props omitted for styled-components + * version. + */ +// export const BoxWithoutAttributesPolution + export default Box diff --git a/packages/atomic-layout/src/components/Composition.tsx b/packages/atomic-layout/src/components/Composition.tsx index 2a205fcc..8652226d 100644 --- a/packages/atomic-layout/src/components/Composition.tsx +++ b/packages/atomic-layout/src/components/Composition.tsx @@ -9,6 +9,7 @@ import { generateComponents, applyStyles, warn, + omitProps, } from '@atomic-layout/core' import Box from './Box' import { withPlaceholder } from '../utils/withPlaceholder'