mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: use ScrollContainer
across frontend platform components (#95601)
* use ScrollContainer in grafana-ui * remove extraneous labels * fix unit tests
This commit is contained in:
parent
9f43724b57
commit
ada6249280
@ -13,9 +13,9 @@ import { DataLinkBuiltInVars, GrafanaTheme2, VariableOrigin, VariableSuggestion
|
||||
import { SlatePrism } from '../../slate-plugins';
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { SCHEMA, makeValue } from '../../utils/slate';
|
||||
import CustomScrollbar from '../CustomScrollbar/CustomScrollbar';
|
||||
import { getInputStyles } from '../Input/Input';
|
||||
import { Portal } from '../Portal/Portal';
|
||||
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
|
||||
|
||||
import { DataLinkSuggestions } from './DataLinkSuggestions';
|
||||
import { SelectionReference } from './SelectionReference';
|
||||
@ -86,6 +86,11 @@ export const DataLinkInput = memo(
|
||||
const [linkUrl, setLinkUrl] = useState<Value>(makeValue(value));
|
||||
const prevLinkUrl = usePrevious<Value>(linkUrl);
|
||||
const [scrollTop, setScrollTop] = useState(0);
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
scrollRef.current?.scrollTo(0, scrollTop);
|
||||
}, [scrollTop]);
|
||||
|
||||
// the order of middleware is important!
|
||||
const middleware = [
|
||||
@ -208,10 +213,10 @@ export const DataLinkInput = memo(
|
||||
{showingSuggestions && (
|
||||
<Portal>
|
||||
<div ref={refs.setFloating} style={floatingStyles}>
|
||||
<CustomScrollbar
|
||||
scrollTop={scrollTop}
|
||||
autoHeightMax="300px"
|
||||
setScrollTop={({ scrollTop }) => setScrollTop(scrollTop)}
|
||||
<ScrollContainer
|
||||
maxHeight="300px"
|
||||
ref={scrollRef}
|
||||
onScroll={(event) => setScrollTop(event.currentTarget.scrollTop)}
|
||||
>
|
||||
<DataLinkSuggestions
|
||||
activeRef={activeRef}
|
||||
@ -220,7 +225,7 @@ export const DataLinkInput = memo(
|
||||
onClose={() => setShowingSuggestions(false)}
|
||||
activeIndex={suggestionsIndex}
|
||||
/>
|
||||
</CustomScrollbar>
|
||||
</ScrollContainer>
|
||||
</div>
|
||||
</Portal>
|
||||
)}
|
||||
|
@ -10,10 +10,10 @@ import { RelativeTimeRange, GrafanaTheme2, TimeOption } from '@grafana/data';
|
||||
import { useStyles2 } from '../../../themes';
|
||||
import { Trans, t } from '../../../utils/i18n';
|
||||
import { Button } from '../../Button';
|
||||
import CustomScrollbar from '../../CustomScrollbar/CustomScrollbar';
|
||||
import { Field } from '../../Forms/Field';
|
||||
import { Icon } from '../../Icon/Icon';
|
||||
import { getInputStyles, Input } from '../../Input/Input';
|
||||
import { ScrollContainer } from '../../ScrollContainer/ScrollContainer';
|
||||
import { Tooltip } from '../../Tooltip/Tooltip';
|
||||
import { TimePickerTitle } from '../TimeRangePicker/TimePickerTitle';
|
||||
import { TimeRangeList } from '../TimeRangePicker/TimeRangeList';
|
||||
@ -157,14 +157,16 @@ export function RelativeTimeRangePicker(props: RelativeTimeRangePickerProps) {
|
||||
<div ref={ref} {...overlayProps} {...dialogProps}>
|
||||
<div className={styles.content} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
|
||||
<div className={styles.body}>
|
||||
<CustomScrollbar className={styles.leftSide} hideHorizontalTrack>
|
||||
<TimeRangeList
|
||||
title={t('time-picker.time-range.example-title', 'Example time ranges')}
|
||||
options={validOptions}
|
||||
onChange={onChangeTimeOption}
|
||||
value={timeOption}
|
||||
/>
|
||||
</CustomScrollbar>
|
||||
<div className={styles.leftSide}>
|
||||
<ScrollContainer showScrollIndicators>
|
||||
<TimeRangeList
|
||||
title={t('time-picker.time-range.example-title', 'Example time ranges')}
|
||||
options={validOptions}
|
||||
onChange={onChangeTimeOption}
|
||||
value={timeOption}
|
||||
/>
|
||||
</ScrollContainer>
|
||||
</div>
|
||||
<div className={styles.rightSide}>
|
||||
<div className={styles.title}>
|
||||
<TimePickerTitle>
|
||||
|
@ -11,9 +11,9 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { t } from '../../utils/i18n';
|
||||
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
|
||||
import { getDragStyles } from '../DragHandle/DragHandle';
|
||||
import { IconButton } from '../IconButton/IconButton';
|
||||
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
|
||||
import { Text } from '../Text/Text';
|
||||
|
||||
import 'rc-drawer/assets/index.css';
|
||||
@ -167,7 +167,7 @@ export function Drawer({
|
||||
</div>
|
||||
)}
|
||||
{typeof title !== 'string' && title}
|
||||
{!scrollableContent ? content : <CustomScrollbar>{content}</CustomScrollbar>}
|
||||
{!scrollableContent ? content : <ScrollContainer showScrollIndicators>{content}</ScrollContainer>}
|
||||
</div>
|
||||
</FocusScope>
|
||||
</RcDrawer>
|
||||
|
@ -9,7 +9,7 @@ import Creatable from 'react-select/creatable';
|
||||
// Components
|
||||
import { SelectableValue, ThemeContext } from '@grafana/data';
|
||||
|
||||
import { CustomScrollbar } from '../../../CustomScrollbar/CustomScrollbar';
|
||||
import { ScrollContainer } from '../../../ScrollContainer/ScrollContainer';
|
||||
import { SingleValue } from '../../../Select/SingleValue';
|
||||
import resetSelectStyles from '../../../Select/resetSelectStyles';
|
||||
import { SelectCommonProps, SelectAsyncProps } from '../../../Select/types';
|
||||
@ -45,9 +45,9 @@ export interface LegacySelectProps<T> extends LegacyCommonProps<T> {
|
||||
export const MenuList = (props: MenuListProps) => {
|
||||
return (
|
||||
<components.MenuList {...props}>
|
||||
<CustomScrollbar autoHide={false} autoHeightMax="inherit">
|
||||
<ScrollContainer showScrollIndicators overflowX="hidden" maxHeight="inherit">
|
||||
{props.children}
|
||||
</CustomScrollbar>
|
||||
</ScrollContainer>
|
||||
</components.MenuList>
|
||||
);
|
||||
};
|
||||
|
@ -267,9 +267,6 @@ describe('SelectBase', () => {
|
||||
});
|
||||
|
||||
describe('toggle all', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
it('renders menu with select all toggle', async () => {
|
||||
render(
|
||||
<SelectBase
|
||||
|
@ -10,8 +10,8 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
import { useTheme2 } from '../../themes/ThemeContext';
|
||||
import { Trans } from '../../utils/i18n';
|
||||
import { clearButtonStyles } from '../Button';
|
||||
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
|
||||
|
||||
import { getSelectStyles } from './getSelectStyles';
|
||||
import { ToggleAllState } from './types';
|
||||
@ -54,7 +54,7 @@ export const SelectMenu = ({
|
||||
style={{ maxHeight }}
|
||||
aria-label="Select options menu"
|
||||
>
|
||||
<CustomScrollbar scrollRefCallback={innerRef} autoHide={false} autoHeightMax="inherit" hideHorizontalTrack>
|
||||
<ScrollContainer ref={innerRef} maxHeight="inherit" overflowX="hidden" showScrollIndicators>
|
||||
{toggleAllOptions && (
|
||||
<ToggleAllOption
|
||||
state={toggleAllOptions.state}
|
||||
@ -64,7 +64,7 @@ export const SelectMenu = ({
|
||||
></ToggleAllOption>
|
||||
)}
|
||||
{children}
|
||||
</CustomScrollbar>
|
||||
</ScrollContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ import { IconButton } from '../../components/IconButton/IconButton';
|
||||
import { TabsBar, Tab, TabContent } from '../../components/Tabs';
|
||||
import { useStyles2, useTheme2 } from '../../themes';
|
||||
import { IconName } from '../../types/icon';
|
||||
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
|
||||
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
|
||||
|
||||
export interface TabConfig {
|
||||
label: string;
|
||||
@ -50,9 +50,9 @@ export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose, t
|
||||
))}
|
||||
<IconButton className={styles.close} onClick={onClose} name="times" tooltip={closeIconTooltip ?? 'Close'} />
|
||||
</TabsBar>
|
||||
<CustomScrollbar autoHeightMin={autoHeight} autoHeightMax={autoHeight}>
|
||||
<ScrollContainer height={autoHeight}>
|
||||
<TabContent className={styles.tabContent}>{tabs.find((t) => t.value === activeTab)?.content}</TabContent>
|
||||
</CustomScrollbar>
|
||||
</ScrollContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
|
||||
import { Button } from '../Button';
|
||||
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
|
||||
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
|
||||
import mdx from '../Toggletip/Toggletip.mdx';
|
||||
|
||||
import { Toggletip } from './Toggletip';
|
||||
@ -96,7 +96,7 @@ export const LongContent: StoryFn<typeof Toggletip> = ({
|
||||
<Toggletip
|
||||
title={<h2>Toggletip with scrollable content and no interactive controls</h2>}
|
||||
content={
|
||||
<CustomScrollbar autoHeightMax="500px">
|
||||
<ScrollContainer maxHeight="500px">
|
||||
{/* one of the few documented cases we can turn this rule off */}
|
||||
{/* https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-noninteractive-tabindex.md#case-shouldnt-i-add-a-tabindex-so-that-users-can-navigate-to-this-item */}
|
||||
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
||||
@ -110,7 +110,7 @@ export const LongContent: StoryFn<typeof Toggletip> = ({
|
||||
<p key={i}>This is some content repeated over and over again to ensure it is scrollable.</p>
|
||||
))}
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
</ScrollContainer>
|
||||
}
|
||||
footer={footer}
|
||||
theme={theme}
|
||||
|
@ -6,7 +6,8 @@ import { useLocation } from 'react-router-dom-v5-compat';
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { CustomScrollbar, Icon, IconButton, useStyles2, Stack } from '@grafana/ui';
|
||||
import { Icon, IconButton, useStyles2, Stack } from '@grafana/ui';
|
||||
import { ScrollContainer } from '@grafana/ui/src/unstable';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { setBookmark } from 'app/core/reducers/navBarTree';
|
||||
@ -134,7 +135,7 @@ export const MegaMenu = memo(
|
||||
</div>
|
||||
)}
|
||||
<nav className={styles.content}>
|
||||
<CustomScrollbar showScrollIndicators hideHorizontalTrack>
|
||||
<ScrollContainer height="100%" overflowX="hidden" showScrollIndicators>
|
||||
<ul className={styles.itemList} aria-label={t('navigation.megamenu.list-label', 'Navigation')}>
|
||||
{navItems.map((link, index) => (
|
||||
<Stack key={link.text} direction={index === 0 ? 'row-reverse' : 'row'} alignItems="start">
|
||||
@ -162,7 +163,7 @@ export const MegaMenu = memo(
|
||||
</Stack>
|
||||
))}
|
||||
</ul>
|
||||
</CustomScrollbar>
|
||||
</ScrollContainer>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
|
@ -120,8 +120,6 @@ beforeEach(() => {
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('Silences', () => {
|
||||
it(
|
||||
'loads and shows silences',
|
||||
|
@ -185,19 +185,19 @@ describe('SpanFilters', () => {
|
||||
jest.advanceTimersByTime(1000);
|
||||
await waitFor(() => {
|
||||
const container = screen.getByText('TagKey0').parentElement?.parentElement?.parentElement;
|
||||
expect(container?.childNodes[0].textContent).toBe('ProcessKey0');
|
||||
expect(container?.childNodes[1].textContent).toBe('ProcessKey1');
|
||||
expect(container?.childNodes[2].textContent).toBe('TagKey0');
|
||||
expect(container?.childNodes[3].textContent).toBe('TagKey1');
|
||||
expect(container?.childNodes[4].textContent).toBe('id');
|
||||
expect(container?.childNodes[5].textContent).toBe('kind');
|
||||
expect(container?.childNodes[6].textContent).toBe('library.name');
|
||||
expect(container?.childNodes[7].textContent).toBe('library.version');
|
||||
expect(container?.childNodes[8].textContent).toBe('status');
|
||||
expect(container?.childNodes[9].textContent).toBe('status.message');
|
||||
expect(container?.childNodes[10].textContent).toBe('trace.state');
|
||||
expect(container?.childNodes[11].textContent).toBe('LogKey0');
|
||||
expect(container?.childNodes[12].textContent).toBe('LogKey1');
|
||||
expect(container?.childNodes[1].textContent).toBe('ProcessKey0');
|
||||
expect(container?.childNodes[2].textContent).toBe('ProcessKey1');
|
||||
expect(container?.childNodes[3].textContent).toBe('TagKey0');
|
||||
expect(container?.childNodes[4].textContent).toBe('TagKey1');
|
||||
expect(container?.childNodes[5].textContent).toBe('id');
|
||||
expect(container?.childNodes[6].textContent).toBe('kind');
|
||||
expect(container?.childNodes[7].textContent).toBe('library.name');
|
||||
expect(container?.childNodes[8].textContent).toBe('library.version');
|
||||
expect(container?.childNodes[9].textContent).toBe('status');
|
||||
expect(container?.childNodes[10].textContent).toBe('status.message');
|
||||
expect(container?.childNodes[11].textContent).toBe('trace.state');
|
||||
expect(container?.childNodes[12].textContent).toBe('LogKey0');
|
||||
expect(container?.childNodes[13].textContent).toBe('LogKey1');
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user