Skip to content

Commit

Permalink
Unified Resources UI: Various Fixes (#32064) (#32138)
Browse files Browse the repository at this point in the history
* Remove old indicator from unified resources

* Use lowercase for unified name sort

* Include friendly name in app icon guess

* Add filters exist indicator

* Remove uppercase from filter buttons and add filter count

* Remove indicator import

* Resource custom sort by contained resource

* Add unified name compare test
  • Loading branch information
avatus authored Sep 19, 2023
1 parent ea06205 commit 8ac0ba0
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 30 deletions.
32 changes: 22 additions & 10 deletions api/types/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,21 +588,33 @@ func unifiedKindCompare(a, b ResourceWithLabels, isDesc bool) bool {

func unifiedNameCompare(a ResourceWithLabels, b ResourceWithLabels, isDesc bool) bool {
var nameA, nameB string
resourceA, ok := a.(Server)
if ok {
nameA = resourceA.GetHostname()
} else {
switch r := a.(type) {
case AppServer:
nameA = r.GetApp().GetName()
case DatabaseServer:
nameA = r.GetDatabase().GetName()
case KubeServer:
nameA = r.GetCluster().GetName()
case Server:
nameA = r.GetHostname()
default:
nameA = a.GetName()
}

resourceB, ok := b.(Server)
if ok {
nameB = resourceB.GetHostname()
} else {
nameB = b.GetName()
switch r := b.(type) {
case AppServer:
nameB = r.GetApp().GetName()
case DatabaseServer:
nameB = r.GetDatabase().GetName()
case KubeServer:
nameB = r.GetCluster().GetName()
case Server:
nameB = r.GetHostname()
default:
nameB = a.GetName()
}

return stringCompare(nameA, nameB, isDesc)
return stringCompare(strings.ToLower(nameA), strings.ToLower(nameB), isDesc)
}

func (r ResourcesWithLabels) SortByCustom(by SortBy) error {
Expand Down
77 changes: 77 additions & 0 deletions api/types/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,83 @@ func TestMatchSearch(t *testing.T) {
}
}

func TestUnifiedNameCompare(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
resourceA func(*testing.T) ResourceWithLabels
resourceB func(*testing.T) ResourceWithLabels
isDesc bool
expect bool
}{
{
name: "sort by same kind",
resourceA: func(t *testing.T) ResourceWithLabels {
server, err := NewServer("node-cloud", KindNode, ServerSpecV2{
Hostname: "node-cloud",
})
require.NoError(t, err)
return server
},
resourceB: func(t *testing.T) ResourceWithLabels {
server, err := NewServer("node-strawberry", KindNode, ServerSpecV2{
Hostname: "node-strawberry",
})
require.NoError(t, err)
return server
},
isDesc: true,
expect: false,
},
{
name: "sort by different kind",
resourceA: func(t *testing.T) ResourceWithLabels {
server := newAppServer(t, "app-cloud")
return server
},
resourceB: func(t *testing.T) ResourceWithLabels {
server, err := NewServer("node-strawberry", KindNode, ServerSpecV2{
Hostname: "node-strawberry",
})
require.NoError(t, err)
return server
},
isDesc: true,
expect: false,
},
{
name: "sort with different cases",
resourceA: func(t *testing.T) ResourceWithLabels {
server := newAppServer(t, "app-cloud")
return server
},
resourceB: func(t *testing.T) ResourceWithLabels {
server, err := NewServer("Node-strawberry", KindNode, ServerSpecV2{
Hostname: "node-strawberry",
})
require.NoError(t, err)
return server
},
isDesc: true,
expect: false,
},
}

for _, tc := range testCases {
tc := tc
resourceA := tc.resourceA(t)
resourceB := tc.resourceB(t)
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

actual := unifiedNameCompare(resourceA, resourceB, tc.isDesc)
if actual != tc.expect {
t.Errorf("Expected %v, but got %v for %+v and %+v with isDesc=%v", tc.expect, actual, resourceA, resourceB, tc.isDesc)
}
})
}
}

func TestMatchSearch_ResourceSpecific(t *testing.T) {
t.Parallel()

Expand Down
47 changes: 46 additions & 1 deletion web/packages/teleport/src/UnifiedResources/FilterPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import React, { useState } from 'react';
import styled from 'styled-components';
import { ButtonBorder, ButtonPrimary, ButtonSecondary } from 'design/Button';
import { SortDir } from 'design/DataTable/types';
import { Text } from 'design';
Expand Down Expand Up @@ -151,6 +152,14 @@ const FilterTypesMenu = ({
setKinds(newKinds);
};

const handleSelectAll = () => {
setKinds(kindOptions.map(k => k.value));
};

const handleClearAll = () => {
setKinds([]);
};

const applyFilters = () => {
onChange(kinds);
handleClose();
Expand All @@ -167,8 +176,9 @@ const FilterTypesMenu = ({
size="small"
onClick={handleOpen}
>
Type
Types {kindsFromParams.length > 0 ? `(${kindsFromParams.length})` : ''}
<ChevronDown ml={2} size="small" color="text.slightlyMuted" />
{kindsFromParams.length > 0 && <FiltersExistIndicator />}
</ButtonSecondary>
<Menu
popoverCss={() => `margin-top: 36px;`}
Expand All @@ -184,6 +194,30 @@ const FilterTypesMenu = ({
open={Boolean(anchorEl)}
onClose={cancelUpdate}
>
<Flex gap={2} p={2}>
<ButtonSecondary
size="small"
onClick={handleSelectAll}
textTransform="none"
css={`
background-color: transparent;
`}
px={2}
>
Select All
</ButtonSecondary>
<ButtonSecondary
size="small"
onClick={handleClearAll}
textTransform="none"
css={`
background-color: transparent;
`}
px={2}
>
Clear All
</ButtonSecondary>
</Flex>
{kindOptions.map(kind => (
<MenuItem
px={2}
Expand Down Expand Up @@ -320,3 +354,14 @@ function kindArraysEqual(arr1: string[], arr2: string[]) {

return true;
}

const FiltersExistIndicator = styled.div`
position: absolute;
top: -4px;
right: -4px;
height: 12px;
width: 12px;
background-color: ${props => props.theme.colors.brand};
border-radius: 50%;
display: inline-block;
`;
19 changes: 1 addition & 18 deletions web/packages/teleport/src/UnifiedResources/Resources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,7 @@ limitations under the License.
import React, { useEffect, useState } from 'react';

import styled from 'styled-components';
import {
Box,
Indicator,
Flex,
ButtonLink,
ButtonSecondary,
Text,
} from 'design';
import { Box, Flex, ButtonLink, ButtonSecondary, Text } from 'design';
import { Magnifier } from 'design/Icon';

import { Danger } from 'design/Alert';
Expand Down Expand Up @@ -171,9 +164,6 @@ export function Resources() {
</ResourcesContainer>
<div ref={setScrollDetector} />
<ListFooter>
<IndicatorContainer status={attempt.status}>
<Indicator size={INDICATOR_SIZE} />
</IndicatorContainer>
{attempt.status === 'failed' && resources.length > 0 && (
<ButtonSecondary onClick={onRetryClicked}>Load more</ButtonSecondary>
)}
Expand Down Expand Up @@ -253,13 +243,6 @@ const ListFooter = styled.div`
text-align: center;
`;

// Line height is set to 0 to prevent the layout engine from adding extra pixels
// to the element's height.
const IndicatorContainer = styled(Box)`
display: ${props => (props.status === 'processing' ? 'block' : 'none')};
line-height: 0;
`;

const emptyStateInfo: EmptyStateInfo = {
title: 'Add your first resource to Teleport',
byline:
Expand Down
5 changes: 4 additions & 1 deletion web/packages/teleport/src/services/apps/makeApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,28 +84,31 @@ export default function makeApp(json: any): App {
}

function guessAppIcon(json: any): GuessedAppType {
const { name, labels, awsConsole = false } = json;
const { name, labels, friendlyName, awsConsole = false } = json;

if (awsConsole) {
return 'Aws';
}

if (
name?.toLocaleLowerCase().includes('slack') ||
friendlyName?.toLocaleLowerCase().includes('slack') ||
labels?.some(l => `${l.name}:${l.value}` === 'icon:slack')
) {
return 'Slack';
}

if (
name?.toLocaleLowerCase().includes('grafana') ||
friendlyName?.toLocaleLowerCase().includes('grafana') ||
labels?.some(l => `${l.name}:${l.value}` === 'icon:grafana')
) {
return 'Grafana';
}

if (
name?.toLocaleLowerCase().includes('jenkins') ||
friendlyName?.toLocaleLowerCase().includes('jenkins') ||
labels?.some(l => `${l.name}:${l.value}` === 'icon:jenkins')
) {
return 'Jenkins';
Expand Down

0 comments on commit 8ac0ba0

Please sign in to comment.