@storybook/addon-console
Version:
Storybook addon for redirecting console output into action logger panel
269 lines (222 loc) • 11.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.setConsoleOptions = setConsoleOptions;
exports.withConsole = withConsole;
var _window = _interopRequireDefault(require("global/window"));
var _addonActions = require("@storybook/addon-actions");
var _reactDecorator = require("./react-decorator");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
if (_addonActions.configureActions) {
(0, _addonActions.configureActions)({
clearOnStoryChange: false
});
}
var logger = console;
var cLogger = {
log: logger.log.bind(logger),
warn: logger.warn.bind(logger),
error: logger.error.bind(logger)
};
/**
* @typedef {Object} addonOptions - This options could be passed to [withConsole]{@link #storybookaddon-consolewithconsoleoptionsorfn--function} or [setConsoleOptions]{@link #module_@storybook/addon-console.setConsoleOptions}
* @property {RegExp[]} [panelExclude = [/[HMR]/]] - Optional. Anything matched to at least one of regular expressions will be excluded from output to Action Logger Panel
* @property {RegExp[]} [panelInclude = []] - Optional. If set, only matched outputs will be shown in Action Logger. Higher priority than `panelExclude`.
* @property {RegExp[]} [consoleExclude = []] - Optional. Anything matched to at least one of regular expressions will be excluded from DevTool console output
* @property {RegExp[]} [consoleInclude = []] - Optional. If set, only matched outputs will be shown in console. Higher priority than `consoleExclude`.
* @property {string} [log = console] - Optional. The marker to display `console.log` outputs in Action Logger
* @property {string} [warn = warn] - Optional. The marker to display warnings in Action Logger
* @property {string} [error = error] - Optional. The marker to display errors in Action Logger
*/
var addonOptions = {
panelExclude: [/\[HMR\]/],
panelInclude: [],
consoleExclude: [],
consoleInclude: [],
log: 'console',
warn: 'warn',
error: 'error'
};
var currentOptions = addonOptions;
var createLogger = function createLogger(options) {
return {
log: (0, _addonActions.action)(options.log),
warn: (0, _addonActions.action)(options.warn),
error: (0, _addonActions.action)(options.error)
};
};
var shouldDisplay = function shouldDisplay(messages, exclude, include) {
if (include.length) {
return messages.filter(function (mess) {
return typeof mess === 'string' ? include.find(function (regExp) {
return mess.match(regExp);
}) : false;
});
}
if (exclude.length) {
return messages.filter(function (mess) {
return typeof mess === 'string' ? !exclude.find(function (regExp) {
return mess.match(regExp);
}) : true;
});
}
return messages;
};
function setScope(options) {
var panelExclude = options.panelExclude,
panelInclude = options.panelInclude,
consoleExclude = options.consoleExclude,
consoleInclude = options.consoleInclude;
var aLogger = createLogger(options);
logger.log = function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var toPanel = shouldDisplay(args, panelExclude, panelInclude);
var toConsole = shouldDisplay(args, consoleExclude, consoleInclude);
if (toPanel.length) aLogger.log.apply(aLogger, _toConsumableArray(toPanel));
if (toConsole.length) cLogger.log.apply(cLogger, _toConsumableArray(toConsole));
};
logger.warn = function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
var toPanel = shouldDisplay(args, panelExclude, panelInclude);
var toConsole = shouldDisplay(args, consoleExclude, consoleInclude);
if (toPanel.length) aLogger.warn.apply(aLogger, _toConsumableArray(toPanel));
if (toConsole.length) cLogger.warn.apply(cLogger, _toConsumableArray(toConsole));
};
logger.error = function () {
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
var toPanel = shouldDisplay(args, panelExclude, panelInclude);
var toConsole = shouldDisplay(args, consoleExclude, consoleInclude);
if (toPanel.length) aLogger.error.apply(aLogger, _toConsumableArray(toPanel));
if (toConsole.length) cLogger.error.apply(cLogger, _toConsumableArray(toConsole));
};
_window["default"].onerror = function () {
var toPanel = shouldDisplay([arguments.length <= 0 ? undefined : arguments[0]], panelExclude, panelInclude);
var toConsole = shouldDisplay([arguments.length <= 0 ? undefined : arguments[0]], consoleExclude, consoleInclude);
if (toPanel.length) aLogger.error.apply(aLogger, arguments);
if (toConsole.length) return false;
return true;
};
}
setScope(addonOptions);
var detectOptions = function detectOptions(prop) {
if (!prop) return {};
if (_typeof(prop) === 'object') {
var _newOptions = _objectSpread({}, prop);
return _newOptions;
}
var newOptions = _objectSpread({}, prop(currentOptions)); // check: should it be currentOptions?
return newOptions;
};
/**
* This callback could be passed to {@link setConsoleOptions} or {@link withConsole}
*
* @example
* import { withConsole } from `@storybook/addon-console`;
*
* const optionsCallback = (options) => ({panelExclude: [...options.panelExclude, /Warning/]});
* addDecorator((storyFn, context) => withConsole(optionsCallback)(storyFn)(context));
*
* @callback optionsCallback
* @param {addonOptions} currentOptions - the current {@link addonOptions}
* @return {addonOptions} - new {@link addonOptions}
*/
/**
* Set addon options and returns a new one
* @param {addonOptions|optionsCallback} optionsOrFn
* @return {addonOptions}
* @see addonOptions
* @see optionsCallback
*
* @example
import { setConsoleOptions } from '@storybook/addon-console';
const panelExclude = setConsoleOptions({}).panelExclude;
setConsoleOptions({
panelExclude: [...panelExclude, /deprecated/],
});
*/
function setConsoleOptions(optionsOrFn) {
var newOptions = detectOptions(optionsOrFn);
currentOptions = _objectSpread({}, currentOptions, {}, newOptions);
setScope(currentOptions);
return currentOptions;
}
function handleStoryLogs() {
switch (_window["default"].STORYBOOK_ENV) {
case 'react':
return _reactDecorator.reactStory;
default:
logger.warn("Warning! withConsole doesn't support @storybook/".concat(_window["default"].STORYBOOK_ENV, ". Use setConsoleOptions instead"));
return function (story) {
return story;
};
}
}
function addConsole(storyFn, context, consoleOptions) {
var prevOptions = _objectSpread({}, currentOptions);
var logNames = context ? {
log: "".concat(context.kind, "/").concat(context.story),
warn: "".concat(context.kind, "/").concat(context.story, "/warn"),
error: "".concat(context.kind, "/").concat(context.story, "/error")
} : {};
var options = _objectSpread({}, currentOptions, {}, logNames, {}, consoleOptions);
setScope(options);
var story = storyFn();
var wrapStory = handleStoryLogs();
var wrappedStory = wrapStory(story, function () {
return setScope(options);
}, function () {
return setScope(currentOptions);
});
currentOptions = prevOptions;
setScope(currentOptions);
return wrappedStory;
}
/**
* Wraps your stories with specified addon options.
* If you don't pass {`log`, `warn`, `error`} in options argument it'll create them from context for each story individually. Hence you'll see from what exact story you got a log or error. You can log from component's lifecycle methods or within your story.
* @param {addonOptions|optionsCallback} [optionsOrFn]
* @see [addonOptions]{@link #storybookaddon-consolesetconsoleoptionsoptionsorfn--addonoptions}
* @see [optionsCallback]{@link #storybookaddon-consoleoptionscallback--addonoptions}
* @return {function} wrappedStoryFn
*
* @example
* import { storiesOf } from '@storybook/react';
* import { withConsole } from '@storybook/addon-console';
*
* storiesOf('withConsole', module)
* .addDecorator((storyFn, context) => withConsole()(storyFn)(context))
* .add('with Log', () => <Button onClick={() => console.log('Data:', 1, 3, 4)}>Log Button</Button>)
* .add('with Warning', () => <Button onClick={() => console.warn('Data:', 1, 3, 4)}>Warn Button</Button>)
* .add('with Error', () => <Button onClick={() => console.error('Test Error')}>Error Button</Button>)
* .add('with Uncatched Error', () =>
* <Button onClick={() => console.log('Data:', T.buu.foo)}>Throw Button</Button>
* )
// Action Logger Panel:
// withConsole/with Log: ["Data:", 1, 3, 4]
// withConsole/with Warning warn: ["Data:", 1, 3, 4]
// withConsole/with Error error: ["Test Error"]
// withConsole/with Uncatched Error error: ["Uncaught TypeError: Cannot read property 'foo' of undefined", "http://localhost:9009/static/preview.bundle.js", 51180, 42, Object]
*/
function withConsole(optionsOrFn) {
var newOptions = detectOptions(optionsOrFn);
return function (storyFn) {
return function (context) {
return addConsole(storyFn, context, newOptions);
};
};
}