From af261af7a73f2b808681f6da703b48034f1cd98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Fern=C3=A1ndez?= Date: Wed, 19 Feb 2025 11:02:16 +0100 Subject: [PATCH] UnifyHistory: Add styles from the docked POC (#100381) --- .../components/AppChrome/AppChromeService.tsx | 6 +- .../AppChrome/History/HistoryContainer.tsx | 2 +- .../AppChrome/History/HistoryWrapper.tsx | 282 +++++++++++------- public/locales/en-US/grafana.json | 2 + public/locales/pseudo-LOCALE/grafana.json | 2 + 5 files changed, 183 insertions(+), 111 deletions(-) diff --git a/public/app/core/components/AppChrome/AppChromeService.tsx b/public/app/core/components/AppChrome/AppChromeService.tsx index 85f2a5cdd83..3a0690b8abd 100644 --- a/public/app/core/components/AppChrome/AppChromeService.tsx +++ b/public/app/core/components/AppChrome/AppChromeService.tsx @@ -147,10 +147,10 @@ export class AppChromeService { const lastEntry = entries[0]; const newEntry = { name: newPageNav.text, views: [], breadcrumbs, time: Date.now(), url: window.location.href }; - const isSameUrl = lastEntry && newEntry.url === lastEntry.url; + const isSamePath = lastEntry && newEntry.url.split('?')[0] === lastEntry.url.split('?')[0]; - // To avoid adding an entry with the same url twice, we always use the latest one - if (isSameUrl) { + // To avoid adding an entry with the same path twice, we always use the latest one + if (isSamePath) { entries[0] = newEntry; } else { entries = [newEntry, ...entries]; diff --git a/public/app/core/components/AppChrome/History/HistoryContainer.tsx b/public/app/core/components/AppChrome/History/HistoryContainer.tsx index 5d8482a3697..917be510f45 100644 --- a/public/app/core/components/AppChrome/History/HistoryContainer.tsx +++ b/public/app/core/components/AppChrome/History/HistoryContainer.tsx @@ -60,7 +60,7 @@ export function HistoryContainer() { onToggleShowHistoryDrawer(false)} /> diff --git a/public/app/core/components/AppChrome/History/HistoryWrapper.tsx b/public/app/core/components/AppChrome/History/HistoryWrapper.tsx index 687f631fd8c..d1a873b61cc 100644 --- a/public/app/core/components/AppChrome/History/HistoryWrapper.tsx +++ b/public/app/core/components/AppChrome/History/HistoryWrapper.tsx @@ -1,9 +1,9 @@ -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import moment from 'moment'; import { useState } from 'react'; import { FieldType, GrafanaTheme2, store } from '@grafana/data'; -import { Button, Card, IconButton, Space, Stack, Text, useStyles2, Box, Sparkline, useTheme2 } from '@grafana/ui'; +import { Button, Card, IconButton, Space, Stack, Text, useStyles2, Box, Sparkline, useTheme2, Icon } from '@grafana/ui'; import { t } from 'app/core/internationalization'; import { HISTORY_LOCAL_STORAGE_KEY } from '../AppChromeService'; @@ -32,34 +32,38 @@ export function HistoryWrapper({ onClose }: { onClose: () => void }) { acc[key] = [...(acc[key] || []), entry]; return acc; }, {}); - + const styles = useStyles2(getStyles); return ( {Object.keys(hist).map((entries, date) => { return ( - - {entries} - - {hist[entries].map((entry, index) => { - return ( - onClose()} - /> - ); - })} + + {entries} + +
+ {hist[entries].map((entry, index) => { + return ( + onClose()} + /> + ); + })} +
); })}
{history.length > numItemsToShow && ( - + + + )}
); @@ -78,7 +82,9 @@ function HistoryEntryAppView({ entry, isSelected, onClick }: ItemProps) { const expandedLabel = isExpanded ? t('nav.history-wrapper.collapse', 'Collapse') : t('nav.history-wrapper.expand', 'Expand'); - + const entryIconLabel = isExpanded + ? t('nav.history-wrapper.icon-selected', 'Selected Entry') + : t('nav.history-wrapper.icon-unselected', 'Normal Entry'); const selectedViewTime = isSelected && entry.views.find((entry) => { @@ -86,119 +92,181 @@ function HistoryEntryAppView({ entry, isSelected, onClick }: ItemProps) { })?.time; return ( - - - {views.length > 0 ? ( - setIsExpanded(!isExpanded)} - aria-label={expandedLabel} - className={styles.iconButton} + + + + {views.length > 0 ? ( + setIsExpanded(!isExpanded)} + aria-label={expandedLabel} + className={styles.iconButton} + /> + ) : ( + + )} + - ) : ( - - )} - - { - store.setObject('CLICKING_HISTORY', true); - onClick(); - }} - href={url} - isCompact={true} - className={isSelected ? undefined : styles.card} - > - -
- {breadcrumbs.map((breadcrumb, index) => ( - - {breadcrumb.text} {index !== breadcrumbs.length - 1 ? '> ' : ''} - - ))} -
- {moment(time).format('h:mm A')} - {sparklineData && ( - { + store.setObject('CLICKING_HISTORY', true); + onClick(); + }} + href={url} + isCompact={true} + className={isSelected ? styles.card : cx(styles.card, styles.cardSelected)} + > + +
+ {breadcrumbs.map((breadcrumb, index) => ( + + {breadcrumb.text} {index !== breadcrumbs.length - 1 ? '> ' : ''} + + ))} +
+ + {moment(time).format('h:mm A')} + + {sparklineData && ( + - )} -
-
+ }} + /> + )} +
+ +
+ {isExpanded && ( +
+ {views.map((view, index) => { + return ( + { + store.setObject('CLICKING_HISTORY', true); + onClick(); + }} + isCompact={true} + className={view.time === selectedViewTime ? undefined : styles.subCard} + > + + {view.name} + {view.description && ( + + {view.description} + + )} + + + ); + })} +
+ )}
- {isExpanded && ( -
- {views.map((view, index) => { - return ( - { - store.setObject('CLICKING_HISTORY', true); - onClick(); - }} - isCompact={true} - className={view.time === selectedViewTime ? undefined : styles.card} - > - - {view.name} - {view.description && ( - - {view.description} - - )} - - - ); - })} -
- )} -
+ ); } const getStyles = (theme: GrafanaTheme2) => { return { card: css({ + label: 'card', + background: 'none', + margin: theme.spacing(0.5, 0), + }), + cardSelected: css({ + label: 'card-selected', background: 'none', }), - iconButton: css({ + subCard: css({ + label: 'subcard', + background: 'none', margin: 0, }), + iconButton: css({ + label: 'expand-button', + margin: 0, + }), + iconButtonCircle: css({ + label: 'blue-circle-icon', + margin: 0, + background: theme.colors.background.primary, + fill: theme.colors.primary.main, + cursor: 'default', + '&:hover:before': { + background: 'none', + }, + //Need this to place the icon on the line, otherwise the line will appear on top of the icon + zIndex: 0, + }), + iconButtonDot: css({ + label: 'blue-dot-icon', + margin: 0, + color: theme.colors.primary.main, + border: theme.shape.radius.circle, + cursor: 'default', + '&:hover:before': { + background: 'none', + }, + //Need this to place the icon on the line, otherwise the line will appear on top of the icon + zIndex: 0, + }), expanded: css({ + label: 'expanded', display: 'flex', flexDirection: 'column', - marginLeft: theme.spacing(5), + marginLeft: theme.spacing(6), gap: theme.spacing(1), position: 'relative', '&:before': { content: '""', position: 'absolute', - left: theme.spacing(-2), + left: 0, top: 0, height: '100%', width: '1px', background: theme.colors.border.weak, }, }), + timeline: css({ + label: 'timeline', + position: 'relative', + height: '100%', + width: '100%', + paddingLeft: theme.spacing(2), + '&:before': { + content: '""', + position: 'absolute', + left: theme.spacing(5.75), + top: 0, + height: '100%', + width: '1px', + borderLeft: `1px dashed ${theme.colors.border.strong}`, + }, + }), }; }; diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 1416699e4d5..e80a59d9129 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -2536,6 +2536,8 @@ "history-wrapper": { "collapse": "Collapse", "expand": "Expand", + "icon-selected": "Selected Entry", + "icon-unselected": "Normal Entry", "show-more": "Show more", "today": "Today", "yesterday": "Yesterday" diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 39128a2cf53..43096c5ce25 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -2536,6 +2536,8 @@ "history-wrapper": { "collapse": "Cőľľäpşę", "expand": "Ēχpäʼnđ", + "icon-selected": "Ŝęľęčŧęđ Ēʼnŧřy", + "icon-unselected": "Ńőřmäľ Ēʼnŧřy", "show-more": "Ŝĥőŵ mőřę", "today": "Ŧőđäy", "yesterday": "Ÿęşŧęřđäy"