UNPKG

@storybook/addon-console

Version:

Storybook addon for redirecting console output into action logger panel

269 lines (222 loc) 11.3 kB
"use strict"; 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); }; }; }