mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tab: Only make Tab
an anchor if a href
is passed (#78540)
* initial work * only make the tab an anchor if it has a href * move things around for smaller diff * use content() * eslint-disable the type assertions * extract props into common object and add missing return statement
This commit is contained in:
parent
778841cabe
commit
5355131aed
@ -7,26 +7,29 @@ import { selectors } from '@grafana/e2e-selectors';
|
|||||||
import { useStyles2 } from '../../themes';
|
import { useStyles2 } from '../../themes';
|
||||||
import { getFocusStyles } from '../../themes/mixins';
|
import { getFocusStyles } from '../../themes/mixins';
|
||||||
import { IconName } from '../../types';
|
import { IconName } from '../../types';
|
||||||
|
import { clearButtonStyles } from '../Button';
|
||||||
import { Icon } from '../Icon/Icon';
|
import { Icon } from '../Icon/Icon';
|
||||||
|
|
||||||
import { Counter } from './Counter';
|
import { Counter } from './Counter';
|
||||||
|
|
||||||
export interface TabProps extends HTMLProps<HTMLAnchorElement> {
|
export interface TabProps extends HTMLProps<HTMLElement> {
|
||||||
label: string;
|
label: string;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
/** When provided, it is possible to use the tab as a hyperlink. Use in cases where the tabs update location. */
|
/** When provided, it is possible to use the tab as a hyperlink. Use in cases where the tabs update location. */
|
||||||
href?: string;
|
href?: string;
|
||||||
icon?: IconName;
|
icon?: IconName;
|
||||||
onChangeTab?: (event?: React.MouseEvent<HTMLAnchorElement>) => void;
|
onChangeTab?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||||
/** A number rendered next to the text. Usually used to display the number of items in a tab's view. */
|
/** A number rendered next to the text. Usually used to display the number of items in a tab's view. */
|
||||||
counter?: number | null;
|
counter?: number | null;
|
||||||
/** Extra content, displayed after the tab label and counter */
|
/** Extra content, displayed after the tab label and counter */
|
||||||
suffix?: NavModelItem['tabSuffix'];
|
suffix?: NavModelItem['tabSuffix'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Tab = React.forwardRef<HTMLAnchorElement, TabProps>(
|
export const Tab = React.forwardRef<HTMLElement, TabProps>(
|
||||||
({ label, active, icon, onChangeTab, counter, suffix: Suffix, className, href, ...otherProps }, ref) => {
|
({ label, active, icon, onChangeTab, counter, suffix: Suffix, className, href, ...otherProps }, ref) => {
|
||||||
const tabsStyles = useStyles2(getStyles);
|
const tabsStyles = useStyles2(getStyles);
|
||||||
|
const clearStyles = useStyles2(clearButtonStyles);
|
||||||
|
|
||||||
const content = () => (
|
const content = () => (
|
||||||
<>
|
<>
|
||||||
{icon && <Icon name={icon} />}
|
{icon && <Icon name={icon} />}
|
||||||
@ -36,26 +39,47 @@ export const Tab = React.forwardRef<HTMLAnchorElement, TabProps>(
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const linkClass = cx(tabsStyles.link, active ? tabsStyles.activeStyle : tabsStyles.notActive);
|
const linkClass = cx(clearStyles, tabsStyles.link, active ? tabsStyles.activeStyle : tabsStyles.notActive);
|
||||||
|
|
||||||
|
const commonProps = {
|
||||||
|
className: linkClass,
|
||||||
|
...otherProps,
|
||||||
|
onClick: onChangeTab,
|
||||||
|
'aria-label': otherProps['aria-label'] || selectors.components.Tab.title(label),
|
||||||
|
role: 'tab',
|
||||||
|
'aria-selected': active,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (href) {
|
||||||
return (
|
return (
|
||||||
<div className={tabsStyles.item}>
|
<div className={tabsStyles.item}>
|
||||||
<a
|
<a
|
||||||
// in case there is no href '#' is set in order to maintain a11y
|
{...commonProps}
|
||||||
href={href ? href : '#'}
|
href={href}
|
||||||
className={linkClass}
|
// don't think we can avoid the type assertion here :(
|
||||||
{...otherProps}
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
onClick={onChangeTab}
|
ref={ref as React.ForwardedRef<HTMLAnchorElement>}
|
||||||
aria-label={otherProps['aria-label'] || selectors.components.Tab.title(label)}
|
|
||||||
role="tab"
|
|
||||||
aria-selected={active}
|
|
||||||
ref={ref}
|
|
||||||
>
|
>
|
||||||
{content()}
|
{content()}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={tabsStyles.item}>
|
||||||
|
<button
|
||||||
|
{...commonProps}
|
||||||
|
type="button"
|
||||||
|
// don't think we can avoid the type assertion here :(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
ref={ref as React.ForwardedRef<HTMLButtonElement>}
|
||||||
|
>
|
||||||
|
{content()}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Tab.displayName = 'Tab';
|
Tab.displayName = 'Tab';
|
||||||
@ -108,10 +132,6 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
color: theme.colors.text.primary,
|
color: theme.colors.text.primary,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
|
||||||
a: {
|
|
||||||
color: theme.colors.text.primary,
|
|
||||||
},
|
|
||||||
|
|
||||||
'&::before': {
|
'&::before': {
|
||||||
backgroundImage: theme.colors.gradients.brandHorizontal,
|
backgroundImage: theme.colors.gradients.brandHorizontal,
|
||||||
},
|
},
|
||||||
|
@ -100,7 +100,7 @@ export {
|
|||||||
} from './Table/types';
|
} from './Table/types';
|
||||||
export { TableInputCSV } from './TableInputCSV/TableInputCSV';
|
export { TableInputCSV } from './TableInputCSV/TableInputCSV';
|
||||||
export { TabsBar } from './Tabs/TabsBar';
|
export { TabsBar } from './Tabs/TabsBar';
|
||||||
export { Tab } from './Tabs/Tab';
|
export { Tab, type TabProps } from './Tabs/Tab';
|
||||||
export { VerticalTab } from './Tabs/VerticalTab';
|
export { VerticalTab } from './Tabs/VerticalTab';
|
||||||
export { TabContent } from './Tabs/TabContent';
|
export { TabContent } from './Tabs/TabContent';
|
||||||
export { Counter } from './Tabs/Counter';
|
export { Counter } from './Tabs/Counter';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Tab, TabProps } from '@grafana/ui/src/components/Tabs/Tab';
|
import { Tab, TabProps } from '@grafana/ui';
|
||||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
|
|
||||||
import { usePanelCombinedRules } from './hooks/usePanelCombinedRules';
|
import { usePanelCombinedRules } from './hooks/usePanelCombinedRules';
|
||||||
|
Loading…
Reference in New Issue
Block a user