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

Merge pull request #17908 from overleaf/ii-bs5-badge

[web] Create Bootstrap 5 badges

GitOrigin-RevId: 72355c7cf7dca2a5d16bc890d7cfa4a432dd15ba
ilkin-overleaf 2 лет назад
Родитель
Сommit
cccd0f06d7
21 измененных файлов с 409 добавлено и 108 удалено
  1. 6 1
      services/web/frontend/js/features/group-management/components/members-table/member-row.tsx
  2. 11 6
      services/web/frontend/js/features/history/components/change-list/tag-tooltip.tsx
  3. 4 1
      services/web/frontend/js/features/history/components/file-tree/history-file-tree-item.tsx
  4. 6 3
      services/web/frontend/js/features/settings/components/emails/email.tsx
  5. 2 1
      services/web/frontend/js/features/settings/components/linking/enable-widget.tsx
  6. 2 1
      services/web/frontend/js/features/settings/components/linking/integration-widget.tsx
  7. 27 0
      services/web/frontend/js/features/ui/components/bootstrap-5/badge.tsx
  8. 41 0
      services/web/frontend/js/features/ui/components/bootstrap-5/wrappers/badge-wrapper.tsx
  9. 17 18
      services/web/frontend/js/shared/components/badge.tsx
  10. 0 53
      services/web/frontend/stories/badge.stories.tsx
  11. 93 0
      services/web/frontend/stories/ui/badge-bs3.stories.tsx
  12. 93 0
      services/web/frontend/stories/ui/badge-bs5.stories.tsx
  13. 8 0
      services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss
  14. 14 0
      services/web/frontend/stylesheets/bootstrap-5/abstracts/variable-overrides.scss
  15. 1 0
      services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss
  16. 1 0
      services/web/frontend/stylesheets/bootstrap-5/components/all.scss
  17. 35 0
      services/web/frontend/stylesheets/bootstrap-5/components/badge.scss
  18. 3 6
      services/web/frontend/stylesheets/components/badge.less
  19. 19 10
      services/web/test/frontend/features/group-management/components/group-members.spec.tsx
  20. 10 4
      services/web/test/frontend/features/group-management/components/managed-group-members.spec.tsx
  21. 16 4
      services/web/test/frontend/features/group-management/components/members-table/member-row.spec.tsx

+ 6 - 1
services/web/frontend/js/features/group-management/components/members-table/member-row.tsx

@@ -46,7 +46,12 @@ export default function MemberRow({
                 id={`pending-invite-symbol-${user._id}`}
                 id={`pending-invite-symbol-${user._id}`}
                 description={t('pending_invite')}
                 description={t('pending_invite')}
               >
               >
-                <Badge aria-label={t('pending_invite')}>
+                <Badge
+                  bsStyle={null}
+                  className="badge-bs3"
+                  aria-label={t('pending_invite')}
+                  data-testid="badge-pending-invite"
+                >
                   {t('pending_invite')}
                   {t('pending_invite')}
                 </Badge>
                 </Badge>
               </Tooltip>
               </Tooltip>

+ 11 - 6
services/web/frontend/js/features/history/components/change-list/tag-tooltip.tsx

@@ -64,16 +64,21 @@ function Tag({ label, currentUserId, ...props }: TagProps) {
     }
     }
   }
   }
 
 
+  const showCloseButton = Boolean(
+    (isOwnedByCurrentUser || isProjectOwner) && !isPseudoCurrentStateLabel
+  )
+
   return (
   return (
     <>
     <>
       <Badge
       <Badge
         prepend={<Icon type="tag" fw />}
         prepend={<Icon type="tag" fw />}
-        onClose={showConfirmationModal}
-        closeButton={Boolean(
-          (isOwnedByCurrentUser || isProjectOwner) && !isPseudoCurrentStateLabel
-        )}
-        closeBtnProps={{ 'aria-label': t('delete') }}
-        className="history-version-badge"
+        closeBtnProps={
+          showCloseButton
+            ? { 'aria-label': t('delete'), onClick: showConfirmationModal }
+            : undefined
+        }
+        bsStyle={null}
+        className="badge-bs3 history-version-badge"
         data-testid="history-version-badge"
         data-testid="history-version-badge"
         {...props}
         {...props}
       >
       >

+ 4 - 1
services/web/frontend/js/features/history/components/file-tree/history-file-tree-item.tsx

@@ -26,7 +26,10 @@ export default function HistoryFileTreeItem({
           {name}
           {name}
         </div>
         </div>
         {operation ? (
         {operation ? (
-          <Badge className="history-file-tree-item-badge" size="sm">
+          <Badge
+            bsStyle={null}
+            className="badge-bs3 history-file-tree-item-badge"
+          >
             {operation}
             {operation}
           </Badge>
           </Badge>
         ) : null}
         ) : null}

+ 6 - 3
services/web/frontend/js/features/settings/components/emails/email.tsx

@@ -2,6 +2,9 @@ import { useTranslation } from 'react-i18next'
 import { UserEmailData } from '../../../../../../types/user-email'
 import { UserEmailData } from '../../../../../../types/user-email'
 import ResendConfirmationEmailButton from './resend-confirmation-email-button'
 import ResendConfirmationEmailButton from './resend-confirmation-email-button'
 import { ssoAvailableForInstitution } from '../../utils/sso'
 import { ssoAvailableForInstitution } from '../../utils/sso'
+import BadgeWrapper from '@/features/ui/components/bootstrap-5/wrappers/badge-wrapper'
+import { isBootstrap5 } from '@/features/utils/bootstrap-5'
+import classnames from 'classnames'
 
 
 type EmailProps = {
 type EmailProps = {
   userEmailData: UserEmailData
   userEmailData: UserEmailData
@@ -37,14 +40,14 @@ function Email({ userEmailData }: EmailProps) {
         </div>
         </div>
       )}
       )}
       {hasBadges && (
       {hasBadges && (
-        <div className="small">
+        <div className={classnames({ small: !isBootstrap5 })}>
           {isPrimary && (
           {isPrimary && (
             <>
             <>
-              <span className="label label-info">Primary</span>{' '}
+              <BadgeWrapper bg="info">Primary</BadgeWrapper>{' '}
             </>
             </>
           )}
           )}
           {isProfessional && (
           {isProfessional && (
-            <span className="label label-primary">{t('professional')}</span>
+            <BadgeWrapper bg="primary">{t('professional')}</BadgeWrapper>
           )}
           )}
         </div>
         </div>
       )}
       )}

+ 2 - 1
services/web/frontend/js/features/settings/components/linking/enable-widget.tsx

@@ -2,6 +2,7 @@ import { ReactNode } from 'react'
 import { useTranslation } from 'react-i18next'
 import { useTranslation } from 'react-i18next'
 import { Button } from 'react-bootstrap'
 import { Button } from 'react-bootstrap'
 import { sendMB } from '@/infrastructure/event-tracking'
 import { sendMB } from '@/infrastructure/event-tracking'
+import BadgeWrapper from '@/features/ui/components/bootstrap-5/wrappers/badge-wrapper'
 
 
 function trackUpgradeClick() {
 function trackUpgradeClick() {
   sendMB('settings-upgrade-click')
   sendMB('settings-upgrade-click')
@@ -48,7 +49,7 @@ export function EnableWidget({
         <div className="title-row">
         <div className="title-row">
           <h4>{title}</h4>
           <h4>{title}</h4>
           {!hasFeature && isPremiumFeature && (
           {!hasFeature && isPremiumFeature && (
-            <span className="label label-info">{t('premium_feature')}</span>
+            <BadgeWrapper bg="info">{t('premium_feature')}</BadgeWrapper>
           )}
           )}
         </div>
         </div>
         <p className="small">
         <p className="small">

+ 2 - 1
services/web/frontend/js/features/settings/components/linking/integration-widget.tsx

@@ -1,6 +1,7 @@
 import { useCallback, useState, ReactNode } from 'react'
 import { useCallback, useState, ReactNode } from 'react'
 import { useTranslation } from 'react-i18next'
 import { useTranslation } from 'react-i18next'
 import AccessibleModal from '../../../../shared/components/accessible-modal'
 import AccessibleModal from '../../../../shared/components/accessible-modal'
+import BadgeWrapper from '@/features/ui/components/bootstrap-5/wrappers/badge-wrapper'
 import { Button, Modal } from 'react-bootstrap'
 import { Button, Modal } from 'react-bootstrap'
 import getMeta from '../../../../utils/meta'
 import getMeta from '../../../../utils/meta'
 import { sendMB } from '../../../../infrastructure/event-tracking'
 import { sendMB } from '../../../../infrastructure/event-tracking'
@@ -57,7 +58,7 @@ export function IntegrationLinkingWidget({
         <div className="title-row">
         <div className="title-row">
           <h4>{title}</h4>
           <h4>{title}</h4>
           {!hasFeature && (
           {!hasFeature && (
-            <span className="label label-info">{t('premium_feature')}</span>
+            <BadgeWrapper bg="info">{t('premium_feature')}</BadgeWrapper>
           )}
           )}
         </div>
         </div>
         <p className="small">
         <p className="small">

+ 27 - 0
services/web/frontend/js/features/ui/components/bootstrap-5/badge.tsx

@@ -0,0 +1,27 @@
+import { Badge as BSBadge } from 'react-bootstrap-5'
+import { MergeAndOverride } from '../../../../../../types/utils'
+import MaterialIcon from '@/shared/components/material-icon'
+
+type BadgeProps = MergeAndOverride<
+  React.ComponentProps<typeof BSBadge>,
+  {
+    prepend?: React.ReactNode
+    closeBtnProps?: React.ComponentProps<'button'>
+  }
+>
+
+function Badge({ prepend, children, closeBtnProps, ...rest }: BadgeProps) {
+  return (
+    <BSBadge {...rest}>
+      {prepend && <span className="badge-prepend">{prepend}</span>}
+      {children}
+      {closeBtnProps && (
+        <button type="button" className="badge-close" {...closeBtnProps}>
+          <MaterialIcon className="badge-close-icon" type="close" />
+        </button>
+      )}
+    </BSBadge>
+  )
+}
+
+export default Badge

+ 41 - 0
services/web/frontend/js/features/ui/components/bootstrap-5/wrappers/badge-wrapper.tsx

@@ -0,0 +1,41 @@
+import { Label } from 'react-bootstrap'
+import Badge from '@/features/ui/components/bootstrap-5/badge'
+import BS3Badge from '@/shared/components/badge'
+import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
+
+type BadgeWrapperProps = React.ComponentProps<typeof Badge> & {
+  bs3Props?: {
+    bsStyle?: React.ComponentProps<typeof Label>['bsStyle'] | null
+  }
+}
+
+function BadgeWrapper(props: BadgeWrapperProps) {
+  const { bs3Props, ...rest } = props
+
+  let bs3BadgeProps: React.ComponentProps<typeof BS3Badge> = {
+    prepend: rest.prepend,
+    children: rest.children,
+    closeBtnProps: rest.closeBtnProps,
+    className: rest.className,
+    bsStyle: rest.bg,
+  }
+
+  if (bs3Props) {
+    const { bsStyle, ...restBs3Props } = bs3Props
+
+    bs3BadgeProps = {
+      ...bs3BadgeProps,
+      ...restBs3Props,
+      bsStyle: 'bsStyle' in bs3Props ? bsStyle : rest.bg,
+    }
+  }
+
+  return (
+    <BootstrapVersionSwitcher
+      bs3={<BS3Badge {...bs3BadgeProps} />}
+      bs5={<Badge {...rest} />}
+    />
+  )
+}
+
+export default BadgeWrapper

+ 17 - 18
services/web/frontend/js/shared/components/badge.tsx

@@ -1,40 +1,39 @@
 import classnames from 'classnames'
 import classnames from 'classnames'
 import { MergeAndOverride } from '../../../../types/utils'
 import { MergeAndOverride } from '../../../../types/utils'
+import BadgeWrapper from '@/features/ui/components/bootstrap-5/wrappers/badge-wrapper'
 
 
 type BadgeProps = MergeAndOverride<
 type BadgeProps = MergeAndOverride<
   React.ComponentProps<'span'>,
   React.ComponentProps<'span'>,
   {
   {
     prepend?: React.ReactNode
     prepend?: React.ReactNode
     children: React.ReactNode
     children: React.ReactNode
-    className?: string
-    closeButton?: boolean
-    onClose?: (e: React.MouseEvent<HTMLButtonElement>) => void
     closeBtnProps?: React.ComponentProps<'button'>
     closeBtnProps?: React.ComponentProps<'button'>
-    size?: 'sm'
+    className?: string
+    bsStyle?: NonNullable<
+      React.ComponentProps<typeof BadgeWrapper>['bs3Props']
+    >['bsStyle']
   }
   }
 >
 >
 
 
 function Badge({
 function Badge({
   prepend,
   prepend,
   children,
   children,
-  className,
-  closeButton = false,
-  onClose,
   closeBtnProps,
   closeBtnProps,
-  size,
+  bsStyle,
+  className,
   ...rest
   ...rest
 }: BadgeProps) {
 }: BadgeProps) {
+  const classNames =
+    bsStyle === null
+      ? className
+      : classnames('label', `label-${bsStyle}`, className)
+
   return (
   return (
-    <span className={classnames('badge-new', className)} {...rest}>
-      {prepend}
-      <span className="badge-new-comment">{children}</span>
-      {closeButton && (
-        <button
-          type="button"
-          className="badge-new-close"
-          onClick={onClose}
-          {...closeBtnProps}
-        >
+    <span className={classNames} {...rest}>
+      {prepend && <span className="badge-bs3-prepend">{prepend}</span>}
+      {children}
+      {closeBtnProps && (
+        <button type="button" className="badge-bs3-close" {...closeBtnProps}>
           <span aria-hidden="true">&times;</span>
           <span aria-hidden="true">&times;</span>
         </button>
         </button>
       )}
       )}

+ 0 - 53
services/web/frontend/stories/badge.stories.tsx

@@ -1,53 +0,0 @@
-import Badge from '../js/shared/components/badge'
-import Icon from '../js/shared/components/icon'
-
-type Args = React.ComponentProps<typeof Badge>
-
-export const NewBadge = (args: Args) => {
-  return <Badge {...args} />
-}
-
-export const NewBadgePrepend = (args: Args) => {
-  return <Badge prepend={<Icon type="tag" fw />} {...args} />
-}
-
-export const NewBadgeWithCloseButton = (args: Args) => {
-  return (
-    <Badge
-      prepend={<Icon type="tag" fw />}
-      closeButton
-      onClose={() => alert('Close triggered!')}
-      {...args}
-    />
-  )
-}
-
-export default {
-  title: 'Shared / Components / Badge',
-  component: Badge,
-  args: {
-    children: 'content',
-  },
-  argTypes: {
-    prepend: {
-      table: {
-        disable: true,
-      },
-    },
-    closeButton: {
-      table: {
-        disable: true,
-      },
-    },
-    onClose: {
-      table: {
-        disable: true,
-      },
-    },
-    closeBtnProps: {
-      table: {
-        disable: true,
-      },
-    },
-  },
-}

+ 93 - 0
services/web/frontend/stories/ui/badge-bs3.stories.tsx

@@ -0,0 +1,93 @@
+import Badge from '@/shared/components/badge'
+import Icon from '@/shared/components/icon'
+import type { Meta, StoryObj } from '@storybook/react'
+import classnames from 'classnames'
+
+const meta: Meta<typeof Badge> = {
+  title: 'Shared / Components / Badge / Bootstrap 3',
+  component: Badge,
+  parameters: {
+    bootstrap5: false,
+  },
+  args: {
+    children: 'Badge',
+  },
+  argTypes: {
+    prepend: {
+      table: {
+        disable: true,
+      },
+    },
+    bsStyle: {
+      options: [null, 'primary', 'warning', 'danger'],
+      control: { type: 'radio' },
+    },
+    className: {
+      table: {
+        disable: true,
+      },
+    },
+    closeBtnProps: {
+      table: {
+        disable: true,
+      },
+    },
+  },
+}
+export default meta
+
+type Story = StoryObj<typeof Badge>
+
+export const BadgeDefault: Story = {
+  render: args => {
+    return (
+      <Badge
+        className={classnames({ 'badge-bs3': args.bsStyle === null })}
+        {...args}
+      />
+    )
+  },
+}
+BadgeDefault.args = {
+  bsStyle: null,
+}
+
+export const BadgePrepend: Story = {
+  render: args => {
+    return (
+      <Badge
+        className={classnames({ 'badge-bs3': args.bsStyle === null })}
+        prepend={<Icon type="tag" fw />}
+        {...args}
+      />
+    )
+  },
+}
+BadgePrepend.args = {
+  bsStyle: null,
+}
+
+export const BadgeWithCloseButton: Story = {
+  render: args => {
+    return (
+      <Badge
+        className={classnames({ 'badge-bs3': args.bsStyle === null })}
+        prepend={<Icon type="tag" fw />}
+        closeBtnProps={{
+          onClick: () => alert('Close triggered!'),
+        }}
+        {...args}
+      />
+    )
+  },
+}
+BadgeWithCloseButton.args = {
+  bsStyle: null,
+}
+BadgeWithCloseButton.argTypes = {
+  bsStyle: {
+    table: {
+      disable: true,
+    },
+  },
+}

+ 93 - 0
services/web/frontend/stories/ui/badge-bs5.stories.tsx

@@ -0,0 +1,93 @@
+import Badge from '@/features/ui/components/bootstrap-5/badge'
+import Icon from '@/shared/components/icon'
+import type { Meta, StoryObj } from '@storybook/react'
+import classnames from 'classnames'
+
+const meta: Meta<typeof Badge> = {
+  title: 'Shared / Components / Badge / Bootstrap 5',
+  component: Badge,
+  parameters: {
+    bootstrap5: true,
+  },
+  args: {
+    children: 'Badge',
+  },
+  argTypes: {
+    bg: {
+      options: ['light', 'info', 'primary', 'warning', 'danger'],
+      control: { type: 'radio' },
+    },
+    prepend: {
+      table: {
+        disable: true,
+      },
+    },
+    className: {
+      table: {
+        disable: true,
+      },
+    },
+    closeBtnProps: {
+      table: {
+        disable: true,
+      },
+    },
+  },
+}
+export default meta
+
+type Story = StoryObj<typeof Badge>
+
+export const BadgeDefault: Story = {
+  render: args => {
+    return (
+      <Badge
+        className={classnames({ 'text-dark': args.bg === 'light' })}
+        {...args}
+      />
+    )
+  },
+}
+BadgeDefault.args = {
+  bg: 'light',
+}
+
+export const BadgePrepend: Story = {
+  render: args => {
+    return (
+      <Badge
+        className={classnames({ 'text-dark': args.bg === 'light' })}
+        prepend={<Icon type="tag" fw />}
+        {...args}
+      />
+    )
+  },
+}
+BadgePrepend.args = {
+  bg: 'light',
+}
+
+export const BadgeWithCloseButton: Story = {
+  render: args => {
+    return (
+      <Badge
+        className={classnames({ 'text-dark': args.bg === 'light' })}
+        prepend={<Icon type="tag" fw />}
+        closeBtnProps={{
+          onClick: () => alert('Close triggered!'),
+        }}
+        {...args}
+      />
+    )
+  },
+}
+BadgeWithCloseButton.args = {
+  bg: 'light',
+}
+BadgeWithCloseButton.argTypes = {
+  bg: {
+    table: {
+      disable: true,
+    },
+  },
+}

+ 8 - 0
services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss

@@ -44,3 +44,11 @@
     border-color: var(--bs-btn-border-color);
     border-color: var(--bs-btn-border-color);
   }
   }
 }
 }
+
+@mixin reset-button() {
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+  -webkit-appearance: none;
+}

+ 14 - 0
services/web/frontend/stylesheets/bootstrap-5/abstracts/variable-overrides.scss

@@ -1,5 +1,7 @@
 // Overrides for Bootstrap 5's default Sass variables
 // Overrides for Bootstrap 5's default Sass variables
 
 
+$prefix: bs-;
+
 // Fonts
 // Fonts
 $font-family-sans-serif: 'Noto Sans', sans-serif;
 $font-family-sans-serif: 'Noto Sans', sans-serif;
 $font-family-serif: 'Merriweather', serif;
 $font-family-serif: 'Merriweather', serif;
@@ -21,6 +23,18 @@ $btn-border-radius-sm: $border-radius-full;
 // Colors
 // Colors
 $primary: $bg-accent-01;
 $primary: $bg-accent-01;
 $secondary: $bg-light-primary;
 $secondary: $bg-light-primary;
+$info: $bg-info-01;
+$warning: $bg-warning-01;
+$danger: $bg-danger-01;
+$light: $bg-light-tertiary;
+$dark: $neutral-90;
+
+// Badges
+$badge-font-size: var(--font-size-01);
+$badge-font-weight: var(--bs-body-font-weight);
+$badge-padding-y: $spacing-01;
+$badge-padding-x: $spacing-02;
+$badge-border-radius: $border-radius-base;
 
 
 // Tooltips
 // Tooltips
 $tooltip-max-width: 320px;
 $tooltip-max-width: 320px;

+ 1 - 0
services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss

@@ -28,6 +28,7 @@
 @import 'bootstrap-5/scss/grid';
 @import 'bootstrap-5/scss/grid';
 @import 'bootstrap-5/scss/buttons';
 @import 'bootstrap-5/scss/buttons';
 @import 'bootstrap-5/scss/dropdown';
 @import 'bootstrap-5/scss/dropdown';
+@import 'bootstrap-5/scss/badge';
 @import 'bootstrap-5/scss/modal';
 @import 'bootstrap-5/scss/modal';
 @import 'bootstrap-5/scss/tooltip';
 @import 'bootstrap-5/scss/tooltip';
 @import 'bootstrap-5/scss/spinners';
 @import 'bootstrap-5/scss/spinners';

+ 1 - 0
services/web/frontend/stylesheets/bootstrap-5/components/all.scss

@@ -4,3 +4,4 @@
 @import 'notifications';
 @import 'notifications';
 @import 'tooltip';
 @import 'tooltip';
 @import 'card';
 @import 'card';
+@import 'badge';

+ 35 - 0
services/web/frontend/stylesheets/bootstrap-5/components/badge.scss

@@ -0,0 +1,35 @@
+.badge {
+  display: inline-flex;
+  align-items: center;
+  overflow: hidden;
+  line-height: var(--line-height-01);
+}
+
+.badge-prepend {
+  margin-left: calc($spacing-01 / -2);
+  margin-right: $spacing-01;
+}
+
+.badge-close {
+  @include reset-button();
+
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  // a random number that would cause the close button to expand enough
+  // so that it won't be affected by badge's padding
+  $expand: 100px;
+  padding: $expand $spacing-01;
+  margin: (-$expand) (-$spacing-02) (-$expand) $spacing-02;
+  user-select: none;
+  color: inherit;
+
+  .badge-close-icon {
+    font-size: $font-size-base;
+  }
+
+  &:hover {
+    background-color: var(--neutral-40);
+  }
+}

+ 3 - 6
services/web/frontend/stylesheets/components/badge.less

@@ -1,4 +1,4 @@
-.badge-new {
+.badge-bs3 {
   @size: 24px;
   @size: 24px;
   @padding: 4px;
   @padding: 4px;
   display: inline-flex;
   display: inline-flex;
@@ -20,10 +20,6 @@
     margin-right: 2px;
     margin-right: 2px;
   }
   }
 
 
-  &-comment {
-    flex: 1;
-  }
-
   &-close {
   &-close {
     .reset-button;
     .reset-button;
     width: @size;
     width: @size;
@@ -34,6 +30,7 @@
     align-items: center;
     align-items: center;
     justify-content: center;
     justify-content: center;
     margin-right: -@padding;
     margin-right: -@padding;
+    color: inherit;
 
 
     &:hover {
     &:hover {
       background-color: @neutral-40;
       background-color: @neutral-40;
@@ -44,7 +41,7 @@
     @size-sm: 20px;
     @size-sm: 20px;
     height: @size-sm;
     height: @size-sm;
     font-size: @font-size-extra-small;
     font-size: @font-size-extra-small;
-    .badge-new-close {
+    .badge-bs3-close {
       width: @size-sm;
       width: @size-sm;
       font-size: @size-sm;
       font-size: @size-sm;
     }
     }

+ 19 - 10
services/web/test/frontend/features/group-management/components/group-members.spec.tsx

@@ -61,14 +61,17 @@ describe('GroupMembers', function () {
           cy.contains('john.doe@test.com')
           cy.contains('john.doe@test.com')
           cy.contains('John Doe')
           cy.contains('John Doe')
           cy.contains('15th Jan 2023')
           cy.contains('15th Jan 2023')
-          cy.get('.badge-new-comment').contains('Pending invite')
+          cy.findByTestId('badge-pending-invite').should(
+            'have.text',
+            'Pending invite'
+          )
         })
         })
 
 
         cy.get('tr:nth-child(2)').within(() => {
         cy.get('tr:nth-child(2)').within(() => {
           cy.contains('bobby.lapointe@test.com')
           cy.contains('bobby.lapointe@test.com')
           cy.contains('Bobby Lapointe')
           cy.contains('Bobby Lapointe')
           cy.contains('2nd Jan 2023')
           cy.contains('2nd Jan 2023')
-          cy.get('.badge-new-comment').should('not.exist')
+          cy.findByTestId('badge-pending-invite').should('not.exist')
         })
         })
       })
       })
     })
     })
@@ -91,7 +94,10 @@ describe('GroupMembers', function () {
         cy.get('tr:nth-child(3)').within(() => {
         cy.get('tr:nth-child(3)').within(() => {
           cy.contains('someone.else@test.com')
           cy.contains('someone.else@test.com')
           cy.contains('N/A')
           cy.contains('N/A')
-          cy.get('.badge-new-comment').contains('Pending invite')
+          cy.findByTestId('badge-pending-invite').should(
+            'have.text',
+            'Pending invite'
+          )
         })
         })
       })
       })
     })
     })
@@ -236,10 +242,10 @@ describe('GroupMembers', function () {
           cy.contains('John Doe')
           cy.contains('John Doe')
           cy.contains('15th Jan 2023')
           cy.contains('15th Jan 2023')
           cy.get('.sr-only').contains('Pending invite')
           cy.get('.sr-only').contains('Pending invite')
-          cy.get('.badge-new-comment').contains('Pending invite')
-          cy.get(`.security-state-invite-pending`).should('exist')
-
-          cy.get('.badge-new-comment').contains('Pending invite')
+          cy.findByTestId('badge-pending-invite').should(
+            'have.text',
+            'Pending invite'
+          )
           cy.get(`.security-state-invite-pending`).should('exist')
           cy.get(`.security-state-invite-pending`).should('exist')
         })
         })
 
 
@@ -247,7 +253,7 @@ describe('GroupMembers', function () {
           cy.contains('bobby.lapointe@test.com')
           cy.contains('bobby.lapointe@test.com')
           cy.contains('Bobby Lapointe')
           cy.contains('Bobby Lapointe')
           cy.contains('2nd Jan 2023')
           cy.contains('2nd Jan 2023')
-          cy.get('.badge-new-comment').should('not.exist')
+          cy.findByTestId('badge-pending-invite').should('not.exist')
           cy.get('.sr-only').contains('Not managed')
           cy.get('.sr-only').contains('Not managed')
         })
         })
 
 
@@ -255,7 +261,7 @@ describe('GroupMembers', function () {
           cy.contains('claire.jennings@test.com')
           cy.contains('claire.jennings@test.com')
           cy.contains('Claire Jennings')
           cy.contains('Claire Jennings')
           cy.contains('3rd Jan 2023')
           cy.contains('3rd Jan 2023')
-          cy.get('.badge-new-comment').should('not.exist')
+          cy.findByTestId('badge-pending-invite').should('not.exist')
           cy.get('.sr-only').contains('Managed')
           cy.get('.sr-only').contains('Managed')
         })
         })
       })
       })
@@ -280,7 +286,10 @@ describe('GroupMembers', function () {
           cy.contains('someone.else@test.com')
           cy.contains('someone.else@test.com')
           cy.contains('N/A')
           cy.contains('N/A')
           cy.get('.sr-only').contains('Pending invite')
           cy.get('.sr-only').contains('Pending invite')
-          cy.get('.badge-new-comment').contains('Pending invite')
+          cy.findByTestId('badge-pending-invite').should(
+            'have.text',
+            'Pending invite'
+          )
           cy.get(`.security-state-invite-pending`).should('exist')
           cy.get(`.security-state-invite-pending`).should('exist')
         })
         })
       })
       })

+ 10 - 4
services/web/test/frontend/features/group-management/components/managed-group-members.spec.tsx

@@ -90,7 +90,10 @@ describe('group members, with managed users', function () {
         cy.contains('15th Jan 2023')
         cy.contains('15th Jan 2023')
         cy.get('.sr-only').contains('Pending invite')
         cy.get('.sr-only').contains('Pending invite')
 
 
-        cy.get('.badge-new-comment').contains('Pending invite')
+        cy.findByTestId('badge-pending-invite').should(
+          'have.text',
+          'Pending invite'
+        )
         cy.get(`.security-state-invite-pending`).should('exist')
         cy.get(`.security-state-invite-pending`).should('exist')
       })
       })
 
 
@@ -98,7 +101,7 @@ describe('group members, with managed users', function () {
         cy.contains('bobby.lapointe@test.com')
         cy.contains('bobby.lapointe@test.com')
         cy.contains('Bobby Lapointe')
         cy.contains('Bobby Lapointe')
         cy.contains('2nd Jan 2023')
         cy.contains('2nd Jan 2023')
-        cy.get('.badge-new-comment').should('not.exist')
+        cy.findByTestId('badge-pending-invite').should('not.exist')
         cy.get('.sr-only').contains('Not managed')
         cy.get('.sr-only').contains('Not managed')
       })
       })
 
 
@@ -106,7 +109,7 @@ describe('group members, with managed users', function () {
         cy.contains('claire.jennings@test.com')
         cy.contains('claire.jennings@test.com')
         cy.contains('Claire Jennings')
         cy.contains('Claire Jennings')
         cy.contains('3rd Jan 2023')
         cy.contains('3rd Jan 2023')
-        cy.get('.badge-new-comment').should('not.exist')
+        cy.findByTestId('badge-pending-invite').should('not.exist')
         cy.get('.sr-only').contains('Managed')
         cy.get('.sr-only').contains('Managed')
       })
       })
     })
     })
@@ -131,7 +134,10 @@ describe('group members, with managed users', function () {
         cy.contains('someone.else@test.com')
         cy.contains('someone.else@test.com')
         cy.contains('N/A')
         cy.contains('N/A')
         cy.get('.sr-only').contains('Pending invite')
         cy.get('.sr-only').contains('Pending invite')
-        cy.get('.badge-new-comment').contains('Pending invite')
+        cy.findByTestId('badge-pending-invite').should(
+          'have.text',
+          'Pending invite'
+        )
         cy.get(`.security-state-invite-pending`).should('exist')
         cy.get(`.security-state-invite-pending`).should('exist')
       })
       })
     })
     })

+ 16 - 4
services/web/test/frontend/features/group-management/components/members-table/member-row.spec.tsx

@@ -97,7 +97,10 @@ describe('MemberRow', function () {
       })
       })
 
 
       it('should render a "Pending invite" badge', function () {
       it('should render a "Pending invite" badge', function () {
-        cy.get('.badge-new-comment').contains('Pending invite')
+        cy.findByTestId('badge-pending-invite').should(
+          'have.text',
+          'Pending invite'
+        )
       })
       })
     })
     })
 
 
@@ -269,7 +272,10 @@ describe('MemberRow', function () {
       })
       })
 
 
       it('should render a "Pending invite" badge', function () {
       it('should render a "Pending invite" badge', function () {
-        cy.get('.badge-new-comment').contains('Pending invite')
+        cy.findByTestId('badge-pending-invite').should(
+          'have.text',
+          'Pending invite'
+        )
       })
       })
     })
     })
 
 
@@ -443,7 +449,10 @@ describe('MemberRow', function () {
       })
       })
 
 
       it('should render a "Pending invite" badge', function () {
       it('should render a "Pending invite" badge', function () {
-        cy.get('.badge-new-comment').contains('Pending invite')
+        cy.findByTestId('badge-pending-invite').should(
+          'have.text',
+          'Pending invite'
+        )
       })
       })
     })
     })
 
 
@@ -618,7 +627,10 @@ describe('MemberRow', function () {
       })
       })
 
 
       it('should render a "Pending invite" badge', function () {
       it('should render a "Pending invite" badge', function () {
-        cy.get('.badge-new-comment').contains('Pending invite')
+        cy.findByTestId('badge-pending-invite').should(
+          'have.text',
+          'Pending invite'
+        )
       })
       })
     })
     })