Update dependency react-select to v5 (#41604)

* Update dependency react-select to v5

* Remove @types/react-select and update types accordingly

* Fix all unit tests

* Add @ts-expect-error to individual errors, remove prefix as it doesn't seem to exist?

* Another minor typescript fix

* Apply fixes from torkel's PR

* Fix last typescript error

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
pull/42134/head
renovate[bot] 4 years ago committed by GitHub
parent 6b79393ccc
commit 035e676cad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      package.json
  2. 3
      packages/grafana-ui/package.json
  3. 13
      packages/grafana-ui/src/components/Forms/Legacy/Select/NoOptionsMessage.tsx
  4. 4
      packages/grafana-ui/src/components/Forms/Legacy/Select/Select.tsx
  5. 6
      packages/grafana-ui/src/components/Forms/Legacy/Select/SelectOption.test.tsx
  6. 1
      packages/grafana-ui/src/components/Forms/Legacy/Select/__snapshots__/SelectOption.test.tsx.snap
  7. 75
      packages/grafana-ui/src/components/Select/Container.tsx
  8. 6
      packages/grafana-ui/src/components/Select/SelectBase.test.tsx
  9. 79
      packages/grafana-ui/src/components/Select/SelectContainer.tsx
  10. 30
      packages/grafana-ui/src/components/Select/SingleValue.tsx
  11. 4
      packages/grafana-ui/src/components/Select/types.ts
  12. 4
      public/app/core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker.test.tsx
  13. 2
      public/app/core/components/TagFilter/TagOption.tsx
  14. 2
      public/app/features/alerting/unified/AlertGroups.test.tsx
  15. 16
      public/app/features/alerting/unified/AmRoutes.test.tsx
  16. 2
      public/app/features/alerting/unified/Receivers.test.tsx
  17. 8
      public/app/features/alerting/unified/RuleEditor.test.tsx
  18. 2
      public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx
  19. 2
      public/app/features/inspector/InspectDataTab.test.tsx
  20. 12
      public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx
  21. 1
      public/app/features/variables/adhoc/actions.ts
  22. 6
      public/app/plugins/datasource/cloudwatch/components/ui/InlineSelect.tsx
  23. 5
      public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/DateHistogramSettingsEditor.tsx
  24. 48
      yarn.lock

@ -126,7 +126,6 @@
"@types/react-loadable": "5.5.2",
"@types/react-redux": "7.1.20",
"@types/react-router-dom": "^5.1.7",
"@types/react-select": "4.0.13",
"@types/react-test-renderer": "17.0.1",
"@types/react-transition-group": "4.4.0",
"@types/react-virtualized-auto-sizer": "1.0.0",
@ -331,7 +330,7 @@
"react-resizable": "3.0.4",
"react-reverse-portal": "^2.0.1",
"react-router-dom": "^5.2.0",
"react-select": "4.3.0",
"react-select": "5.2.1",
"react-split-pane": "0.1.89",
"react-transition-group": "4.4.1",
"react-use": "17.2.4",

@ -76,7 +76,7 @@
"react-inlinesvg": "2.3.0",
"react-popper": "2.2.4",
"react-router-dom": "^5.2.0",
"react-select": "4.3.0",
"react-select": "5.2.1",
"react-select-event": "^5.1.0",
"react-table": "7.7.0",
"react-transition-group": "4.4.1",
@ -139,7 +139,6 @@
"@types/react-color": "3.0.1",
"@types/react-dom": "17.0.11",
"@types/react-router-dom": "^5.1.7",
"@types/react-select": "4.0.13",
"@types/react-table": "7.7.2",
"@types/react-test-renderer": "17.0.1",
"@types/react-transition-group": "4.4.0",

@ -1,18 +1,17 @@
import React from 'react';
import { components, OptionProps } from 'react-select';
import { components, NoticeProps, GroupBase } from 'react-select';
import { SelectableValue } from '@grafana/data';
export interface Props {
children: Element;
}
export type Props<T> = NoticeProps<SelectableValue<T>, boolean, GroupBase<SelectableValue<T>>>;
export const NoOptionsMessage = (props: OptionProps<any, any>) => {
export const NoOptionsMessage = <T extends unknown>(props: Props<T>) => {
const { children } = props;
return (
<components.Option {...props}>
<components.NoOptionsMessage {...props}>
<div className="gf-form-select-box__desc-option">
<div className="gf-form-select-box__desc-option__body">{children}</div>
</div>
</components.Option>
</components.NoOptionsMessage>
);
};

@ -216,7 +216,6 @@ export class AsyncSelect<T> extends PureComponent<AsyncProps<T>> {
<WrapInTooltip onCloseMenu={onCloseMenu} onOpenMenu={onOpenMenu} tooltipContent={tooltipContent} isOpen={isOpen}>
{(onOpenMenuInternal, onCloseMenuInternal) => {
return (
//@ts-expect-error
<ReactAsyncSelect
captureMenuScroll={false}
classNamePrefix="gf-form-select-box"
@ -229,14 +228,17 @@ export class AsyncSelect<T> extends PureComponent<AsyncProps<T>> {
}}
defaultValue={defaultValue}
value={value}
//@ts-expect-error
getOptionLabel={getOptionLabel}
getOptionValue={getOptionValue}
menuShouldScrollIntoView={false}
//@ts-expect-error
onChange={onChange}
loadOptions={loadOptions}
isLoading={isLoading}
defaultOptions={defaultOptions}
placeholder={placeholder || 'Choose'}
//@ts-expect-error
styles={resetSelectStyles()}
loadingMessage={() => loadingMessage}
noOptionsMessage={noOptionsMessage}

@ -1,9 +1,8 @@
import React from 'react';
import renderer from 'react-test-renderer';
import SelectOption from './SelectOption';
import { OptionProps } from 'react-select/src/components/Option';
import { OptionProps } from 'react-select';
// @ts-ignore
const model: OptionProps<any> = {
data: jest.fn(),
cx: jest.fn(),
@ -14,12 +13,13 @@ const model: OptionProps<any> = {
isMulti: false,
options: [],
selectOption: jest.fn(),
// @ts-ignore
selectProps: {},
setValue: jest.fn(),
isDisabled: false,
isFocused: false,
isSelected: false,
innerRef: null,
innerRef: jest.fn(),
innerProps: {
id: '',
key: '',

@ -2,6 +2,7 @@
exports[`SelectOption renders correctly 1`] = `
<div
aria-disabled={false}
className="css-o8533i-Option"
id=""
onClick={[MockFunction]}

@ -6,20 +6,15 @@ import { css, cx } from '@emotion/css';
import { stylesFactory } from '../../themes';
import { GrafanaTheme2 } from '@grafana/data';
import { focusCss } from '../../themes/mixins';
import { components, ContainerProps, GroupTypeBase } from 'react-select';
import { components, ContainerProps, GroupBase } from 'react-select';
export const SelectContainer = <Option, isMulti extends boolean, Group extends GroupTypeBase<Option>>(
export const SelectContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>(
props: ContainerProps<Option, isMulti, Group> & { isFocused: boolean }
) => {
const {
isDisabled,
isFocused,
children,
selectProps: { prefix },
} = props;
const { isDisabled, isFocused, children } = props;
const theme = useTheme2();
const styles = getSelectContainerStyles(theme, isFocused, isDisabled, !!prefix);
const styles = getSelectContainerStyles(theme, isFocused, isDisabled);
return (
<components.SelectContainer {...props} className={cx(styles.wrapper, props.className)}>
@ -28,41 +23,35 @@ export const SelectContainer = <Option, isMulti extends boolean, Group extends G
);
};
const getSelectContainerStyles = stylesFactory(
(theme: GrafanaTheme2, focused: boolean, disabled: boolean, withPrefix: boolean) => {
const styles = getInputStyles({ theme, invalid: false });
const getSelectContainerStyles = stylesFactory((theme: GrafanaTheme2, focused: boolean, disabled: boolean) => {
const styles = getInputStyles({ theme, invalid: false });
return {
wrapper: cx(
styles.wrapper,
sharedInputStyle(theme, false),
focused &&
css`
${focusCss(theme.v1)}
`,
disabled && styles.inputDisabled,
return {
wrapper: cx(
styles.wrapper,
sharedInputStyle(theme, false),
focused &&
css`
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
${focusCss(theme.v1)}
`,
disabled && styles.inputDisabled,
css`
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
min-height: 32px;
height: auto;
max-width: 100%;
min-height: 32px;
height: auto;
max-width: 100%;
/* Input padding is applied to the InputControl so the menu is aligned correctly */
padding: 0;
cursor: ${disabled ? 'not-allowed' : 'pointer'};
`,
withPrefix &&
css`
padding-left: 0;
`
),
};
}
);
/* Input padding is applied to the InputControl so the menu is aligned correctly */
padding: 0;
cursor: ${disabled ? 'not-allowed' : 'pointer'};
`
),
};
});

@ -62,7 +62,7 @@ describe('SelectBase', () => {
describe('is provided', () => {
it('opens on focus', () => {
render(<SelectBase menuShouldPortal onChange={onChangeHandler} openMenuOnFocus />);
fireEvent.focus(screen.getByRole('textbox'));
fireEvent.focus(screen.getByRole('combobox'));
expect(screen.queryByText(/no options found/i)).toBeVisible();
});
});
@ -74,8 +74,8 @@ describe('SelectBase', () => {
${' '}
`('opens on arrow down/up or space', ({ key }) => {
render(<SelectBase menuShouldPortal onChange={onChangeHandler} />);
fireEvent.focus(screen.getByRole('textbox'));
fireEvent.keyDown(screen.getByRole('textbox'), { key });
fireEvent.focus(screen.getByRole('combobox'));
fireEvent.keyDown(screen.getByRole('combobox'), { key });
expect(screen.queryByText(/no options found/i)).toBeVisible();
});
});

@ -6,26 +6,21 @@ import { css, cx } from '@emotion/css';
import { stylesFactory } from '../../themes';
import { GrafanaTheme2 } from '@grafana/data';
import { focusCss } from '../../themes/mixins';
import { components, ContainerProps as BaseContainerProps, GroupTypeBase } from 'react-select';
import { components, ContainerProps as BaseContainerProps, GroupBase } from 'react-select';
// isFocus prop is actually available, but its not in the types for the version we have.
export interface ContainerProps<Option, isMulti extends boolean, Group extends GroupTypeBase<Option>>
export interface ContainerProps<Option, isMulti extends boolean, Group extends GroupBase<Option>>
extends BaseContainerProps<Option, isMulti, Group> {
isFocused: boolean;
}
export const SelectContainer = <Option, isMulti extends boolean, Group extends GroupTypeBase<Option>>(
export const SelectContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>(
props: ContainerProps<Option, isMulti, Group>
) => {
const {
isDisabled,
isFocused,
children,
selectProps: { prefix },
} = props;
const { isDisabled, isFocused, children } = props;
const theme = useTheme2();
const styles = getSelectContainerStyles(theme, isFocused, isDisabled, !!prefix);
const styles = getSelectContainerStyles(theme, isFocused, isDisabled);
return (
<components.SelectContainer {...props} className={cx(styles.wrapper, props.className)}>
@ -34,41 +29,35 @@ export const SelectContainer = <Option, isMulti extends boolean, Group extends G
);
};
const getSelectContainerStyles = stylesFactory(
(theme: GrafanaTheme2, focused: boolean, disabled: boolean, withPrefix: boolean) => {
const styles = getInputStyles({ theme, invalid: false });
const getSelectContainerStyles = stylesFactory((theme: GrafanaTheme2, focused: boolean, disabled: boolean) => {
const styles = getInputStyles({ theme, invalid: false });
return {
wrapper: cx(
styles.wrapper,
sharedInputStyle(theme, false),
focused &&
css`
${focusCss(theme.v1)}
`,
disabled && styles.inputDisabled,
return {
wrapper: cx(
styles.wrapper,
sharedInputStyle(theme, false),
focused &&
css`
position: relative;
box-sizing: border-box;
/* The display property is set by the styles prop in SelectBase because it's dependant on the width prop */
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
min-height: 32px;
height: auto;
max-width: 100%;
/* Input padding is applied to the InputControl so the menu is aligned correctly */
padding: 0;
cursor: ${disabled ? 'not-allowed' : 'pointer'};
${focusCss(theme.v1)}
`,
withPrefix &&
css`
padding-left: 0;
`
),
};
}
);
disabled && styles.inputDisabled,
css`
position: relative;
box-sizing: border-box;
/* The display property is set by the styles prop in SelectBase because it's dependant on the width prop */
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
min-height: 32px;
height: auto;
max-width: 100%;
/* Input padding is applied to the InputControl so the menu is aligned correctly */
padding: 0;
cursor: ${disabled ? 'not-allowed' : 'pointer'};
`
),
};
});

@ -1,12 +1,12 @@
import React from 'react';
import { css, cx } from '@emotion/css';
import { components, SingleValueProps } from 'react-select';
import { components, GroupBase, SingleValueProps } from 'react-select';
import { useDelayedSwitch } from '../../utils/useDelayedSwitch';
import { useStyles2 } from '../../themes';
import { SlideOutTransition } from '../transitions/SlideOutTransition';
import { FadeTransition } from '../transitions/FadeTransition';
import { Spinner } from '../Spinner/Spinner';
import { GrafanaTheme2 } from '@grafana/data';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import tinycolor from 'tinycolor2';
const getStyles = (theme: GrafanaTheme2) => {
@ -44,27 +44,23 @@ const getStyles = (theme: GrafanaTheme2) => {
type StylesType = ReturnType<typeof getStyles>;
interface Props
extends SingleValueProps<{
imgUrl?: string;
label?: string;
value: string;
loading?: boolean;
hideText?: boolean;
}> {
disabled?: boolean;
}
export type Props<T> = SingleValueProps<SelectableValue<T>, boolean, GroupBase<SelectableValue<T>>>;
export const SingleValue = (props: Props) => {
const { children, data, disabled } = props;
export const SingleValue = <T extends unknown>(props: Props<T>) => {
const { children, data, isDisabled } = props;
const styles = useStyles2(getStyles);
const loading = useDelayedSwitch(data.loading || false, { delay: 250, duration: 750 });
return (
<components.SingleValue {...props}>
<div className={cx(styles.singleValue, disabled && styles.disabled)}>
<div className={cx(styles.singleValue, isDisabled && styles.disabled)}>
{data.imgUrl ? (
<FadeWithImage loading={loading} imgUrl={data.imgUrl} styles={styles} alt={data.label || data.value} />
<FadeWithImage
loading={loading}
imgUrl={data.imgUrl}
styles={styles}
alt={(data.label || data.value) as string}
/>
) : (
<SlideOutTransition horizontal size={16} visible={loading} duration={150}>
<div className={styles.container}>
@ -78,7 +74,7 @@ export const SingleValue = (props: Props) => {
);
};
const FadeWithImage = (props: { loading: boolean; imgUrl: string; styles: StylesType; alt: string }) => {
const FadeWithImage = (props: { loading: boolean; imgUrl: string; styles: StylesType; alt?: string }) => {
return (
<div className={props.styles.container}>
<FadeTransition duration={150} visible={props.loading}>

@ -1,6 +1,6 @@
import { SelectableValue } from '@grafana/data';
import React from 'react';
import { ActionMeta as SelectActionMeta } from 'react-select';
import { ActionMeta as SelectActionMeta, GroupBase, OptionsOrGroups } from 'react-select';
export type SelectValue<T> = T | SelectableValue<T> | T[] | Array<SelectableValue<T>>;
export type ActionMeta = SelectActionMeta<{}>;
@ -77,7 +77,7 @@ export interface SelectCommonProps<T> {
isValidNewOption?: (
inputValue: string,
value: SelectableValue<T> | null,
options: Readonly<Array<SelectableValue<T>>>
options: OptionsOrGroups<unknown, GroupBase<unknown>>
) => boolean;
}

@ -74,9 +74,9 @@ describe('ReadonlyFolderPicker', () => {
it('then query is passed correctly to getFoldersAsOptions', async () => {
const { getFoldersAsOptionsSpy, selectors } = await getTestContext();
expect(within(selectors.container.get()).getByRole('textbox')).toBeInTheDocument();
expect(within(selectors.container.get()).getByRole('combobox')).toBeInTheDocument();
getFoldersAsOptionsSpy.mockClear();
userEvent.type(within(selectors.container.get()).getByRole('textbox'), 'A');
userEvent.type(within(selectors.container.get()).getByRole('combobox'), 'A');
await waitFor(() => expect(getFoldersAsOptionsSpy).toHaveBeenCalledTimes(1));
expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({

@ -3,7 +3,7 @@ import { css, cx } from '@emotion/css';
import { useTheme, stylesFactory } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { OptionProps } from 'react-select/src/components/Option';
import { OptionProps } from 'react-select';
import { TagBadge } from './TagBadge';
// https://github.com/JedWatson/react-select/issues/3038

@ -49,7 +49,7 @@ const ui = {
sourceButton: byText('See source'),
matcherInput: byTestId('search-query-input'),
groupByContainer: byTestId('group-by-container'),
groupByInput: byRole('textbox', { name: /group by label keys/i }),
groupByInput: byRole('combobox', { name: /group by label keys/i }),
clearButton: byRole('button', { name: 'Clear filters' }),
};

@ -258,7 +258,7 @@ describe('AmRoutes', () => {
await clickSelectOption(receiverSelect, 'critical');
const groupSelect = ui.groupSelect.get();
userEvent.type(byRole('textbox').get(groupSelect), 'namespace{enter}');
userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}');
// configure timing intervals
userEvent.click(byText('Timing options').get(rootRouteContainer));
@ -317,8 +317,8 @@ describe('AmRoutes', () => {
await clickSelectOption(receiverSelect, 'default');
const groupSelect = ui.groupSelect.get();
userEvent.type(byRole('textbox').get(groupSelect), 'severity{enter}');
userEvent.type(byRole('textbox').get(groupSelect), 'namespace{enter}');
userEvent.type(byRole('combobox').get(groupSelect), 'severity{enter}');
userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}');
//save
userEvent.click(ui.saveButton.get(rootRouteContainer));
@ -531,14 +531,14 @@ describe('AmRoutes', () => {
});
const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise<void> => {
userEvent.click(byRole('textbox').get(selectElement));
userEvent.click(byRole('combobox').get(selectElement));
await selectOptionInTest(selectElement, optionText);
};
const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise<void> => {
const inputs = byRole('textbox').queryAll(selectElement);
expect(inputs).toHaveLength(2);
userEvent.type(inputs[0], value);
userEvent.click(inputs[1]);
const input = byRole('textbox').get(selectElement);
const select = byRole('combobox').get(selectElement);
userEvent.type(input, value);
userEvent.click(select);
await selectOptionInTest(selectElement, timeUnit);
};

@ -113,7 +113,7 @@ const ui = {
};
const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise<void> => {
userEvent.click(byRole('textbox').get(selectElement));
userEvent.click(byRole('combobox').get(selectElement));
await selectOptionInTest(selectElement, optionText);
};

@ -129,7 +129,7 @@ describe('RuleEditor', () => {
userEvent.type(await ui.inputs.name.find(), 'my great new rule');
await clickSelectOption(ui.inputs.alertType.get(), /Cortex\/Loki managed alert/);
const dataSourceSelect = ui.inputs.dataSource.get();
userEvent.click(byRole('textbox').get(dataSourceSelect));
userEvent.click(byRole('combobox').get(dataSourceSelect));
await clickSelectOption(dataSourceSelect, 'Prom (default)');
await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled());
await clickSelectOption(ui.inputs.namespace.get(), 'namespace2');
@ -270,7 +270,7 @@ describe('RuleEditor', () => {
userEvent.type(await ui.inputs.name.find(), 'my great new recording rule');
await clickSelectOption(ui.inputs.alertType.get(), /Cortex\/Loki managed recording rule/);
const dataSourceSelect = ui.inputs.dataSource.get();
userEvent.click(byRole('textbox').get(dataSourceSelect));
userEvent.click(byRole('combobox').get(dataSourceSelect));
await clickSelectOption(dataSourceSelect, 'Prom (default)');
await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled());
await clickSelectOption(ui.inputs.namespace.get(), 'namespace2');
@ -496,7 +496,7 @@ describe('RuleEditor', () => {
// check that only rules sources that have ruler available are there
const dataSourceSelect = ui.inputs.dataSource.get();
userEvent.click(byRole('textbox').get(dataSourceSelect));
userEvent.click(byRole('combobox').get(dataSourceSelect));
expect(await byText('loki with ruler').query()).toBeInTheDocument();
expect(byText('cortex with ruler').query()).toBeInTheDocument();
expect(byText('loki with local rule store').query()).not.toBeInTheDocument();
@ -507,6 +507,6 @@ describe('RuleEditor', () => {
});
const clickSelectOption = async (selectElement: HTMLElement, optionText: Matcher): Promise<void> => {
userEvent.click(byRole('textbox').get(selectElement));
userEvent.click(byRole('combobox').get(selectElement));
await selectOptionInTest(selectElement, optionText as string);
};

@ -50,7 +50,7 @@ describe('General Settings', () => {
const { props } = setupTestContext({});
userEvent.click(screen.getByTestId(selectors.components.TimeZonePicker.containerV2));
const timeZonePicker = screen.getByTestId(selectors.components.TimeZonePicker.containerV2);
userEvent.click(byRole('textbox').get(timeZonePicker));
userEvent.click(byRole('combobox').get(timeZonePicker));
await selectOptionInTest(timeZonePicker, 'Browser Time');
expect(props.updateTimeZone).toHaveBeenCalledWith('browser');
expect(props.dashboard.timezone).toBe('browser');

@ -57,7 +57,7 @@ describe('InspectDataTab', () => {
render(<InspectDataTab {...createProps()} />);
const dataOptions = screen.getByText(/Data options/i);
userEvent.click(dataOptions);
const dataFrameInput = screen.getByRole('textbox', { name: /Select dataframe/i });
const dataFrameInput = screen.getByRole('combobox', { name: /Select dataframe/i });
userEvent.click(dataFrameInput);
expect(screen.getByText(/Second data frame/i)).toBeInTheDocument();
});

@ -141,7 +141,7 @@ describe('LibraryPanelsSearch', () => {
expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument();
expect(screen.getByText(/no library panels found./i)).toBeInTheDocument();
expect(screen.getByRole('textbox', { name: /panel type filter/i })).toBeInTheDocument();
expect(screen.getByRole('combobox', { name: /panel type filter/i })).toBeInTheDocument();
});
describe('and user changes panel filter', () => {
@ -149,8 +149,8 @@ describe('LibraryPanelsSearch', () => {
const { getLibraryPanelsSpy } = await getTestContext({ showPanelFilter: true });
getLibraryPanelsSpy.mockClear();
userEvent.type(screen.getByRole('textbox', { name: /panel type filter/i }), 'Graph{enter}');
userEvent.type(screen.getByRole('textbox', { name: /panel type filter/i }), 'Time Series{enter}');
userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Graph{enter}');
userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Time Series{enter}');
await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1));
expect(getLibraryPanelsSpy).toHaveBeenCalledWith({
searchString: '',
@ -169,7 +169,7 @@ describe('LibraryPanelsSearch', () => {
expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument();
expect(screen.getByText(/no library panels found./i)).toBeInTheDocument();
expect(screen.getByRole('textbox', { name: /folder filter/i })).toBeInTheDocument();
expect(screen.getByRole('combobox', { name: /folder filter/i })).toBeInTheDocument();
});
describe('and user changes folder filter', () => {
@ -177,8 +177,8 @@ describe('LibraryPanelsSearch', () => {
const { getLibraryPanelsSpy } = await getTestContext({ showFolderFilter: true });
getLibraryPanelsSpy.mockClear();
userEvent.click(screen.getByRole('textbox', { name: /folder filter/i }));
userEvent.type(screen.getByRole('textbox', { name: /folder filter/i }), '{enter}', {
userEvent.click(screen.getByRole('combobox', { name: /folder filter/i }));
userEvent.type(screen.getByRole('combobox', { name: /folder filter/i }), '{enter}', {
skipClick: true,
});
await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1));

@ -30,7 +30,6 @@ const filterTableName = 'Filters';
export const applyFilterFromTable = (options: AdHocTableOptions): ThunkResult<void> => {
return async (dispatch, getState) => {
let variable = getVariableByOptions(options, getState());
console.log('getVariableByOptions', options, getState().templating.variables);
if (!variable) {
dispatch(createAdHocVariable(options));

@ -7,7 +7,7 @@ import {
} from '@grafana/ui/src/components/Select/SelectContainer';
import { SelectCommonProps } from '@grafana/ui/src/components/Select/types';
import React, { useState } from 'react';
import { GroupTypeBase } from 'react-select';
import { GroupBase } from 'react-select';
interface InlineSelectProps<T> extends SelectCommonProps<T> {
label?: string;
@ -38,7 +38,7 @@ function InlineSelect<T>({ label: labelProp, ...props }: InlineSelectProps<T>) {
export default InlineSelect;
const SelectContainer = <Option, isMulti extends boolean, Group extends GroupTypeBase<Option>>(
const SelectContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>(
props: ContainerProps<Option, isMulti, Group>
) => {
const { children } = props;
@ -53,7 +53,7 @@ const SelectContainer = <Option, isMulti extends boolean, Group extends GroupTyp
);
};
const ValueContainer = <Option, isMulti extends boolean, Group extends GroupTypeBase<Option>>(
const ValueContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>(
props: ContainerProps<Option, isMulti, Group>
) => {
const { className, children } = props;

@ -1,4 +1,5 @@
import React from 'react';
import { GroupBase, OptionsOrGroups } from 'react-select';
import { InlineField, Input, Select, TimeZonePicker } from '@grafana/ui';
import { DateHistogram } from '../aggregations';
import { bucketAggregationConfig } from '../utils';
@ -25,11 +26,11 @@ const hasValue = (searchValue: string) => ({ value }: SelectableValue<string>) =
const isValidNewOption = (
inputValue: string,
_: SelectableValue<string> | null,
options: Readonly<Array<SelectableValue<string>>>
options: OptionsOrGroups<unknown, GroupBase<unknown>>
) => {
// TODO: would be extremely nice here to allow only template variables and values that are
// valid date histogram's Interval options
const valueExists = options.some(hasValue(inputValue));
const valueExists = (options as Array<SelectableValue<string>>).some(hasValue(inputValue));
// we also don't want users to create "empty" values
return !valueExists && inputValue.trim().length > 0;
};

@ -1995,7 +1995,7 @@ __metadata:
languageName: node
linkType: hard
"@emotion/cache@npm:^11.0.0, @emotion/cache@npm:^11.1.3, @emotion/cache@npm:^11.5.0":
"@emotion/cache@npm:^11.1.3, @emotion/cache@npm:^11.4.0, @emotion/cache@npm:^11.5.0":
version: 11.5.0
resolution: "@emotion/cache@npm:11.5.0"
dependencies:
@ -2761,7 +2761,6 @@ __metadata:
"@types/react-color": 3.0.1
"@types/react-dom": 17.0.11
"@types/react-router-dom": ^5.1.7
"@types/react-select": 4.0.13
"@types/react-table": 7.7.2
"@types/react-test-renderer": 17.0.1
"@types/react-transition-group": 4.4.0
@ -2821,7 +2820,7 @@ __metadata:
react-inlinesvg: 2.3.0
react-popper: 2.2.4
react-router-dom: ^5.2.0
react-select: 4.3.0
react-select: 5.2.1
react-select-event: ^5.1.0
react-table: 7.7.0
react-test-renderer: 17.0.1
@ -8378,18 +8377,6 @@ __metadata:
languageName: node
linkType: hard
"@types/react-select@npm:4.0.13":
version: 4.0.13
resolution: "@types/react-select@npm:4.0.13"
dependencies:
"@emotion/serialize": ^1.0.0
"@types/react": "*"
"@types/react-dom": "*"
"@types/react-transition-group": "*"
checksum: 1a3fdd93a33e8b3fd59ba32c4e952cf057e0f745a3506ac556172ceba4c849e33494dfba1553783f6f55d864a530869f33feefc55306b212146758d612117dba
languageName: node
linkType: hard
"@types/react-syntax-highlighter@npm:11.0.5":
version: 11.0.5
resolution: "@types/react-syntax-highlighter@npm:11.0.5"
@ -8417,21 +8404,21 @@ __metadata:
languageName: node
linkType: hard
"@types/react-transition-group@npm:*":
version: 4.4.4
resolution: "@types/react-transition-group@npm:4.4.4"
"@types/react-transition-group@npm:4.4.0":
version: 4.4.0
resolution: "@types/react-transition-group@npm:4.4.0"
dependencies:
"@types/react": "*"
checksum: 86e9ff9731798e12bc2afe0304678918769633b531dcf6397f86af81718fb7930ef8648e894eeb3718fc6eab6eb885cfb9b82a44d1d74e10951ee11ebc4643ae
checksum: ba44361d7b055922b60a557389a68d5320a190785cd80173839ef8c096aeb0808bd9c35fc9a70badbfe959dc668fdb1b82211e4914140a5edc9ed87d760c9812
languageName: node
linkType: hard
"@types/react-transition-group@npm:4.4.0":
version: 4.4.0
resolution: "@types/react-transition-group@npm:4.4.0"
"@types/react-transition-group@npm:^4.4.0":
version: 4.4.4
resolution: "@types/react-transition-group@npm:4.4.4"
dependencies:
"@types/react": "*"
checksum: ba44361d7b055922b60a557389a68d5320a190785cd80173839ef8c096aeb0808bd9c35fc9a70badbfe959dc668fdb1b82211e4914140a5edc9ed87d760c9812
checksum: 86e9ff9731798e12bc2afe0304678918769633b531dcf6397f86af81718fb7930ef8648e894eeb3718fc6eab6eb885cfb9b82a44d1d74e10951ee11ebc4643ae
languageName: node
linkType: hard
@ -17980,7 +17967,6 @@ __metadata:
"@types/react-loadable": 5.5.2
"@types/react-redux": 7.1.20
"@types/react-router-dom": ^5.1.7
"@types/react-select": 4.0.13
"@types/react-test-renderer": 17.0.1
"@types/react-transition-group": 4.4.0
"@types/react-virtualized-auto-sizer": 1.0.0
@ -18135,7 +18121,7 @@ __metadata:
react-resizable: 3.0.4
react-reverse-portal: ^2.0.1
react-router-dom: ^5.2.0
react-select: 4.3.0
react-select: 5.2.1
react-select-event: ^5.1.0
react-split-pane: 0.1.89
react-test-renderer: 17.0.1
@ -27678,21 +27664,21 @@ __metadata:
languageName: node
linkType: hard
"react-select@npm:4.3.0":
version: 4.3.0
resolution: "react-select@npm:4.3.0"
"react-select@npm:5.2.1":
version: 5.2.1
resolution: "react-select@npm:5.2.1"
dependencies:
"@babel/runtime": ^7.12.0
"@emotion/cache": ^11.0.0
"@emotion/cache": ^11.4.0
"@emotion/react": ^11.1.1
"@types/react-transition-group": ^4.4.0
memoize-one: ^5.0.0
prop-types: ^15.6.0
react-input-autosize: ^3.0.0
react-transition-group: ^4.3.0
peerDependencies:
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
checksum: 00946ed4dfa46523bc44cd26aab11431396632097207081b471d1965fa53cf7f5a006ac11d89b8a65df05cc5376b1c4438edb1d5259323bdc6d3adf01c79d53e
checksum: 014cde5d23ca466795bd7f02455769efda74522c41f11109ec2d6f0e3c77f401ea91ae23cdccce997efb653aa6ea88688a16f40e59bfdef2a2414d0cfd10f374
languageName: node
linkType: hard

Loading…
Cancel
Save