Skip to content

Commit c87afbc

Browse files
authored
Merge pull request #501 from AppQuality/UN-643-meta-component
UN-643-meta-component
2 parents fd46bb0 + 69d8f20 commit c87afbc

File tree

14 files changed

+250
-139
lines changed

14 files changed

+250
-139
lines changed

src/common/components/Meta.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { MD, SM } from '@appquality/unguess-design-system';
2+
import styled from 'styled-components';
3+
4+
export type MetaSize = 'medium' | 'large';
5+
6+
export interface MetaArgs extends React.HTMLAttributes<HTMLDivElement> {
7+
size?: MetaSize;
8+
children?: React.ReactNode;
9+
color?: string;
10+
icon?: React.ReactNode;
11+
secondaryText?: React.ReactNode;
12+
}
13+
14+
const IconWrapper = styled.span``;
15+
16+
const StyledSpan = styled.span``;
17+
18+
const StyledMeta = styled.div<{ size: MetaSize }>`
19+
margin-right: ${(p) => p.theme.space.sm};
20+
&:last-of-type {
21+
margin-right: 0;
22+
}
23+
display: inline-flex;
24+
align-items: center;
25+
flex-wrap: nowrap;
26+
white-space: nowrap;
27+
padding: 0;
28+
min-width: 24px;
29+
background: transparent;
30+
text-decoration: none;
31+
color: ${(p) => p.color || p.theme.palette.grey[700]};
32+
33+
${IconWrapper} {
34+
display: inline-flex;
35+
align-items: center;
36+
margin-right: ${(p) => p.theme.space.xxs};
37+
> * {
38+
width: ${(p) => (p.size === 'large' ? '24px' : '16px')};
39+
height: ${(p) => (p.size === 'large' ? '24px' : '16px')};
40+
}
41+
}
42+
${StyledSpan} {
43+
color: ${(p) => p.theme.palette.grey[700]};
44+
font-weight: ${(p) => p.theme.fontWeights.medium};
45+
margin-left: ${(p) => p.theme.space.xxs};
46+
}
47+
`;
48+
49+
const Meta = ({
50+
size = 'medium',
51+
children,
52+
color,
53+
icon,
54+
secondaryText,
55+
}: MetaArgs) => (
56+
<StyledMeta size={size} color={color}>
57+
{icon && <IconWrapper>{icon}</IconWrapper>}
58+
{size === 'medium' ? (
59+
<SM isBold color={color}>
60+
{children}
61+
</SM>
62+
) : (
63+
<MD isBold color={color}>
64+
{children}
65+
</MD>
66+
)}
67+
{typeof secondaryText !== 'undefined' && (
68+
<StyledSpan>{secondaryText}</StyledSpan>
69+
)}
70+
</StyledMeta>
71+
);
72+
73+
export { Meta };
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* eslint-disable security/detect-object-injection */
2+
import { theme } from 'src/app/theme';
3+
import { capitalizeFirstLetter } from 'src/common/capitalizeFirstLetter';
4+
import { Meta, MetaSize } from '../Meta';
5+
6+
interface SeverityMetaProps {
7+
severity: Severities;
8+
counter?: number;
9+
size?: MetaSize;
10+
}
11+
export const SeverityMeta = ({
12+
severity,
13+
counter,
14+
size,
15+
}: SeverityMetaProps) => (
16+
<Meta
17+
size={size}
18+
color={theme.colors.bySeverity[severity]}
19+
secondaryText={counter}
20+
>
21+
{capitalizeFirstLetter(severity)}
22+
</Meta>
23+
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { CampaignStatus } from 'src/types';
2+
import { Meta } from '../Meta';
3+
import { getStatusInfo } from '../utils/getStatusInfo';
4+
5+
interface StatusMetaArgs extends React.HTMLAttributes<HTMLDivElement> {
6+
status: CampaignStatus;
7+
children?: React.ReactNode;
8+
counter?: number | string;
9+
isRound?: boolean;
10+
}
11+
12+
export const StatusMeta = ({ status, counter, ...props }: StatusMetaArgs) => {
13+
const statusInfo = getStatusInfo(status);
14+
15+
return (
16+
<Meta
17+
size="large"
18+
className={`campaign-status-pill ${status}`}
19+
color={statusInfo.color}
20+
icon={statusInfo.icon}
21+
secondaryText={counter}
22+
{...props}
23+
>
24+
{statusInfo.text}
25+
</Meta>
26+
);
27+
};

src/common/components/tag/StatusTag.tsx

Lines changed: 9 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,7 @@
1-
import {
2-
Tag,
3-
StatusCompletedIcon,
4-
StatusIncomingIcon,
5-
StatusRunningIcon,
6-
CampaignExperientialIcon,
7-
CampaignFunctionalIcon,
8-
} from '@appquality/unguess-design-system';
1+
import { Tag } from '@appquality/unguess-design-system';
92
import { theme } from 'src/app/theme';
10-
import { TFunction, useTranslation } from 'react-i18next';
11-
12-
export type CampaignStatus =
13-
| 'running'
14-
| 'completed'
15-
| 'incoming'
16-
| 'functional'
17-
| 'experiential';
3+
import { CampaignStatus } from 'src/types';
4+
import { getStatusInfo } from '../utils/getStatusInfo';
185

196
interface StatusTagArgs extends React.HTMLAttributes<HTMLDivElement> {
207
status: CampaignStatus;
@@ -23,83 +10,30 @@ interface StatusTagArgs extends React.HTMLAttributes<HTMLDivElement> {
2310
isRound?: boolean;
2411
}
2512

26-
const getColorByStatus = (status: CampaignStatus) => {
27-
switch (status) {
28-
case 'running':
29-
return theme.palette.yellow[700];
30-
case 'completed':
31-
return theme.palette.green[800];
32-
case 'incoming':
33-
return theme.palette.azure[600];
34-
case 'functional':
35-
return theme.palette.blue[700];
36-
case 'experiential':
37-
return theme.palette.green[700];
38-
default:
39-
return theme.palette.grey[700];
40-
}
41-
};
42-
43-
const getIconByStatus = (status: CampaignStatus) => {
44-
switch (status) {
45-
case 'running':
46-
return <StatusRunningIcon />;
47-
case 'completed':
48-
return <StatusCompletedIcon />;
49-
case 'incoming':
50-
return <StatusIncomingIcon />;
51-
case 'functional':
52-
return <CampaignFunctionalIcon />;
53-
case 'experiential':
54-
return <CampaignExperientialIcon />;
55-
default:
56-
return undefined;
57-
}
58-
};
59-
60-
const getDefaultTextByStatus = (status: CampaignStatus, t: TFunction) => {
61-
switch (status) {
62-
case 'running':
63-
return t('__CAMPAIGN_STATUS_RUNNING__');
64-
case 'completed':
65-
return t('__CAMPAIGN_STATUS_COMPLETED__');
66-
case 'incoming':
67-
return t('__CAMPAIGN_STATUS_INCOMING__');
68-
case 'functional':
69-
return t('__CAMPAIGN_TYPE_FUNCTIONAL__');
70-
case 'experiential':
71-
return t('__CAMPAIGN_TYPE_EXPERIENTIAL__');
72-
default:
73-
return undefined;
74-
}
75-
};
76-
7713
export const StatusTag = ({
7814
status,
7915
children,
8016
counter,
8117
isRound,
8218
...props
8319
}: StatusTagArgs) => {
84-
const { t } = useTranslation();
20+
const statusInfo = getStatusInfo(status);
21+
8522
return (
8623
<Tag
8724
size="large"
8825
className={`campaign-status-pill ${status}`}
89-
color={getColorByStatus(status)}
26+
color={statusInfo.color}
9027
hue="rgba(0,0,0,0)"
9128
isRound={isRound}
9229
{...props}
9330
>
94-
{typeof getIconByStatus(status) !== 'undefined' && (
95-
<Tag.Avatar>{getIconByStatus(status)}</Tag.Avatar>
31+
{typeof statusInfo.icon !== 'undefined' && (
32+
<Tag.Avatar>{statusInfo.icon}</Tag.Avatar>
9633
)}
9734
{
9835
// children if passed, otherwise default text for normal pills and icon for round pills
99-
children ||
100-
(isRound
101-
? getIconByStatus(status)
102-
: getDefaultTextByStatus(status, t))
36+
children || (isRound ? statusInfo.icon : statusInfo.text)
10337
}
10438
{typeof counter !== 'undefined' && (
10539
<Tag.SecondaryText isBold color={theme.palette.grey[700]}>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { CampaignStatus } from 'src/types';
2+
import {
3+
StatusCompletedIcon,
4+
StatusIncomingIcon,
5+
StatusRunningIcon,
6+
CampaignExperientialIcon,
7+
CampaignFunctionalIcon,
8+
} from '@appquality/unguess-design-system';
9+
import { theme } from 'src/app/theme';
10+
import React from 'react';
11+
import { useTranslation } from 'react-i18next';
12+
13+
type StatusInfo = {
14+
icon?: React.ReactNode;
15+
text?: string;
16+
color?: string;
17+
};
18+
19+
export const getStatusInfo = (status: CampaignStatus): StatusInfo => {
20+
const { t } = useTranslation();
21+
22+
switch (status) {
23+
case 'running':
24+
return {
25+
icon: <StatusRunningIcon />,
26+
text: t('__CAMPAIGN_STATUS_RUNNING__'),
27+
color: theme.palette.yellow[700],
28+
};
29+
case 'completed':
30+
return {
31+
icon: <StatusCompletedIcon />,
32+
text: t('__CAMPAIGN_STATUS_COMPLETED__'),
33+
color: theme.palette.green[800],
34+
};
35+
case 'incoming':
36+
return {
37+
icon: <StatusIncomingIcon />,
38+
text: t('__CAMPAIGN_STATUS_INCOMING__'),
39+
color: theme.palette.azure[600],
40+
};
41+
case 'functional':
42+
return {
43+
icon: <CampaignFunctionalIcon />,
44+
text: t('__CAMPAIGN_TYPE_FUNCTIONAL__'),
45+
color: theme.palette.blue[700],
46+
};
47+
case 'experiential':
48+
return {
49+
icon: <CampaignExperientialIcon />,
50+
text: t('__CAMPAIGN_TYPE_EXPERIENTIAL__'),
51+
color: theme.palette.green[700],
52+
};
53+
default:
54+
return {};
55+
}
56+
};

src/pages/Bugs/PageHeader/Tools/index.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Button, Skeleton } from '@appquality/unguess-design-system';
22
import styled from 'styled-components';
3-
import { SeverityTag } from 'src/common/components/tag/SeverityTag';
4-
import { CampaignStatus, StatusTag } from 'src/common/components/tag/StatusTag';
3+
import { SeverityMeta } from 'src/common/components/meta/SeverityMeta';
54
import { Pipe } from 'src/common/components/Pipe';
65
import { ReactComponent as ArrowDowloadIcon } from 'src/assets/icons/download-stroke.svg';
76
import { ReactComponent as GearIcon } from 'src/assets/icons/gear.svg';
@@ -10,6 +9,8 @@ import { getLocalizeIntegrationCenterRoute } from 'src/hooks/useLocalizeIntegrat
109
import WPAPI from 'src/common/wpapi';
1110
import useWindowSize from 'src/hooks/useWindowSize';
1211
import { theme as globalTheme } from 'src/app/theme';
12+
import { StatusMeta } from 'src/common/components/meta/StatusMeta';
13+
import { CampaignStatus } from 'src/types';
1314
import { UniqueBugsCounter } from './UniqueBugsCounter';
1415
import { useCampaign } from './useCampaign';
1516

@@ -65,15 +66,15 @@ export const Tools = ({
6566
{!hide && <UniqueBugsCounter campaignId={campaignId} />}
6667
<SeveritiesWrapper>
6768
{Object.keys(severities).map((severity) => (
68-
<SeverityTag
69+
<SeverityMeta
6970
key={severity}
7071
counter={severities[severity as Severities]}
7172
severity={severity as Severities}
7273
size="large"
7374
/>
7475
))}
7576
{!hide && <Pipe />}
76-
<StatusTag status={status.name as CampaignStatus} />
77+
<StatusMeta status={status.name as CampaignStatus} />
7778
</SeveritiesWrapper>
7879
</ToolsWrapper>
7980
<ButtonsWrapper>

src/pages/Campaign/pageHeader/Tags/CampaignDurationTag.tsx renamed to src/pages/Campaign/pageHeader/Meta/CampaignDurationMeta.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { format } from 'date-fns';
22
import { ReactComponent as ClockIcon } from 'src/assets/icons/pill-icon-clock.svg';
33
import { theme } from 'src/app/theme';
44
import { useTranslation, Trans } from 'react-i18next';
5-
import { Span, Tag } from '@appquality/unguess-design-system';
5+
import { Span } from '@appquality/unguess-design-system';
6+
import { Meta } from 'src/common/components/Meta';
67

7-
export const CampaignDurationTag = ({
8+
export const CampaignDurationMeta = ({
89
start,
910
end,
1011
}: {
@@ -22,22 +23,19 @@ export const CampaignDurationTag = ({
2223
const formattedEndDate = format(endDate, 'dd/MM/yyyy');
2324

2425
return (
25-
<Tag
26+
<Meta
2627
size="large"
2728
className="campaign-duration-pill"
2829
color={theme.palette.azure[600]}
29-
hue="rgba(0,0,0,0)"
30-
>
31-
<Tag.Avatar>
32-
<ClockIcon />
33-
</Tag.Avatar>
34-
{t('__CAMPAIGN_PAGE_INFO_HEADER_TEST_TIMING')}
35-
<Tag.SecondaryText isBold color={theme.palette.grey[700]}>
30+
icon={<ClockIcon />}
31+
secondaryText={
3632
<Trans i18nKey="__CAMPAIGN_PAGE_INFO_HEADER_FROM_DATE_TO_DATE">
3733
<Span>{{ start_date: formattedStartDate }}</Span> to{' '}
3834
<Span>{{ end_date: formattedEndDate }}</Span>
3935
</Trans>
40-
</Tag.SecondaryText>
41-
</Tag>
36+
}
37+
>
38+
{t('__CAMPAIGN_PAGE_INFO_HEADER_TEST_TIMING')}
39+
</Meta>
4240
);
4341
};
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { useTranslation } from 'react-i18next';
2-
import { Tag } from '@appquality/unguess-design-system';
32
import { ReactComponent as DesktopIcon } from 'src/assets/icons/pill-icon-desktop.svg';
3+
import { Meta } from 'src/common/components/Meta';
44

5-
export const DesktopTag = () => {
5+
export const DesktopMeta = () => {
66
const { t } = useTranslation();
77
return (
8-
<Tag id="pill-desktop-header" size="large" hue="rgba(0,0,0,0)">
9-
<Tag.Avatar>
10-
<DesktopIcon />
11-
</Tag.Avatar>
8+
<Meta id="pill-desktop-header" size="large" icon={<DesktopIcon />}>
129
{t('__CAMPAIGN_PAGE_INFO_HEADER_DESKTOP')}
13-
</Tag>
10+
</Meta>
1411
);
1512
};

0 commit comments

Comments
 (0)