Browse Source

feat(invite-modal): Implement customization hook (#15177)

Evan Purkhiser 5 years ago
parent
commit
06e98bc273

+ 25 - 3
src/sentry/static/sentry/app/components/modals/inviteMembersModal/index.tsx

@@ -6,6 +6,7 @@ import {MEMBER_ROLES} from 'app/constants';
 import {ModalRenderProps} from 'app/actionCreators/modal';
 import InlineSvg from 'app/components/inlineSvg';
 import Button from 'app/components/button';
+import HookOrDefault from 'app/components/hookOrDefault';
 import space from 'app/styles/space';
 import AsyncComponent from 'app/components/asyncComponent';
 import {Organization} from 'app/types';
@@ -30,6 +31,14 @@ type State = AsyncComponent['state'] & {
 
 const DEFAULT_ROLE = 'member';
 
+const InviteModalHook = HookOrDefault({
+  hookName: 'member-invite-modal:customization',
+  defaultComponent: ({onSendInvites, children}) =>
+    children({sendInvites: onSendInvites, canSend: true}),
+});
+
+type InviteModalRenderFunc = React.ComponentProps<typeof InviteModalHook>['children'];
+
 class InviteMembersModal extends AsyncComponent<Props, State> {
   get inviteTemplate(): InviteRow {
     return {emails: new Set(), teams: new Set(), role: DEFAULT_ROLE};
@@ -239,7 +248,8 @@ class InviteMembersModal extends AsyncComponent<Props, State> {
 
     const disableInputs = sendingInvites || complete;
 
-    return (
+    // eslint-disable-next-line react/prop-types
+    const hookRenderer: InviteModalRenderFunc = ({sendInvites, canSend, headerInfo}) => (
       <React.Fragment>
         <Heading>
           <InlineSvg src="icon-mail" size="36px" />
@@ -266,6 +276,8 @@ class InviteMembersModal extends AsyncComponent<Props, State> {
               )}
         </Subtext>
 
+        {headerInfo}
+
         <InviteeHeadings>
           <div>{t('Email addresses')}</div>
           <div>{t('Role')}</div>
@@ -332,8 +344,8 @@ class InviteMembersModal extends AsyncComponent<Props, State> {
                   size="small"
                   data-test-id="send-invites"
                   priority="primary"
-                  disabled={!this.isValidInvites || disableInputs}
-                  onClick={this.sendInvites}
+                  disabled={!canSend || !this.isValidInvites || disableInputs}
+                  onClick={sendInvites}
                 >
                   {this.inviteButtonLabel}
                 </Button>
@@ -343,6 +355,16 @@ class InviteMembersModal extends AsyncComponent<Props, State> {
         </Footer>
       </React.Fragment>
     );
+
+    return (
+      <InviteModalHook
+        organization={this.props.organization}
+        willInvite={this.willInvite}
+        onSendInvites={this.sendInvites}
+      >
+        {hookRenderer}
+      </InviteModalHook>
+    );
   }
 }
 

+ 1 - 0
src/sentry/static/sentry/app/stores/hookStore.tsx

@@ -33,6 +33,7 @@ const validHookNames = new Set<HookName>([
   'feature-disabled:sso-saml2',
   'footer',
   'integrations:feature-gates',
+  'member-invite-modal:customization',
   'metrics:event',
   'onboarding:extra-chrome',
   'onboarding:invite-members',

+ 38 - 0
src/sentry/static/sentry/app/types/hooks.ts

@@ -58,6 +58,7 @@ export type ComponentHooks = {
  */
 export type CustomizationHooks = {
   'integrations:feature-gates': IntegrationsFeatureGatesHook;
+  'member-invite-modal:customization': InviteModalCustomizationHook;
 };
 
 /**
@@ -348,3 +349,40 @@ type IntegrationsFeatureGatesHook = () => {
    */
   FeatureList: React.ComponentType<IntegrationFeatureListProps>;
 };
+
+/**
+ * Invite Modal customization allows for a render-prop component to add
+ * additional react elements into the modal, and add invite-send middleware.
+ */
+type InviteModalCustomizationHook = () => React.ComponentType<{
+  /**
+   * The organization that members will be invited to.
+   */
+  organization: Organization;
+  /**
+   * Indicates if clicking 'send invites' will immediately send invites, or
+   * would just create invite requests.
+   */
+  willInvite: boolean;
+  /**
+   * When the children's sendInvites renderProp is called, this will also be
+   * triggered.
+   */
+  onSendInvites: () => void;
+  children: (opts: {
+    /**
+     * Additional react elements to render in the header of the modal, just
+     * under the description.
+     */
+    headerInfo?: React.ReactNode;
+    /**
+     * Indicates that the modal's send invites button should be enabled and
+     * invites may currently be sent.
+     */
+    canSend: boolean;
+    /**
+     * Trigger sending invites
+     */
+    sendInvites: () => void;
+  }) => React.ReactElement;
+}>;