diff --git a/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap b/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap index 6e5c63342819..b67db362d53c 100644 --- a/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap +++ b/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap @@ -1,7 +1,72 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`render a riot element can accept a constructor 1`] = `"
HACKED : true ; simple test (with a parameter). Oh, by the way (value is mapped to riotValue)
"`; +exports[`render a riot element can accept a constructor 1`] = ` + +
+ HACKED : true ; simple test (with a parameter). Oh, by the way (value is mapped to riotValue) +
+
+`; -exports[`render a riot element can nest several tags 1`] = `"
Inside tag1:
  • Inside tag2:
    • Inside tag3:
      • Inside tag4:
        • Inside tag5:
          • Content
"`; +exports[`render a riot element can nest several tags 1`] = ` + +
+ +
+ Inside tag1: +
    +
  • + +
    + Inside tag2: +
      +
    • + +
      + Inside tag3: +
        +
      • + +
        + Inside tag4: +
          +
        • + +
          + Inside tag5: +
            +
          • + Content +
          • +
          +
          +
          +
        • +
        +
        +
        +
      • +
      +
      +
      +
    • +
    +
    +
    +
  • +
+
+
+
+
+`; -exports[`render a riot element can template some vars 1`] = `"
simple test (with a parameter). Oh, by the way (value is mapped to riotValue)
"`; +exports[`render a riot element can template some vars 1`] = ` + +
+ simple test (with a parameter). Oh, by the way (value is mapped to riotValue) +
+
+`; diff --git a/jest.config.js b/jest.config.js index 6c222f69f539..463d034d6425 100644 --- a/jest.config.js +++ b/jest.config.js @@ -75,7 +75,7 @@ module.exports = { DOCS_MODE: false, PREVIEW_URL: undefined, }, - snapshotSerializers: ['jest-emotion', 'enzyme-to-json/serializer'], + snapshotSerializers: ['jest-emotion', 'enzyme-to-json/serializer', 'jest-serializer-html'], coverageDirectory: 'coverage', setupFilesAfterEnv: ['./scripts/jest.init.js'], coverageReporters: ['lcov'], diff --git a/lib/components/package.json b/lib/components/package.json index be47b1f58ee3..b7b0bbf13abd 100644 --- a/lib/components/package.json +++ b/lib/components/package.json @@ -30,6 +30,7 @@ "dependencies": { "@storybook/client-logger": "6.0.0-alpha.22", "@storybook/theming": "6.0.0-alpha.22", + "@types/overlayscrollbars": "^1.9.0", "@types/react-syntax-highlighter": "11.0.4", "@types/react-textarea-autosize": "^4.3.3", "core-js": "^3.0.1", @@ -37,6 +38,8 @@ "lodash": "^4.17.15", "markdown-to-jsx": "^6.9.1", "memoizerific": "^1.11.3", + "overlayscrollbars": "^1.10.2", + "overlayscrollbars-react": "^0.2.1", "polished": "^3.4.4", "popper.js": "^1.14.7", "react": "^16.8.3", @@ -46,10 +49,10 @@ "react-popper-tooltip": "^2.8.3", "react-syntax-highlighter": "^11.0.2", "react-textarea-autosize": "^7.1.0", - "simplebar-react": "^1.0.0-alpha.6", "ts-dedent": "^1.1.1" }, "devDependencies": { + "css": "^2.2.4", "enzyme": "^3.9.0", "jest": "^25.1.0", "jest-enzyme": "^7.0.2" diff --git a/lib/components/scripts/writeCssScript.js b/lib/components/scripts/writeCssScript.js new file mode 100644 index 000000000000..07fa841d93f5 --- /dev/null +++ b/lib/components/scripts/writeCssScript.js @@ -0,0 +1,132 @@ +/* eslint-disable no-underscore-dangle */ +/* eslint-disable no-param-reassign */ + +// This little script converts the overflowscrollbars CSS file into the css-in-js file +// it's normal you have to run prettier over the file after + +const fs = require('fs'); +const { parse } = require('css'); +const { isNaN } = require('global'); + +const INPUT = require.resolve('overlayscrollbars/css/OverlayScrollbars.min.css'); +const OUTPUT = `${__dirname}/../src/ScrollArea/ScrollAreaStyles.ts`; +const OPTIONS = { camelCase: true, numbers: true }; + +const read = file => { + return fs + .readFileSync(file) + .toString() + .replace(/(?:\r\n|\r|\n)/g, ''); +}; + +const convert = (css, opts) => { + const ast = parse(css, { source: css }); + const obj = cssToObject(opts)(ast.stylesheet.rules); + return obj; +}; + +const cssToObject = opts => (rules, result = {}) => { + rules.forEach(rule => { + if (rule.type === 'media') { + const key = `@media ${rule.media}`; + const decs = cssToObject(opts)(rule.rules); + result[key] = decs; + return; + } + if (rule.type === 'keyframes') { + result.__keyframes = Object.assign(result.__keyframes || {}, { [camel(rule.name)]: rule }); + return; + } + if (rule.type === 'comment') { + return; + } + + const key = rule.selectors.filter(s => !s.includes('.os-theme-none')).join(', '); + + if (key.length) { + Object.assign(result, { + [key]: Object.assign(result[key] || {}, getDeclarations(rule.declarations, opts)), + }); + } + }); + return result; +}; + +const getDeclarations = (decs, opts = {}) => { + const result = decs + .filter(d => { + const filtered = d.type === 'comment' || d.property.match(/^(?:-webkit-|-ms-|-moz-)/); + return !filtered; + }) + .map(d => ({ + key: opts.camelCase ? camel(d.property) : d.property, + value: opts.numbers ? parsePx(d.value) : d.value, + })) + .reduce((a, b) => { + a[b.key] = b.value; + return a; + }, {}); + return result; +}; + +const camel = str => str.replace(/(-[a-z])/g, x => x.toUpperCase()).replace(/-/g, ''); + +const parsePx = val => { + return /px$/.test(val) || val === '' || (val.match(/\d$/) && !isNaN(parseInt(val, 10))) + ? parseFloat(val.replace(/px$/, '')) + : val; +}; + +const { __keyframes, ...styles } = convert(read(INPUT), OPTIONS); + +const stringifiedKeyFrames = Object.values(__keyframes) + .map(k => { + return `const ${camel(k.name)} = keyframes\`${k.keyframes.reduce( + (acc, item) => + `${acc}${k.position.source.substring( + item.position.start.column - 1, + item.position.end.column - 1 + )}`, + '' + )}\`;`; + }) + .join('\n'); + +const stringifiedStyles = JSON.stringify( + Object.entries(styles).reduce((acc, [key, item]) => { + if (item.animationName && __keyframes[camel(item.animationName)]) { + item.animationName = camel(item.animationName); + } + + if (item.backgroundImage && item.backgroundImage.match(/^url/)) { + item.backgroundImage = + 'linear-gradient(135deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.4) 50%, rgba(0,0,0,0.4) 100%)'; + } + + acc[key] = item; + return acc; + }, {}), + null, + 2 +); + +const stringifiedStylesWithReplacedKeyframes = Object.keys(__keyframes) + .reduce((acc, item) => { + // replace keyframes + return acc.replace(`"${item}"`, `\`\${${item}}\``); + }, stringifiedStyles) + .replace(/"([^\s]+)!important"/g, (f, p1) => { + // make "!important" rules work with TS + const v = parsePx(p1); + return `"${p1}!important" as any as ${JSON.stringify(v)}`; + }); + +const result = ` + import { Theme, CSSObject, keyframes } from '@storybook/theming'; + + ${stringifiedKeyFrames} + + export const getScrollAreaStyles: (theme: Theme) => CSSObject = (theme: Theme) => (${stringifiedStylesWithReplacedKeyframes}); +`; + +fs.writeFileSync(OUTPUT, result); diff --git a/lib/components/src/ScrollArea/ScrollArea.tsx b/lib/components/src/ScrollArea/ScrollArea.tsx index 04d05f9779d2..e0834fc2f437 100644 --- a/lib/components/src/ScrollArea/ScrollArea.tsx +++ b/lib/components/src/ScrollArea/ScrollArea.tsx @@ -1,10 +1,7 @@ -// Storybook's implementation of SimpleBar https://github.com/Grsmto/simplebar -// Note: "SimpleBar can't be used on the ,