From 49976f8fe2a03adea8065a8ac65bc36b77cc0fd7 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Wed, 12 Jul 2023 15:57:31 +0200 Subject: [PATCH 1/4] Add sorting order buttons --- src/components/Assets/AssetList/index.tsx | 90 +++++++++++++------ src/components/Assets/AssetList/styles.ts | 81 ++++++++++++++++- src/components/Assets/AssetList/types.ts | 15 +++- src/components/common/Menu/styles.ts | 34 +++++++ .../common/Popover/PopoverContent/index.tsx | 6 +- .../common/Popover/PopoverTrigger/index.tsx | 6 +- .../common/Popover/PopoverTrigger/styles.ts | 9 -- src/components/common/icons/SortIcon.tsx | 24 +++++ 8 files changed, 218 insertions(+), 47 deletions(-) delete mode 100644 src/components/common/Popover/PopoverTrigger/styles.ts create mode 100644 src/components/common/icons/SortIcon.tsx diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 52afab239..5c1126a40 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -6,6 +6,7 @@ import { PopoverContent } from "../../common/Popover/PopoverContent"; import { PopoverTrigger } from "../../common/Popover/PopoverTrigger"; import { ChevronIcon } from "../../common/icons/ChevronIcon"; import { MagnifierIcon } from "../../common/icons/MagnifierIcon"; +import { SortIcon } from "../../common/icons/SortIcon"; import { Direction } from "../../common/icons/types"; import { getAssetTypeInfo } from "../utils"; import { AssetEntry as AssetEntryComponent } from "./AssetEntry"; @@ -14,6 +15,7 @@ import { AssetListProps, ExtendedAssetEntryWithServices, SORTING_CRITERION, + SORTING_ORDER, Sorting } from "./types"; @@ -23,6 +25,8 @@ const sortEntries = ( ): ExtendedAssetEntryWithServices[] => { entries = [...entries]; + const isDesc = sorting.order === SORTING_ORDER.DESC; + const sortByName = ( a: ExtendedAssetEntryWithServices, b: ExtendedAssetEntryWithServices, @@ -83,38 +87,32 @@ const sortEntries = ( ).length; return ( - (sorting.isDesc + (isDesc ? aHighestImportance - bHighestImportance : bHighestImportance - aHighestImportance) || - (sorting.isDesc + (isDesc ? bMostImportantInsightCount - aMostImportantInsightCount : aMostImportantInsightCount - bMostImportantInsightCount) || - sortByName(a, b, sorting.isDesc) + sortByName(a, b, isDesc) ); }); case SORTING_CRITERION.PERFORMANCE: - return entries.sort((a, b) => - sortByPercentile(a, b, 0.5, sorting.isDesc) - ); + return entries.sort((a, b) => sortByPercentile(a, b, 0.5, isDesc)); case SORTING_CRITERION.SLOWEST_FIVE_PERCENT: - return entries.sort((a, b) => - sortByPercentile(a, b, 0.95, sorting.isDesc) - ); + return entries.sort((a, b) => sortByPercentile(a, b, 0.95, isDesc)); case SORTING_CRITERION.LATEST: return entries.sort((a, b) => { const aDateTime = new Date(a.lastSpanInstanceInfo.startTime).valueOf(); const bDateTime = new Date(b.lastSpanInstanceInfo.startTime).valueOf(); return ( - (sorting.isDesc ? bDateTime - aDateTime : aDateTime - bDateTime) || - sortByName(a, b, sorting.isDesc) + (isDesc ? bDateTime - aDateTime : aDateTime - bDateTime) || + sortByName(a, b, isDesc) ); }); case SORTING_CRITERION.NAME: return entries.sort((a, b) => - sorting.isDesc - ? sortByName(b, a, sorting.isDesc) - : sortByName(a, b, sorting.isDesc) + isDesc ? sortByName(b, a, isDesc) : sortByName(a, b, isDesc) ); default: return entries; @@ -151,13 +149,29 @@ const getSortingMenuChevronColor = (theme: DefaultTheme) => { } }; +const getSortIconColor = (theme: DefaultTheme, selected: boolean) => { + if (selected) { + switch (theme.mode) { + case "light": + return "#f1f5fa"; + case "dark": + case "dark-jetbrains": + return "#dadada"; + } + } + switch (theme.mode) { + case "light": + return "#828797"; + case "dark": + case "dark-jetbrains": + return "#dadada"; + } +}; + export const AssetList = (props: AssetListProps) => { - const [sorting, setSorting] = useState<{ - criterion: SORTING_CRITERION; - isDesc: boolean; - }>({ + const [sorting, setSorting] = useState({ criterion: SORTING_CRITERION.CRITICAL_INSIGHTS, - isDesc: true + order: SORTING_ORDER.DESC }); const [isSortingMenuOpen, setIsSortingMenuOpen] = useState(false); const [searchInputValue, setSearchInputValue] = useState(""); @@ -181,15 +195,10 @@ export const AssetList = (props: AssetListProps) => { }; const handleSortingMenuItemSelect = (value: string) => { - if (sorting.criterion === value) { - setSorting({ - ...sorting, - isDesc: !sorting.isDesc - }); - } else { + if (sorting.criterion !== value) { setSorting({ criterion: value as SORTING_CRITERION, - isDesc: false + order: SORTING_ORDER.DESC }); } handleSortingMenuToggle(); @@ -227,6 +236,13 @@ export const AssetList = (props: AssetListProps) => { return sortEntries(filteredEntries, sorting); }, [entries, sorting, searchInputValue]); + const handleSortingOrderToggleOptionButtonClick = (order: SORTING_ORDER) => { + setSorting({ + ...sorting, + order + }); + }; + return ( @@ -260,14 +276,14 @@ export const AssetList = (props: AssetListProps) => { placement={"bottom-start"} > - + Sort by {sorting.criterion} - + { + + {[SORTING_ORDER.DESC, SORTING_ORDER.ASC].map((order) => { + const isSelected = sorting.order === order; + const iconColor = getSortIconColor(theme, isSelected); + + return ( + handleSortingOrderToggleOptionButtonClick(order)} + > + + + + + ); + })} + {sortedEntries.length > 0 ? ( diff --git a/src/components/Assets/AssetList/styles.ts b/src/components/Assets/AssetList/styles.ts index f2a726753..6f30d4c7b 100644 --- a/src/components/Assets/AssetList/styles.ts +++ b/src/components/Assets/AssetList/styles.ts @@ -1,4 +1,5 @@ import styled from "styled-components"; +import { SortingMenuButtonProps, SortingOrderOptionProps } from "./types"; export const Container = styled.div` display: flex; @@ -45,6 +46,7 @@ export const Toolbar = styled.div` display: flex; justify-content: space-between; padding: 8px; + gap: 4px; `; export const PopoverContainer = styled.div` @@ -70,7 +72,7 @@ export const SearchInput = styled.input` font-size: 10px; padding: 4px 4px 4px 18px; border-radius: 4px; - width: 140px; + width: 70px; outline: none; caret-color: ${({ theme }) => { switch (theme.mode) { @@ -132,14 +134,17 @@ export const SearchInput = styled.input` } `; -export const SortingMenuContainer = styled.div` +export const SortingMenuButton = styled.button` + background: none; + cursor: pointer; display: flex; gap: 2px; font-weight: 500; font-size: 10px; line-height: 12px; align-items: center; - height: 20px; + border-radius: 4px; + padding: 4px 8px; color: ${({ theme }) => { switch (theme.mode) { case "light": @@ -149,6 +154,41 @@ export const SortingMenuContainer = styled.div` return "#9b9b9b"; } }}; + border: 1px solid + ${({ theme, isOpen }) => { + if (isOpen) { + switch (theme.mode) { + case "light": + return "#7891d0"; + case "dark": + case "dark-jetbrains": + return "#9b9b9b"; + } + } + + switch (theme.mode) { + case "light": + return "#d0d6eb"; + case "dark": + case "dark-jetbrains": + return "#49494d"; + } + }}; + + &:hover, + &:focus { + outline: none; + border: 1px solid + ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#7891d0"; + case "dark": + case "dark-jetbrains": + return "#9b9b9b"; + } + }}; + } `; export const SortingLabel = styled.span` @@ -167,6 +207,41 @@ export const SortingLabel = styled.span` }}; `; +export const SortingOrderToggle = styled.div` + display: flex; + border-radius: 4px; + padding: 4px; + gap: 4px; + border: 1px solid + ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#b9c0d4"; + case "dark": + case "dark-jetbrains": + return "#49494d"; + } + }}; +`; + +export const SortingOrderToggleOptionButton = styled.button` + border: none; + outline: none; + padding: 0; + border-radius: 2px; + cursor: pointer; + background: ${({ selected }) => (selected ? "#3538cd" : "transparent")}; +`; + +export const SortingOrderIconContainer = styled.div<{ + sortingOrder: "asc" | "desc"; +}>` + display: flex; + transform: scaleY( + ${({ sortingOrder }) => (sortingOrder === "desc" ? -1 : 1)} + ); +`; + export const ItemsCount = styled.span` margin-left: auto; color: ${({ theme }) => { diff --git a/src/components/Assets/AssetList/types.ts b/src/components/Assets/AssetList/types.ts index a993f6192..606681505 100644 --- a/src/components/Assets/AssetList/types.ts +++ b/src/components/Assets/AssetList/types.ts @@ -19,7 +19,20 @@ export enum SORTING_CRITERION { NAME = "Name" } +export enum SORTING_ORDER { + ASC = "asc", + DESC = "desc" +} + export interface Sorting { criterion: SORTING_CRITERION; - isDesc: boolean; + order: SORTING_ORDER; +} + +export interface SortingMenuButtonProps { + isOpen: boolean; +} + +export interface SortingOrderOptionProps { + selected: boolean; } diff --git a/src/components/common/Menu/styles.ts b/src/components/common/Menu/styles.ts index a8365cf79..9a37faeae 100644 --- a/src/components/common/Menu/styles.ts +++ b/src/components/common/Menu/styles.ts @@ -41,6 +41,7 @@ export const List = styled.ul` export const ListItem = styled.li` flex-direction: row; width: 100%; + box-sizing: border-box; list-style-type: none; padding: 6px 8px; font-size: 10px; @@ -55,4 +56,37 @@ export const ListItem = styled.li` return "#9b9b9b"; } }}; + + &:hover { + background: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#f1f5fa"; + case "dark": + case "dark-jetbrains": + return "#383838"; + } + }}; + } + + &:active { + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#4d668a"; + case "dark": + case "dark-jetbrains": + return "#e2e7ff"; + } + }}; + background: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#e9eef4"; + case "dark": + case "dark-jetbrains": + return "#49494d"; + } + }}; + } `; diff --git a/src/components/common/Popover/PopoverContent/index.tsx b/src/components/common/Popover/PopoverContent/index.tsx index 0e56bb390..dbc8b8319 100644 --- a/src/components/common/Popover/PopoverContent/index.tsx +++ b/src/components/common/Popover/PopoverContent/index.tsx @@ -5,7 +5,7 @@ import { FloatingPortal, useMergeRefs } from "@floating-ui/react"; -import { CSSProperties, ForwardedRef, forwardRef, HTMLProps } from "react"; +import { CSSProperties, ForwardedRef, HTMLProps, forwardRef } from "react"; import { usePopoverContext } from "../hooks"; const PopoverContentComponent = ( @@ -26,7 +26,9 @@ const PopoverContentComponent = ( position: context.strategy, top: context.y ?? 0, left: context.x ?? 0, - width: "max-content", + width: + context.elements.reference?.getBoundingClientRect().width, + outline: "none", ...props.style } as CSSProperties } diff --git a/src/components/common/Popover/PopoverTrigger/index.tsx b/src/components/common/Popover/PopoverTrigger/index.tsx index 260d427b8..77f294323 100644 --- a/src/components/common/Popover/PopoverTrigger/index.tsx +++ b/src/components/common/Popover/PopoverTrigger/index.tsx @@ -10,7 +10,6 @@ import { Ref } from "react"; import { usePopoverContext } from "../hooks"; -import * as s from "./styles"; import { PopoverTriggerProps } from "./types"; const PopoverTriggerComponent = ( @@ -43,15 +42,14 @@ const PopoverTriggerComponent = ( } return ( - {children} - + ); }; diff --git a/src/components/common/Popover/PopoverTrigger/styles.ts b/src/components/common/Popover/PopoverTrigger/styles.ts deleted file mode 100644 index f228829d5..000000000 --- a/src/components/common/Popover/PopoverTrigger/styles.ts +++ /dev/null @@ -1,9 +0,0 @@ -import styled from "styled-components"; - -export const Button = styled.button` - background: none; - border: none; - display: flex; - padding: 0; - cursor: pointer; -`; diff --git a/src/components/common/icons/SortIcon.tsx b/src/components/common/icons/SortIcon.tsx new file mode 100644 index 000000000..aae2fe52f --- /dev/null +++ b/src/components/common/icons/SortIcon.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { useIconProps } from "./hooks"; +import { IconProps } from "./types"; + +const SortIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + ); +}; + +export const SortIcon = React.memo(SortIconComponent); From 4067fcbd91eea60f126b42448e9e375a2c9e8b6f Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Thu, 13 Jul 2023 12:28:12 +0200 Subject: [PATCH 2/4] Change default sorting order Update styles --- .../Assets/AssetList/AssetEntry/index.tsx | 15 ++++++++- .../Assets/AssetList/AssetEntry/styles.ts | 14 +++------ src/components/Assets/AssetList/index.tsx | 31 ++++++++++++++++--- .../InstallationWizard/InstallStep/index.tsx | 2 +- src/components/common/icons/GlobeIcon.tsx | 31 +++++++++++++++++++ 5 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 src/components/common/icons/GlobeIcon.tsx diff --git a/src/components/Assets/AssetList/AssetEntry/index.tsx b/src/components/Assets/AssetList/AssetEntry/index.tsx index c17655e08..170d4fb97 100644 --- a/src/components/Assets/AssetList/AssetEntry/index.tsx +++ b/src/components/Assets/AssetList/AssetEntry/index.tsx @@ -1,14 +1,26 @@ -import { useTheme } from "styled-components"; +import { DefaultTheme, useTheme } from "styled-components"; import { getInsightImportanceColor } from "../../../../utils/getInsightImportanceColor"; import { getInsightTypeInfo } from "../../../../utils/getInsightTypeInfo"; import { getInsightTypeOrderPriority } from "../../../../utils/getInsightTypeOrderPriority"; import { timeAgo } from "../../../../utils/timeAgo"; +import { GlobeIcon } from "../../../common/icons/GlobeIcon"; import { getAssetTypeInfo } from "../../utils"; import * as s from "./styles"; import { AssetEntryProps } from "./types"; +const getServiceIconColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#4d668a"; + case "dark": + case "dark-jetbrains": + return "#dadada"; + } +}; + export const AssetEntry = (props: AssetEntryProps) => { const theme = useTheme(); + const serviceIconColor = getServiceIconColor(theme); const handleLinkClick = () => { props.onAssetLinkClick(props.entry); @@ -74,6 +86,7 @@ export const AssetEntry = (props: AssetEntryProps) => { Services + {props.entry.serviceName} {otherServices.length > 0 && ( diff --git a/src/components/Assets/AssetList/AssetEntry/styles.ts b/src/components/Assets/AssetList/AssetEntry/styles.ts index b45aa6634..e6755efd1 100644 --- a/src/components/Assets/AssetList/AssetEntry/styles.ts +++ b/src/components/Assets/AssetList/AssetEntry/styles.ts @@ -107,7 +107,10 @@ export const ServicesContainer = styled.div` `; export const ServiceName = styled.div` - padding: 4px 6px; + display: flex; + gap: 4px; + align-items: center; + padding: 4px 0; border-radius: 23px; line-height: 8px; overflow: hidden; @@ -122,15 +125,6 @@ export const ServiceName = styled.div` return "#dadada"; } }}; - background: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#e9eef4"; - case "dark": - case "dark-jetbrains": - return "#2e2e2e"; - } - }}; `; export const ValueContainer = styled.div` diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 5c1126a40..28b324af2 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -111,9 +111,7 @@ const sortEntries = ( ); }); case SORTING_CRITERION.NAME: - return entries.sort((a, b) => - isDesc ? sortByName(b, a, isDesc) : sortByName(a, b, isDesc) - ); + return entries.sort((a, b) => sortByName(a, b, isDesc)); default: return entries; } @@ -168,6 +166,21 @@ const getSortIconColor = (theme: DefaultTheme, selected: boolean) => { } }; +const getDefaultSortingOrder = ( + criterion: SORTING_CRITERION +): SORTING_ORDER => { + switch (criterion) { + case SORTING_CRITERION.NAME: + return SORTING_ORDER.ASC; + case SORTING_CRITERION.PERFORMANCE: + case SORTING_CRITERION.SLOWEST_FIVE_PERCENT: + case SORTING_CRITERION.CRITICAL_INSIGHTS: + case SORTING_CRITERION.LATEST: + default: + return SORTING_ORDER.DESC; + } +}; + export const AssetList = (props: AssetListProps) => { const [sorting, setSorting] = useState({ criterion: SORTING_CRITERION.CRITICAL_INSIGHTS, @@ -195,10 +208,18 @@ export const AssetList = (props: AssetListProps) => { }; const handleSortingMenuItemSelect = (value: string) => { - if (sorting.criterion !== value) { + if (sorting.criterion === value) { + setSorting({ + ...sorting, + order: + sorting.order === SORTING_ORDER.DESC + ? SORTING_ORDER.ASC + : SORTING_ORDER.DESC + }); + } else { setSorting({ criterion: value as SORTING_CRITERION, - order: SORTING_ORDER.DESC + order: getDefaultSortingOrder(value as SORTING_CRITERION) }); } handleSortingMenuToggle(); diff --git a/src/components/InstallationWizard/InstallStep/index.tsx b/src/components/InstallationWizard/InstallStep/index.tsx index db8713352..99f72cadd 100644 --- a/src/components/InstallationWizard/InstallStep/index.tsx +++ b/src/components/InstallationWizard/InstallStep/index.tsx @@ -132,7 +132,7 @@ export const InstallStep = (props: InstallStepProps) => { openLinkInDefaultBrowser(DOCKER_DESKTOP_URL)}> Docker Desktop {" "} - 4.10.0 or later installed) + 4.10.0 or higher installed) { + const { size, color } = useIconProps(props); + + return ( + + + + + + + + + + + ); +}; + +export const GlobeIcon = React.memo(GlobeIconComponent); From 7e0c6967de9eae55e1c323d7f0e826bbfff79981 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Thu, 13 Jul 2023 14:16:34 +0200 Subject: [PATCH 3/4] Update styles --- .../Assets/AssetList/AssetEntry/index.tsx | 25 +++++++++---------- .../Assets/AssetList/AssetEntry/styles.ts | 19 +++----------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/components/Assets/AssetList/AssetEntry/index.tsx b/src/components/Assets/AssetList/AssetEntry/index.tsx index 170d4fb97..b71f766d8 100644 --- a/src/components/Assets/AssetList/AssetEntry/index.tsx +++ b/src/components/Assets/AssetList/AssetEntry/index.tsx @@ -81,12 +81,12 @@ export const AssetEntry = (props: AssetEntryProps) => { - + Services + - {props.entry.serviceName} {otherServices.length > 0 && ( @@ -96,15 +96,6 @@ export const AssetEntry = (props: AssetEntryProps) => { )} - - Last - - {timeAgo(lastSeenDateTime)} - ago - - - - Performance @@ -114,7 +105,15 @@ export const AssetEntry = (props: AssetEntryProps) => { )} - + + + + Last + + {timeAgo(lastSeenDateTime)} + ago + + Slowest 5% @@ -126,7 +125,7 @@ export const AssetEntry = (props: AssetEntryProps) => { )} - + ); diff --git a/src/components/Assets/AssetList/AssetEntry/styles.ts b/src/components/Assets/AssetList/AssetEntry/styles.ts index e6755efd1..bb0a04c45 100644 --- a/src/components/Assets/AssetList/AssetEntry/styles.ts +++ b/src/components/Assets/AssetList/AssetEntry/styles.ts @@ -74,7 +74,7 @@ export const InsightIconContainer = styled(AssetTypeIconContainer)` export const StatsContainer = styled.div` display: flex; - gap: 15px 12px; + gap: 16px 12px; flex-wrap: wrap; font-size: 10px; line-height: 12px; @@ -84,20 +84,12 @@ export const Stats = styled.div` display: flex; flex-direction: column; gap: 4px; + width: 120px; `; -export const StatsColumn = styled.div` +export const StatsRow = styled.div` display: flex; - flex-direction: column; - gap: 12px; - - &:first-child { - width: 35%; - } - - &:nth-child(2) { - width: 35%; - } + gap: 16px; `; export const ServicesContainer = styled.div` @@ -107,9 +99,6 @@ export const ServicesContainer = styled.div` `; export const ServiceName = styled.div` - display: flex; - gap: 4px; - align-items: center; padding: 4px 0; border-radius: 23px; line-height: 8px; From 9d1f3c64a4401c06d24338a84b1f1c97d254d8f8 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Thu, 13 Jul 2023 15:28:00 +0200 Subject: [PATCH 4/4] Update types --- src/components/Assets/AssetList/index.tsx | 14 +++++++------- src/components/Assets/AssetList/styles.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 28b324af2..c681771c1 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -229,6 +229,13 @@ export const AssetList = (props: AssetListProps) => { props.onAssetLinkClick(entry); }; + const handleSortingOrderToggleOptionButtonClick = (order: SORTING_ORDER) => { + setSorting({ + ...sorting, + order + }); + }; + const assetTypeInfo = getAssetTypeInfo(props.assetTypeId); const entries: ExtendedAssetEntryWithServices[] = useMemo( @@ -257,13 +264,6 @@ export const AssetList = (props: AssetListProps) => { return sortEntries(filteredEntries, sorting); }, [entries, sorting, searchInputValue]); - const handleSortingOrderToggleOptionButtonClick = (order: SORTING_ORDER) => { - setSorting({ - ...sorting, - order - }); - }; - return ( diff --git a/src/components/Assets/AssetList/styles.ts b/src/components/Assets/AssetList/styles.ts index 6f30d4c7b..67042bec0 100644 --- a/src/components/Assets/AssetList/styles.ts +++ b/src/components/Assets/AssetList/styles.ts @@ -1,5 +1,9 @@ import styled from "styled-components"; -import { SortingMenuButtonProps, SortingOrderOptionProps } from "./types"; +import { + SORTING_ORDER, + SortingMenuButtonProps, + SortingOrderOptionProps +} from "./types"; export const Container = styled.div` display: flex; @@ -234,11 +238,11 @@ export const SortingOrderToggleOptionButton = styled.button` display: flex; transform: scaleY( - ${({ sortingOrder }) => (sortingOrder === "desc" ? -1 : 1)} + ${({ sortingOrder }) => (sortingOrder === SORTING_ORDER.DESC ? -1 : 1)} ); `;