@atlaskit/editor-core
Version:
A package contains Atlassian editor core functionality
156 lines (152 loc) • 7.04 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React from 'react';
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
import uuid from 'uuid';
import { default as AnalyticsReactContext } from '@atlaskit/analytics-next-stable-react-context';
import { ACTION, ACTION_SUBJECT, editorAnalyticsChannel, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
import { getDocStructure } from '@atlaskit/editor-common/core-utils';
import { IntlErrorBoundary } from '@atlaskit/editor-common/intl-error-boundary';
import { logException } from '@atlaskit/editor-common/monitoring';
import { fg } from '@atlaskit/platform-feature-flags';
import { componentWithCondition } from '@atlaskit/platform-feature-flags-react';
import { isOutdatedBrowser } from '../utils/outdatedBrowsers';
import { WithEditorView } from './WithEditorView';
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/react/no-class-components
export class ErrorBoundaryWithEditorView extends React.Component {
constructor(props) {
super(props);
_defineProperty(this, "browserExtensions", undefined);
_defineProperty(this, "state", {
error: undefined
});
_defineProperty(this, "sendErrorData", async analyticsErrorPayload => {
var _window, _window$navigator;
const product = await this.getProductName();
const {
error,
errorInfo
} = analyticsErrorPayload;
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
const sharedId = uuid();
const browserInfo = ((_window = window) === null || _window === void 0 ? void 0 : (_window$navigator = _window.navigator) === null || _window$navigator === void 0 ? void 0 : _window$navigator.userAgent) || 'unknown';
const attributes = {
product,
browserInfo,
error: error.toString(),
errorInfo,
errorId: sharedId,
browserExtensions: this.browserExtensions,
docStructure: this.featureFlags.errorBoundaryDocStructure && this.props.editorView ? getDocStructure(this.props.editorView.state.doc, {
compact: true
}) : undefined,
outdatedBrowser: isOutdatedBrowser(browserInfo)
};
this.fireAnalyticsEvent({
action: ACTION.EDITOR_CRASHED,
actionSubject: ACTION_SUBJECT.EDITOR,
eventType: EVENT_TYPE.OPERATIONAL,
attributes
});
this.fireAnalyticsEvent({
action: ACTION.EDITOR_CRASHED_ADDITIONAL_INFORMATION,
actionSubject: ACTION_SUBJECT.EDITOR,
eventType: EVENT_TYPE.OPERATIONAL,
attributes: {
errorId: sharedId
}
});
this.logException(error, {
location: 'editor-core/create-editor',
product
});
});
_defineProperty(this, "getProductName", async () => {
const {
contextIdentifierProvider
} = this.props;
if (contextIdentifierProvider) {
const context = await contextIdentifierProvider;
if (context.product) {
return context.product;
}
}
return 'atlaskit';
});
_defineProperty(this, "fireAnalyticsEvent", event => {
var _this$props$createAna, _this$props;
(_this$props$createAna = (_this$props = this.props).createAnalyticsEvent) === null || _this$props$createAna === void 0 ? void 0 : _this$props$createAna.call(_this$props, event).fire(editorAnalyticsChannel);
});
_defineProperty(this, "logException", (error, tags) => {
logException(error, tags);
});
this.featureFlags = props.featureFlags;
}
componentDidCatch(error, errorInfo) {
// Only report and re-render once, to avoid over-reporting errors and infinite rerendering
if (this.state.error) {
return;
}
if (this.props.errorTracking) {
this.sendErrorData({
error,
errorInfo,
errorStack: error.stack
});
}
// Update state to allow a re-render to attempt graceful recovery (in the event that
// the error was caused by a race condition or is intermittent)
this.setState({
error
}, () => {
if (this.props.rethrow) {
// Now that a re-render has occurred, we re-throw to allow product error boundaries
// to catch and handle the error too.
//
// Note that when rethrowing inside a error boundary, the stack trace
// from a higher error boundary's componentDidCatch.info param will reset
// to this component, instead of the original component which threw it.
throw error;
}
});
}
// Ignored via go/ees007
// eslint-disable-next-line @atlaskit/editor/enforce-todo-comment-format
// FIXME: This is causing more problems then it's solving. The async check to sniff the browser extensions is block some
// react unit tests. Essentially jest never completes and just hangs. This was code was added 3yrs ago so that errors
// would detail if the browser had grammarly extension installed or not. I'm not sure if anyone has every inspecting this
// as it doesn't look like any dashboards exist for it.
// You can see the open handles that are block tests if you run unit tests with --detectOpenHandles
// async componentDidMount() {
// this.browserExtensions = await sniffUserBrowserExtensions({
// extensions: ['grammarly'],
// async: true,
// asyncTimeoutMs: 20000,
// });
// }
render() {
return /*#__PURE__*/React.createElement(IntlErrorBoundary, null, this.props.children);
}
}
_defineProperty(ErrorBoundaryWithEditorView, "defaultProps", {
rethrow: true,
errorTracking: true
});
export class ErrorBoundaryWithEditorViewWithAnalyticsReactContext extends ErrorBoundaryWithEditorView {
constructor(props) {
super(props);
_defineProperty(this, "logException", (error, tags) => {
var _this$context, _this$context$getAtla, _this$context$getAtla2, _this$context$getAtla3;
const extraTags = {};
extraTags.editorSessionId = (_this$context = this.context) === null || _this$context === void 0 ? void 0 : (_this$context$getAtla = _this$context.getAtlaskitAnalyticsContext()) === null || _this$context$getAtla === void 0 ? void 0 : (_this$context$getAtla2 = _this$context$getAtla[0]) === null || _this$context$getAtla2 === void 0 ? void 0 : (_this$context$getAtla3 = _this$context$getAtla2.fabricEditorCtx) === null || _this$context$getAtla3 === void 0 ? void 0 : _this$context$getAtla3.editorSessionId;
logException(error, {
...tags,
...extraTags
});
});
}
}
// eslint-disable-next-line @typescript-eslint/ban-types
_defineProperty(ErrorBoundaryWithEditorViewWithAnalyticsReactContext, "contextType", AnalyticsReactContext);
const _default_1 = componentWithCondition(() => fg('platform_editor_sentry_breadcrumbs'), WithEditorView(ErrorBoundaryWithEditorViewWithAnalyticsReactContext), WithEditorView(ErrorBoundaryWithEditorView));
export default _default_1;