Skip to content

Commit

Permalink
Merge branch 'angular-versions-support' of github.com:storybooks/stor…
Browse files Browse the repository at this point in the history
…ybook into angular-versions-support
  • Loading branch information
alterx committed Dec 20, 2017
2 parents 1ecd649 + 6ed2fd8 commit cc32f92
Show file tree
Hide file tree
Showing 106 changed files with 2,793 additions and 1,153 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module.exports = {
singleQuote: true,
},
],
'no-debugger': process.env.NODE_ENV === 'production' ? error : ignore,
quotes: [warn, 'single', { avoidEscape: true }],
'class-methods-use-this': ignore,
'arrow-parens': [warn, 'as-needed'],
Expand Down
7 changes: 0 additions & 7 deletions .remarkrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,5 @@ module.exports = {
},
},
],
[
'remark-toc',
{
tight: true,
maxDepth: 3,
},
],
],
};
174 changes: 174 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion addons/actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
},
"dependencies": {
"deep-equal": "^1.0.1",
"global": "^4.3.2",
"make-error": "^1.3.0",
"prop-types": "^15.6.0",
"react-inspector": "^2.2.1",
"react-inspector": "^2.2.2",
"uuid": "^3.1.0"
},
"peerDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion addons/actions/src/containers/ActionLogger/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import deepEqual from 'deep-equal';
import { CYCLIC_KEY, isObject, retrocycle } from '../../util';
import { CYCLIC_KEY, retrocycle } from '../../lib';
import { isObject } from '../../lib/util';

import ActionLoggerComponent from '../../components/ActionLogger/';
import { EVENT_ID } from '../../';
Expand Down
45 changes: 45 additions & 0 deletions addons/actions/src/lib/__mocks__/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { File } from 'global';

const date = '2017-12-02T11:13:22.492Z';
const file = new File([''], 'filename.txt', {
type: 'text/plain',
lastModified: new Date(date),
});

const input = {
a: 'A',
b: 1,
c: true,
d: /AA/g,
e: date,
f: file,
};
input.circular = input;

const output = {
'$___storybook.objectName': 'Object',
'$___storybook.isCyclic': true,
a: 'A',
b: 1,
c: true,
circular: {
$ref: '$',
},
d: {
'$___storybook.regExpKey': '/AA/g',
},
e: '2017-12-02T11:13:22.492Z',
f: {
'$___storybook.objectName': 'File',
isClosed: false,
lastModified: 1512213202492,
name: 'filename.txt',
size: 0,
type: 'text/plain',
},
};

export default {
input,
output,
};
8 changes: 8 additions & 0 deletions addons/actions/src/lib/__tests__/decycle.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import decycle from '../decycle';
import example from '../__mocks__/example';

describe('Decycle', () => {
it('can handle cyclic object', () => {
expect(decycle(example.input)).toEqual(example.output);
});
});
42 changes: 42 additions & 0 deletions addons/actions/src/lib/__tests__/retrocycle.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import retrocycle from '../retrocycle';
import example from '../__mocks__/example';

describe('Retrocycle', () => {
it('can restore cyclic object', () => {
const FileMock = function File() {
this.close = function close() {};
this.isClosed = example.input.f.isClosed;
this.lastModified = example.input.f.lastModified;
this.name = example.input.f.name;
this.size = 0;
this.type = 'text/plain';
};

const file = new FileMock();

const result = {
a: example.input.a,
b: example.input.b,
c: example.input.c,
d: example.input.d,
e: example.input.e,
f: file,
};

result.circular = result;

const revived = retrocycle(JSON.stringify(example.output));

expect(revived.a).toEqual(example.input.a);
expect(revived.b).toEqual(example.input.b);
expect(revived.c).toEqual(example.input.c);
expect(revived.d).toEqual(example.input.d);
expect(revived.e).toEqual(example.input.e);
expect(revived.f.constructor.name).toEqual('File');
expect(revived.f.isClosed).toEqual(example.input.f.isClosed);
expect(revived.f.lastModified).toEqual(example.input.f.lastModified);
expect(revived.f.name).toEqual(example.input.f.name);
expect(revived.f.size).toEqual(example.input.f.size);
expect(revived.f.type).toEqual(example.input.f.type);
});
});
80 changes: 80 additions & 0 deletions addons/actions/src/lib/decycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { DecycleError } from './errors';

import { getPropertiesList, typeReplacer } from './util';

import { CYCLIC_KEY } from './';

import { objectType } from './types';

export default function decycle(object, depth = 10) {
const objects = new WeakMap();

let isCyclic = false;

const res = (function derez(value, path, _depth) {
let oldPath;
let obj;

if (Object(value) === value && _depth > depth) {
const name = value.constructor ? value.constructor.name : typeof value;

return `[${name}...]`;
}

const result = typeReplacer(value);

if (result) {
return result.value;
}

const type = typeof value;

if (value instanceof Boolean || value instanceof Number || value instanceof String) {
return value;
}

if (type === 'object' && value !== null) {
oldPath = objects.get(value);
if (oldPath !== undefined) {
isCyclic = true;

return { $ref: oldPath };
}

try {
objects.set(value, path);
} catch (error) {
console.error(error); // eslint-disable-line no-console
return new DecycleError(error.message);
}

if (Array.isArray(value)) {
obj = [];
for (let i = 0; i < value.length; i += 1) {
obj[i] = derez(value[i], `${path}[${i}]`, _depth + 1);
}
} else {
obj = objectType.serialize(value);

getPropertiesList(value).forEach(name => {
try {
obj[name] = derez(value[name], `${path}[${JSON.stringify(name)}]`, _depth + 1);
} catch (error) {
console.error(error); // eslint-disable-line no-console
obj[name] = new DecycleError(error.message);
}
});
}

if (_depth === 0 && value instanceof Object && isCyclic) {
obj[CYCLIC_KEY] = true;
}

return obj;
}

return value;
})(object, '$', 0);

return res;
}
3 changes: 3 additions & 0 deletions addons/actions/src/lib/errors/DecycleError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { BaseError } from 'make-error';

export default class DecycleError extends BaseError {}
1 change: 1 addition & 0 deletions addons/actions/src/lib/errors/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export DecycleError from './DecycleError';
4 changes: 4 additions & 0 deletions addons/actions/src/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const CYCLIC_KEY = '$___storybook.isCyclic';
export decycle from './decycle';
export retrocycle from './retrocycle';
export reviver from './reviver';
50 changes: 50 additions & 0 deletions addons/actions/src/lib/retrocycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import reviver from './reviver';
import { muteProperty } from './util';
import { CYCLIC_KEY } from './';

const pathReg = /^\$(?:\[(?:\d+|"(?:[^\\"\u0000-\u001f]|\\([\\"/bfnrt]|u[0-9a-zA-Z]{4}))*")])*$/;

export default function retrocycle(json) {
const $ = JSON.parse(json, reviver);

if (typeof $ !== 'object' || $ === null) {
return $;
}

(function rez(value) {
if (value && typeof value === 'object') {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i += 1) {
const item = value[i];
if (item && typeof item === 'object') {
const path = item.$ref;
if (typeof path === 'string' && pathReg.test(path)) {
value[i] = eval(path); // eslint-disable-line no-eval, no-param-reassign
} else {
rez(item);
}
}
}
} else {
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const name in value) {
const item = value[name];

if (typeof item === 'object' && item !== null) {
const path = item.$ref;

if (typeof path === 'string' && pathReg.test(path)) {
value[name] = eval(path); // eslint-disable-line no-eval, no-param-reassign
} else {
rez(item);
}
}
}
}
}
})($);

muteProperty(CYCLIC_KEY, $);

return $;
}
15 changes: 15 additions & 0 deletions addons/actions/src/lib/reviver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { isObject, typeReviver } from './util';

function reviver(key, value) {
if (isObject(value)) {
const result = typeReviver(value);

if (result) {
return result.value;
}
}

return value;
}

export default reviver;
19 changes: 19 additions & 0 deletions addons/actions/src/lib/types/date/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import dateType from '../';

const date = new Date(1512137134873);
const isoString = date.toISOString();

describe('Date', () => {
it('Recognizes Date', () => {
expect(dateType.is(date)).toBe(true);
expect(dateType.is(1)).toBe(false);
});

it('Serializes Date', () => {
expect(dateType.serialize(date)).toEqual({ [dateType.KEY]: isoString });
});

it('Deserializes Date', () => {
expect(dateType.deserialize({ [dateType.KEY]: isoString })).toEqual(date);
});
});
10 changes: 10 additions & 0 deletions addons/actions/src/lib/types/date/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const KEY = '$___storybook.Date';

const dateType = {
KEY,
is: value => value instanceof Date,
serialize: value => ({ [KEY]: value.toISOString() }),
deserialize: value => new Date(value[KEY]),
};

export default dateType;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import createFunction from '../createFunction';
import reservedKeywords from '../reservedKeywords';

describe('createFunction', () => {
it('Can create functions with reserved names', () => {
reservedKeywords.forEach(reservedKeyword => {
expect(createFunction(reservedKeyword).name).toBe(`${reservedKeyword}`);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import createFunctionEval from '../createFunctionEval';
import reservedKeywords from '../reservedKeywords';

describe('createFunctionEval', () => {
it('Adds $ suffix for reserved names', () => {
reservedKeywords.forEach(reservedKeyword => {
expect(createFunctionEval(reservedKeyword).name).toBe(`${reservedKeyword}$`);
});
});
});
46 changes: 46 additions & 0 deletions addons/actions/src/lib/types/function/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import functionType from '../';
import reservedKeywords from '../reservedKeywords';
import createFunction from '../createFunction';
import createBoundFunction from '../createBoundFunction';

const A = createFunction('A');
const B = createBoundFunction('B');
const C = createFunction();

describe('function', () => {
it('Recognizes function', () => {
expect(functionType.is(A)).toBe(true);
});

it('Serializes function', () => {
expect(functionType.serialize(A)).toEqual({ [functionType.KEY]: 'A' });
});

it('Serializes anonymous function', () => {
expect(functionType.serialize(C)).toEqual({ [functionType.KEY]: '' });
});

it('Serializes bound function', () => {
expect(functionType.serialize(B)).toEqual({ [functionType.KEY]: 'bound B' });
});

it('Deserializes function', () => {
const func = functionType.deserialize({ [functionType.KEY]: 'A' });

expect(func.name).toEqual('A');
});

it('Deserializes bound function', () => {
const func = functionType.deserialize({ [functionType.KEY]: 'bound B' });

expect(func.name).toEqual('bound B');
});

it('Deserializes functions with reserved names', () => {
reservedKeywords.forEach(reservedKeyword => {
const func = functionType.deserialize({ [functionType.KEY]: reservedKeyword });

expect(func.name).toEqual(reservedKeyword);
});
});
});
Loading

0 comments on commit cc32f92

Please sign in to comment.