|
|
@ -2,23 +2,25 @@ import React, { FC, useCallback } from 'react'; |
|
|
|
import { css, cx } from '@emotion/css'; |
|
|
|
import { css, cx } from '@emotion/css'; |
|
|
|
import { useLocalStorage } from 'react-use'; |
|
|
|
import { useLocalStorage } from 'react-use'; |
|
|
|
import { GrafanaTheme } from '@grafana/data'; |
|
|
|
import { GrafanaTheme } from '@grafana/data'; |
|
|
|
import { selectors } from '@grafana/e2e-selectors'; |
|
|
|
import { CollapsableSection, Icon, stylesFactory, useTheme } from '@grafana/ui'; |
|
|
|
import { Icon, Spinner, stylesFactory, useTheme } from '@grafana/ui'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { DashboardSection, OnToggleChecked } from '../types'; |
|
|
|
import { DashboardSection, OnToggleChecked } from '../types'; |
|
|
|
import { SearchCheckbox } from './SearchCheckbox'; |
|
|
|
import { SearchCheckbox } from './SearchCheckbox'; |
|
|
|
import { getSectionIcon, getSectionStorageKey } from '../utils'; |
|
|
|
import { getSectionIcon, getSectionStorageKey } from '../utils'; |
|
|
|
|
|
|
|
import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId'; |
|
|
|
|
|
|
|
|
|
|
|
interface SectionHeaderProps { |
|
|
|
interface SectionHeaderProps { |
|
|
|
editable?: boolean; |
|
|
|
editable?: boolean; |
|
|
|
onSectionClick: (section: DashboardSection) => void; |
|
|
|
onSectionClick: (section: DashboardSection) => void; |
|
|
|
onToggleChecked?: OnToggleChecked; |
|
|
|
onToggleChecked?: OnToggleChecked; |
|
|
|
section: DashboardSection; |
|
|
|
section: DashboardSection; |
|
|
|
|
|
|
|
children: React.ReactNode; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const SectionHeader: FC<SectionHeaderProps> = ({ |
|
|
|
export const SectionHeader: FC<SectionHeaderProps> = ({ |
|
|
|
section, |
|
|
|
section, |
|
|
|
onSectionClick, |
|
|
|
onSectionClick, |
|
|
|
|
|
|
|
children, |
|
|
|
onToggleChecked, |
|
|
|
onToggleChecked, |
|
|
|
editable = false, |
|
|
|
editable = false, |
|
|
|
}) => { |
|
|
|
}) => { |
|
|
@ -33,49 +35,52 @@ export const SectionHeader: FC<SectionHeaderProps> = ({ |
|
|
|
|
|
|
|
|
|
|
|
const handleCheckboxClick = useCallback( |
|
|
|
const handleCheckboxClick = useCallback( |
|
|
|
(ev: React.MouseEvent) => { |
|
|
|
(ev: React.MouseEvent) => { |
|
|
|
console.log('section header handleCheckboxClick'); |
|
|
|
|
|
|
|
ev.stopPropagation(); |
|
|
|
ev.stopPropagation(); |
|
|
|
ev.preventDefault(); |
|
|
|
ev.preventDefault(); |
|
|
|
|
|
|
|
|
|
|
|
if (onToggleChecked) { |
|
|
|
onToggleChecked?.(section); |
|
|
|
onToggleChecked(section); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
[onToggleChecked, section] |
|
|
|
[onToggleChecked, section] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const id = useUniqueId(); |
|
|
|
|
|
|
|
const labelId = `section-header-label-${id}`; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div |
|
|
|
<CollapsableSection |
|
|
|
|
|
|
|
isOpen={section.expanded ?? false} |
|
|
|
|
|
|
|
onToggle={onSectionExpand} |
|
|
|
className={styles.wrapper} |
|
|
|
className={styles.wrapper} |
|
|
|
onClick={onSectionExpand} |
|
|
|
contentClassName={styles.content} |
|
|
|
data-testid={ |
|
|
|
loading={section.itemsFetching} |
|
|
|
section.expanded |
|
|
|
labelId={labelId} |
|
|
|
? selectors.components.Search.collapseFolder(section.id?.toString()) |
|
|
|
label={ |
|
|
|
: selectors.components.Search.expandFolder(section.id?.toString()) |
|
|
|
<> |
|
|
|
} |
|
|
|
<SearchCheckbox |
|
|
|
> |
|
|
|
className={styles.checkbox} |
|
|
|
<SearchCheckbox |
|
|
|
editable={editable} |
|
|
|
className={styles.checkbox} |
|
|
|
checked={section.checked} |
|
|
|
editable={editable} |
|
|
|
onClick={handleCheckboxClick} |
|
|
|
checked={section.checked} |
|
|
|
aria-label="Select folder" |
|
|
|
onClick={handleCheckboxClick} |
|
|
|
/> |
|
|
|
aria-label="Select folder" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.icon}> |
|
|
|
<div className={styles.icon}> |
|
|
|
<Icon name={getSectionIcon(section)} /> |
|
|
|
<Icon name={getSectionIcon(section)} /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.text}> |
|
|
|
<div className={styles.text}> |
|
|
|
{section.title} |
|
|
|
<span id={labelId}>{section.title}</span> |
|
|
|
{section.url && ( |
|
|
|
{section.url && ( |
|
|
|
<a href={section.url} className={styles.link}> |
|
|
|
<a href={section.url} className={styles.link}> |
|
|
|
<span className={styles.separator}>|</span> <Icon name="folder-upload" /> Go to folder |
|
|
|
<span className={styles.separator}>|</span> <Icon name="folder-upload" /> Go to folder |
|
|
|
</a> |
|
|
|
</a> |
|
|
|
)} |
|
|
|
)} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
{section.itemsFetching ? <Spinner /> : <Icon name={section.expanded ? 'angle-down' : 'angle-right'} />} |
|
|
|
</> |
|
|
|
</div> |
|
|
|
} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{children} |
|
|
|
|
|
|
|
</CollapsableSection> |
|
|
|
); |
|
|
|
); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -84,18 +89,21 @@ const getSectionHeaderStyles = stylesFactory((theme: GrafanaTheme, selected = fa |
|
|
|
return { |
|
|
|
return { |
|
|
|
wrapper: cx( |
|
|
|
wrapper: cx( |
|
|
|
css` |
|
|
|
css` |
|
|
|
display: flex; |
|
|
|
|
|
|
|
align-items: center; |
|
|
|
align-items: center; |
|
|
|
font-size: ${theme.typography.size.base}; |
|
|
|
font-size: ${theme.typography.size.base}; |
|
|
|
padding: 12px; |
|
|
|
padding: 12px; |
|
|
|
|
|
|
|
border-bottom: none; |
|
|
|
color: ${theme.colors.textWeak}; |
|
|
|
color: ${theme.colors.textWeak}; |
|
|
|
|
|
|
|
z-index: 1; |
|
|
|
|
|
|
|
|
|
|
|
&:hover, |
|
|
|
&:hover, |
|
|
|
&.selected { |
|
|
|
&.selected { |
|
|
|
color: ${theme.colors.text}; |
|
|
|
color: ${theme.colors.text}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&:hover { |
|
|
|
&:hover, |
|
|
|
|
|
|
|
&:focus-visible, |
|
|
|
|
|
|
|
&:focus-within { |
|
|
|
a { |
|
|
|
a { |
|
|
|
opacity: 1; |
|
|
|
opacity: 1; |
|
|
|
} |
|
|
|
} |
|
|
@ -123,5 +131,9 @@ const getSectionHeaderStyles = stylesFactory((theme: GrafanaTheme, selected = fa |
|
|
|
separator: css` |
|
|
|
separator: css` |
|
|
|
margin-right: 6px; |
|
|
|
margin-right: 6px; |
|
|
|
`,
|
|
|
|
`,
|
|
|
|
|
|
|
|
content: css` |
|
|
|
|
|
|
|
padding-top: 0px; |
|
|
|
|
|
|
|
padding-bottom: 0px; |
|
|
|
|
|
|
|
`,
|
|
|
|
}; |
|
|
|
}; |
|
|
|
}); |
|
|
|
}); |
|
|
|