deprecate collapsible prop, improve Collapse to allow for buttons in the header
This commit is contained in:
@@ -111,7 +111,6 @@ export const QueryPatternsModal = (props: Props) => {
|
||||
}
|
||||
)}
|
||||
isOpen={isOpen}
|
||||
collapsible={true}
|
||||
onToggle={() => {
|
||||
const action = isOpen ? 'close' : 'open';
|
||||
reportInteraction(`grafana_prom_kickstart_toggle_pattern_card`, {
|
||||
|
||||
@@ -20,7 +20,6 @@ export function QueryOptionGroup({ title, children, collapsedInfo }: Props) {
|
||||
<div className={styles.wrapper}>
|
||||
<Collapse
|
||||
className={styles.collapse}
|
||||
collapsible
|
||||
isOpen={isOpen}
|
||||
onToggle={toggleOpen}
|
||||
label={
|
||||
|
||||
@@ -2,6 +2,9 @@ import { action } from '@storybook/addon-actions';
|
||||
import { useArgs } from '@storybook/preview-api';
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
|
||||
import { IconButton } from '../IconButton/IconButton';
|
||||
import { Stack } from '../Layout/Stack/Stack';
|
||||
|
||||
import { Collapse, ControlledCollapse } from './Collapse';
|
||||
import mdx from './Collapse.mdx';
|
||||
|
||||
@@ -22,7 +25,6 @@ const meta: Meta<typeof Collapse> = {
|
||||
children: 'Panel data',
|
||||
isOpen: false,
|
||||
label: 'Collapse panel',
|
||||
collapsible: true,
|
||||
},
|
||||
argTypes: {
|
||||
onToggle: { action: 'toggled' },
|
||||
@@ -57,4 +59,39 @@ Controlled.parameters = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomLabel: StoryFn<typeof Collapse> = (args) => {
|
||||
const [, updateArgs] = useArgs();
|
||||
return (
|
||||
<Collapse
|
||||
{...args}
|
||||
onToggle={() => {
|
||||
action('onToggle')({ isOpen: !args.isOpen });
|
||||
updateArgs({ isOpen: !args.isOpen });
|
||||
}}
|
||||
label={
|
||||
<Stack flex={1} alignItems="center" justifyContent="space-between">
|
||||
Collapse panel
|
||||
<Stack alignItems="center">
|
||||
<IconButton
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
action('onDeleteClick')();
|
||||
}}
|
||||
aria-label="Delete"
|
||||
name="trash-alt"
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<p>{args.children}</p>
|
||||
</Collapse>
|
||||
);
|
||||
};
|
||||
WithCustomLabel.parameters = {
|
||||
controls: {
|
||||
exclude: [...EXCLUDED_PROPS, 'label'],
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('Collapse', () => {
|
||||
const onToggle = jest.fn();
|
||||
|
||||
const { user } = setup(
|
||||
<Collapse label={TEST_LABEL} onToggle={onToggle} collapsible>
|
||||
<Collapse label={TEST_LABEL} onToggle={onToggle}>
|
||||
<div>{contentText}</div>
|
||||
</Collapse>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { useState } from 'react';
|
||||
import { useId, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
|
||||
import { useStyles2 } from '../../themes/ThemeContext';
|
||||
import { clearButtonStyles } from '../Button/Button';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { IconButton } from '../IconButton/IconButton';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
collapse: css({
|
||||
@@ -74,25 +74,21 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
},
|
||||
}),
|
||||
header: css({
|
||||
cursor: 'pointer',
|
||||
label: 'collapse__header',
|
||||
padding: theme.spacing(1, 2, 1, 2),
|
||||
padding: theme.spacing(1),
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
headerCollapsed: css({
|
||||
label: 'collapse__header--collapsed',
|
||||
padding: theme.spacing(1, 2, 1, 2),
|
||||
button: css({
|
||||
marginRight: 0,
|
||||
}),
|
||||
headerLabel: css({
|
||||
label: 'collapse__header-label',
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
marginRight: theme.spacing(1),
|
||||
fontSize: theme.typography.size.md,
|
||||
display: 'flex',
|
||||
flex: '0 0 100%',
|
||||
}),
|
||||
icon: css({
|
||||
label: 'collapse__icon',
|
||||
margin: theme.spacing(0.25, 1, 0, -1),
|
||||
flex: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -103,12 +99,12 @@ export interface Props {
|
||||
label: React.ReactNode;
|
||||
/** Indicates loading state of the content */
|
||||
loading?: boolean;
|
||||
/** Toggle collapsed header icon */
|
||||
collapsible?: boolean;
|
||||
/** Callback for the toggle functionality */
|
||||
onToggle?: (isOpen: boolean) => void;
|
||||
/** Additional class name for the root element */
|
||||
className?: string;
|
||||
/** @deprecated this prop is no longer used and will be removed in Grafana 13 */
|
||||
collapsible?: boolean;
|
||||
}
|
||||
|
||||
export const ControlledCollapse = ({ isOpen, onToggle, ...otherProps }: React.PropsWithChildren<Props>) => {
|
||||
@@ -116,7 +112,6 @@ export const ControlledCollapse = ({ isOpen, onToggle, ...otherProps }: React.Pr
|
||||
return (
|
||||
<Collapse
|
||||
isOpen={open}
|
||||
collapsible
|
||||
{...otherProps}
|
||||
onToggle={() => {
|
||||
setOpen(!open);
|
||||
@@ -128,35 +123,39 @@ export const ControlledCollapse = ({ isOpen, onToggle, ...otherProps }: React.Pr
|
||||
);
|
||||
};
|
||||
|
||||
export const Collapse = ({
|
||||
isOpen,
|
||||
label,
|
||||
loading,
|
||||
collapsible,
|
||||
onToggle,
|
||||
className,
|
||||
children,
|
||||
}: React.PropsWithChildren<Props>) => {
|
||||
const buttonStyles = useStyles2(clearButtonStyles);
|
||||
export const Collapse = ({ isOpen, label, loading, onToggle, className, children }: React.PropsWithChildren<Props>) => {
|
||||
const style = useStyles2(getStyles);
|
||||
const labelId = useId();
|
||||
|
||||
const onClickToggle = () => {
|
||||
if (onToggle) {
|
||||
onToggle(!isOpen);
|
||||
}
|
||||
};
|
||||
|
||||
const panelClass = cx([style.collapse, className]);
|
||||
const loaderClass = loading ? cx([style.loader, style.loaderActive]) : cx([style.loader]);
|
||||
const headerClass = collapsible ? cx([style.header]) : cx([style.headerCollapsed]);
|
||||
const loaderClass = loading ? cx([style.loader, style.loaderActive]) : style.loader;
|
||||
|
||||
return (
|
||||
<div className={panelClass}>
|
||||
<button type="button" className={cx(buttonStyles, headerClass)} onClick={onClickToggle}>
|
||||
{collapsible && <Icon className={style.icon} name={isOpen ? 'angle-down' : 'angle-right'} />}
|
||||
<div className={cx([style.headerLabel])}>{label}</div>
|
||||
</button>
|
||||
{/* the inner button handles keyboard a11y. this is a convenience for mouse users */}
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
|
||||
<div className={style.header} onClick={onClickToggle}>
|
||||
<IconButton
|
||||
aria-describedby={labelId}
|
||||
className={style.button}
|
||||
aria-label={
|
||||
isOpen
|
||||
? t('grafana-ui.collapse.aria-label-collapse', 'Collapse panel')
|
||||
: t('grafana-ui.collapse.aria-label-expand', 'Expand panel')
|
||||
}
|
||||
name={isOpen ? 'angle-down' : 'angle-right'}
|
||||
/>
|
||||
<div id={labelId} className={style.headerLabel}>
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div className={cx([style.collapseBody])}>
|
||||
<div className={style.collapseBody}>
|
||||
<div className={loaderClass} />
|
||||
<div className={style.bodyContentWrapper}>{children}</div>
|
||||
</div>
|
||||
|
||||
@@ -385,7 +385,6 @@ function TargetFolderNotEmptyWarning({ targetFolderRules }: { targetFolderRules:
|
||||
)}
|
||||
isOpen={showTargetRules}
|
||||
onToggle={toggleShowTargetRules}
|
||||
collapsible={true}
|
||||
>
|
||||
<RulesPreview rules={targetFolderRules} />
|
||||
</Collapse>
|
||||
|
||||
@@ -150,7 +150,6 @@ const ImportToGMARules = () => {
|
||||
label={t('alerting.import-to-gma.additional-settings', 'Additional settings')}
|
||||
isOpen={optionsShowing}
|
||||
onToggle={toggleOptions}
|
||||
collapsible={true}
|
||||
>
|
||||
<Box marginLeft={1}>
|
||||
<Box marginBottom={2}>
|
||||
|
||||
-1
@@ -130,7 +130,6 @@ export const AmRootRouteForm = ({ actionButtons, alertManagerSourceName, onSubmi
|
||||
/>
|
||||
</Field>
|
||||
<Collapse
|
||||
collapsible
|
||||
className={styles.collapse}
|
||||
isOpen={isTimingOptionsExpanded}
|
||||
label={t('alerting.am-root-route-form.label-timing-options', 'Timing options')}
|
||||
|
||||
@@ -171,7 +171,6 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
|
||||
})}
|
||||
</pre>
|
||||
<Collapse
|
||||
collapsible
|
||||
isOpen={isLabelDescOpen}
|
||||
onToggle={() => {
|
||||
setIsLabelDescOpen(!isLabelDescOpen);
|
||||
@@ -201,7 +200,6 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
|
||||
</Field>
|
||||
</Collapse>
|
||||
<Collapse
|
||||
collapsible
|
||||
isOpen={isTransformOpen}
|
||||
onToggle={() => {
|
||||
setIsTransformOpen(!isTransformOpen);
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { t } from '@grafana/i18n';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { Collapse } from '@grafana/ui';
|
||||
import { PanelChrome } from '@grafana/ui';
|
||||
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
|
||||
import { GetFieldLinksFn } from 'app/plugins/panel/logs/types';
|
||||
import { ExploreItemState } from 'app/types/explore';
|
||||
@@ -308,8 +308,8 @@ class LogsContainer extends PureComponent<LogsContainerProps, LogsContainerState
|
||||
|
||||
return (
|
||||
<>
|
||||
<LogsCrossFadeTransition visible={isLive}>
|
||||
<Collapse label={t('explore.logs-container.label-logs', 'Logs')} loading={false} isOpen>
|
||||
<LogsCrossFadeTransition visible={!isLive}>
|
||||
<PanelChrome title={t('explore.logs-container.label-logs', 'Logs')}>
|
||||
<LiveTailControls exploreId={exploreId}>
|
||||
{(controls) => (
|
||||
<LiveLogsWithTheme
|
||||
@@ -324,9 +324,9 @@ class LogsContainer extends PureComponent<LogsContainerProps, LogsContainerState
|
||||
/>
|
||||
)}
|
||||
</LiveTailControls>
|
||||
</Collapse>
|
||||
</PanelChrome>
|
||||
</LogsCrossFadeTransition>
|
||||
<LogsCrossFadeTransition visible={!isLive}>
|
||||
<LogsCrossFadeTransition visible={isLive}>
|
||||
<Logs
|
||||
exploreId={exploreId}
|
||||
datasourceType={this.props.datasourceInstance?.type}
|
||||
|
||||
@@ -156,7 +156,6 @@ export function LogsSamplePanel(props: Props) {
|
||||
</div>
|
||||
}
|
||||
isOpen={enabled}
|
||||
collapsible={true}
|
||||
onToggle={onToggleLogsSampleCollapse}
|
||||
>
|
||||
<OpenInSplitViewButton />
|
||||
|
||||
+1
-1
@@ -151,7 +151,7 @@ export const SpanFilters = memo((props: SpanFilterProps) => {
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Collapse label={collapseLabel} collapsible={true} isOpen={showSpanFilters} onToggle={setShowSpanFilters}>
|
||||
<Collapse label={collapseLabel} isOpen={showSpanFilters} onToggle={setShowSpanFilters}>
|
||||
<InlineFieldRow className={styles.flexContainer}>
|
||||
<InlineField label={t('explore.span-filters.label-service-name', 'Service name')} labelWidth={16}>
|
||||
<Stack gap={0.5}>
|
||||
|
||||
@@ -329,7 +329,6 @@ export const LogLineContext = memo(
|
||||
<div>{getLogRowContextUi(log, updateResults)}</div>
|
||||
)}
|
||||
<Collapse
|
||||
collapsible={true}
|
||||
isOpen={showLog}
|
||||
onToggle={() => setShowLog(!showLog)}
|
||||
className={styles.referenceLogLine}
|
||||
|
||||
@@ -131,7 +131,6 @@ export const LogLineDetailsComponent = memo(
|
||||
<ControlledCollapse
|
||||
className={styles.collapsable}
|
||||
label={t('logs.log-line-details.log-line-section', 'Log line')}
|
||||
collapsible
|
||||
isOpen={logLineOpen}
|
||||
onToggle={(isOpen: boolean) => handleToggle('logLineOpen', isOpen)}
|
||||
>
|
||||
@@ -140,7 +139,6 @@ export const LogLineDetailsComponent = memo(
|
||||
{displayedFields.length > 0 && setDisplayedFields && (
|
||||
<ControlledCollapse
|
||||
label={t('logs.log-line-details.displayed-fields-section', 'Organize displayed fields')}
|
||||
collapsible
|
||||
isOpen={displayedFieldsOpen}
|
||||
onToggle={(isOpen: boolean) => handleToggle('displayedFieldsOpen', isOpen)}
|
||||
>
|
||||
@@ -151,7 +149,6 @@ export const LogLineDetailsComponent = memo(
|
||||
<ControlledCollapse
|
||||
className={styles.collapsable}
|
||||
label={t('logs.log-line-details.links-section', 'Links')}
|
||||
collapsible
|
||||
isOpen={linksOpen}
|
||||
onToggle={(isOpen: boolean) => handleToggle('linksOpen', isOpen)}
|
||||
>
|
||||
@@ -161,7 +158,6 @@ export const LogLineDetailsComponent = memo(
|
||||
{trace && (
|
||||
<ControlledCollapse
|
||||
label={t('logs.log-line-details.trace-section', 'Trace')}
|
||||
collapsible
|
||||
isOpen={traceOpen}
|
||||
onToggle={(isOpen: boolean) => handleToggle('traceOpen', isOpen)}
|
||||
>
|
||||
@@ -174,7 +170,6 @@ export const LogLineDetailsComponent = memo(
|
||||
className={styles.collapsable}
|
||||
key={'fields'}
|
||||
label={t('logs.log-line-details.fields-section', 'Fields')}
|
||||
collapsible
|
||||
isOpen={fieldsOpen}
|
||||
onToggle={(isOpen: boolean) => handleToggle('fieldsOpen', isOpen)}
|
||||
>
|
||||
@@ -186,7 +181,6 @@ export const LogLineDetailsComponent = memo(
|
||||
className={styles.collapsable}
|
||||
key={group}
|
||||
label={group}
|
||||
collapsible
|
||||
isOpen={store.getBool(`${logOptionsStorageKey}.log-details.${groupOptionName(group)}`, true)}
|
||||
onToggle={(isOpen: boolean) => handleToggle(groupOptionName(group), isOpen)}
|
||||
>
|
||||
@@ -199,7 +193,6 @@ export const LogLineDetailsComponent = memo(
|
||||
className={styles.collapsable}
|
||||
key={'fields'}
|
||||
label={t('logs.log-line-details.fields-section', 'Fields')}
|
||||
collapsible
|
||||
isOpen={fieldsOpen}
|
||||
onToggle={(isOpen: boolean) => handleToggle('fieldsOpen', isOpen)}
|
||||
>
|
||||
|
||||
@@ -38,7 +38,6 @@ export const DataHoverRows = ({ layers, activeTabIndex }: Props) => {
|
||||
return shouldDisplayCollapse ? (
|
||||
<Collapse
|
||||
key={key}
|
||||
collapsible
|
||||
label={generateLabel(feature, idx)}
|
||||
isOpen={rowMap.get(key)}
|
||||
onToggle={() => {
|
||||
|
||||
-1
@@ -186,7 +186,6 @@ const AzureCheatSheet = (props: AzureCheatSheetProps) => {
|
||||
return (
|
||||
<Collapse
|
||||
label={category + ' ' + `(${visibleQueries[category]!.length})`}
|
||||
collapsible={true}
|
||||
isOpen={areDropdownsOpen[category]}
|
||||
onToggle={(isOpen) => setAreDropdownsOpen({ ...areDropdownsOpen, [category]: isOpen })}
|
||||
key={category}
|
||||
|
||||
@@ -19,7 +19,6 @@ const AdvancedMulti = ({ resources, onChange, renderAdvanced }: ResourcePickerPr
|
||||
return (
|
||||
<div data-testid={selectors.components.queryEditor.resourcePicker.advanced.collapse}>
|
||||
<Collapse
|
||||
collapsible
|
||||
label={t('components.advanced-multi.label-advanced', 'Advanced')}
|
||||
isOpen={isAdvancedOpen}
|
||||
onToggle={() => setIsAdvancedOpen(!isAdvancedOpen)}
|
||||
|
||||
@@ -80,7 +80,7 @@ interface CollapseProps {
|
||||
const CheatSheetCollapse = (props: CollapseProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
return (
|
||||
<Collapse label={props.label} isOpen={isOpen} onToggle={setIsOpen} key={props.key} collapsible>
|
||||
<Collapse label={props.label} isOpen={isOpen} onToggle={setIsOpen} key={props.key}>
|
||||
{props.children}
|
||||
</Collapse>
|
||||
);
|
||||
|
||||
+1
-1
@@ -113,7 +113,7 @@ export const PostgresConfigEditor = (props: DataSourcePluginOptionsEditorProps<P
|
||||
|
||||
<Divider />
|
||||
|
||||
<Collapse collapsible label="User Permissions" isOpen={isOpen} onToggle={() => setIsOpen((x) => !x)}>
|
||||
<Collapse label="User Permissions" isOpen={isOpen} onToggle={() => setIsOpen((x) => !x)}>
|
||||
The database user should only be granted SELECT permissions on the specified database & tables you want to
|
||||
query. <br />
|
||||
Grafana does not validate that queries are safe so queries can contain any SQL statement. For example,
|
||||
|
||||
-1
@@ -19,7 +19,6 @@ export function QueryOptionGroup({ title, children, collapsedInfo }: Props) {
|
||||
<div className={styles.wrapper}>
|
||||
<Collapse
|
||||
className={styles.collapse}
|
||||
collapsible
|
||||
isOpen={isOpen}
|
||||
onToggle={toggleOpen}
|
||||
label={
|
||||
|
||||
@@ -296,7 +296,6 @@ export function LokiContextUi(props: LokiContextUiProps) {
|
||||
</div>
|
||||
|
||||
<Collapse
|
||||
collapsible={true}
|
||||
isOpen={isOpen}
|
||||
onToggle={() => {
|
||||
window.localStorage.setItem(IS_LOKI_LOG_CONTEXT_UI_OPEN, (!isOpen).toString());
|
||||
|
||||
@@ -99,7 +99,6 @@ export const QueryPatternsModal = (props: Props) => {
|
||||
key={patternType}
|
||||
label={`${capitalize(patternType)} query starters`}
|
||||
isOpen={openTabs.includes(patternType)}
|
||||
collapsible={true}
|
||||
onToggle={() =>
|
||||
setOpenTabs((tabs) =>
|
||||
// close tab if it's already open, otherwise open it
|
||||
|
||||
@@ -60,7 +60,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
|
||||
|
||||
<Divider />
|
||||
|
||||
<Collapse collapsible label="User Permission" isOpen={isOpen} onToggle={() => setIsOpen((x) => !x)}>
|
||||
<Collapse label="User Permission" isOpen={isOpen} onToggle={() => setIsOpen((x) => !x)}>
|
||||
The database user should only be granted SELECT permissions on the specified database & tables you want to
|
||||
query. <br />
|
||||
Grafana does not validate that queries are safe so queries can contain any SQL statement. For example,
|
||||
|
||||
-1
@@ -25,7 +25,6 @@ export function QueryOptionGroup({ title, children, collapsedInfo, queryStats, o
|
||||
<div className={styles.wrapper}>
|
||||
<Collapse
|
||||
className={styles.collapse}
|
||||
collapsible
|
||||
isOpen={propsIsOpen ?? isOpen}
|
||||
onToggle={onToggle ?? toggleOpen}
|
||||
label={
|
||||
|
||||
Reference in New Issue
Block a user