Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addon-actions: Fix types and refactor #2438

Merged
merged 34 commits into from
Dec 14, 2017
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ad89dca
Addon-actions: display enumerable properties from prototype chain
Hypnosphi Nov 29, 2017
84244f3
Merge remote-tracking branch 'origin/action-list-all-enumerable-props…
rhalff Dec 5, 2017
24c0868
refactor code to better support all types
rhalff Dec 5, 2017
611a021
add global dependency
rhalff Dec 5, 2017
af94539
add buttons to test each type individually
rhalff Dec 5, 2017
440b333
moved to All types
rhalff Dec 5, 2017
1c3ed2d
separate tests
rhalff Dec 5, 2017
a398583
import from global instead of using eslint
rhalff Dec 6, 2017
9bda5ab
do not list non-enumerable properties
rhalff Dec 7, 2017
9a46c6e
Revert "do not list non-enumerable properties"
rhalff Dec 7, 2017
9f577c3
do not list non-enumerable properties & ignore non own functions
rhalff Dec 7, 2017
238367b
fix typos & rename some values
rhalff Dec 7, 2017
4279540
include direct action call in examples
rhalff Dec 7, 2017
6403dd3
Merge branch 'release/3.3' into fix-types-and-refactor
Hypnosphi Dec 7, 2017
84f3343
prefer configure to eval
rhalff Dec 7, 2017
442df6e
update storyshot
rhalff Dec 7, 2017
db9e5ad
Merge remote-tracking branch 'origin/fix-types-and-refactor' into fix…
rhalff Dec 7, 2017
8f98583
remove obsolete indexOf check
rhalff Dec 7, 2017
7463bdf
invert property check
rhalff Dec 7, 2017
415c34f
create named function safely
rhalff Dec 7, 2017
11170f2
Merge branch 'release/3.3' into fix-types-and-refactor
danielduan Dec 7, 2017
5b9e213
rename/move class to objectType
rhalff Dec 8, 2017
8e1e167
Merge branch 'fix-types-and-refactor' of github.com:rhalff/storybook …
rhalff Dec 8, 2017
0ce1ad2
Merge branch 'release/3.3' into fix-types-and-refactor
Hypnosphi Dec 12, 2017
a35d8e4
add error handling
rhalff Dec 13, 2017
d693516
Merge remote-tracking branch 'upstream/release/3.3' into fix-types-an…
rhalff Dec 13, 2017
9d9a728
Merge remote-tracking branch 'origin/fix-types-and-refactor' into fix…
rhalff Dec 13, 2017
1a10635
Update lockfile
rhalff Dec 13, 2017
a9022b5
Merge branch 'release/3.3' into fix-types-and-refactor
Hypnosphi Dec 13, 2017
8aba7c8
update storyshots
rhalff Dec 14, 2017
7dab15f
Merge remote-tracking branch 'origin/fix-types-and-refactor' into fix…
rhalff Dec 14, 2017
b08bc28
Merge branch 'release/3.3' into fix-types-and-refactor
Hypnosphi Dec 14, 2017
d35e696
ignore __mocks__/ folders
rhalff Dec 14, 2017
ddb21db
Merge remote-tracking branch 'origin/fix-types-and-refactor' into fix…
rhalff Dec 14, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions addons/actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
},
"dependencies": {
"deep-equal": "^1.0.1",
"global": "^4.3.2",
"prop-types": "^15.6.0",
"react-inspector": "^2.2.1",
"uuid": "^3.1.0"
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.className': '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.className': '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);
});
});
67 changes: 67 additions & 0 deletions addons/actions/src/lib/decycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { getPropertiesList, typeReplacer } from './util';

import { CYCLIC_KEY } from './';

import { classType } 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 };
}

objects.set(value, path);

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 = classType.serialize(value);

getPropertiesList(value).forEach(name => {
obj[name] = derez(value[name], `${path}[${JSON.stringify(name)}]`, _depth + 1);
});
}

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

return obj;
}

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

return res;
}
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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import createFakeConstructor from '../createFakeConstructor';

describe('createFakeConstructor', () => {
it('creates fake constructor', () => {
expect(
createFakeConstructor(
{
name_key: 'A',
},
'name_key'
).constructor.name
).toEqual('A');
});
});
26 changes: 26 additions & 0 deletions addons/actions/src/lib/types/class/__tests__/getClassName.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import getClassName from '../getClassName';

class A {}
const a = new A();
function B() {}
const b = new B();

describe('getClassName', () => {
/* Transpiled cannot be tested.
it('get name of class',() => {
expect(getClassName(A)).toBe('A')
});
*/

it('get name of class instance', () => {
expect(getClassName(a)).toBe('A');
});

it('get name of function', () => {
expect(getClassName(B)).toBe('B');
});

it('get constructor name', () => {
expect(getClassName(b)).toBe('B');
});
});
19 changes: 19 additions & 0 deletions addons/actions/src/lib/types/class/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import classType from '../';

describe('Class', () => {
it('Serializes Class', () => {
class C {}
const c = new C();

expect(classType.serialize(c)).toEqual({ [classType.KEY]: 'C' });
});

it('Deserializes Class', () => {
const value = { [classType.KEY]: 'C' };
const c = classType.deserialize(value);

expect(c.constructor.name).toEqual('C');

expect(value).toEqual({});
});
});
13 changes: 13 additions & 0 deletions addons/actions/src/lib/types/class/createFakeConstructor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default function createFakeConstructor(obj, key) {
function FakeConstructor(data) {
Object.assign(this, data);
}

Object.defineProperty(FakeConstructor, 'name', {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break in android emulator, see #2324

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding canConfigureName check should solve this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be fixed now, I didn't test it on android though.

value: obj[key],
});

delete obj[key]; // eslint-disable-line no-param-reassign

return new FakeConstructor(obj);
}
23 changes: 23 additions & 0 deletions addons/actions/src/lib/types/class/getClassName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export default function getClassName(value) {
if (value.toString) {
const stringValue = value.toString();

if (stringValue.slice(0, 5) === 'class') {
return stringValue.slice(6, -3);
}

const type = stringValue.slice(8, -1);

if (stringValue.slice(1, 7) === 'object' && type !== 'Object') {
return type;
}

const parts = stringValue.match(/function (\w+).*/);

if (parts && parts.length === 2) {
return parts[1];
}
}

return value.constructor ? value.constructor.name : 'Object';
}
13 changes: 13 additions & 0 deletions addons/actions/src/lib/types/class/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import createFakeConstructor from './createFakeConstructor';
import getClassName from './getClassName';

const KEY = '$___storybook.className';

const classType = {
KEY,
// is: (value) => , // not used
serialize: value => ({ [KEY]: getClassName(value) }),
deserialize: value => createFakeConstructor(value, KEY),
};

export default classType;
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);
});
});
Loading