diff --git a/CHANGELOG.md b/CHANGELOG.md index e4f574d7fe..c499d50d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix Teams theme styles for `Alert` [redlines] @codepretty ([#1226](https://github.com/stardust-ui/react/pull/1226)) - Fix selected status of `Dropdown` when focus is on the `List` @silviuavram ([#1258](https://github.com/stardust-ui/react/pull/1258)) - Fix `propTypes` warning in `ListItem` @layershifter ([#1266](https://github.com/stardust-ui/react/pull/1266)) +- Expand css shorthands for correct merging of the styles @mnajdova ([#869](https://github.com/stardust-ui/react/pull/869)) ## [v0.29.0](https://github.com/stardust-ui/react/tree/v0.29.0) (2019-04-24) diff --git a/build/gulp/tasks/test-projects.tsx b/build/gulp/tasks/test-projects.tsx index be6da9f299..747f4c422e 100644 --- a/build/gulp/tasks/test-projects.tsx +++ b/build/gulp/tasks/test-projects.tsx @@ -179,6 +179,7 @@ task('test:projects:rollup', async () => { 'rollup-plugin-replace', 'rollup-plugin-commonjs', 'rollup-plugin-node-resolve', + 'rollup-plugin-json', 'react', 'react-dom', ].join(' ') diff --git a/build/gulp/tasks/test-projects/rollup/rollup.config.js b/build/gulp/tasks/test-projects/rollup/rollup.config.js index 830cf3496c..5c84b1f362 100644 --- a/build/gulp/tasks/test-projects/rollup/rollup.config.js +++ b/build/gulp/tasks/test-projects/rollup/rollup.config.js @@ -1,6 +1,7 @@ import replace from 'rollup-plugin-replace' import resolve from 'rollup-plugin-node-resolve' import commonjs from 'rollup-plugin-commonjs' +import json from 'rollup-plugin-json' const warningWhitelist = [ 'THIS_IS_UNDEFINED', // comes from TS transforms @@ -57,5 +58,10 @@ export default { ], }, }), + json({ + include: 'node_modules/**', + preferConst: true, + compact: true, + }), ], } diff --git a/packages/react/package.json b/packages/react/package.json index fff33b88a6..24884f17ce 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -9,7 +9,9 @@ "@stardust-ui/react-component-nesting-registry": "^0.29.0", "@stardust-ui/react-proptypes": "^0.29.0", "classnames": "^2.2.5", + "css-shorthand-expand": "^1.2.0", "downshift": "^3.2.6", + "fast-memoize": "^2.5.1", "fela": "^10.2.0", "fela-plugin-fallback-value": "^10.2.0", "fela-plugin-placeholder-prefixer": "^10.2.0", diff --git a/packages/react/src/lib/felaExpandCssShorthandsPlugin.ts b/packages/react/src/lib/felaExpandCssShorthandsPlugin.ts new file mode 100644 index 0000000000..2369e6f49d --- /dev/null +++ b/packages/react/src/lib/felaExpandCssShorthandsPlugin.ts @@ -0,0 +1,56 @@ +import * as _ from 'lodash' +import * as _expand from 'css-shorthand-expand' +import * as _memoize from 'fast-memoize' + +// `fast-memoize` is a CJS library, there are known issues with them: +// https://github.com/rollup/rollup/issues/1267#issuecomment-446681320 +const memoize = (_memoize as any).default || _memoize + +// `css-shorthand-expand` is a CJS library, there are known issues with them: +// https://github.com/rollup/rollup/issues/1267#issuecomment-446681320 +const expand = memoize((_expand as any).default || _expand) + +// _.camelCase is quite fast, but we are running it for the same values many times +const camelCase = memoize(_.camelCase) + +const handledCssPropsMap = { + font: 'font', + padding: 'padding', + margin: 'margin', + border: 'border', + borderWidth: 'border-width', + borderStyle: 'border-style', + borderColor: 'border-color', + borderTop: 'border-top', + borderRight: 'border-right', + borderBottom: 'border-bottom', + borderLeft: 'border-left', + borderRadius: 'border-radius', + background: 'background', + outline: 'outline', +} + +export default () => { + const expandCssShorthands = (styles: Object) => { + return Object.keys(styles).reduce((acc, cssPropertyName) => { + const cssPropertyValue = styles[cssPropertyName] + + if (typeof cssPropertyValue === 'object') { + return { ...acc, [cssPropertyName]: expandCssShorthands(cssPropertyValue) } + } + + if (handledCssPropsMap[cssPropertyName]) { + const expandedProps = expand(handledCssPropsMap[cssPropertyName], `${cssPropertyValue}`) + if (expandedProps) { + return { ...acc, ...convertKeysToCamelCase(expandedProps) } + } + } + + return { ...acc, [cssPropertyName]: cssPropertyValue } + }, {}) + } + + return expandCssShorthands +} + +const convertKeysToCamelCase = obj => _.mapKeys(obj, (value, key) => camelCase(key)) diff --git a/packages/react/src/lib/felaRenderer.tsx b/packages/react/src/lib/felaRenderer.tsx index 2bb3e9a074..14abf66444 100644 --- a/packages/react/src/lib/felaRenderer.tsx +++ b/packages/react/src/lib/felaRenderer.tsx @@ -1,5 +1,6 @@ import { createRenderer } from 'fela' import felaSanitizeCss from './felaSanitizeCssPlugin' +import felaExpandCssShorthandsPlugin from './felaExpandCssShorthandsPlugin' import felaPluginFallbackValue from 'fela-plugin-fallback-value' import felaPluginPlaceholderPrefixer from 'fela-plugin-placeholder-prefixer' import felaPluginPrefixer from 'fela-plugin-prefixer' @@ -50,6 +51,7 @@ const createRendererConfig = (options: any = {}) => ({ skip: ['content'], }), + felaExpandCssShorthandsPlugin(), felaPluginPlaceholderPrefixer(), felaPluginPrefixer(), diff --git a/packages/react/src/themes/teams/components/Segment/segmentStyles.ts b/packages/react/src/themes/teams/components/Segment/segmentStyles.ts index a15b1a2498..80230c8441 100644 --- a/packages/react/src/themes/teams/components/Segment/segmentStyles.ts +++ b/packages/react/src/themes/teams/components/Segment/segmentStyles.ts @@ -15,7 +15,7 @@ const segmentStyles: ComponentSlotStylesInput = boxShadow: `0 1px 1px 1px ${v.boxShadowColor}`, color: v.color, backgroundColor: v.backgroundColor, - borderColor: segmentColor, + ...(p.color && { borderColor: segmentColor }), ...(p.inverted && { color: v.backgroundColor, backgroundColor: segmentColor || v.color, diff --git a/packages/react/test/specs/lib/felaExpandCssShorthandsPlugin-test.ts b/packages/react/test/specs/lib/felaExpandCssShorthandsPlugin-test.ts new file mode 100644 index 0000000000..047b1be0c6 --- /dev/null +++ b/packages/react/test/specs/lib/felaExpandCssShorthandsPlugin-test.ts @@ -0,0 +1,82 @@ +import felaExpandCssShorthandsPlugin from 'src/lib/felaExpandCssShorthandsPlugin' + +const expandCssShorthands = felaExpandCssShorthandsPlugin() + +describe('felaExpandCssShorthandsPlugin', () => { + test('should expand margin prop', () => { + const style = { + display: 'block', + margin: '0px 10px', + } + + expect(expandCssShorthands(style)).toMatchObject({ + display: 'block', + marginTop: '0px', + marginRight: '10px', + marginBottom: '0px', + marginLeft: '10px', + }) + }) + + test('should expand pseudo object', () => { + const style = { + display: 'block', + '::before': { + margin: '0px', + }, + } + + expect(expandCssShorthands(style)).toMatchObject({ + display: 'block', + '::before': { + marginTop: '0px', + marginRight: '0px', + marginBottom: '0px', + marginLeft: '0px', + }, + }) + }) + + test('should expand nested pseudo object', () => { + const style = { + display: 'block', + '::before': { + margin: '0px', + ':hover': { + padding: '10px', + }, + }, + } + + expect(expandCssShorthands(style)).toMatchObject({ + display: 'block', + '::before': { + marginTop: '0px', + marginRight: '0px', + marginBottom: '0px', + marginLeft: '0px', + ':hover': { + paddingTop: '10px', + paddingRight: '10px', + paddingBottom: '10px', + paddingLeft: '10px', + }, + }, + }) + }) + + test('should merge expanded prop with its shorthand', () => { + const style = { + marginTop: '3px', + margin: '10px', + marginRight: '15px', + } + + expect(expandCssShorthands(style)).toMatchObject({ + marginTop: '10px', // overridden by margin: '10px' + marginRight: '15px', // overridden by marginRight: '15px' + marginBottom: '10px', + marginLeft: '10px', + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index e89259ef9d..35ca556ff8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4099,6 +4099,11 @@ crypto-random-string@^1.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= +css-color-names@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.1.tgz#5d0548fa256456ede4a9a0c2ac7ab19d3eb1ad81" + integrity sha1-XQVI+iVkVu3kqaDCrHqxnT6xrYE= + css-in-js-utils@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" @@ -4124,6 +4129,27 @@ css-select@^1.1.0, css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" +css-shorthand-expand@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-shorthand-expand/-/css-shorthand-expand-1.2.0.tgz#bd6ac8d79f99928581eaca9fe05a03d316d17fe5" + integrity sha512-L3RS1VNYuXgMOfVGX4WzP9AFK6KL0JuioSoO8661egEac2eHX9/s4yFO8mgK6QEtm8UmU8IvuKzPgdQpU0DhpQ== + dependencies: + css-color-names "0.0.1" + css-url-regex "0.0.1" + hex-color-regex "^1.0.1" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + map-obj "^1.0.0" + repeat-element "^1.1.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + xtend "^4.0.0" + +css-url-regex@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-0.0.1.tgz#e05af8c6c290d451ef1632b455ea5c81b4b1395c" + integrity sha1-4Fr4xsKQ1FHvFjK0VepcgbSxOVw= + css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" @@ -6934,6 +6960,11 @@ he@1.1.x: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= +hex-color-regex@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + history@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b" @@ -6983,6 +7014,16 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.6.0, hosted-git-info@^2.7.1: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + html-element-map@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.0.tgz#19a41940225153ecdfead74f8509154ff1cdc18b" @@ -12101,6 +12142,11 @@ renderkid@^2.0.1: strip-ansi "^3.0.0" utila "~0.3" +repeat-element@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" @@ -12339,6 +12385,16 @@ rfc6902@^3.0.1: resolved "https://registry.yarnpkg.com/rfc6902/-/rfc6902-3.0.1.tgz#03a3d38329dbc266fbc92aa7fc14546d7839e89f" integrity sha512-a4t5OlaOgAejBg48/lkyQMcV6EWpljjSjmXAtSXLhw83x1OhlcVGLMLf//GoUSpHsWt8x/7oxaf5FEGM9QH/iQ== +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + rimraf@2, rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"