@@ -30,9 +30,12 @@ import { CopyButton } from '../shared/CopyButton';
3030import {
3131 BackgroundColorProps ,
3232 getBackgroundColor ,
33+ getStatusBackgroundColor ,
3334} from '../shared/getBackgroundColor' ;
3435import { PinButton } from '../shared/PinButton' ;
36+ import { SingleLineBox } from '../shared/SingleLineBox' ;
3537import { ResourceItemProps } from '../types' ;
38+ import { WarningRightEdgeBadgeSvg } from './WarningRightEdgeBadgeSvg' ;
3639
3740// Since we do a lot of manual resizing and some absolute positioning, we have
3841// to put some layout constants in place here.
@@ -51,20 +54,26 @@ const ResTypeIconBox = styled(Box)`
5154` ;
5255
5356export function ResourceCard ( {
54- name,
55- primaryIconName,
56- SecondaryIcon,
5757 onLabelClick,
58- cardViewProps,
59- ActionButton,
60- labels,
6158 pinningSupport,
6259 pinned,
6360 pinResource,
6461 selectResource,
65- requiresRequest,
6662 selected,
67- } : Omit < ResourceItemProps , 'listViewProps' | 'expandAllLabels' > ) {
63+ onShowStatusInfo,
64+ showingStatusInfo,
65+ viewItem,
66+ } : Omit < ResourceItemProps , 'expandAllLabels' > ) {
67+ const {
68+ name,
69+ primaryIconName,
70+ SecondaryIcon,
71+ cardViewProps,
72+ ActionButton,
73+ labels,
74+ requiresRequest,
75+ status,
76+ } = viewItem ;
6877 const { primaryDesc, secondaryDesc } = cardViewProps ;
6978
7079 const [ showMoreLabelsButton , setShowMoreLabelsButton ] = useState ( false ) ;
@@ -157,12 +166,18 @@ export function ResourceCard({
157166 }
158167 } ;
159168
169+ const hasUnhealthyStatus = status && status !== 'healthy' ;
170+
160171 return (
161172 < CardContainer
162173 onMouseEnter = { ( ) => setHovered ( true ) }
163174 onMouseLeave = { ( ) => setHovered ( false ) }
175+ showingStatusInfo = { showingStatusInfo }
164176 >
165- < CardOuterContainer showAllLabels = { showAllLabels } >
177+ < CardOuterContainer
178+ showAllLabels = { showAllLabels }
179+ hasUnhealthyStatus = { hasUnhealthyStatus }
180+ >
166181 < CardInnerContainer
167182 ref = { innerContainer }
168183 p = { 3 }
@@ -174,6 +189,12 @@ export function ResourceCard({
174189 pinned = { pinned }
175190 requiresRequest = { requiresRequest }
176191 selected = { selected }
192+ showingStatusInfo = { showingStatusInfo }
193+ hasUnhealthyStatus = { hasUnhealthyStatus }
194+ // we set extra padding to push contents to the right to make
195+ // space for the WarningRightEdgeBadgeIcon.
196+ { ...( hasUnhealthyStatus && ! showAllLabels && { pr : '35px' } ) }
197+ { ...( hasUnhealthyStatus && showAllLabels && { pr : '7px' } ) }
177198 >
178199 < HoverTooltip tipContent = { selected ? 'Deselect' : 'Select' } >
179200 < CheckboxInput
@@ -242,7 +263,10 @@ export function ResourceCard({
242263 ) }
243264 </ Flex >
244265 < LabelsContainer showAll = { showAllLabels } >
245- < LabelsInnerContainer ref = { labelsInnerContainer } >
266+ < LabelsInnerContainer
267+ ref = { labelsInnerContainer }
268+ hasUnhealthyStatus = { hasUnhealthyStatus }
269+ >
246270 < MoreLabelsButton
247271 style = { {
248272 visibility :
@@ -271,36 +295,88 @@ export function ResourceCard({
271295 </ LabelsInnerContainer >
272296 </ LabelsContainer >
273297 </ Flex >
298+ { hasUnhealthyStatus && ! showAllLabels && (
299+ < HoverTooltip tipContent = { 'Show Connection Issue' } placement = "left" >
300+ < WarningRightEdgeBadgeIcon onClick = { onShowStatusInfo } />
301+ </ HoverTooltip >
302+ ) }
274303 </ CardInnerContainer >
275304 </ CardOuterContainer >
305+ { /* This is to let the WarningRightEdgeBadgeIcon stay in place while the
306+ InnerContainer pops out and expands vertically from rendering all
307+ labels. */ }
308+ { hasUnhealthyStatus && showAllLabels && < WarningRightEdgeBadgeIcon /> }
276309 </ CardContainer >
277310 ) ;
278311}
279312
313+ const WarningRightEdgeBadgeIcon = ( { onClick } : { onClick ?( ) : void } ) => {
314+ return (
315+ < Box
316+ onClick = { onClick }
317+ css = { `
318+ position: absolute;
319+ top: 0;
320+ right: 0;
321+ cursor: pointer;
322+ height: 100%;
323+ ` }
324+ >
325+ < WarningRightEdgeBadgeSvg />
326+ </ Box >
327+ ) ;
328+ } ;
329+
280330/**
281331 * The outer container's purpose is to reserve horizontal space on the resource
282332 * grid. It holds the inner container that normally holds a regular layout of
283333 * the card, and is fully contained inside the outer container. Once the user
284334 * clicks the "more" button, the inner container "pops out" by changing its
285335 * position to absolute.
286336 *
337+ * The card height is fixed to allow the WarningRightEdgeBadgeIcon to stay in
338+ * place when user clicks on "showAllLabels". Without the fixed height, the
339+ * container's height shrinks when the inner container pops out, resulting in
340+ * the svg to jump around (from size difference) and or disappearing.
341+ *
287342 * TODO(bl-nero): Known issue: this doesn't really work well with one-column
288- * layout; we may need to globally set the card height to fixed size on the
289- * outer container.
343+ * layout;
290344 */
291- const CardContainer = styled ( Box ) `
345+ const CardContainer = styled ( Box ) < {
346+ showingStatusInfo : boolean ;
347+ } > `
348+ height: 110px;
349+
292350 position: relative;
351+ .resource-health-status-svg {
352+ width: 100%;
353+ height: 100%;
354+
355+ fill: ${ p =>
356+ p . showingStatusInfo
357+ ? p . theme . colors . interactive . solid . alert . active
358+ : p . theme . colors . interactive . solid . alert . default } ;
359+ }
360+ &:hover {
361+ .resource-health-status-svg {
362+ fill: ${ p => p . theme . colors . interactive . solid . alert . hover } ;
363+ }
364+ }
293365` ;
294366
295- const CardOuterContainer = styled ( Box ) < { showAllLabels ?: boolean } > `
367+ const CardOuterContainer = styled ( Box ) < {
368+ showAllLabels ?: boolean ;
369+ hasUnhealthyStatus : boolean ;
370+ } > `
296371 border-radius: ${ props => props . theme . radii [ 3 ] } px;
297372
298373 ${ props =>
299374 props . showAllLabels &&
300375 css `
301376 position : absolute;
302377 left : 0 ;
303- right : 0 ;
378+ // The padding is required to show the WarningRightEdgeBadgeIcon
379+ right : ${ props . hasUnhealthyStatus ? '28px' : 0 } ;
304380 z-index : 1 ;
305381 ` }
306382 transition: all 150ms;
@@ -340,16 +416,40 @@ const CardInnerContainer = styled(Flex)<BackgroundColorProps>`
340416 border-radius: ${ props => props . theme . radii [ 3 ] } px;
341417 background-color: ${ props => getBackgroundColor ( props ) } ;
342418
419+ ${ p =>
420+ p . hasUnhealthyStatus &&
421+ css `
422+ border : 2px solid ${ p . theme . colors . interactive . solid . alert . default } ;
423+ background-color : ${ getStatusBackgroundColor ( {
424+ showingStatusInfo : p . showingStatusInfo ,
425+ theme : p . theme ,
426+ action : '' ,
427+ viewType : 'card' ,
428+ } ) } ;
429+ ` }
430+
431+ ${ p =>
432+ p . showingStatusInfo &&
433+ css `
434+ border : 2px solid ${ p . theme . colors . interactive . solid . alert . active } ;
435+ ` }
436+
343437 &:hover {
344438 // Make the border invisible instead of removing it, this is to prevent things from shifting due to the size change.
345439 border: ${ props => props . theme . borders [ 2 ] } rgba(0, 0, 0, 0);
346- }
347- ` ;
348440
349- const SingleLineBox = styled ( Box ) `
350- overflow: hidden;
351- white-space: nowrap;
352- text-overflow: ellipsis;
441+ ${ p =>
442+ p . hasUnhealthyStatus &&
443+ css `
444+ border-color : ${ p . theme . colors . interactive . solid . alert . hover } ;
445+ background-color : ${ getStatusBackgroundColor ( {
446+ showingStatusInfo : p . showingStatusInfo ,
447+ theme : p . theme ,
448+ action : 'hover' ,
449+ viewType : 'card' ,
450+ } ) } ;
451+ ` }
452+ }
353453` ;
354454
355455/**
@@ -376,12 +476,16 @@ const StyledLabel = styled(Label)`
376476 * The inner labels container always adapts to the size of labels. Its height
377477 * is measured by the resize observer.
378478 */
379- const LabelsInnerContainer = styled ( Flex ) `
479+ const LabelsInnerContainer = styled ( Flex ) < { hasUnhealthyStatus : boolean } > `
380480 position: relative;
381481 flex-wrap: wrap;
382482 align-items: start;
383483 gap: ${ props => props . theme . space [ 1 ] } px;
384- padding-right: 60px;
484+ // Padding is required to prevent the more label button to not collide
485+ // with the rendered labels. Just a tiny bit more padding needed to
486+ // accomodate contents getting pushed more to the right when a
487+ // WarningRightEdgeBadgeIcon renders.
488+ padding-right: ${ p => ( p . hasUnhealthyStatus ? '75px' : '74px' ) } ;
385489` ;
386490
387491/**
@@ -397,10 +501,14 @@ const MoreLabelsButton = styled(ButtonLink)`
397501 margin: ${ labelVerticalMargin } px 0;
398502 min-height: 0;
399503
400- background-color: ${ props => getBackgroundColor ( props ) } ;
504+ background-color: transparent ;
401505 color: ${ props => props . theme . colors . text . slightlyMuted } ;
402506 font-style: italic;
403507
404508 transition: visibility 0s;
405509 transition: background 150ms;
510+
511+ &:hover {
512+ background-color: transparent;
513+ }
406514` ;
0 commit comments