Skip to content

Commit 0cd012f

Browse files
committed
Merge branch 'develop' into feat/delete-confirmation
2 parents a600963 + 6613f81 commit 0cd012f

File tree

3 files changed

+167
-102
lines changed

3 files changed

+167
-102
lines changed

src/Shared/Components/CICDHistory/StatusFilterButtonComponent.tsx

Lines changed: 93 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -14,131 +14,124 @@
1414
* limitations under the License.
1515
*/
1616

17-
/* eslint-disable eqeqeq */
18-
import { ALL_RESOURCE_KIND_FILTER } from '@Shared/constants'
17+
import { ChangeEvent, useEffect, useMemo, useState } from 'react'
18+
1919
import { ReactComponent as ICCaretDown } from '@Icons/ic-caret-down.svg'
2020
import { PopupMenu, StyledRadioGroup as RadioGroup } from '../../../Common'
21-
import { NodeFilters, NodeStatus, StatusFilterButtonType } from './types'
21+
import { StatusFilterButtonType } from './types'
22+
2223
import './StatusFilterButtonComponent.scss'
24+
import { getNodesCount, getStatusFilters } from './utils'
2325

24-
export const StatusFilterButtonComponent = ({ nodes, selectedTab, handleFilterClick }: StatusFilterButtonType) => {
25-
const maxInlineFilterCount = 4
26-
let allNodeCount: number = 0
27-
let healthyNodeCount: number = 0
28-
let progressingNodeCount: number = 0
29-
let failedNodeCount: number = 0
30-
let missingNodeCount: number = 0
31-
let driftedNodeCount: number = 0
26+
export const StatusFilterButtonComponent = ({
27+
nodes,
28+
selectedTab,
29+
handleFilterClick,
30+
maxInlineFiltersCount = 0,
31+
}: StatusFilterButtonType) => {
32+
// STATES
33+
const [overflowFilterIndex, setOverflowFilterIndex] = useState(0)
3234

33-
nodes?.forEach((_node) => {
34-
const _nodeHealth = _node.health?.status
35+
// STATUS FILTERS
36+
const { allResourceKindFilter, statusFilters } = useMemo(() => getStatusFilters(getNodesCount(nodes)), [nodes])
3537

36-
if (_node.hasDrift) {
37-
driftedNodeCount += 1
38-
}
38+
useEffect(() => {
39+
const filterIndex = statusFilters.findIndex(({ status }) => status === selectedTab)
40+
setOverflowFilterIndex(Math.max(filterIndex, 0))
41+
}, [statusFilters])
3942

40-
if (_nodeHealth?.toLowerCase() === NodeStatus.Healthy) {
41-
healthyNodeCount += 1
42-
} else if (_nodeHealth?.toLowerCase() === NodeStatus.Degraded) {
43-
failedNodeCount += 1
44-
} else if (_nodeHealth?.toLowerCase() === NodeStatus.Progressing) {
45-
progressingNodeCount += 1
46-
} else if (_nodeHealth?.toLowerCase() === NodeStatus.Missing) {
47-
missingNodeCount += 1
43+
const showOverflowFilters = maxInlineFiltersCount > 0 && statusFilters.length > maxInlineFiltersCount
44+
45+
const inlineFilters = useMemo(() => {
46+
if (showOverflowFilters) {
47+
const min = Math.max(0, Math.min(overflowFilterIndex - 1, statusFilters.length - maxInlineFiltersCount))
48+
const max = Math.min(min + maxInlineFiltersCount, statusFilters.length)
49+
50+
return statusFilters.slice(min, max)
4851
}
49-
allNodeCount += 1
50-
})
5152

52-
const handleInlineFilterClick = (e) => {
53-
handleFilterClick(e.target.value)
54-
}
53+
return statusFilters
54+
}, [statusFilters.length, overflowFilterIndex, maxInlineFiltersCount])
5555

56-
const filterOptions = [
57-
{ status: ALL_RESOURCE_KIND_FILTER, count: allNodeCount, isSelected: selectedTab == ALL_RESOURCE_KIND_FILTER },
58-
{ status: NodeStatus.Missing, count: missingNodeCount, isSelected: NodeStatus.Missing == selectedTab },
59-
{ status: NodeStatus.Degraded, count: failedNodeCount, isSelected: NodeStatus.Degraded == selectedTab },
60-
{
61-
status: NodeStatus.Progressing,
62-
count: progressingNodeCount,
63-
isSelected: NodeStatus.Progressing == selectedTab,
64-
},
65-
{ status: NodeStatus.Healthy, count: healthyNodeCount, isSelected: NodeStatus.Healthy == selectedTab },
66-
window._env_.FEATURE_CONFIG_DRIFT_ENABLE && {
67-
status: NodeFilters.drifted,
68-
count: driftedNodeCount,
69-
isSelected: selectedTab === NodeFilters.drifted,
70-
},
71-
]
72-
const validFilterOptions = filterOptions.filter(({ count }) => count > 0)
73-
const displayedInlineFilters = validFilterOptions.slice(
74-
0,
75-
Math.min(maxInlineFilterCount, validFilterOptions.length),
76-
)
77-
const overflowFilters =
78-
validFilterOptions.length > maxInlineFilterCount ? validFilterOptions.slice(maxInlineFilterCount) : null
56+
const handleInlineFilterClick = (e: ChangeEvent<HTMLInputElement>) => {
57+
const { value } = e.target
58+
if (value === allResourceKindFilter.status) {
59+
setOverflowFilterIndex(0)
60+
}
61+
if (selectedTab !== value) {
62+
handleFilterClick(value)
63+
}
64+
}
7965

80-
const renderOverflowFilters = () =>
81-
overflowFilters ? (
82-
<PopupMenu autoClose>
83-
<PopupMenu.Button
84-
isKebab
85-
rootClassName="flex p-4 dc__border dc__no-left-radius dc__right-radius-4 bg__primary dc__hover-n50"
86-
>
87-
<ICCaretDown className="icon-dim-14 scn-6" />
88-
</PopupMenu.Button>
89-
<PopupMenu.Body rootClassName="w-150 py-4 mt-4" style={{ left: '136px' }}>
90-
{overflowFilters.map((filter) => (
91-
<button
92-
key={filter.status}
93-
type="button"
94-
className={`dc__transparent w-100 py-6 px-8 flex left dc__gap-8 fs-13 lh-20 fw-4 cn-9 ${filter.isSelected ? 'bcb-1' : 'bg__primary dc__hover-n50'}`}
95-
onClick={() => handleFilterClick(filter.status)}
96-
>
97-
<span
98-
className={`dc__app-summary__icon icon-dim-16 ${filter.status} ${filter.status}--node`}
99-
style={{ zIndex: 'unset' }}
100-
/>
101-
<span className="dc__first-letter-capitalize flex-grow-1 text-left">{filter.status}</span>
102-
<span>{filter.count}</span>
103-
</button>
104-
))}
105-
</PopupMenu.Body>
106-
</PopupMenu>
107-
) : null
66+
const handleOverflowFilterClick = (status: string, index: number) => () => {
67+
if (selectedTab !== status) {
68+
setOverflowFilterIndex(index)
69+
handleFilterClick(status)
70+
}
71+
}
10872

10973
return (
110-
<>
74+
<div className="flexbox">
11175
<RadioGroup
112-
className={`gui-yaml-switch status-filter-button ${overflowFilters ? 'with-menu-button' : ''}`}
76+
className={`gui-yaml-switch status-filter-button ${showOverflowFilters ? 'with-menu-button' : ''}`}
11377
name="status-filter-button"
11478
initialTab={selectedTab}
11579
disabled={false}
11680
onChange={handleInlineFilterClick}
11781
>
118-
{displayedInlineFilters.map((filter, index) => (
82+
<RadioGroup.Radio
83+
key={allResourceKindFilter.status}
84+
value={allResourceKindFilter.status}
85+
tippyPlacement="top"
86+
tippyContent={allResourceKindFilter.status}
87+
tippyClass="w-100 dc__first-letter-capitalize"
88+
>
89+
<span className="dc__first-letter-capitalize">{`${allResourceKindFilter.status} (${allResourceKindFilter.count})`}</span>
90+
</RadioGroup.Radio>
91+
{inlineFilters.map(({ status, count }) => (
11992
<RadioGroup.Radio
120-
key={filter.status}
121-
value={filter.status}
122-
showTippy={index !== 0}
93+
key={status}
94+
value={status}
95+
showTippy
12396
tippyPlacement="top"
124-
tippyContent={filter.status}
97+
tippyContent={status}
12598
tippyClass="w-100 dc__first-letter-capitalize"
12699
>
127-
{index !== 0 ? (
128-
<>
129-
<span
130-
className={`dc__app-summary__icon icon-dim-16 ${filter.status} ${filter.status}--node`}
131-
style={{ zIndex: 'unset' }}
132-
/>
133-
<span>{filter.count}</span>
134-
</>
135-
) : (
136-
<span className="dc__first-letter-capitalize">{`${filter.status} (${filter.count})`}</span>
137-
)}
100+
<span
101+
className={`dc__app-summary__icon icon-dim-16 ${status} ${status}--node`}
102+
style={{ zIndex: 'unset' }}
103+
/>
104+
<span>{count}</span>
138105
</RadioGroup.Radio>
139106
))}
140107
</RadioGroup>
141-
{renderOverflowFilters()}
142-
</>
108+
{showOverflowFilters && (
109+
<PopupMenu autoClose>
110+
<PopupMenu.Button
111+
isKebab
112+
rootClassName="flex p-4 dc__border dc__no-left-radius dc__right-radius-4 bg__primary dc__hover-n50"
113+
>
114+
<ICCaretDown className="icon-dim-14 scn-6" />
115+
</PopupMenu.Button>
116+
<PopupMenu.Body rootClassName="w-150 py-4 mt-4" style={{ left: '136px' }}>
117+
{statusFilters.map(({ status, count }, index) => (
118+
<button
119+
key={status}
120+
type="button"
121+
className={`dc__transparent w-100 py-6 px-8 flex left dc__gap-8 fs-13 lh-20 fw-4 cn-9 dc__hover-n50 ${selectedTab === status ? 'bcb-1' : ''}`}
122+
onClick={handleOverflowFilterClick(status, index)}
123+
>
124+
<span
125+
className={`dc__app-summary__icon icon-dim-16 ${status} ${status}--node`}
126+
style={{ zIndex: 'unset' }}
127+
/>
128+
<span className="dc__first-letter-capitalize flex-grow-1 text-left">{status}</span>
129+
<span>{count}</span>
130+
</button>
131+
))}
132+
</PopupMenu.Body>
133+
</PopupMenu>
134+
)}
135+
</div>
143136
)
144137
}

src/Shared/Components/CICDHistory/types.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ export interface StatusFilterButtonType {
568568
nodes: Array<Node>
569569
selectedTab: string
570570
handleFilterClick?: (selectedFilter: string) => void
571+
maxInlineFiltersCount?: number
571572
}
572573

573574
export enum NodeStatus {

src/Shared/Components/CICDHistory/utils.tsx

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { TIMELINE_STATUS } from '@Shared/constants'
16+
import { ALL_RESOURCE_KIND_FILTER, TIMELINE_STATUS } from '@Shared/constants'
1717
import { ReactComponent as ICAborted } from '@Icons/ic-aborted.svg'
1818
import { ReactComponent as ICErrorCross } from '@Icons/ic-error-cross.svg'
1919
import { ReactComponent as Close } from '@Icons/ic-close.svg'
@@ -26,13 +26,15 @@ import { ReactComponent as TimeOut } from '@Icons/ic-timeout-red.svg'
2626
import { ReactComponent as ICCheck } from '@Icons/ic-check.svg'
2727
import { ReactComponent as ICInProgress } from '@Icons/ic-in-progress.svg'
2828
import { TERMINAL_STATUS_MAP } from './constants'
29-
import { ResourceKindType } from '../../types'
29+
import { Node, ResourceKindType } from '../../types'
3030
import {
3131
TriggerHistoryFilterCriteriaProps,
3232
DeploymentHistoryResultObject,
3333
DeploymentHistory,
3434
TriggerHistoryFilterCriteriaType,
3535
StageStatusType,
36+
NodeStatus,
37+
NodeFilters,
3638
} from './types'
3739

3840
export const getTriggerHistoryFilterCriteria = ({
@@ -203,3 +205,72 @@ export const getLogSearchIndex = ({
203205
stageIndex,
204206
lineNumberInsideStage,
205207
}: Record<'stageIndex' | 'lineNumberInsideStage', number>) => `${stageIndex}-${lineNumberInsideStage}`
208+
209+
export const getNodesCount = (nodes: Node[]) =>
210+
(nodes || []).reduce(
211+
(acc, node) => {
212+
const nodeHealthStatus = node.health?.status?.toLowerCase() ?? ''
213+
214+
if (node.hasDrift) {
215+
acc.driftedNodeCount += 1
216+
}
217+
218+
switch (nodeHealthStatus) {
219+
case NodeStatus.Healthy:
220+
acc.healthyNodeCount += 1
221+
break
222+
case NodeStatus.Degraded:
223+
acc.failedNodeCount += 1
224+
break
225+
case NodeStatus.Progressing:
226+
acc.progressingNodeCount += 1
227+
break
228+
case NodeStatus.Missing:
229+
acc.missingNodeCount += 1
230+
break
231+
default:
232+
}
233+
234+
acc.allNodeCount += 1
235+
236+
return acc
237+
},
238+
{
239+
allNodeCount: 0,
240+
healthyNodeCount: 0,
241+
progressingNodeCount: 0,
242+
failedNodeCount: 0,
243+
missingNodeCount: 0,
244+
driftedNodeCount: 0,
245+
},
246+
)
247+
248+
export const getStatusFilters = ({
249+
allNodeCount,
250+
missingNodeCount,
251+
failedNodeCount,
252+
progressingNodeCount,
253+
healthyNodeCount,
254+
driftedNodeCount,
255+
}: ReturnType<typeof getNodesCount>) => {
256+
const allResourceKindFilter = { status: ALL_RESOURCE_KIND_FILTER, count: allNodeCount }
257+
const statusFilters = [
258+
{ status: NodeStatus.Missing, count: missingNodeCount },
259+
{ status: NodeStatus.Degraded, count: failedNodeCount },
260+
{
261+
status: NodeStatus.Progressing,
262+
count: progressingNodeCount,
263+
},
264+
{ status: NodeStatus.Healthy, count: healthyNodeCount },
265+
...(window._env_.FEATURE_CONFIG_DRIFT_ENABLE
266+
? [
267+
{
268+
status: NodeFilters.drifted,
269+
count: driftedNodeCount,
270+
},
271+
]
272+
: []),
273+
]
274+
275+
return { allResourceKindFilter, statusFilters: statusFilters.filter(({ count }) => count > 0) }
276+
}

0 commit comments

Comments
 (0)