Просмотр исходного кода

[web] new notification style for dashboard (#15878)

* Create `new-notification-style` split test

GitOrigin-RevId: 3b6d8894da049939c424459ddb7d0afe967dab4c
Miguel Serrano 2 лет назад
Родитель
Сommit
b1978d9362

+ 17 - 0
services/web/app/src/Features/Project/ProjectListController.js

@@ -447,6 +447,22 @@ async function projectListPage(req, res, next) {
     logger.error({ err: error }, 'Failed to get individual subscription')
   }
 
+  let newNotificationStyle
+  try {
+    const newNotificationStyleAssignment =
+      await SplitTestHandler.promises.getAssignment(
+        req,
+        res,
+        'new-notification-style'
+      )
+    newNotificationStyle = newNotificationStyleAssignment.variant === 'enabled'
+  } catch (error) {
+    logger.error(
+      { err: error },
+      'failed to get "new-notification-style" split test assignment'
+    )
+  }
+
   res.render('project/list-react', {
     title: 'your_projects',
     usersBestSubscription,
@@ -477,6 +493,7 @@ async function projectListPage(req, res, next) {
         groupName: subscription.teamName,
       })),
     hasIndividualRecurlySubscription,
+    newNotificationStyle,
   })
 }
 

+ 1 - 0
services/web/app/views/project/list-react.pug

@@ -36,6 +36,7 @@ block append meta
 	meta(name="ol-welcomePageRedesignVariant" data-type="string" content=welcomePageRedesignVariant)
 	meta(name="ol-groupSubscriptionsPendingEnrollment" data-type="json" content=groupSubscriptionsPendingEnrollment)
 	meta(name="ol-hasIndividualRecurlySubscription" data-type="boolean" content=hasIndividualRecurlySubscription)
+	meta(name="ol-newNotificationStyle" data-type="boolean" content=newNotificationStyle)
 
 block content
 	main.content.content-alt.project-list-react#project-list-root

+ 36 - 12
services/web/frontend/js/features/project-list/components/modals/projects-action-modal.tsx

@@ -7,6 +7,8 @@ import { getUserFacingMessage } from '../../../../infrastructure/fetch-json'
 import useIsMounted from '../../../../shared/hooks/use-is-mounted'
 import * as eventTracking from '../../../../infrastructure/event-tracking'
 import { isMobileDevice } from '../../../../infrastructure/event-tracking'
+import getMeta from '@/utils/meta'
+import Notification from '@/shared/components/notification'
 
 type ProjectsActionModalProps = {
   title?: string
@@ -80,18 +82,7 @@ function ProjectsActionModal({
       <Modal.Footer>
         {!isProcessing &&
           errors.length > 0 &&
-          errors.map((e, i) => (
-            <Alert
-              bsStyle="danger"
-              key={`action-error-${i}`}
-              className="text-center"
-              aria-live="polite"
-            >
-              <b>{e.projectName}</b>
-              <br />
-              {getUserFacingMessage(e.error)}
-            </Alert>
-          ))}
+          errors.map((e, i) => <ErrorNotification error={e} key={i} />)}
         <button className="btn btn-secondary" onClick={handleCloseModal}>
           {t('cancel')}
         </button>
@@ -107,4 +98,37 @@ function ProjectsActionModal({
   )
 }
 
+type ErrorNotificationProps = {
+  error: any
+}
+
+function ErrorNotification({ error }: ErrorNotificationProps) {
+  const newNotificationStyle = getMeta(
+    'ol-newNotificationStyle',
+    false
+  ) as boolean
+
+  if (newNotificationStyle) {
+    return (
+      // `notification-list` sets the margin-bottom correctly also when used individually in each notification.
+      // Once the legacy alerts are cleaned up we should move the styled div up to the notification list container.
+      <div className="notification-list">
+        <Notification
+          type="error"
+          title={error.projectName}
+          content={getUserFacingMessage(error.error) as string}
+        />
+      </div>
+    )
+  } else {
+    return (
+      <Alert bsStyle="danger" className="text-center" aria-live="polite">
+        <b>{error.projectName}</b>
+        <br />
+        {getUserFacingMessage(error.error)}
+      </Alert>
+    )
+  }
+}
+
 export default memo(ProjectsActionModal)

+ 19 - 5
services/web/frontend/js/features/project-list/components/modals/rename-project-modal.tsx

@@ -17,6 +17,8 @@ import { useProjectListContext } from '../../context/project-list-context'
 import { getUserFacingMessage } from '../../../../infrastructure/fetch-json'
 import { debugConsole } from '@/utils/debugging'
 import { isMobileDevice } from '../../../../infrastructure/event-tracking'
+import Notification from '@/shared/components/notification'
+import getMeta from '@/utils/meta'
 
 type RenameProjectModalProps = {
   handleCloseModal: () => void
@@ -33,6 +35,10 @@ function RenameProjectModal({
   const [newProjectName, setNewProjectName] = useState(project.name)
   const { error, isError, isLoading, runAsync } = useAsync()
   const { updateProjectViewData } = useProjectListContext()
+  const newNotificationStyle = getMeta(
+    'ol-newNotificationStyle',
+    false
+  ) as boolean
 
   useEffect(() => {
     if (showModal) {
@@ -94,6 +100,19 @@ function RenameProjectModal({
         <Modal.Title>{t('rename_project')}</Modal.Title>
       </Modal.Header>
       <Modal.Body>
+        {isError &&
+          (newNotificationStyle ? (
+            <div className="notification-list">
+              <Notification
+                type="error"
+                content={getUserFacingMessage(error) as string}
+              />
+            </div>
+          ) : (
+            <Alert bsStyle="danger" className="text-center" aria-live="polite">
+              {getUserFacingMessage(error)}
+            </Alert>
+          ))}
         <form id="rename-project-form" onSubmit={handleSubmit}>
           <FormGroup>
             <ControlLabel htmlFor="rename-project-form-name">
@@ -112,11 +131,6 @@ function RenameProjectModal({
         </form>
       </Modal.Body>
       <Modal.Footer>
-        {isError && (
-          <Alert bsStyle="danger" className="text-center" aria-live="polite">
-            {getUserFacingMessage(error)}
-          </Alert>
-        )}
         <Button
           bsStyle={null}
           className="btn-secondary"

+ 17 - 3
services/web/frontend/js/features/project-list/components/new-project-button/modal-content-new-project-form.tsx

@@ -8,6 +8,8 @@ import {
 } from '../../../../infrastructure/fetch-json'
 import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-focus'
 import { useLocation } from '../../../../shared/hooks/use-location'
+import getMeta from '@/utils/meta'
+import Notification from '@/shared/components/notification'
 
 type NewProjectData = {
   project_id: string
@@ -31,6 +33,10 @@ function ModalContentNewProjectForm({ onCancel, template = 'none' }: Props) {
   const [projectName, setProjectName] = useState('')
   const { isLoading, isError, error, runAsync } = useAsync<NewProjectData>()
   const location = useLocation()
+  const newNotificationStyle = getMeta(
+    'ol-newNotificationStyle',
+    false
+  ) as boolean
 
   const createNewProject = () => {
     runAsync(
@@ -68,9 +74,17 @@ function ModalContentNewProjectForm({ onCancel, template = 'none' }: Props) {
       </Modal.Header>
 
       <Modal.Body>
-        {isError && (
-          <Alert bsStyle="danger">{getUserFacingMessage(error)}</Alert>
-        )}
+        {isError &&
+          (newNotificationStyle ? (
+            <div className="notification-list">
+              <Notification
+                type="error"
+                content={getUserFacingMessage(error) as string}
+              />
+            </div>
+          ) : (
+            <Alert bsStyle="danger">{getUserFacingMessage(error)}</Alert>
+          ))}
         <Form onSubmit={handleSubmit}>
           <input
             type="text"