UNPKG

@atlaskit/editor-plugin-extension

Version:

editor-plugin-extension plugin for @atlaskit/editor-core

548 lines (541 loc) 26.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _bindEventListener = require("bind-event-listener"); var _isEqual2 = _interopRequireDefault(require("lodash/isEqual")); var _merge = _interopRequireDefault(require("lodash/merge")); var _memoizeOne = _interopRequireDefault(require("memoize-one")); var _reactIntl = require("react-intl"); var _analyticsNext = require("@atlaskit/analytics-next"); var _browserApis = require("@atlaskit/browser-apis"); var _buttonGroup = _interopRequireDefault(require("@atlaskit/button/button-group")); var _new = _interopRequireDefault(require("@atlaskit/button/new")); var _analytics = require("@atlaskit/editor-common/analytics"); var _extensions = require("@atlaskit/editor-common/extensions"); var _hooks = require("@atlaskit/editor-common/hooks"); var _form = _interopRequireWildcard(require("@atlaskit/form")); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _experiments = require("@atlaskit/tmp-editor-statsig/experiments"); var _constants = require("./constants"); var _DescriptionSummary = require("./DescriptionSummary"); var _ErrorMessage = _interopRequireDefault(require("./ErrorMessage")); var _FormContent = _interopRequireDefault(require("./FormContent")); var _FormErrorBoundary = require("./FormErrorBoundary"); var _Header = _interopRequireDefault(require("./Header")); var _LoadingState = _interopRequireDefault(require("./LoadingState")); var _transformers = require("./transformers"); var _utils = require("./utils"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } function ConfigForm(_ref) { var canSave = _ref.canSave, errorMessage = _ref.errorMessage, extensionManifest = _ref.extensionManifest, fields = _ref.fields, firstVisibleFieldName = _ref.firstVisibleFieldName, hasParsedParameters = _ref.hasParsedParameters, intl = _ref.intl, isLoading = _ref.isLoading, onCancel = _ref.onCancel, onFieldChange = _ref.onFieldChange, parameters = _ref.parameters, submitting = _ref.submitting, contextIdentifierProvider = _ref.contextIdentifierProvider, featureFlags = _ref.featureFlags, disableFields = _ref.disableFields; (0, _react.useEffect)(function () { if (fields) { var firstDuplicateField = (0, _transformers.findDuplicateFields)(fields); if (firstDuplicateField) { throw new Error("Possible duplicate field name: `".concat(firstDuplicateField.name, "`.")); } } }, [fields]); if (isLoading || !hasParsedParameters && errorMessage === null) { return /*#__PURE__*/_react.default.createElement(_LoadingState.default, null); } if (errorMessage || !fields) { return /*#__PURE__*/_react.default.createElement(_ErrorMessage.default, { errorMessage: errorMessage || '' }); } return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_FormContent.default, { fields: fields, parameters: parameters, extensionManifest: extensionManifest, onFieldChange: onFieldChange, firstVisibleFieldName: firstVisibleFieldName, contextIdentifierProvider: contextIdentifierProvider, featureFlags: featureFlags, isDisabled: disableFields }), /*#__PURE__*/_react.default.createElement("div", { style: canSave ? {} : { display: 'none' } }, /*#__PURE__*/_react.default.createElement(_form.FormFooter, { align: "start" }, /*#__PURE__*/_react.default.createElement(_buttonGroup.default, null, /*#__PURE__*/_react.default.createElement(_new.default, { type: "submit", appearance: "primary" }, intl.formatMessage(_extensions.configPanelMessages.submit)), /*#__PURE__*/_react.default.createElement(_new.default, { appearance: "default", isDisabled: submitting, onClick: onCancel }, intl.formatMessage(_extensions.configPanelMessages.cancel)))))); } var ConfigFormIntl = (0, _reactIntl.injectIntl)(ConfigForm); var WithOnFieldChange = function WithOnFieldChange(_ref2) { var getState = _ref2.getState, autoSave = _ref2.autoSave, handleSubmit = _ref2.handleSubmit, children = _ref2.children; var getStateRef = (0, _react.useRef)(getState); (0, _react.useEffect)(function () { getStateRef.current = getState; }, [getState]); var handleFieldChange = (0, _react.useCallback)(function (name, isDirty) { if (!autoSave) { return; } // Don't trigger submit if nothing actually changed if (!isDirty) { return; } var _getStateRef$current = getStateRef.current(), errors = _getStateRef$current.errors, values = _getStateRef$current.values; // Get only values that does not contain errors var validValues = {}; for (var _i = 0, _Object$keys = Object.keys(values); _i < _Object$keys.length; _i++) { var key = _Object$keys[_i]; if (!errors[key]) { // not has error validValues[key] = values[key]; } } handleSubmit(validValues); }, [autoSave, handleSubmit]); return children(handleFieldChange); }; // eslint-disable-next-line @repo/internal/react/no-class-components var ConfigPanel = /*#__PURE__*/function (_React$Component) { function ConfigPanel(props) { var _this; (0, _classCallCheck2.default)(this, ConfigPanel); _this = _callSuper(this, ConfigPanel, [props]); (0, _defineProperty2.default)(_this, "handleKeyDown", function (e) { if ((e.key === 'Esc' || e.key === 'Escape') && _this.props.closeOnEsc) { _this.props.onCancel(); } }); // https://product-fabric.atlassian.net/browse/DST-2697 // workaround for DST-2697, remove this function once fix. (0, _defineProperty2.default)(_this, "backfillTabFormData", function (fields, formData, currentParameters) { var getRelevantData = function getRelevantData(field, formParams, currentParams, backfill) { if (field.hasGroupedValues && !(field.name in backfill)) { backfill[field.name] = {}; } var actualFormParams = field.hasGroupedValues ? formParams[field.name] || {} : formParams; var actualCurrentParams = field.hasGroupedValues ? currentParams[field.name] || {} : currentParams; var actualBackfillParams = field.hasGroupedValues ? backfill[field.name] : backfill; return { formParams: actualFormParams, currentParams: actualCurrentParams, backfillParams: actualBackfillParams }; }; // Traverse any tab structures and backfill field values on tabs // which aren't shown. This filter should be ok because tabs are // currently only allowed on top level var mergedTabGroups = fields.filter(_extensions.isTabGroup).reduce(function (missingBackfill, tabGroup) { var _getRelevantData = getRelevantData(tabGroup, formData, currentParameters, missingBackfill), tabGroupFormData = _getRelevantData.formParams, tabGroupCurrentData = _getRelevantData.currentParams, tabGroupParams = _getRelevantData.backfillParams; // Loop through tabs and see what fields are missing from current data tabGroup.fields.forEach(function (tabField) { var _getRelevantData2 = getRelevantData(tabField, tabGroupFormData, tabGroupCurrentData, tabGroupParams), tabFormData = _getRelevantData2.formParams, tabCurrentData = _getRelevantData2.currentParams, tabParams = _getRelevantData2.backfillParams; tabField.fields.forEach(function (field) { if (field.name in tabFormData || !(field.name in tabCurrentData)) { return; } tabParams[field.name] = tabCurrentData[field.name]; }); }); return missingBackfill; }, {}); return (0, _merge.default)({}, mergedTabGroups, formData); }); (0, _defineProperty2.default)(_this, "handleSubmit", /*#__PURE__*/function () { var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(formData) { var _this$props, fields, extensionManifest, onChange, autoSaveReject, serializedData; return _regenerator.default.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _this$props = _this.props, fields = _this$props.fields, extensionManifest = _this$props.extensionManifest, onChange = _this$props.onChange, autoSaveReject = _this$props.autoSaveReject; if (!(!extensionManifest || !fields)) { _context.next = 4; break; } if (!extensionManifest) { autoSaveReject === null || autoSaveReject === void 0 || autoSaveReject(new Error('Extension manifest not loaded')); } else if (!fields) { autoSaveReject === null || autoSaveReject === void 0 || autoSaveReject(new Error('Config fields not loaded')); } return _context.abrupt("return"); case 4: _context.prev = 4; _context.next = 7; return (0, _transformers.serialize)(extensionManifest, _this.backfillTabFormData(fields, formData, _this.state.currentParameters), fields); case 7: serializedData = _context.sent; if (!(0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true, { exposure: true })) { _context.next = 13; break; } _context.next = 11; return onChange(serializedData); case 11: _context.next = 14; break; case 13: onChange(serializedData); case 14: _context.next = 20; break; case 16: _context.prev = 16; _context.t0 = _context["catch"](4); autoSaveReject === null || autoSaveReject === void 0 || autoSaveReject(_context.t0); // eslint-disable-next-line no-console console.error("Error serializing parameters", _context.t0); case 20: case "end": return _context.stop(); } }, _callee, null, [[4, 16]]); })); return function (_x) { return _ref3.apply(this, arguments); }; }()); (0, _defineProperty2.default)(_this, "parseParameters", /*#__PURE__*/function () { var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(fields, parameters) { var extensionManifest, currentParameters; return _regenerator.default.wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: extensionManifest = _this.props.extensionManifest; if (!(!extensionManifest || !fields || fields.length === 0)) { _context2.next = 3; break; } return _context2.abrupt("return"); case 3: if (!(typeof parameters === 'undefined')) { _context2.next = 6; break; } _this.setState({ currentParameters: {}, hasParsedParameters: true }); return _context2.abrupt("return"); case 6: _context2.next = 8; return (0, _transformers.deserialize)(extensionManifest, parameters, fields); case 8: currentParameters = _context2.sent; _this.setState({ currentParameters: currentParameters, hasParsedParameters: true }); case 10: case "end": return _context2.stop(); } }, _callee2); })); return function (_x2, _x3) { return _ref4.apply(this, arguments); }; }()); /** * Remove renderHeader when when cleaning platform_editor_ai_object_sidebar_injection FG * Because header will br rendered separately outside of ConfigPanel. * ConfigPanel will be body component of ContextPanel. */ // memoized to prevent rerender on new parameters (0, _defineProperty2.default)(_this, "renderHeader", (0, _memoizeOne.default)(function (extensionManifest) { // Remove below line when cleaning platform_editor_ai_object_sidebar_injection FG if (_this.props.usingObjectSidebarPanel) { return null; } var _this$props2 = _this.props, onCancel = _this$props2.onCancel, showHeader = _this$props2.showHeader; // Use a temporary allowlist of top 3 macros to test out a new "Documentation" CTA ("Need help?") // This will be removed when Top 5 Modernized Macros updates are rolled out var modernizedMacrosList = ['children', 'recently-updated', 'excerpt']; var enableHelpCTA = modernizedMacrosList.includes(extensionManifest.key); if (!showHeader) { return null; } return /*#__PURE__*/_react.default.createElement(_Header.default, { icon: extensionManifest.icons['48'], title: extensionManifest.title, description: extensionManifest.description, deprecation: extensionManifest.deprecation, summary: extensionManifest.summary, documentationUrl: extensionManifest.documentationUrl, onClose: onCancel, enableHelpCTA: enableHelpCTA }); })); (0, _defineProperty2.default)(_this, "getFirstVisibleFieldName", (0, _memoizeOne.default)(function (fields) { function nonHidden(field) { if ('isHidden' in field) { return !field.isHidden; } return true; } // finds the first visible field, true for FieldSets too var firstVisibleField = fields.find(nonHidden); var newFirstVisibleFieldName; if (firstVisibleField) { // if it was a fieldset, go deeper trying to locate the field if (firstVisibleField.type === 'fieldset') { var firstVisibleFieldWithinFieldset = firstVisibleField.fields.find(nonHidden); newFirstVisibleFieldName = firstVisibleFieldWithinFieldset && firstVisibleFieldWithinFieldset.name; } else { newFirstVisibleFieldName = firstVisibleField.name; } } return newFirstVisibleFieldName; })); (0, _defineProperty2.default)(_this, "setFirstVisibleFieldName", function (fields) { var newFirstVisibleFieldName = _this.getFirstVisibleFieldName(fields); if (newFirstVisibleFieldName !== _this.state.firstVisibleFieldName) { _this.setState({ firstVisibleFieldName: newFirstVisibleFieldName }); } }); _this.state = { hasParsedParameters: false, currentParameters: {}, firstVisibleFieldName: props.fields ? _this.getFirstVisibleFieldName(props.fields) : undefined }; _this.onFieldChange = null; _this.unbindKeyDownHandler = null; return _this; } (0, _inherits2.default)(ConfigPanel, _React$Component); return (0, _createClass2.default)(ConfigPanel, [{ key: "componentDidMount", value: function componentDidMount() { var _this$props3 = this.props, fields = _this$props3.fields, parameters = _this$props3.parameters; this.parseParameters(fields, parameters); if ((0, _expValEquals.expValEquals)('platform_editor_a11y_eslint_fix', 'isEnabled', true)) { var doc = (0, _browserApis.getDocument)(); if (doc) { this.unbindKeyDownHandler = (0, _bindEventListener.bind)(doc, { type: 'keydown', listener: this.handleKeyDown }); } } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { var _this$unbindKeyDownHa; var _this$props4 = this.props, createAnalyticsEvent = _this$props4.createAnalyticsEvent, extensionManifest = _this$props4.extensionManifest, fields = _this$props4.fields; var currentParameters = this.state.currentParameters; (_this$unbindKeyDownHa = this.unbindKeyDownHandler) === null || _this$unbindKeyDownHa === void 0 || _this$unbindKeyDownHa.call(this); (0, _analytics.fireAnalyticsEvent)(createAnalyticsEvent)({ payload: { action: _analytics.ACTION.CLOSED, actionSubject: _analytics.ACTION_SUBJECT.CONFIG_PANEL, eventType: _analytics.EVENT_TYPE.UI, attributes: _objectSpread({ extensionKey: extensionManifest === null || extensionManifest === void 0 ? void 0 : extensionManifest.key, extensionType: extensionManifest === null || extensionManifest === void 0 ? void 0 : extensionManifest.type }, extensionManifest !== null && extensionManifest !== void 0 && extensionManifest.key && _constants.ALLOWED_LOGGED_MACRO_PARAMS[extensionManifest.key] ? { parameters: (0, _utils.getLoggedParameters)(extensionManifest.key, currentParameters, fields) } : {}) } }); } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { var _this$props5 = this.props, parameters = _this$props5.parameters, fields = _this$props5.fields, autoSaveTrigger = _this$props5.autoSaveTrigger, extensionManifest = _this$props5.extensionManifest; if (parameters && parameters !== prevProps.parameters || fields && (!prevProps.fields || !(0, _isEqual2.default)(fields, prevProps.fields))) { this.parseParameters(fields, parameters); } if (fields && (!prevProps.fields || !(0, _isEqual2.default)(fields, prevProps.fields))) { this.setFirstVisibleFieldName(fields); } if (prevProps.autoSaveTrigger !== autoSaveTrigger) { if (this.onFieldChange) { this.onFieldChange('', true); } } if (prevProps.extensionManifest === undefined && prevProps.extensionManifest !== extensionManifest) { // This will only be fired once when extensionManifest is loaded initially // Can't do this in componentDidMount because extensionManifest is still undefined at that point (0, _analytics.fireAnalyticsEvent)(this.props.createAnalyticsEvent)({ payload: { action: _analytics.ACTION.OPENED, actionSubject: _analytics.ACTION_SUBJECT.CONFIG_PANEL, eventType: _analytics.EVENT_TYPE.UI, attributes: { extensionKey: extensionManifest === null || extensionManifest === void 0 ? void 0 : extensionManifest.key, extensionType: extensionManifest === null || extensionManifest === void 0 ? void 0 : extensionManifest.type } } }); } } }, { key: "render", value: function render() { var _this2 = this; var _this$props6 = this.props, extensionManifest = _this$props6.extensionManifest, featureFlags = _this$props6.featureFlags; if (!extensionManifest) { return /*#__PURE__*/_react.default.createElement(_LoadingState.default, null); } var _this$props7 = this.props, errorMessage = _this$props7.errorMessage, fields = _this$props7.fields, isLoading = _this$props7.isLoading, onCancel = _this$props7.onCancel, api = _this$props7.api; var _this$state = this.state, currentParameters = _this$state.currentParameters, hasParsedParameters = _this$state.hasParsedParameters, firstVisibleFieldName = _this$state.firstVisibleFieldName; var handleSubmit = this.handleSubmit, handleKeyDown = this.handleKeyDown; return /*#__PURE__*/_react.default.createElement(_form.default, { onSubmit: handleSubmit }, function (_ref5) { var formProps = _ref5.formProps, getState = _ref5.getState, submitting = _ref5.submitting; return /*#__PURE__*/_react.default.createElement(WithOnFieldChange, { autoSave: true, getState: getState, handleSubmit: handleSubmit }, function (onFieldChange) { _this2.onFieldChange = onFieldChange; return /*#__PURE__*/_react.default.createElement("form", (0, _extends2.default)({}, formProps, { noValidate: true, onKeyDown: (0, _expValEquals.expValEquals)('platform_editor_a11y_eslint_fix', 'isEnabled', true) ? undefined : handleKeyDown, "data-testid": "extension-config-panel" }), _this2.renderHeader(extensionManifest), (!(0, _platformFeatureFlags.fg)('platform_editor_conditionally_add_sidebar_summary') || _this2.props.usingObjectSidebarPanel) && (0, _platformFeatureFlags.fg)('platform_editor_ai_object_sidebar_injection') && /*#__PURE__*/_react.default.createElement(_DescriptionSummary.DescriptionSummary, { extensionManifest: extensionManifest }), /*#__PURE__*/_react.default.createElement(ConfigFormIntlWithBoundary, { api: api, canSave: false, errorMessage: errorMessage, extensionManifest: extensionManifest, fields: fields !== null && fields !== void 0 ? fields : [], firstVisibleFieldName: firstVisibleFieldName, hasParsedParameters: hasParsedParameters, isLoading: isLoading || false, onCancel: onCancel, onFieldChange: onFieldChange, parameters: currentParameters, submitting: submitting, featureFlags: featureFlags, disableFields: _this2.props.disableFields })); }); }); } }]); }(_react.default.Component); var selector = function selector(states) { var _states$contextIdenti; return { contextIdentifierProvider: (_states$contextIdenti = states.contextIdentifierState) === null || _states$contextIdenti === void 0 ? void 0 : _states$contextIdenti.contextIdentifierProvider }; }; function ConfigFormIntlWithBoundary(_ref6) { var api = _ref6.api, fields = _ref6.fields, submitting = _ref6.submitting, parameters = _ref6.parameters, featureFlags = _ref6.featureFlags, canSave = _ref6.canSave, extensionManifest = _ref6.extensionManifest, onFieldChange = _ref6.onFieldChange, onCancel = _ref6.onCancel, isLoading = _ref6.isLoading, hasParsedParameters = _ref6.hasParsedParameters, firstVisibleFieldName = _ref6.firstVisibleFieldName, errorMessage = _ref6.errorMessage, disableFields = _ref6.disableFields; var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['contextIdentifier'], selector), contextIdentifierProvider = _useSharedPluginState.contextIdentifierProvider; return /*#__PURE__*/_react.default.createElement(_FormErrorBoundary.FormErrorBoundary, { contextIdentifierProvider: contextIdentifierProvider, extensionKey: extensionManifest.key, fields: fields }, /*#__PURE__*/_react.default.createElement(ConfigFormIntl, { canSave: canSave, errorMessage: errorMessage, extensionManifest: extensionManifest, fields: fields, firstVisibleFieldName: firstVisibleFieldName, hasParsedParameters: hasParsedParameters, isLoading: isLoading || false, onCancel: onCancel, onFieldChange: onFieldChange, parameters: parameters, submitting: submitting, contextIdentifierProvider: contextIdentifierProvider, featureFlags: featureFlags, disableFields: disableFields })); } var result = (0, _analyticsNext.withAnalyticsContext)({ source: 'ConfigPanel' })((0, _analyticsNext.withAnalyticsEvents)()(ConfigPanel)); var _default = exports.default = result;