From 0c6b2547102fa2bbcf5d0fad2dcceaaf415dd354 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 18 Mar 2024 15:20:26 +0100 Subject: [PATCH 1/2] Update Asset View mode toggle, update styles --- src/components/Assets/AssetList/index.tsx | 6 ++ src/components/Assets/AssetList/styles.ts | 9 -- src/components/Assets/AssetList/types.ts | 1 + .../AssetTypeList/AssetTypeListItem/index.tsx | 2 +- .../AssetTypeList/AssetTypeListItem/styles.ts | 21 +---- src/components/Assets/AssetTypeList/index.tsx | 11 ++- src/components/Assets/AssetTypeList/styles.ts | 9 -- src/components/Assets/AssetTypeList/types.ts | 1 + .../AssetsViewScopeConfiguration.stories.tsx | 22 ++++- .../AssetsViewScopeConfiguration/index.tsx | 87 ++++++++++++------- .../AssetsViewScopeConfiguration/styles.ts | 16 ++-- .../AssetsViewScopeConfiguration/types.ts | 3 +- src/components/Assets/index.tsx | 43 +++++---- src/components/Assets/styles.ts | 35 ++------ .../common/icons/12px/ArrowIcon.tsx | 47 ++++++++++ .../common/icons/12px/TreeNodesIcon.tsx | 34 ++++++++ src/components/common/v3/Toggle/index.tsx | 4 + src/components/common/v3/Toggle/styles.ts | 5 +- src/components/common/v3/Toggle/types.ts | 9 +- 19 files changed, 242 insertions(+), 123 deletions(-) create mode 100644 src/components/common/icons/12px/ArrowIcon.tsx create mode 100644 src/components/common/icons/12px/TreeNodesIcon.tsx diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index ba3b33a0c..6754447dc 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -301,6 +301,12 @@ export const AssetList = (props: AssetListProps) => { }; }, []); + useEffect(() => { + if (previousData?.filteredCount !== data?.filteredCount && data) { + props.onAssetCountChange(data.filteredCount); + } + }, [previousData, data, props.onAssetCountChange]); + useEffect(() => { props.setRefresher(refreshData); }, [refreshData, props.setRefresher]); diff --git a/src/components/Assets/AssetList/styles.ts b/src/components/Assets/AssetList/styles.ts index c35b594e5..c1a1f1b62 100644 --- a/src/components/Assets/AssetList/styles.ts +++ b/src/components/Assets/AssetList/styles.ts @@ -35,15 +35,6 @@ export const Header = styled.div` font-weight: 400; font-size: 14px; padding: 8px 12px 8px 8px; - background: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#f1f5fa"; - case "dark": - case "dark-jetbrains": - return "#383838"; - } - }}; color: ${({ theme }) => { switch (theme.mode) { case "light": diff --git a/src/components/Assets/AssetList/types.ts b/src/components/Assets/AssetList/types.ts index b8e34fa21..d5a5ec4bc 100644 --- a/src/components/Assets/AssetList/types.ts +++ b/src/components/Assets/AssetList/types.ts @@ -11,6 +11,7 @@ export interface AssetListProps { searchQuery: string; scopeViewOptions: AssetScopeOption | null; setRefresher: (refresher: () => void) => void; + onAssetCountChange: (count: number) => void; } export enum SORTING_CRITERION { diff --git a/src/components/Assets/AssetTypeList/AssetTypeListItem/index.tsx b/src/components/Assets/AssetTypeList/AssetTypeListItem/index.tsx index 2b7c565f6..2cec1f0e1 100644 --- a/src/components/Assets/AssetTypeList/AssetTypeListItem/index.tsx +++ b/src/components/Assets/AssetTypeList/AssetTypeListItem/index.tsx @@ -8,7 +8,7 @@ export const AssetTypeListItem = (props: AssetTypeListItemProps) => { return ( - {props.icon && } + {props.icon && } {props.label || props.id} {props.entryCount} diff --git a/src/components/Assets/AssetTypeList/AssetTypeListItem/styles.ts b/src/components/Assets/AssetTypeList/AssetTypeListItem/styles.ts index 7f8b1f498..4c8382dd6 100644 --- a/src/components/Assets/AssetTypeList/AssetTypeListItem/styles.ts +++ b/src/components/Assets/AssetTypeList/AssetTypeListItem/styles.ts @@ -10,26 +10,11 @@ export const ListItem = styled.li` letter-spacing: -0.1px; user-select: none; border-radius: 4px; - background: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#f1f5fa"; - case "dark": - case "dark-jetbrains": - return "#383838"; - } - }}; + border: 1px solid ${({ theme }) => theme.colors.v3.stroke.tertiary}; + background: ${({ theme }) => theme.colors.v3.surface.secondary}; + color: ${({ theme }) => theme.colors.v3.text.primary}; `; export const EntryCount = styled.span` margin-left: auto; - color: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#828797"; - case "dark": - case "dark-jetbrains": - return "#9b9b9b"; - } - }}; `; diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index f5179062a..a6e3e59e6 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -63,6 +63,9 @@ const getData = ( }); }; +const getAssetCount = (assetCategoriesData: AssetCategoriesData) => + assetCategoriesData.assetCategories.reduce((acc, cur) => acc + cur.count, 0); + export const AssetTypeList = (props: AssetTypeListProps) => { const [data, setData] = useState(); const previousData = usePrevious(data); @@ -109,7 +112,7 @@ export const AssetTypeList = (props: AssetTypeListProps) => { useEffect(() => { props.setRefresher(refreshData); - }, [refreshData]); + }, [refreshData, props.setRefresher]); const areAnyFiltersApplied = checkIfAnyFiltersApplied( isComplexFilterEnabled, @@ -141,6 +144,12 @@ export const AssetTypeList = (props: AssetTypeListProps) => { }; }, []); + useEffect(() => { + if (previousData !== data && data) { + props.onAssetCountChange(getAssetCount(data)); + } + }, [previousData, data, props.onAssetCountChange]); + useEffect(() => { if ( (isEnvironment(previousEnvironment) && diff --git a/src/components/Assets/AssetTypeList/styles.ts b/src/components/Assets/AssetTypeList/styles.ts index fd035dddd..2f2e0ef59 100644 --- a/src/components/Assets/AssetTypeList/styles.ts +++ b/src/components/Assets/AssetTypeList/styles.ts @@ -6,13 +6,4 @@ export const List = styled.ul` gap: 8px; padding: 0 8px 8px; margin: 0; - color: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#828797"; - case "dark": - case "dark-jetbrains": - return "#dadada"; - } - }}; `; diff --git a/src/components/Assets/AssetTypeList/types.ts b/src/components/Assets/AssetTypeList/types.ts index d54f1d8a6..92c5a4110 100644 --- a/src/components/Assets/AssetTypeList/types.ts +++ b/src/components/Assets/AssetTypeList/types.ts @@ -11,6 +11,7 @@ export interface AssetTypeListProps { searchQuery: string; scopeViewOptions: AssetScopeOption | null; setRefresher: (refresher: () => void) => void; + onAssetCountChange: (count: number) => void; } export interface AssetCategoriesData { diff --git a/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx b/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx index 8571043c7..330a6d595 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx +++ b/src/components/Assets/AssetsViewScopeConfiguration/AssetsViewScopeConfiguration.stories.tsx @@ -17,5 +17,25 @@ export default meta; type Story = StoryObj; export const Default: Story = { - args: {} + args: { + currentScope: { + span: { + displayName: "displayName", + spanCodeObjectId: "spanCodeObjectId", + serviceName: null, + role: "Entry" + }, + code: { + relatedCodeDetailsList: [], + codeDetailsList: [] + }, + hasErrors: false, + issuesInsightsCount: 0, + analyticsInsightsCount: 0 + }, + assetsCount: 1, + onAssetViewChange: () => { + return undefined; + } + } }; diff --git a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx index a9e32b6b9..85b0634b8 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/index.tsx +++ b/src/components/Assets/AssetsViewScopeConfiguration/index.tsx @@ -1,44 +1,71 @@ import { useEffect, useState } from "react"; -import { ToggleSwitch } from "../../common/ToggleSwitch"; +import { isNumber } from "../../../typeGuards/isNumber"; +import { ArrowIcon } from "../../common/icons/12px/ArrowIcon"; +import { TreeNodesIcon } from "../../common/icons/12px/TreeNodesIcon"; +import { Toggle } from "../../common/v3/Toggle"; +import { ToggleOption } from "../../common/v3/Toggle/types"; import * as s from "./styles"; import { AssetsViewConfigurationProps as AssetsViewScopeConfigurationProps } from "./types"; -export const AssetsViewScopeConfiguration = ( - props: AssetsViewScopeConfigurationProps -) => { - const [isEntry, setIsEntry] = useState(false); - const [isDirect, setIsDirect] = useState(false); - const scope = props.currentScope; +type ViewMode = "descendants" | "children"; + +export const AssetsViewScopeConfiguration = ({ + currentScope, + onAssetViewChange, + assetsCount +}: AssetsViewScopeConfigurationProps) => { + const [viewMode, setViewMode] = useState("descendants"); useEffect(() => { - const isEntryPoint = !scope || scope.span?.role === "Entry"; - props.onAssetViewChanged({ - scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId, + const isEntryPoint = !currentScope || currentScope.span?.role === "Entry"; + + setViewMode(isEntryPoint ? "children" : "descendants"); + + onAssetViewChange({ + scopedSpanCodeObjectId: currentScope?.span?.spanCodeObjectId, isDirect: !isEntryPoint }); - setIsEntry(isEntryPoint); - setIsDirect(!isEntryPoint); - }, [scope]); + }, [currentScope, onAssetViewChange]); + + const handleToggleOptionChange = (value: ViewMode) => { + setViewMode(value); + + onAssetViewChange && + onAssetViewChange({ + isDirect: value === "children", + scopedSpanCodeObjectId: currentScope?.span?.spanCodeObjectId + }); + }; + + const toggleOptions: ToggleOption[] = [ + { + value: "descendants", + icon: TreeNodesIcon + }, + { + value: "children", + icon: ArrowIcon + } + ]; + + const assetTypeDescription = + viewMode === "descendants" ? "descendant" : "child"; return ( - Assets filtered to current scope - - { - setIsDirect(val); - props.onAssetViewChanged && - props.onAssetViewChanged({ - isDirect: val, - scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId - }); - }} - checked={isDirect} - disabled={!isEntry} - /> - + + + Showing + + {isNumber(assetsCount) ? ` ${assetsCount} ` : " "} + + {assetTypeDescription} asset{assetsCount === 1 ? "" : "s"} + ); }; diff --git a/src/components/Assets/AssetsViewScopeConfiguration/styles.ts b/src/components/Assets/AssetsViewScopeConfiguration/styles.ts index 7f88637c5..d898467bf 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/styles.ts +++ b/src/components/Assets/AssetsViewScopeConfiguration/styles.ts @@ -1,12 +1,18 @@ import styled from "styled-components"; +import { subscriptRegularTypography } from "../../common/App/typographies"; export const Container = styled.div` display: flex; - font-size: 14px; - flex-grow: 1; - justify-content: space-between; + gap: 8px; + align-items: center; `; -export const Item = styled.div` - display: flex; +export const Label = styled.div` + ${subscriptRegularTypography} + + color: ${({ theme }) => theme.colors.v3.text.tertiary}; +`; + +export const AssetsCount = styled.span` + color: ${({ theme }) => theme.colors.v3.text.primary}; `; diff --git a/src/components/Assets/AssetsViewScopeConfiguration/types.ts b/src/components/Assets/AssetsViewScopeConfiguration/types.ts index 09e48dcce..da43b8398 100644 --- a/src/components/Assets/AssetsViewScopeConfiguration/types.ts +++ b/src/components/Assets/AssetsViewScopeConfiguration/types.ts @@ -1,8 +1,9 @@ import { Scope } from "../../common/App/types"; export interface AssetsViewConfigurationProps { - onAssetViewChanged: (assetViewScope: AssetScopeOption) => void; + onAssetViewChange: (assetViewScope: AssetScopeOption) => void; currentScope: Scope; + assetsCount?: number; } export interface AssetScopeOption { diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index 15d0ea0b6..94f0b5d5e 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useMemo, useState } from "react"; +import { useCallback, useContext, useEffect, useMemo, useState } from "react"; import { lt, valid } from "semver"; import { featureFlagMinBackendVersions, @@ -26,6 +26,7 @@ import { trackingEvents } from "./tracking"; import { DataRefresher } from "./types"; export const Assets = () => { + const [assetsCount, setAssetsCount] = useState(); const [selectedAssetTypeId, setSelectedAssetTypeId] = useState( null ); @@ -113,6 +114,18 @@ export const Assets = () => { currentRefresher && currentRefresher.refresh(); }; + const handleAssetCountChange = useCallback((count: number) => { + setAssetsCount(count); + }, []); + + const handleAssetViewModeChange = useCallback((val: AssetScopeOption) => { + setAssetScopeOption(val); + }, []); + + const handleRefresherChange = useCallback((refresher: () => void) => { + setAssetTypeListRefresher({ refresh: refresher }); + }, []); + const renderContent = () => { if (isBackendUpgradeMessageVisible) { return ( @@ -145,9 +158,8 @@ export const Assets = () => { filters={selectedFilters} searchQuery={debouncedSearchInputValue} scopeViewOptions={assetScopeOption} - setRefresher={(refresher) => { - setAssetTypeListRefresher({ refresh: refresher }); - }} + setRefresher={handleRefresherChange} + onAssetCountChange={handleAssetCountChange} /> ); } @@ -160,9 +172,8 @@ export const Assets = () => { filters={selectedFilters} searchQuery={debouncedSearchInputValue} scopeViewOptions={assetScopeOption} - setRefresher={(refresher) => { - setAssetListRefresher({ refresh: refresher }); - }} + setRefresher={handleRefresherChange} + onAssetCountChange={handleAssetCountChange} /> ); }; @@ -170,6 +181,15 @@ export const Assets = () => { return ( + {config.scope && config.scope.span && ( + + + + )} {window.assetsSearch === true && ( { {config.scope && config.scope.span && ( - - { - setAssetScopeOption(val); - }} - /> - + Assets filtered to current scope )} {renderContent()} diff --git a/src/components/Assets/styles.ts b/src/components/Assets/styles.ts index e305121ce..1e223d025 100644 --- a/src/components/Assets/styles.ts +++ b/src/components/Assets/styles.ts @@ -1,20 +1,11 @@ import styled from "styled-components"; -import { grayScale } from "../common/App/v2colors"; import { Button } from "../common/v3/Button"; export const Container = styled.div` height: 100%; display: flex; flex-direction: column; - background: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#fbfdff"; - case "dark": - case "dark-jetbrains": - return "#2b2d30"; - } - }}; + background: ${({ theme }) => theme.colors.v3.surface.primary}; `; export const Header = styled.div` @@ -64,16 +55,8 @@ export const SearchInput = styled.input` padding: 4px 4px 4px 20px; border-radius: 4px; outline: none; - border: 1px solid ${({ theme }) => theme.colors.stroke.primary}; - background: ${({ theme }) => { - switch (theme.mode) { - case "light": - return grayScale[50]; - case "dark": - case "dark-jetbrains": - return grayScale[1000]; - } - }}; + border: 1px solid ${({ theme }) => theme.colors.v3.stroke.dark}; + background: ${({ theme }) => theme.colors.v3.surface.primary}; box-shadow: 1px 1px 4px 0 rgb(0 0 0 / 25%); caret-color: ${({ theme }) => { switch (theme.mode) { @@ -126,16 +109,8 @@ export const UpgradeMessage = styled.div` export const RefreshButton = styled(Button)` color: ${({ theme }) => theme.colors.v3.icon.tertiary}; - border: 1px solid ${({ theme }) => theme.colors.stroke.primary}; - background: ${({ theme }) => { - switch (theme.mode) { - case "light": - return grayScale[50]; - case "dark": - case "dark-jetbrains": - return grayScale[1000]; - } - }}; + border: 1px solid ${({ theme }) => theme.colors.v3.stroke.dark}; + background: ${({ theme }) => theme.colors.v3.surface.primary}; &:hover:enabled { color: ${({ theme }) => theme.colors.v3.icon.white}; diff --git a/src/components/common/icons/12px/ArrowIcon.tsx b/src/components/common/icons/12px/ArrowIcon.tsx new file mode 100644 index 000000000..e1e445c4b --- /dev/null +++ b/src/components/common/icons/12px/ArrowIcon.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { useIconProps } from "../hooks"; +import { Direction, RotatableIconProps } from "../types"; + +const directionRotateMap: { [key in Direction]: string } = { + [Direction.DOWN]: "0", + [Direction.LEFT]: "90", + [Direction.UP]: "180", + [Direction.RIGHT]: "270" +}; + +const ArrowIconComponent = (props: RotatableIconProps) => { + const { size, color } = useIconProps(props); + + const transform = { + transform: `rotate(${ + directionRotateMap[props.direction || Direction.DOWN] + })` + }; + + return ( + + + + + + + + + + + ); +}; + +export const ArrowIcon = React.memo(ArrowIconComponent); diff --git a/src/components/common/icons/12px/TreeNodesIcon.tsx b/src/components/common/icons/12px/TreeNodesIcon.tsx new file mode 100644 index 000000000..178db55ac --- /dev/null +++ b/src/components/common/icons/12px/TreeNodesIcon.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { useIconProps } from "../hooks"; +import { IconProps } from "../types"; + +const TreeNodesIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + + + + + + + + + + ); +}; + +export const TreeNodesIcon = React.memo(TreeNodesIconComponent); diff --git a/src/components/common/v3/Toggle/index.tsx b/src/components/common/v3/Toggle/index.tsx index fa321afbe..ed20de331 100644 --- a/src/components/common/v3/Toggle/index.tsx +++ b/src/components/common/v3/Toggle/index.tsx @@ -2,6 +2,8 @@ import * as s from "./styles"; import { ToggleProps, ToggleValue } from "./types"; export const Toggle = (props: ToggleProps) => { + const size = props.size || "large"; + const handleOptionButtonClick = (value: T) => { props.onValueChange(value); }; @@ -10,11 +12,13 @@ export const Toggle = (props: ToggleProps) => { {props.options.map((option) => ( handleOptionButtonClick(option.value)} > {option.label} + {option.icon && } ))} diff --git a/src/components/common/v3/Toggle/styles.ts b/src/components/common/v3/Toggle/styles.ts index ddd4ccb5e..21e8a92aa 100644 --- a/src/components/common/v3/Toggle/styles.ts +++ b/src/components/common/v3/Toggle/styles.ts @@ -15,13 +15,14 @@ export const Container = styled.div` export const OptionButton = styled.button` ${footnoteRegularTypography} + display: flex; font-family: inherit; border: none; outline: none; - border-radius: 4px; - padding: 2px 4px; cursor: pointer; user-select: none; + border-radius: ${({ $size }) => ($size === "small" ? "2px" : " 4px")}; + padding: ${({ $size }) => ($size === "small" ? "2px" : "2px 4px")}; color: ${({ theme, $selected }) => $selected ? theme.colors.v3.text.white : theme.colors.v3.text.primary}; background: ${({ theme, $selected }) => diff --git a/src/components/common/v3/Toggle/types.ts b/src/components/common/v3/Toggle/types.ts index bd726acb1..1fe9c4010 100644 --- a/src/components/common/v3/Toggle/types.ts +++ b/src/components/common/v3/Toggle/types.ts @@ -1,16 +1,23 @@ +import { ComponentType } from "react"; +import { IconProps } from "../../icons/types"; + export type ToggleValue = string | number; +export type ToggleSize = "small" | "large"; export interface ToggleOption { value: T; - label: string; + label?: string; + icon?: ComponentType; } export interface ToggleProps { options: ToggleOption[]; onValueChange: (value: T) => void; value: T; + size?: ToggleSize; } export interface OptionButtonProps { $selected: boolean; + $size: ToggleSize; } From 417a121dc69f61b0d3a85dddbff0973e645a0568 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 18 Mar 2024 15:24:18 +0100 Subject: [PATCH 2/2] Improve conditions --- src/components/Assets/AssetList/index.tsx | 2 +- src/components/Assets/AssetTypeList/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx index 6754447dc..3451cec11 100644 --- a/src/components/Assets/AssetList/index.tsx +++ b/src/components/Assets/AssetList/index.tsx @@ -302,7 +302,7 @@ export const AssetList = (props: AssetListProps) => { }, []); useEffect(() => { - if (previousData?.filteredCount !== data?.filteredCount && data) { + if (data && previousData?.filteredCount !== data?.filteredCount) { props.onAssetCountChange(data.filteredCount); } }, [previousData, data, props.onAssetCountChange]); diff --git a/src/components/Assets/AssetTypeList/index.tsx b/src/components/Assets/AssetTypeList/index.tsx index a6e3e59e6..8bc3b2095 100644 --- a/src/components/Assets/AssetTypeList/index.tsx +++ b/src/components/Assets/AssetTypeList/index.tsx @@ -145,7 +145,7 @@ export const AssetTypeList = (props: AssetTypeListProps) => { }, []); useEffect(() => { - if (previousData !== data && data) { + if (data && previousData !== data) { props.onAssetCountChange(getAssetCount(data)); } }, [previousData, data, props.onAssetCountChange]);