mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #17908 from overleaf/ii-bs5-badge
[web] Create Bootstrap 5 badges GitOrigin-RevId: 72355c7cf7dca2a5d16bc890d7cfa4a432dd15ba
This commit is contained in:
parent
774b300e17
commit
cccd0f06d7
21 changed files with 409 additions and 108 deletions
|
@ -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>
|
||||||
|
|
|
@ -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}
|
closeBtnProps={
|
||||||
closeButton={Boolean(
|
showCloseButton
|
||||||
(isOwnedByCurrentUser || isProjectOwner) && !isPseudoCurrentStateLabel
|
? { 'aria-label': t('delete'), onClick: showConfirmationModal }
|
||||||
)}
|
: undefined
|
||||||
closeBtnProps={{ 'aria-label': t('delete') }}
|
}
|
||||||
className="history-version-badge"
|
bsStyle={null}
|
||||||
|
className="badge-bs3 history-version-badge"
|
||||||
data-testid="history-version-badge"
|
data-testid="history-version-badge"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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,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">
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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}>
|
<span className={classNames} {...rest}>
|
||||||
{prepend}
|
{prepend && <span className="badge-bs3-prepend">{prepend}</span>}
|
||||||
<span className="badge-new-comment">{children}</span>
|
{children}
|
||||||
{closeButton && (
|
{closeBtnProps && (
|
||||||
<button
|
<button type="button" className="badge-bs3-close" {...closeBtnProps}>
|
||||||
type="button"
|
|
||||||
className="badge-new-close"
|
|
||||||
onClick={onClose}
|
|
||||||
{...closeBtnProps}
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -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
services/web/frontend/stories/ui/badge-bs3.stories.tsx
Normal file
93
services/web/frontend/stories/ui/badge-bs3.stories.tsx
Normal file
|
@ -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
services/web/frontend/stories/ui/badge-bs5.stories.tsx
Normal file
93
services/web/frontend/stories/ui/badge-bs5.stories.tsx
Normal file
|
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
@import 'notifications';
|
@import 'notifications';
|
||||||
@import 'tooltip';
|
@import 'tooltip';
|
||||||
@import 'card';
|
@import 'card';
|
||||||
|
@import 'badge';
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.findByTestId('badge-pending-invite').should(
|
||||||
cy.get(`.security-state-invite-pending`).should('exist')
|
'have.text',
|
||||||
|
'Pending invite'
|
||||||
cy.get('.badge-new-comment').contains('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')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue