Provisioning: Collapse repository status messages (#113666)

* Provisioning: Add collapsible pull status messages

* Use text styles

* Add animation

* Fix error message

* cleanup

* a11y

* more a11y
This commit is contained in:
Alex Khomenko
2025-11-12 12:21:58 +00:00
committed by GitHub
parent e463781077
commit 4eca53890f
4 changed files with 90 additions and 10 deletions
@@ -112,7 +112,7 @@ function ExpandedRow({ row }: ExpandedRowProps) {
const isValidState = state && ['success', 'warning', 'error'].includes(state);
const alertProps = isValidState
? {
[state]: { message: row.status?.errors },
[state]: { message: row.status?.message },
}
: null;
@@ -1,5 +1,6 @@
import { css, cx } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data/';
import { t, Trans } from '@grafana/i18n';
import { Badge, Card, Grid, Text, TextLink, useStyles2 } from '@grafana/ui';
import { Repository } from 'app/api/clients/provisioning/v0alpha1';
@@ -44,7 +45,10 @@ export function RepositoryPullStatusCard({ repo }: { repo: Repository }) {
<Text variant="body">{status?.sync.job ?? 'N/A'}</Text>
</div>
<div className={cx(styles.historicalData, { [styles.historicalDataOverlay]: isWorking })}>
<div
className={cx(styles.historicalData, { [styles.historicalDataOverlay]: isWorking })}
aria-busy={isWorking}
>
{/* Last Ref */}
<Text color="secondary">
<Trans i18nKey="provisioning.repository-overview.last-ref">Last Ref:</Trans>
@@ -92,7 +96,7 @@ export function RepositoryPullStatusCard({ repo }: { repo: Repository }) {
);
}
const getStyles = () => {
const getStyles = (theme: GrafanaTheme2) => {
return {
spanTwo: css({
gridColumn: 'span 2',
@@ -101,6 +105,7 @@ const getStyles = () => {
gridColumn: '1 / -1',
display: 'grid',
gridTemplateColumns: 'subgrid',
gap: theme.spacing(1),
}),
historicalDataOverlay: css({
opacity: 0.6,
@@ -1,7 +1,9 @@
import { css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Text, useStyles2 } from '@grafana/ui';
import { Trans } from '@grafana/i18n';
import { Box, Text, useStyles2 } from '@grafana/ui';
interface MessageListProps {
messages: string[];
@@ -10,19 +12,87 @@ interface MessageListProps {
export function MessageList({ messages, variant }: MessageListProps) {
const styles = useStyles2(getStyles);
const [showFull, setShowFull] = useState(false);
const hasMultipleMessages = messages.length > 1;
const handleExpand = () => {
setShowFull(true);
};
const handleCollapse = () => {
setShowFull(false);
};
const displayMessages = showFull ? messages : messages.slice(0, 1);
return (
<ul className={styles.messageList}>
{messages.map((msg, index) => (
<li key={index}>{variant ? <Text variant={variant}>{msg}</Text> : msg}</li>
))}
</ul>
<>
<div className={cx(styles.messageListWrapper, { [styles.messageListWrapperExpanded]: showFull })}>
<ul className={styles.messageList}>
{displayMessages.map((msg, index) => (
<li key={index}>
{variant ? <Text variant={variant}>{msg}</Text> : msg}
{!showFull && hasMultipleMessages && index === 0 && (
<>
{' '}
<span aria-hidden="true">… </span>
<span className="sr-only">
<Trans i18nKey="provisioning.message.truncated">Message truncated</Trans>
</span>
<button
type="button"
className={cx(styles.showMore, styles.showMoreInline)}
onClick={handleExpand}
aria-expanded={false}
>
<Trans i18nKey="provisioning.message.show-more">show more</Trans>
</button>
</>
)}
</li>
))}
</ul>
</div>
{showFull && hasMultipleMessages && (
<Box paddingLeft={3} paddingTop={0.5}>
<button type="button" className={styles.showMore} onClick={handleCollapse} aria-expanded={true}>
<Trans i18nKey="provisioning.message.show-less">show less</Trans>
</button>
</Box>
)}
</>
);
}
const getStyles = (theme: GrafanaTheme2) => ({
messageListWrapper: css({
overflow: 'hidden',
maxHeight: '200px',
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
transition: theme.transitions.create('max-height', {
duration: theme.transitions.duration.standard,
easing: theme.transitions.easing.easeInOut,
}),
},
}),
messageListWrapperExpanded: css({
maxHeight: '9999px',
}),
messageList: css({
margin: 0,
paddingLeft: theme.spacing(3),
listStyle: 'disc',
}),
showMore: css({
backgroundColor: 'transparent',
border: 'none',
padding: 0,
margin: 0,
textDecoration: 'underline',
cursor: 'pointer',
color: theme.colors.text.primary,
}),
showMoreInline: css({
marginLeft: theme.spacing(0.5),
}),
});
+5
View File
@@ -11763,6 +11763,11 @@
"path-label": "Repository Path",
"path-required": "Repository path is required"
},
"message": {
"show-less": "show less",
"show-more": "show more",
"truncated": "Message truncated"
},
"mode-options": {
"folder": {
"description": "After setup, a new Grafana folder will be created and synced with external storage. If any resources are present in external storage, they will be provisioned to this new folder. All new resources created in this folder will be stored and versioned in external storage.",