Skip to content

Commit

Permalink
Merge pull request #8373 from vigneshm/artifact-list-status-bubble
Browse files Browse the repository at this point in the history
feat(core/managed): Add status bubble in artifact list
  • Loading branch information
vigneshm committed Jun 30, 2020
2 parents fcbcc31 + 9ec2003 commit 7ae702e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 24 deletions.
3 changes: 2 additions & 1 deletion app/scripts/modules/core/src/managed/ArtifactRow.module.css
Expand Up @@ -3,8 +3,9 @@
display: block;
cursor: pointer;
}

.ArtifactRow:not(.selected):hover {
background-image: linear-gradient(to bottom, rgba(160, 180, 220, 0.15), rgba(160, 180, 220, 0.15));
background-color: #f1f4fa;
}

.selected {
Expand Down
65 changes: 42 additions & 23 deletions app/scripts/modules/core/src/managed/ArtifactsList.tsx
@@ -1,11 +1,15 @@
import React from 'react';
import classNames from 'classnames';
import React, { useState } from 'react';

import { Pill } from './Pill';
import { StatusBubble } from './StatusBubble';

import { IManagedArtifactSummary, IManagedArtifactVersion } from '../domain/IManagedEntity';
import { ISelectedArtifactVersion } from './Environments';
import {
IManagedArtifactSummary,
IManagedArtifactVersion,
IStatefulConstraint,
StatefulConstraintStatus,
} from '../domain/IManagedEntity';
import { Pill } from './Pill';
import { IStatusBubbleStackProps, StatusBubbleStack } from './StatusBubbleStack';

import styles from './ArtifactRow.module.css';

Expand Down Expand Up @@ -44,19 +48,16 @@ interface IArtifactRowProps {
name?: string;
}

export const ArtifactRow = ({
isSelected,
clickHandler,
version: { version, displayName, environments, build, git },
reference,
name,
}: IArtifactRowProps) => {
const pinnedEnvironments = environments.filter(({ pinned }) => pinned).length;
export const ArtifactRow = ({ isSelected, clickHandler, version: versionInfo, reference, name }: IArtifactRowProps) => {
const { version, displayName, environments, build, git } = versionInfo;
const [isHovered, setIsHovered] = useState(false);

return (
<div
className={classNames(styles.ArtifactRow, { [styles.selected]: isSelected })}
onClick={() => clickHandler({ reference, version })}
onMouseOver={() => setIsHovered(true)}
onMouseOut={() => setIsHovered(false)}
>
<div className={styles.content}>
{build?.id && (
Expand All @@ -68,16 +69,11 @@ export const ArtifactRow = ({
<div className={styles.sha}>{git?.commit || displayName}</div>
{name && <div className={styles.name}>{name}</div>}
</div>
{pinnedEnvironments > 0 && (
<div className="sp-margin-s-right">
<StatusBubble
iconName="pin"
appearance="warning"
size="small"
quantity={pinnedEnvironments > 1 ? pinnedEnvironments : null}
/>
</div>
)}
<StatusBubbleStack
borderColor={isSelected ? 'var(--color-white)' : isHovered ? '#f1f4fa' : 'var(--color-alabaster)'}
maxBubbles={3}
statuses={getArtifactStatuses(versionInfo)}
/>
</div>
<div className={styles.stages}>
{environments
Expand All @@ -87,3 +83,26 @@ export const ArtifactRow = ({
</div>
);
};

type ArtifactStatusList = IStatusBubbleStackProps['statuses'];
function getArtifactStatuses({ environments }: IManagedArtifactVersion): ArtifactStatusList {
const statuses: ArtifactStatusList = [];
// NOTE: The order in which entries are added to `statuses` is important. The highest priority
// item must be inserted first.

const isConstraintPendingManualJudgement = (constraint: IStatefulConstraint) =>
constraint.type == 'manual-judgement' && constraint.status == StatefulConstraintStatus.PENDING;
const requiresManualApproval = environments.some(environment =>
environment.statefulConstraints?.some(isConstraintPendingManualJudgement),
);
if (requiresManualApproval) {
statuses.push({ appearance: 'progress', iconName: 'manualJudgement' });
}

const isPinned = environments.some(({ pinned }) => pinned);
if (isPinned) {
statuses.push({ appearance: 'warning', iconName: 'pin' });
}

return statuses;
}
15 changes: 15 additions & 0 deletions app/scripts/modules/core/src/managed/StatusBubbleStack.less
@@ -0,0 +1,15 @@
.StatusBubbleContainer {
background-color: var(--color-alabaster);
border-radius: 50%;
padding: 2px;
margin-left: -8px;
}

.HiddenStatusCount {
border-radius: 50%;
background-color: var(--color-status-neutral);
color: var(--color-white);
margin-left: -4px;
margin-right: 6px;
padding: 4px 8px;
}
37 changes: 37 additions & 0 deletions app/scripts/modules/core/src/managed/StatusBubbleStack.tsx
@@ -0,0 +1,37 @@
import React from 'react';
import { take } from 'lodash';

import { IStatusBubbleProps, StatusBubble } from './StatusBubble';

import './StatusBubbleStack.less';

export interface IStatusBubbleStackProps {
borderColor: string;
maxBubbles: number;
statuses: Array<Pick<IStatusBubbleProps, 'iconName' | 'appearance'>>;
}

export const StatusBubbleStack = ({ borderColor, maxBubbles, statuses }: IStatusBubbleStackProps) => {
// We only have limited space in the UI and we cannot show all the statuses in the stack, so only take the first few
// important statuses.
const statusBubblesToRender = take(statuses, statuses.length <= maxBubbles ? maxBubbles : maxBubbles - 1);
const hiddenStatusBubbleCount = statuses.length - statusBubblesToRender.length;

return (
<div className="flex-container-h middle sp-margin-s-right">
{statusBubblesToRender.map((status, index) => (
<div
className="StatusBubbleContainer"
key={status.iconName}
style={{
backgroundColor: borderColor,
zIndex: statusBubblesToRender.length - index,
}}
>
<StatusBubble iconName={status.iconName} appearance={status.appearance} size="small" />
</div>
))}
{!!hiddenStatusBubbleCount && <div className="HiddenStatusCount">+{hiddenStatusBubbleCount}</div>}
</div>
);
};

0 comments on commit 7ae702e

Please sign in to comment.