Skip to content

Commit

Permalink
refactor(ui): [Theme] Migrate to MUI. (#278)
Browse files Browse the repository at this point in the history
  • Loading branch information
doniyor2109 authored Jan 4, 2022
1 parent d977761 commit 168530b
Show file tree
Hide file tree
Showing 27 changed files with 1,626 additions and 202 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ module.exports = {
{
files: '*.js',
extends: 'plugin:@superdispatch/node',
rules: {
'import/no-extraneous-dependencies': 'off',
},
},

{
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
steps:
- uses: umidbekk/actions/prepare-node-repo@v2
with:
cache-key: yarn-v3
cache-key: yarn-v4

- run: yarn check:types
- run: yarn lint
Expand All @@ -26,7 +26,7 @@ jobs:
steps:
- uses: umidbekk/actions/prepare-node-repo@v2
with:
cache-key: yarn-v3
cache-key: yarn-v4

- run: yarn docs
- id: deploy
Expand All @@ -45,7 +45,7 @@ jobs:
steps:
- uses: umidbekk/actions/prepare-node-repo@v2
with:
cache-key: yarn-v3
cache-key: yarn-v4
- run: yarn cypress install
- run: yarn percy exec -- cypress run
env:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
steps:
- uses: umidbekk/actions/prepare-node-repo@v2
with:
cache-key: yarn-v3
cache-key: yarn-v4
- run: yarn check:types
- run: yarn lint
- run: yarn test
Expand All @@ -22,7 +22,7 @@ jobs:
steps:
- uses: umidbekk/actions/prepare-node-repo@v2
with:
cache-key: yarn-v3
cache-key: yarn-v4
- uses: superdispatch/actions/build-size/limit@v1
with:
install_command: 'yarn install'
Expand All @@ -37,7 +37,7 @@ jobs:
steps:
- uses: umidbekk/actions/prepare-node-repo@v2
with:
cache-key: yarn-v3
cache-key: yarn-v4
- run: yarn storybook:build
- run: yarn playroom:build
- id: deploy
Expand All @@ -58,7 +58,7 @@ jobs:
- uses: actions/checkout@v2
- uses: umidbekk/actions/npm/install@v2
with:
cache-key: yarn-v3
cache-key: yarn-v4
- run: yarn cypress install
- run: yarn percy exec -- cypress run
env:
Expand Down
170 changes: 170 additions & 0 deletions DiffReporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
'use strict';

const identity = require('lodash/identity');
const fs = require('fs/promises');
const path = require('path');
const traverse = require('@babel/traverse').default;
const { diff } = require('jest-diff');
const { parseAsync } = require('@babel/core');

function diffString(a, b) {
return diff(a, b, {
expand: false,
contextLines: -1, // Forces to use default from Jest
aAnnotation: 'v4',
bAnnotation: 'v5',

aColor: identity,
bColor: identity,
changeColor: identity,
commonColor: identity,
patchColor: identity,
});
}

async function parseAST(filename) {
const plugins = [];
const presets = [require.resolve('babel-preset-current-node-syntax')];

if (/\.tsx?$/.test(filename)) {
plugins.push([
require.resolve('@babel/plugin-syntax-typescript'),
{ isTSX: filename.endsWith('x') },
]);
}

const content = await fs.readFile(filename);
return parseAsync(content.toString(), {
filename,
plugins,
presets,
root: path.dirname(filename),
});
}

function traverseAST(ast, results) {
traverse(ast, {
CallExpression({ node }) {
const { arguments: args, callee } = node;
if (
callee.type !== 'MemberExpression' ||
callee.property.type !== 'Identifier' ||
callee.property.loc == null
) {
return;
}

const templateLiteral = args.find(
({ type }) => type === 'TemplateLiteral',
);

if (templateLiteral) {
const [{ value }] = templateLiteral.quasis;
results.push(value.cooked);
}
},
});
}

class DiffReporter {
constructor(globalConfig, options) {
this._globalConfig = globalConfig;
this._options = options;
}

async onTestFileResult(test) {
// todo check for snapshot from testResult
if (test.path.includes('/v5')) {
await processFileSnapshots(test);
await processInlineSnapshots(test);
}
}
}

async function processInlineSnapshots(test) {
const pathV4 = getPathV4(test.path);
const snapshots = await getInlineSnapshotValues(test.path);
const snapshotsV4 = await getInlineSnapshotValues(pathV4);

if (!snapshotsV4) {
console.warn('DiffReporter: v4 snapshots not found for ' + test.path);
return;
}

if (snapshots.length !== snapshotsV4.length) {
console.warn(
'DiffReporter: v4 and v5 snapshots are not the same length for ' +
test.path,
);
return;
}

const diffs = [];

for (let i = 0; i < snapshots.length; i++) {
const snapshot = snapshots[i];
const snapshotV4 = snapshotsV4[i];
const diffContent = diffString(snapshot, snapshotV4);

if (diffContent.length) {
diffs.push(diffString(snapshotV4, snapshot));
}
}

if (diffs.length) {
const diffPath = getDiffPath(test.path);
await fs.writeFile(diffPath, diffs.join('\n'));
}
}

async function getInlineSnapshotValues(filename) {
const results = [];
const content = await fs.readFile(filename);

if (content.includes('toMatchInlineSnapshot')) {
const ast = await parseAST(filename);

traverseAST(ast, results);
}

return results;
}

async function processFileSnapshots(test) {
const pathV4 = getPathV4(test.path);
const snapshotPath = getSnapshotPath(test.path);
const snapshotPathV4 = getSnapshotPath(pathV4);

try {
await fs.access(snapshotPath);
await fs.access(snapshotPathV4);
} catch (err) {
return;
}

const snapshot = await fs.readFile(snapshotPath);
const snapshotV4 = await fs.readFile(snapshotPathV4);
if (snapshot.toString() !== snapshotV4.toString()) {
const diffPath = getDiffPath(snapshotPath);
await fs.writeFile(
diffPath,
diffString(snapshotV4.toString(), snapshot.toString()),
);
}
}

function getDiffPath(filename) {
return filename + '.diff';
}

function getSnapshotPath(filename) {
const dir = path.dirname(filename);
const basename = path.basename(filename);
return dir + '/__snapshots__/' + basename + '.snap';
}

function getPathV4(filename) {
return filename.replace('/v5', '');
}

module.exports = DiffReporter;
13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,17 @@
"tsx",
"cjs"
],
"moduleNameMapper": {
"^@mui/styled-engine(.*)": "<rootDir>/node_modules/@mui/styled-engine$1",
"^styled-components$": "<rootDir>/node_modules/styled-components/dist/styled-components.browser.cjs.js"
},
"modulePathIgnorePatterns": [
".+/pkg/.+"
],
"reporters": [
"default",
"<rootDir>/DiffReporter.js"
],
"resetMocks": true,
"resolver": "./jest-resolver.js",
"roots": [
Expand Down Expand Up @@ -88,6 +96,7 @@
]
},
"resolutions": {
"@mui/styled-engine": "npm:@mui/styled-engine-sc@^5.1.0",
"react-docgen-typescript": "2.1.0"
},
"dependencies": {
Expand All @@ -100,6 +109,10 @@
"@material-ui/icons": "4.11.2",
"@material-ui/lab": "4.0.0-alpha.60",
"@mdi/js": "6.1.95",
"@mui/icons-material": "5.2.5",
"@mui/lab": "5.0.0-alpha.62",
"@mui/material": "5.2.3",
"@mui/styles": "5.2.3",
"@percy/cli": "1.0.0-beta.71",
"@percy/cypress": "3.1.1",
"@pika/pack": "0.5.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/__testutils__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export * from './defer';
export * from './renderComponent';
export * from './renderCSS';
export * from './renderTheme';
export * from './serializers';
export * as v5 from './v5';
9 changes: 9 additions & 0 deletions packages/__testutils__/serializers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ForwardRef } from 'react-is';

export function getForwardRefSerializer() {
return {
test: (value: any) => value?.$$typeof === ForwardRef,
serialize: (value: any) =>
`React.forwardRef(${value.displayName || 'unknown'})`,
};
}
3 changes: 3 additions & 0 deletions packages/__testutils__/v5/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './renderComponent';
export * from './renderCSS';
export * from './renderTheme';
69 changes: 69 additions & 0 deletions packages/__testutils__/v5/renderCSS.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { v5 } from '@superdispatch/ui';
import { render } from '@testing-library/react';
import { parse as parseCSS, stringify as stringifyCSS, Stylesheet } from 'css';
import { format } from 'prettier';

const colors = new Map<string, string>(
Object.entries(v5.Color).map(([k, v]) => [v, `Color.${k}`]),
);

const colorRegExp = new RegExp(
Array.from(colors.keys(), (x) =>
x.replace('(', '\\(').replace(')', '\\)'),
).join('|'),
'g',
);

const renderedCSS = new Set<string>();

function getAllSheets(): Element[] {
return Array.from(document.querySelectorAll('[data-styled]'));
}

function getSheets(): Element[] {
const sheets = getAllSheets();

if (sheets.length === 0) {
throw new Error('There are no mounted JSS components.');
}

return sheets;
}

function parseStyleSheet(): Stylesheet {
return parseCSS(
getSheets()
.map((node) => node.textContent)
.join('\n'),
);
}

function formatAST(sheet: Stylesheet): string {
return format(
stringifyCSS(sheet).replace(
colorRegExp,
(color) => colors.get(color) as string,
),
{ parser: 'css', singleQuote: true },
).trim();
}

expect.addSnapshotSerializer({
test: (value) => typeof value === 'string' && renderedCSS.has(value),
print: (value) => String(value),
});

export function extractGlobalCSS(): string {
const targetSheet = parseStyleSheet();
const formatted = formatAST(targetSheet);

renderedCSS.add(formatted);

return formatted;
}

export function renderGlobalCSS() {
render(<v5.ThemeProvider />);

return extractGlobalCSS();
}
18 changes: 18 additions & 0 deletions packages/__testutils__/v5/renderComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { v5 } from '@superdispatch/ui';
import { render, RenderOptions, RenderResult } from '@testing-library/react';
import { ReactElement, Suspense } from 'react';

function Wrapper({ children }: v5.ThemeProviderProps): ReactElement {
return (
<Suspense fallback="Suspended…">
<v5.ThemeProvider>{children}</v5.ThemeProvider>
</Suspense>
);
}

export function renderComponent(
ui: ReactElement,
options?: Omit<RenderOptions, 'queries' | 'wrapper'>,
): RenderResult {
return render(ui, { ...options, wrapper: Wrapper });
}
Loading

0 comments on commit 168530b

Please sign in to comment.