mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Select: Make portalling the menu opt-in, but opt-in *everywhere* (#37501)
* Select: Don't portal by default * Select: Portal all the Selects * Fix indendentation in this comment * Select: Remove @example docs until formatting is correct * Docs: Add some documentation for the Select changes * Update docs/sources/whatsnew/whats-new-in-v8-1.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/whatsnew/whats-new-in-v8-1.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update packages/grafana-ui/src/components/Select/types.ts Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update public/app/core/components/TransformersUI/configFromQuery/ConfigFromQueryTransformerEditor.tsx Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update public/app/core/components/TransformersUI/configFromQuery/ConfigFromQueryTransformerEditor.tsx Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update public/app/core/components/TransformersUI/configFromQuery/ConfigFromQueryTransformerEditor.tsx Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update public/app/core/components/TransformersUI/prepareTimeSeries/PrepareTimeSeriesEditor.tsx Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Docs: Variants instead of varients * Update public/app/core/components/TransformersUI/configFromQuery/ConfigFromQueryTransformerEditor.tsx Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com>
This commit is contained in:
@@ -116,6 +116,22 @@ We’ve made some changes to the plugins UI to help make it easier to discover a
|
||||
|
||||
New panel summaries and preview on the top level [Visualizations]({{< relref "../panels/visualizations/_index.md" >}}) page to help users pick or learn about specific visualizations more easily.
|
||||
|
||||
### Upcoming changes to the Select component
|
||||
|
||||
The `@grafana/ui` exposes a `Select` component, and its variants `MultiSelect`, `AsyncSelect`, and `AsyncMultiSelect`. We have made some internal changes to these components to make the behavior and positioning more consistent in all scenarios.
|
||||
|
||||
To test the changes, you can use the `menuShouldPortal` property:
|
||||
```jsx
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...otherProps}
|
||||
/>
|
||||
```
|
||||
|
||||
Tests are most likely to be affected. There are some tips for fixing these in the original pull request at [https://github.com/grafana/grafana/pull/36398](https://github.com/grafana/grafana/pull/36398).
|
||||
|
||||
We’d love as much feedback as possible about this change, because we are considering making this the default behavior in a future release of Grafana.
|
||||
|
||||
## Enterprise features
|
||||
|
||||
These features are included in the Grafana Enterprise edition.
|
||||
|
@@ -157,8 +157,9 @@ const addVariable = (config: PartialAddVariableConfig, isFirst: boolean): AddVar
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelect()
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e.components.Select.singleValue().should('have.text', 'Query').click().type(`${type}{enter}`);
|
||||
e2e.components.Select.singleValue().should('have.text', 'Query').click();
|
||||
});
|
||||
e2e.components.Select.option().should('be.visible').contains(type).click();
|
||||
}
|
||||
|
||||
if (label) {
|
||||
|
@@ -144,6 +144,7 @@ export class DataSourcePicker extends PureComponent<DataSourcePickerProps, DataS
|
||||
return (
|
||||
<div aria-label={selectors.components.DataSourcePicker.container}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className={styles.select}
|
||||
isMulti={false}
|
||||
isClearable={false}
|
||||
|
@@ -193,6 +193,7 @@ export class Cascader extends React.PureComponent<CascaderProps, CascaderState>
|
||||
<div>
|
||||
{isSearching ? (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
allowCustomValue={allowCustomValue}
|
||||
placeholder={placeholder}
|
||||
autoFocus={!focusCascade}
|
||||
|
@@ -100,6 +100,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
||||
|
||||
const accessSelect = (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={20}
|
||||
options={ACCESS_OPTIONS}
|
||||
value={ACCESS_OPTIONS.filter((o) => o.value === dataSourceConfig.access)[0] || DEFAULT_ACCESS_OPTION}
|
||||
|
@@ -43,6 +43,7 @@ export const TimeZonePicker: React.FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={selected}
|
||||
placeholder="Type to search (country, city, abbreviation)"
|
||||
autoFocus={autoFocus}
|
||||
|
@@ -28,7 +28,7 @@ describe('Field', () => {
|
||||
it('renders with the inputId of its children', () => {
|
||||
render(
|
||||
<Field label="My other label">
|
||||
<Select inputId="my-select-input" onChange={() => {}} />
|
||||
<Select menuShouldPortal inputId="my-select-input" onChange={() => {}} />
|
||||
</Field>
|
||||
);
|
||||
|
||||
|
@@ -111,7 +111,7 @@ const renderForm = (defaultValues?: FormDTO) => (
|
||||
rules={{
|
||||
required: true,
|
||||
}}
|
||||
render={({ field }) => <Select {...field} options={selectOptions} />}
|
||||
render={({ field }) => <Select menuShouldPortal {...field} options={selectOptions} />}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
|
@@ -43,6 +43,7 @@ export const withSelect = () => {
|
||||
return (
|
||||
<InlineField label="Select option">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={16}
|
||||
onChange={action('item selected')}
|
||||
options={[
|
||||
|
@@ -28,7 +28,7 @@ describe('InlineField', () => {
|
||||
it('renders with the inputId of its children', () => {
|
||||
render(
|
||||
<InlineField label="My other label">
|
||||
<Select inputId="my-select-input" onChange={() => {}} />
|
||||
<Select menuShouldPortal inputId="my-select-input" onChange={() => {}} />
|
||||
</InlineField>
|
||||
);
|
||||
|
||||
|
@@ -69,6 +69,7 @@ export const Basic: Story = (args) => {
|
||||
{(value, updateValue) => {
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...args}
|
||||
onChange={(value: SelectableValue<string>) => {
|
||||
action('onChanged fired')(value);
|
||||
|
@@ -20,7 +20,7 @@ export const FieldNameMatcherEditor = memo<MatcherUIProps<string>>((props) => {
|
||||
);
|
||||
|
||||
const selectedOption = selectOptions.find((v) => v.value === options);
|
||||
return <Select value={selectedOption} options={selectOptions} onChange={onChange} />;
|
||||
return <Select menuShouldPortal value={selectedOption} options={selectOptions} onChange={onChange} />;
|
||||
});
|
||||
FieldNameMatcherEditor.displayName = 'FieldNameMatcherEditor';
|
||||
|
||||
|
@@ -28,6 +28,7 @@ export const FieldNamePicker: React.FC<StandardEditorProps<string, FieldNamePick
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={selectedOption}
|
||||
options={selectOptions}
|
||||
onChange={onSelectChange}
|
||||
|
@@ -36,7 +36,7 @@ export const FieldNamesMatcherEditor = memo<MatcherUIProps<ByNamesMatcherOptions
|
||||
return <Input value={displayNames} readOnly={true} disabled={true} prefix={prefix} />;
|
||||
}
|
||||
|
||||
return <MultiSelect value={options.names} options={selectOptions} onChange={onChange} />;
|
||||
return <MultiSelect menuShouldPortal value={options.names} options={selectOptions} onChange={onChange} />;
|
||||
});
|
||||
FieldNamesMatcherEditor.displayName = 'FieldNameMatcherEditor';
|
||||
|
||||
|
@@ -16,7 +16,7 @@ export const FieldTypeMatcherEditor = memo<MatcherUIProps<string>>((props) => {
|
||||
);
|
||||
|
||||
const selectedOption = selectOptions.find((v) => v.value === options);
|
||||
return <Select value={selectedOption} options={selectOptions} onChange={onChange} />;
|
||||
return <Select menuShouldPortal value={selectedOption} options={selectOptions} onChange={onChange} />;
|
||||
});
|
||||
FieldTypeMatcherEditor.displayName = 'FieldTypeMatcherEditor';
|
||||
|
||||
|
@@ -23,7 +23,7 @@ export const FieldsByFrameRefIdMatcher = memo<MatcherUIProps<string>>((props) =>
|
||||
);
|
||||
|
||||
const selectedOption = selectOptions.find((v) => v.value === options);
|
||||
return <Select value={selectedOption} options={selectOptions} onChange={onChange} />;
|
||||
return <Select menuShouldPortal value={selectedOption} options={selectOptions} onChange={onChange} />;
|
||||
});
|
||||
|
||||
FieldsByFrameRefIdMatcher.displayName = 'FieldsByFrameRefIdMatcher';
|
||||
|
@@ -74,7 +74,14 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
|
||||
if (mode === FieldColorModeId.Fixed) {
|
||||
return (
|
||||
<div className={styles.group}>
|
||||
<Select minMenuHeight={200} options={options} value={mode} onChange={onModeChange} className={styles.select} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
minMenuHeight={200}
|
||||
options={options}
|
||||
value={mode}
|
||||
onChange={onModeChange}
|
||||
className={styles.select}
|
||||
/>
|
||||
<ColorValueEditor value={value?.fixedColor} onChange={onColorChange} />
|
||||
</div>
|
||||
);
|
||||
@@ -90,7 +97,7 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
|
||||
return (
|
||||
<>
|
||||
<div style={{ marginBottom: theme.spacing(2) }}>
|
||||
<Select minMenuHeight={200} options={options} value={mode} onChange={onModeChange} />
|
||||
<Select menuShouldPortal minMenuHeight={200} options={options} value={mode} onChange={onModeChange} />
|
||||
</div>
|
||||
<Field label="Color series by">
|
||||
<RadioButtonGroup value={value?.seriesBy ?? 'last'} options={seriesModes} onChange={onSeriesModeChange} />
|
||||
@@ -99,7 +106,7 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
|
||||
);
|
||||
}
|
||||
|
||||
return <Select minMenuHeight={200} options={options} value={mode} onChange={onModeChange} />;
|
||||
return <Select menuShouldPortal minMenuHeight={200} options={options} value={mode} onChange={onModeChange} />;
|
||||
};
|
||||
|
||||
interface ModeProps {
|
||||
|
@@ -44,6 +44,7 @@ export function SegmentSelect<T>({
|
||||
return (
|
||||
<div {...rest} ref={ref}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={width}
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
placeholder={placeholder}
|
||||
|
@@ -91,6 +91,7 @@ export const Basic: Story<StoryProps> = (args) => {
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -111,6 +112,7 @@ export const BasicSelectPlainValue: Story<StoryProps> = (args) => {
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -144,6 +146,7 @@ export const SelectWithOptionDescriptions: Story = (args) => {
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -166,6 +169,7 @@ export const MultiPlainValue: Story = (args) => {
|
||||
return (
|
||||
<>
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -184,6 +188,7 @@ export const MultiSelectWithOptionGroups: Story = (args) => {
|
||||
return (
|
||||
<>
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
options={[
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2', options: [{ label: '5', value: '5' }] },
|
||||
@@ -206,6 +211,7 @@ export const MultiSelectBasic: Story = (args) => {
|
||||
return (
|
||||
<>
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -229,6 +235,7 @@ export const MultiSelectAsync: Story = (args) => {
|
||||
|
||||
return (
|
||||
<AsyncMultiSelect
|
||||
menuShouldPortal
|
||||
loadOptions={loadAsyncOptions}
|
||||
defaultOptions
|
||||
value={value}
|
||||
@@ -250,6 +257,7 @@ export const BasicSelectAsync: Story = (args) => {
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
loadOptions={loadAsyncOptions}
|
||||
defaultOptions
|
||||
value={value}
|
||||
@@ -270,6 +278,7 @@ export const AutoMenuPlacement: Story = (args) => {
|
||||
<>
|
||||
<div style={{ width: '100%', height: '95vh', display: 'flex', alignItems: 'flex-end' }}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -294,6 +303,7 @@ export const CustomValueCreation: Story = (args) => {
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={[...options, ...customOptions]}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
|
@@ -19,11 +19,11 @@ describe('SelectBase', () => {
|
||||
];
|
||||
|
||||
it('renders without error', () => {
|
||||
render(<SelectBase onChange={onChangeHandler} />);
|
||||
render(<SelectBase menuShouldPortal onChange={onChangeHandler} />);
|
||||
});
|
||||
|
||||
it('renders empty options information', () => {
|
||||
render(<SelectBase onChange={onChangeHandler} />);
|
||||
render(<SelectBase menuShouldPortal onChange={onChangeHandler} />);
|
||||
userEvent.click(screen.getByText(/choose/i));
|
||||
expect(screen.queryByText(/no options found/i)).toBeVisible();
|
||||
});
|
||||
@@ -32,7 +32,7 @@ describe('SelectBase', () => {
|
||||
render(
|
||||
<>
|
||||
<label htmlFor="my-select">My select</label>
|
||||
<SelectBase onChange={onChangeHandler} options={options} inputId="my-select" />
|
||||
<SelectBase menuShouldPortal onChange={onChangeHandler} options={options} inputId="my-select" />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('SelectBase', () => {
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setValue(null)}>clear value</button>
|
||||
<SelectBase value={value} onChange={setValue} options={[option]} />
|
||||
<SelectBase menuShouldPortal value={value} onChange={setValue} options={[option]} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -61,7 +61,7 @@ describe('SelectBase', () => {
|
||||
describe('when openMenuOnFocus prop', () => {
|
||||
describe('is provided', () => {
|
||||
it('opens on focus', () => {
|
||||
render(<SelectBase onChange={onChangeHandler} openMenuOnFocus />);
|
||||
render(<SelectBase menuShouldPortal onChange={onChangeHandler} openMenuOnFocus />);
|
||||
fireEvent.focus(screen.getByRole('textbox'));
|
||||
expect(screen.queryByText(/no options found/i)).toBeVisible();
|
||||
});
|
||||
@@ -73,7 +73,7 @@ describe('SelectBase', () => {
|
||||
${'ArrowUp'}
|
||||
${' '}
|
||||
`('opens on arrow down/up or space', ({ key }) => {
|
||||
render(<SelectBase onChange={onChangeHandler} />);
|
||||
render(<SelectBase menuShouldPortal onChange={onChangeHandler} />);
|
||||
fireEvent.focus(screen.getByRole('textbox'));
|
||||
fireEvent.keyDown(screen.getByRole('textbox'), { key });
|
||||
expect(screen.queryByText(/no options found/i)).toBeVisible();
|
||||
@@ -112,6 +112,7 @@ describe('SelectBase', () => {
|
||||
it('should only display maxVisibleValues options, and additional number of values should be displayed as indicator', () => {
|
||||
render(
|
||||
<SelectBase
|
||||
menuShouldPortal
|
||||
onChange={onChangeHandler}
|
||||
isMulti={true}
|
||||
maxVisibleValues={3}
|
||||
@@ -128,6 +129,7 @@ describe('SelectBase', () => {
|
||||
it('should show all selected options when menu is open', () => {
|
||||
render(
|
||||
<SelectBase
|
||||
menuShouldPortal
|
||||
onChange={onChangeHandler}
|
||||
isMulti={true}
|
||||
maxVisibleValues={3}
|
||||
@@ -147,6 +149,7 @@ describe('SelectBase', () => {
|
||||
it('should not show all selected options when menu is open', () => {
|
||||
render(
|
||||
<SelectBase
|
||||
menuShouldPortal
|
||||
onChange={onChangeHandler}
|
||||
isMulti={true}
|
||||
maxVisibleValues={3}
|
||||
@@ -167,6 +170,7 @@ describe('SelectBase', () => {
|
||||
it('should always show all selected options', () => {
|
||||
render(
|
||||
<SelectBase
|
||||
menuShouldPortal
|
||||
onChange={onChangeHandler}
|
||||
isMulti={true}
|
||||
options={excessiveOptions}
|
||||
@@ -183,7 +187,7 @@ describe('SelectBase', () => {
|
||||
|
||||
describe('options', () => {
|
||||
it('renders menu with provided options', () => {
|
||||
render(<SelectBase options={options} onChange={onChangeHandler} />);
|
||||
render(<SelectBase menuShouldPortal options={options} onChange={onChangeHandler} />);
|
||||
userEvent.click(screen.getByText(/choose/i));
|
||||
const menuOptions = screen.getAllByLabelText('Select option');
|
||||
expect(menuOptions).toHaveLength(2);
|
||||
@@ -192,7 +196,7 @@ describe('SelectBase', () => {
|
||||
it('call onChange handler when option is selected', async () => {
|
||||
const spy = jest.fn();
|
||||
|
||||
render(<SelectBase onChange={spy} options={options} aria-label="My select" />);
|
||||
render(<SelectBase menuShouldPortal onChange={spy} options={options} aria-label="My select" />);
|
||||
|
||||
const selectEl = screen.getByLabelText('My select');
|
||||
expect(selectEl).toBeInTheDocument();
|
||||
|
@@ -117,8 +117,8 @@ export function SelectBase<T>({
|
||||
minMenuHeight,
|
||||
maxVisibleValues,
|
||||
menuPlacement = 'auto',
|
||||
menuPortalTarget,
|
||||
menuPosition,
|
||||
menuShouldPortal = false,
|
||||
noOptionsMessage = 'No options found',
|
||||
onBlur,
|
||||
onChange,
|
||||
@@ -138,10 +138,8 @@ export function SelectBase<T>({
|
||||
width,
|
||||
isValidNewOption,
|
||||
}: SelectBaseProps<T>) {
|
||||
if (menuPortalTarget !== undefined) {
|
||||
deprecationWarning('SelectBase', 'menuPortalTarget');
|
||||
} else {
|
||||
menuPortalTarget = document.body;
|
||||
if (menuShouldPortal === false) {
|
||||
deprecationWarning('SelectBase', 'menuShouldPortal={false}', 'menuShouldPortal={true}');
|
||||
}
|
||||
const theme = useTheme2();
|
||||
const styles = getSelectStyles(theme);
|
||||
@@ -206,9 +204,9 @@ export function SelectBase<T>({
|
||||
maxVisibleValues,
|
||||
menuIsOpen: isOpen,
|
||||
menuPlacement,
|
||||
menuPortalTarget,
|
||||
menuPosition,
|
||||
menuShouldBlockScroll: true,
|
||||
menuPortalTarget: menuShouldPortal ? document.body : undefined,
|
||||
menuShouldScrollIntoView: false,
|
||||
onBlur,
|
||||
onChange: onChangeWithEmpty,
|
||||
@@ -350,6 +348,7 @@ export function SelectBase<T>({
|
||||
bottom,
|
||||
position,
|
||||
minWidth: '100%',
|
||||
zIndex: theme.zIndex.dropdown,
|
||||
}),
|
||||
container: () => ({
|
||||
position: 'relative',
|
||||
|
@@ -41,13 +41,13 @@ export interface SelectCommonProps<T> {
|
||||
minMenuHeight?: number;
|
||||
maxVisibleValues?: number;
|
||||
menuPlacement?: 'auto' | 'bottom' | 'top';
|
||||
/**
|
||||
* @deprecated Setting `menuPortalTarget` to null will revert to previous `Select` behaviour.
|
||||
* This is provided as an escape hatch for cases where portalling is a breaking change (e.g. `Select` wrapped in `ClickOutsideWrapper`).
|
||||
* It's recommended to achieve the same behaviour using `onCloseMenu`, as this will be removed at some point.
|
||||
*/
|
||||
menuPortalTarget?: HTMLElement | null;
|
||||
menuPosition?: 'fixed' | 'absolute';
|
||||
/**
|
||||
* @deprecated
|
||||
* Setting to true will portal the menu to `document.body`.
|
||||
* This property will soon be removed and portalling will be the default behavior.
|
||||
*/
|
||||
menuShouldPortal?: boolean;
|
||||
/** The message to display when no options could be found */
|
||||
noOptionsMessage?: string;
|
||||
onBlur?: () => void;
|
||||
|
@@ -68,6 +68,7 @@ export class StatsPicker extends PureComponent<Props> {
|
||||
const select = fieldReducers.selectOptions(stats);
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={select.current}
|
||||
className={className}
|
||||
isClearable={!defaultStat}
|
||||
|
@@ -134,7 +134,12 @@ export const ThemeDemo = () => {
|
||||
<Input placeholder="Placeholder" value="Disabled value" />
|
||||
</Field>
|
||||
<Field label="Select">
|
||||
<Select options={selectOptions} value={selectValue} onChange={(v) => setSelectValue(v?.value!)} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={selectOptions}
|
||||
value={selectValue}
|
||||
onChange={(v) => setSelectValue(v?.value!)}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Radio label">
|
||||
<RadioButtonGroup options={radioOptions} value={radioValue} onChange={setRadioValue} />
|
||||
|
@@ -148,6 +148,7 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
|
||||
)}
|
||||
{mapping.type === MappingType.SpecialValue && (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={specialMatchOptions.find((v) => v.value === mapping.specialMatch)}
|
||||
options={specialMatchOptions}
|
||||
onChange={onChangeSpecialMatch}
|
||||
|
@@ -56,6 +56,7 @@ export function ValuePicker<T>({
|
||||
{isPicking && (
|
||||
<span>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
placeholder={label}
|
||||
options={options}
|
||||
aria-label={selectors.components.ValuePicker.select(label)}
|
||||
|
@@ -134,6 +134,7 @@ const ScaleDistributionEditor: React.FC<FieldOverrideEditorProps<ScaleDistributi
|
||||
/>
|
||||
{value.type === ScaleDistribution.Log && (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
allowCustomValue={false}
|
||||
autoFocus
|
||||
options={LOG_DISTRIBUTION_OPTIONS}
|
||||
|
@@ -69,7 +69,7 @@ export class InputQueryEditor extends PureComponent<Props, State> {
|
||||
<div>
|
||||
<InlineField label="Data" labelWidth={8}>
|
||||
<>
|
||||
<Select width={20} options={options} value={selected} onChange={this.onSourceChange} />
|
||||
<Select menuShouldPortal width={20} options={options} value={selected} onChange={this.onSourceChange} />
|
||||
{query.data ? (
|
||||
<div style={{ alignSelf: 'center' }}>{describeDataFrame(query.data)}</div>
|
||||
) : (
|
||||
|
@@ -50,6 +50,7 @@ export function FolderFilter({ onChange: propsOnChange, maxMenuHeight }: FolderF
|
||||
</span>
|
||||
)}
|
||||
<AsyncMultiSelect
|
||||
menuShouldPortal
|
||||
{...selectOptions}
|
||||
isLoading={loading}
|
||||
loadOptions={debouncedLoadOptions}
|
||||
|
@@ -56,7 +56,7 @@ export const PanelTypeFilter = ({ onChange: propsOnChange, maxMenuHeight }: Prop
|
||||
Clear types
|
||||
</span>
|
||||
)}
|
||||
<MultiSelect {...selectOptions} prefix={<Icon name="filter" />} aria-label="Panel Type filter" />
|
||||
<MultiSelect menuShouldPortal {...selectOptions} prefix={<Icon name="filter" />} aria-label="Panel Type filter" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -100,6 +100,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
|
||||
{() => (
|
||||
<HorizontalGroup>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
isSearchable={false}
|
||||
value={this.state.type}
|
||||
options={dashboardAclTargets}
|
||||
@@ -117,6 +118,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
|
||||
<span className={styles.label}>Can</span>
|
||||
|
||||
<Select
|
||||
menuShouldPortal
|
||||
isSearchable={false}
|
||||
value={this.state.permission}
|
||||
options={dashboardPermissionLevels}
|
||||
|
@@ -25,6 +25,7 @@ export default class DisabledPermissionListItem extends Component<Props, any> {
|
||||
<td>
|
||||
<div className="gf-form">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={dashboardPermissionLevels}
|
||||
onChange={() => {}}
|
||||
disabled={true}
|
||||
|
@@ -75,6 +75,7 @@ export default class PermissionsListItem extends PureComponent<Props> {
|
||||
<td className="query-keyword">Can</td>
|
||||
<td>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
isSearchable={false}
|
||||
options={dashboardPermissionLevels}
|
||||
onChange={this.onPermissionChanged}
|
||||
|
@@ -35,6 +35,7 @@ export const DashboardPicker: FC<Props> = ({ onChange, value, width, isClearable
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
width={width}
|
||||
isClearable={isClearable}
|
||||
defaultOptions={true}
|
||||
|
@@ -165,6 +165,7 @@ export class FolderPicker extends PureComponent<Props, State> {
|
||||
return (
|
||||
<div aria-label={selectors.components.FolderPicker.container}>
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
loadingMessage="Loading folders..."
|
||||
defaultOptions
|
||||
defaultValue={folder}
|
||||
|
@@ -24,6 +24,7 @@ export const MetricSelect: FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className={className}
|
||||
isMulti={false}
|
||||
isClearable={false}
|
||||
|
@@ -48,6 +48,7 @@ export class OrgPicker extends PureComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
className={className}
|
||||
isLoading={isLoading}
|
||||
defaultOptions={true}
|
||||
|
@@ -28,6 +28,7 @@ export const SortPicker: FC<Props> = ({ onChange, value, placeholder, filter })
|
||||
const selected = options?.find((opt) => opt.value === value);
|
||||
return !loading ? (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
key={value}
|
||||
width={25}
|
||||
onChange={onChange}
|
||||
|
@@ -57,6 +57,7 @@ export class TeamPicker extends Component<Props, State> {
|
||||
return (
|
||||
<div className="user-picker" data-testid="teamPicker">
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
isLoading={isLoading}
|
||||
defaultOptions={true}
|
||||
loadOptions={this.debouncedSearch}
|
||||
|
@@ -65,6 +65,7 @@ export class UserPicker extends Component<Props, State> {
|
||||
return (
|
||||
<div className="user-picker" data-testid="userPicker">
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
isClearable
|
||||
className={className}
|
||||
isLoading={isLoading}
|
||||
|
@@ -10,6 +10,7 @@ exports[`FolderPicker should render 1`] = `
|
||||
defaultValue={null}
|
||||
loadOptions={[Function]}
|
||||
loadingMessage="Loading folders..."
|
||||
menuShouldPortal={true}
|
||||
onChange={[Function]}
|
||||
onCreateOption={[Function]}
|
||||
value={null}
|
||||
|
@@ -145,6 +145,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
aria-label="User preferences home dashboard drop down"
|
||||
>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={dashboards.find((dashboard) => dashboard.id === homeDashboardId)}
|
||||
getOptionValue={(i) => i.id}
|
||||
getOptionLabel={this.getFullDashName}
|
||||
|
@@ -107,7 +107,7 @@ export const TagFilter: FC<Props> = ({
|
||||
Clear tags
|
||||
</span>
|
||||
)}
|
||||
<AsyncMultiSelect {...selectOptions} prefix={<Icon name="tag-alt" />} aria-label="Tag filter" />
|
||||
<AsyncMultiSelect menuShouldPortal {...selectOptions} prefix={<Icon name="tag-alt" />} aria-label="Tag filter" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -300,6 +300,7 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
allowCustomValue={true}
|
||||
placeholder="Field or number"
|
||||
options={leftNames}
|
||||
@@ -308,12 +309,14 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
onChange={this.onBinaryLeftChanged}
|
||||
/>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className="width-8 gf-form-spacing"
|
||||
options={ops}
|
||||
value={options.operator ?? ops[0].value}
|
||||
onChange={this.onBinaryOperationChanged}
|
||||
/>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
allowCustomValue={true}
|
||||
placeholder="Field or number"
|
||||
className="min-width-10"
|
||||
@@ -341,6 +344,7 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label width-8">Mode</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className="width-18"
|
||||
options={calculationModes}
|
||||
value={calculationModes.find((v) => v.value === mode)}
|
||||
|
@@ -57,6 +57,7 @@ export class ConcatenateTransformerEditor extends React.PureComponent<Concatenat
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label width-8">Name</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className="width-18"
|
||||
options={nameModes}
|
||||
value={nameModes.find((v) => v.value === frameNameMode)}
|
||||
|
@@ -77,6 +77,7 @@ export const FilterByValueFilterEditor: React.FC<Props> = (props) => {
|
||||
<div className="gf-form gf-form-spacing">
|
||||
<div className="gf-form-label width-7">Field</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className="min-width-15 max-width-24"
|
||||
placeholder="Field Name"
|
||||
options={fieldsAsOptions}
|
||||
@@ -87,6 +88,7 @@ export const FilterByValueFilterEditor: React.FC<Props> = (props) => {
|
||||
<div className="gf-form gf-form-spacing">
|
||||
<div className="gf-form-label">Match</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className="width-12"
|
||||
placeholder="Select test"
|
||||
options={matcherOptions}
|
||||
|
@@ -86,6 +86,7 @@ export const GroupByFieldConfiguration: React.FC<FieldProps> = ({ fieldName, con
|
||||
<div className={cx('gf-form', styles.cell)}>
|
||||
<div className={cx('gf-form-spacing', styles.rowSpacing)}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
className="width-12"
|
||||
options={options}
|
||||
value={config?.operation}
|
||||
|
@@ -42,6 +42,7 @@ export const LabelsAsFieldsTransformerEditor: React.FC<TransformerUIProps<Labels
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label width-8">Value field name</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
isClearable={true}
|
||||
allowCustomValue={false}
|
||||
placeholder="(Optional) Select label"
|
||||
|
@@ -64,6 +64,7 @@ export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransfor
|
||||
Mode
|
||||
</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={modes}
|
||||
value={modes.find((v) => v.value === options.mode) || modes[0]}
|
||||
onChange={onSelectMode}
|
||||
|
@@ -32,7 +32,7 @@ export const SeriesToFieldsTransformerEditor: React.FC<TransformerUIProps<Series
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label width-8">Field name</div>
|
||||
<Select options={fieldNames} value={options.byField} onChange={onSelectField} isClearable />
|
||||
<Select menuShouldPortal options={fieldNames} value={options.byField} onChange={onSelectField} isClearable />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -28,6 +28,7 @@ export const SortByTransformerEditor: React.FC<TransformerUIProps<SortByTransfor
|
||||
<InlineFieldRow key={`${s.field}/${index}`}>
|
||||
<InlineField label="Field" labelWidth={10} grow={true}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={fieldNames}
|
||||
value={s.field}
|
||||
placeholder="Select field"
|
||||
|
@@ -51,12 +51,12 @@ export function ConfigFromQueryTransformerEditor({ input, onChange, options }: P
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Config query" labelWidth={20}>
|
||||
<Select onChange={onRefIdChange} options={refIds} value={currentRefId} width={30} />
|
||||
<Select menuShouldPortal onChange={onRefIdChange} options={refIds} value={currentRefId} width={30} />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Apply to" labelWidth={20}>
|
||||
<Select onChange={onMatcherChange} options={matchers} value={currentMatcher.id} width={30} />
|
||||
<Select menuShouldPortal onChange={onMatcherChange} options={matchers} value={currentMatcher.id} width={30} />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
<InlineFieldRow>
|
||||
@@ -91,10 +91,10 @@ export const configFromQueryTransformRegistryItem: TransformerRegistryItem<Confi
|
||||
description: configFromDataTransformer.description,
|
||||
state: PluginState.beta,
|
||||
help: `
|
||||
### Use cases
|
||||
### Use cases
|
||||
|
||||
This transformation allow you select one query and from it extract standard options like
|
||||
**Min**, **Max**, **Unit** and **Thresholds** and apply it to other query results.
|
||||
This transformation allows you select one query and from it extract standard options such as
|
||||
**Min**, **Max**, **Unit**, and **Thresholds** and apply them to other query results.
|
||||
This enables dynamic query driven visualization configuration.
|
||||
|
||||
### Options
|
||||
@@ -130,15 +130,14 @@ Output (Same as Input[0] but now with config on the Value field)
|
||||
| 1626178119127 | 10 |
|
||||
| 1626178119129 | 30 |
|
||||
|
||||
As you can see each row in the source data becomes a separate field. Each field now also has a max
|
||||
config option set. Options like min, max, unit and thresholds are all part of field configuration
|
||||
and if set like this will be used by the visualization instead of any options manually configured
|
||||
Each row in the source data becomes a separate field. Each field now also has a maximum
|
||||
configuration option set. Options such as **min**, **max**, **unit**, and **thresholds** are all part of field configuration, and if they are set like this, they will be used by the visualization instead of any options that are manually configured.
|
||||
in the panel editor options pane.
|
||||
|
||||
## Value mappings
|
||||
|
||||
You can also transform a query result into value mappings. This is is a bit different as here every
|
||||
row in the config query result will be used to define a single value mapping row. See example below.
|
||||
You can also transform a query result into value mappings. This is is a bit different because every
|
||||
row in the configuration query result is used to define a single value mapping row. See the following example.
|
||||
|
||||
Config query result:
|
||||
|
||||
|
@@ -73,6 +73,7 @@ export function FieldToConfigMappingEditor({ frame, mappings, onChange, withRedu
|
||||
<td className={styles.labelCell}>{row.fieldName}</td>
|
||||
<td className={styles.selectCell} data-testid={`${row.fieldName}-config-key`}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={configProps}
|
||||
value={row.configOption}
|
||||
placeholder={row.placeholder}
|
||||
|
@@ -53,6 +53,7 @@ export function PrepareTimeSeriesEditor(props: TransformerUIProps<PrepareTimeSer
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Format" labelWidth={12}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={35}
|
||||
options={formats}
|
||||
value={formats.find((v) => v.value === options.format) || formats[0]}
|
||||
@@ -85,9 +86,9 @@ export const prepareTimeseriesTransformerRegistryItem: TransformerRegistryItem<P
|
||||
name: prepareTimeSeriesTransformer.name,
|
||||
description: prepareTimeSeriesTransformer.description,
|
||||
help: `
|
||||
### Use cases
|
||||
|
||||
This will take query results and transform them into a predictable timeseries format.
|
||||
### Use cases
|
||||
|
||||
This takes query results and transforms them into a predictable timeseries format.
|
||||
This transformer may be especially useful when using old panels that only expect the
|
||||
many-frame timeseries format.
|
||||
`,
|
||||
|
@@ -12,6 +12,7 @@ const options = Object.keys(OrgRole).map((key) => ({ label: key, value: key }));
|
||||
|
||||
export const OrgRolePicker: FC<Props> = ({ value, onChange, ...restProps }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={value}
|
||||
options={options}
|
||||
onChange={(val) => onChange(val.value as OrgRole)}
|
||||
|
@@ -106,6 +106,7 @@ export class AlertRuleListUnconnected extends PureComponent<Props> {
|
||||
|
||||
<div className="width-13">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={this.stateFilters}
|
||||
onChange={this.onStateFilterChanged}
|
||||
value={this.getStateFilter()}
|
||||
|
@@ -30,7 +30,7 @@ export const BasicSettings: FC<Props> = ({
|
||||
<Field label="Type">
|
||||
<InputControl
|
||||
name="type"
|
||||
render={({ field: { ref, ...field } }) => <Select {...field} options={channels} />}
|
||||
render={({ field: { ref, ...field } }) => <Select menuShouldPortal {...field} options={channels} />}
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
/>
|
||||
|
@@ -29,7 +29,7 @@ export const OptionElement: FC<Props> = ({ control, option, register, invalid })
|
||||
control={control}
|
||||
name={`${modelValue}`}
|
||||
render={({ field: { ref, ...field } }) => (
|
||||
<Select {...field} options={option.selectOptions ?? undefined} invalid={invalid} />
|
||||
<Select menuShouldPortal {...field} options={option.selectOptions ?? undefined} invalid={invalid} />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
@@ -46,6 +46,7 @@ export const AlertManagerPicker: FC<Props> = ({ onChange, current, disabled = fa
|
||||
data-testid="alertmanager-picker"
|
||||
>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={29}
|
||||
className="ds-picker select-container"
|
||||
backspaceRemovesValue={false}
|
||||
|
@@ -42,6 +42,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={styles.input}
|
||||
onChange={(value) => onChange(mapSelectValueToString(value))}
|
||||
@@ -68,6 +69,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
allowCustomValue
|
||||
className={styles.input}
|
||||
@@ -113,6 +115,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={styles.input}
|
||||
onChange={(value) => onChange(mapSelectValueToString(value))}
|
||||
@@ -147,6 +150,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={styles.input}
|
||||
onChange={(value) => onChange(mapSelectValueToString(value))}
|
||||
@@ -181,6 +185,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={styles.input}
|
||||
menuPlacement="top"
|
||||
|
@@ -117,6 +117,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={formStyles.input}
|
||||
onChange={(value) => onChange(mapSelectValueToString(value))}
|
||||
@@ -141,6 +142,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
allowCustomValue
|
||||
className={formStyles.input}
|
||||
@@ -188,6 +190,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={formStyles.input}
|
||||
onChange={(value) => onChange(mapSelectValueToString(value))}
|
||||
@@ -221,6 +224,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={formStyles.input}
|
||||
onChange={(value) => onChange(mapSelectValueToString(value))}
|
||||
@@ -254,6 +258,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
className={formStyles.input}
|
||||
menuPlacement="top"
|
||||
|
@@ -75,7 +75,13 @@ export function ChannelSubForm<R extends ChannelValues>({
|
||||
name={name('type')}
|
||||
defaultValue={defaultValues.type}
|
||||
render={({ field: { ref, onChange, ...field } }) => (
|
||||
<Select {...field} width={37} options={typeOptions} onChange={(value) => onChange(value?.value)} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
width={37}
|
||||
options={typeOptions}
|
||||
onChange={(value) => onChange(value?.value)}
|
||||
/>
|
||||
)}
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
|
@@ -96,6 +96,7 @@ const OptionInput: FC<Props & { id: string }> = ({ option, invalid, id, pathPref
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
options={option.selectOptions ?? undefined}
|
||||
invalid={invalid}
|
||||
|
@@ -73,7 +73,12 @@ export const AlertTypeStep: FC<Props> = ({ editingExistingRule }) => {
|
||||
>
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select {...field} options={alertTypeOptions} onChange={(v: SelectableValue) => onChange(v?.value)} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
options={alertTypeOptions}
|
||||
onChange={(v: SelectableValue) => onChange(v?.value)}
|
||||
/>
|
||||
)}
|
||||
name="type"
|
||||
control={control}
|
||||
|
@@ -30,6 +30,7 @@ export const CloudConditionsStep: FC = () => {
|
||||
name="forTimeUnit"
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
options={timeOptions}
|
||||
onChange={(value) => onChange(value?.value)}
|
||||
|
@@ -47,6 +47,7 @@ export const ConditionField: FC = () => {
|
||||
name="condition"
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
{...field}
|
||||
width={42}
|
||||
options={options}
|
||||
|
@@ -21,5 +21,5 @@ export const GrafanaAlertStatePicker: FC<Props> = ({ includeNoData, ...props })
|
||||
}
|
||||
return options.filter((opt) => opt.value !== GrafanaAlertStateDecision.NoData);
|
||||
}, [includeNoData]);
|
||||
return <Select options={opts} {...props} />;
|
||||
return <Select menuShouldPortal options={opts} {...props} />;
|
||||
};
|
||||
|
@@ -55,6 +55,7 @@ export const SelectWithAdd: FC<Props> = ({
|
||||
} else {
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={width}
|
||||
options={_options}
|
||||
value={value}
|
||||
|
@@ -147,6 +147,7 @@ export class AnnotationFieldMapper extends PureComponent<Props, State> {
|
||||
</td>
|
||||
{/* <td>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={valueOptions.find(v => v.value === mapping.source) || valueOptions[0]}
|
||||
options={valueOptions}
|
||||
onChange={(v: SelectableValue<AnnotationEventFieldSource>) => {
|
||||
@@ -156,6 +157,7 @@ export class AnnotationFieldMapper extends PureComponent<Props, State> {
|
||||
</td> */}
|
||||
<td>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={currentValue}
|
||||
options={picker}
|
||||
placeholder={row.placeholder || row.key}
|
||||
|
@@ -87,7 +87,7 @@ export const LinkSettingsEdit: React.FC<LinkSettingsEditProps> = ({ editLinkIdx,
|
||||
<Input name="title" id="title" value={linkSettings.title} onChange={onChange} autoFocus={isNew} />
|
||||
</Field>
|
||||
<Field label="Type">
|
||||
<Select value={linkSettings.type} options={linkTypeOptions} onChange={onTypeChange} />
|
||||
<Select menuShouldPortal value={linkSettings.type} options={linkTypeOptions} onChange={onTypeChange} />
|
||||
</Field>
|
||||
{linkSettings.type === 'dashboards' && (
|
||||
<>
|
||||
@@ -105,7 +105,7 @@ export const LinkSettingsEdit: React.FC<LinkSettingsEditProps> = ({ editLinkIdx,
|
||||
<Input name="tooltip" value={linkSettings.tooltip} onChange={onChange} placeholder="Open dashboard" />
|
||||
</Field>
|
||||
<Field label="Icon">
|
||||
<Select value={linkSettings.icon} options={linkIconOptions} onChange={onIconChange} />
|
||||
<Select menuShouldPortal value={linkSettings.icon} options={linkIconOptions} onChange={onIconChange} />
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
|
@@ -146,6 +146,7 @@ export function getPanelFrameCategory(props: OptionPaneRenderProps): OptionsPane
|
||||
const maxPerRowOptions = [2, 3, 4, 6, 8, 12].map((value) => ({ label: value.toString(), value }));
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={maxPerRowOptions}
|
||||
value={panel.maxPerRow}
|
||||
onChange={(value) => onPanelConfigChange('maxPerRow', value.value)}
|
||||
|
@@ -36,5 +36,5 @@ export const RepeatRowSelect: FC<Props> = ({ repeat, onChange }) => {
|
||||
|
||||
const onSelectChange = useCallback((option: SelectableValue<string | null>) => onChange(option.value!), [onChange]);
|
||||
|
||||
return <Select value={repeat} onChange={onSelectChange} options={variableOptions} />;
|
||||
return <Select menuShouldPortal value={repeat} onChange={onSelectChange} options={variableOptions} />;
|
||||
};
|
||||
|
@@ -226,7 +226,13 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
||||
<Input width={30} value={snapshotName} onChange={this.onSnapshotNameChange} />
|
||||
</Field>
|
||||
<Field label="Expire">
|
||||
<Select width={30} options={expireOptions} value={selectedExpireOption} onChange={this.onExpireChange} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={30}
|
||||
options={expireOptions}
|
||||
value={selectedExpireOption}
|
||||
onChange={this.onExpireChange}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Timeout (seconds)"
|
||||
|
@@ -200,6 +200,7 @@ export function RichHistoryQueriesTab(props: Props) {
|
||||
{!activeDatasourceOnly && (
|
||||
<div aria-label="Filter datasources" className={styles.multiselect}>
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
options={listOfDatasources}
|
||||
value={datasourceFilters}
|
||||
placeholder="Filter queries for data sources(s)"
|
||||
@@ -218,6 +219,7 @@ export function RichHistoryQueriesTab(props: Props) {
|
||||
</div>
|
||||
<div aria-label="Sort queries" className={styles.sort}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={sortOrderOptions.filter((order) => order.value === sortOrder)}
|
||||
options={sortOrderOptions}
|
||||
placeholder="Sort queries by"
|
||||
|
@@ -84,7 +84,12 @@ export function RichHistorySettings(props: RichHistorySettingsProps) {
|
||||
className="space-between"
|
||||
>
|
||||
<div className={styles.input}>
|
||||
<Select value={selectedOption} options={retentionPeriodOptions} onChange={onChangeRetentionPeriod}></Select>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={selectedOption}
|
||||
options={retentionPeriodOptions}
|
||||
onChange={onChangeRetentionPeriod}
|
||||
></Select>
|
||||
</div>
|
||||
</Field>
|
||||
<Field label="Default active tab" description=" " className="space-between">
|
||||
|
@@ -118,6 +118,7 @@ export function RichHistoryStarredTab(props: Props) {
|
||||
{!activeDatasourceOnly && (
|
||||
<div aria-label="Filter datasources" className={styles.multiselect}>
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
options={listOfDatasources}
|
||||
value={datasourceFilters}
|
||||
placeholder="Filter queries for specific data sources(s)"
|
||||
@@ -136,6 +137,7 @@ export function RichHistoryStarredTab(props: Props) {
|
||||
</div>
|
||||
<div aria-label="Sort queries" className={styles.sort}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={sortOrderOptions}
|
||||
value={sortOrderOptions.filter((order) => order.value === sortOrder)}
|
||||
placeholder="Sort queries by"
|
||||
|
@@ -45,7 +45,13 @@ export class ExpressionQueryEditor extends PureComponent<Props> {
|
||||
return (
|
||||
<div>
|
||||
<InlineField label="Operation" labelWidth={labelWidth}>
|
||||
<Select options={gelTypes} value={selected} onChange={this.onSelectExpressionType} width={25} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={gelTypes}
|
||||
value={selected}
|
||||
onChange={this.onSelectExpressionType}
|
||||
width={25}
|
||||
/>
|
||||
</InlineField>
|
||||
{this.renderExpressionType()}
|
||||
</div>
|
||||
|
@@ -79,6 +79,7 @@ export const Condition: FC<Props> = ({ condition, index, onChange, onRemoveCondi
|
||||
/>
|
||||
)}
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={reducerFunctions}
|
||||
onChange={onReducerFunctionChange}
|
||||
width={20}
|
||||
@@ -86,6 +87,7 @@ export const Condition: FC<Props> = ({ condition, index, onChange, onRemoveCondi
|
||||
/>
|
||||
<div className={styles.button}>OF</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
onChange={onRefIdChange}
|
||||
options={refIds}
|
||||
width={15}
|
||||
|
@@ -24,10 +24,10 @@ export const Reduce: FC<Props> = ({ labelWidth, onChange, refIds, query }) => {
|
||||
return (
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Function" labelWidth={labelWidth}>
|
||||
<Select options={reducerTypes} value={reducer} onChange={onSelectReducer} width={25} />
|
||||
<Select menuShouldPortal options={reducerTypes} value={reducer} onChange={onSelectReducer} width={25} />
|
||||
</InlineField>
|
||||
<InlineField label="Input" labelWidth={labelWidth}>
|
||||
<Select onChange={onRefIdChange} options={refIds} value={query.expression} width={20} />
|
||||
<Select menuShouldPortal onChange={onRefIdChange} options={refIds} value={query.expression} width={20} />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
);
|
||||
|
@@ -34,7 +34,7 @@ export const Resample: FC<Props> = ({ labelWidth, onChange, refIds, query }) =>
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Input" labelWidth={labelWidth}>
|
||||
<Select onChange={onRefIdChange} options={refIds} value={query.expression} width={20} />
|
||||
<Select menuShouldPortal onChange={onRefIdChange} options={refIds} value={query.expression} width={20} />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
<InlineFieldRow>
|
||||
@@ -42,10 +42,22 @@ export const Resample: FC<Props> = ({ labelWidth, onChange, refIds, query }) =>
|
||||
<Input onChange={onWindowChange} value={query.window} width={15} />
|
||||
</InlineField>
|
||||
<InlineField label="Downsample">
|
||||
<Select options={downsamplingTypes} value={downsampler} onChange={onSelectDownsampler} width={25} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={downsamplingTypes}
|
||||
value={downsampler}
|
||||
onChange={onSelectDownsampler}
|
||||
width={25}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Upsample">
|
||||
<Select options={upsamplingTypes} value={upsampler} onChange={onSelectUpsampler} width={25} />
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={upsamplingTypes}
|
||||
value={upsampler}
|
||||
onChange={onSelectUpsampler}
|
||||
width={25}
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
</>
|
||||
|
@@ -104,6 +104,7 @@ export const InspectDataOptions: FC<Props> = ({
|
||||
{data!.length > 1 && (
|
||||
<Field label="Show data frame">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
options={selectableOptions}
|
||||
value={selectedDataFrame}
|
||||
onChange={onDataFrameChange}
|
||||
|
@@ -132,7 +132,7 @@ export class InspectJSONTab extends PureComponent<Props, State> {
|
||||
<>
|
||||
<div className={styles.toolbar} aria-label={selectors.components.PanelInspector.Json.content}>
|
||||
<Field label="Select source" className="flex-grow-1">
|
||||
<Select options={jsonOptions} value={selected} onChange={this.onSelectChanged} />
|
||||
<Select menuShouldPortal options={jsonOptions} value={selected} onChange={this.onSelectChanged} />
|
||||
</Field>
|
||||
{this.hasPanelJSON && isPanelJSON && canEdit && (
|
||||
<Button className={styles.toolbarItem} onClick={this.onApplyPanelModel}>
|
||||
|
@@ -54,6 +54,7 @@ export function OpenLibraryPanelModal({ libraryPanel, onDismiss }: OpenLibraryPa
|
||||
.Please choose which dashboard to view the panel in:
|
||||
</p>
|
||||
<AsyncSelect
|
||||
menuShouldPortal
|
||||
isClearable
|
||||
isLoading={loading}
|
||||
defaultOptions={true}
|
||||
|
@@ -86,6 +86,7 @@ export default function Browse({ route }: GrafanaRouteComponentProps): ReactElem
|
||||
</div>
|
||||
<div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={24}
|
||||
value={sortBy}
|
||||
onChange={onSortByChange}
|
||||
|
@@ -55,6 +55,7 @@ export class TeamMemberRow extends PureComponent<Props> {
|
||||
<div className="gf-form">
|
||||
{signedInUserIsTeamAdmin && (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
isSearchable={false}
|
||||
options={teamsPermissionLevels}
|
||||
onChange={(item) => this.onPermissionChange(item, member)}
|
||||
|
@@ -107,6 +107,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
|
||||
isMulti={false}
|
||||
isSearchable={false}
|
||||
maxMenuHeight={300}
|
||||
menuShouldPortal={true}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
@@ -197,6 +198,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
|
||||
isMulti={false}
|
||||
isSearchable={false}
|
||||
maxMenuHeight={300}
|
||||
menuShouldPortal={true}
|
||||
onChange={[Function]}
|
||||
openMenuOnFocus={false}
|
||||
options={
|
||||
|
@@ -33,6 +33,7 @@ export function VariableSelectField({
|
||||
</InlineFormLabel>
|
||||
<div aria-label={ariaLabel}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
width={width ?? 25}
|
||||
|
@@ -21,6 +21,7 @@ export const Aggregation: FC<Props> = (props) => {
|
||||
return (
|
||||
<QueryEditorField labelWidth={18} label="Group by function" data-testid="cloud-monitoring-aggregation">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={16}
|
||||
onChange={({ value }) => props.onChange(value!)}
|
||||
value={selected}
|
||||
|
@@ -20,6 +20,7 @@ export const AlignmentFunction: FC<Props> = ({ query, templateVariableOptions, o
|
||||
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
onChange={({ value }) => onChange({ ...query, perSeriesAligner: value! })}
|
||||
value={[...alignOptions, ...templateVariableOptions].find((s) => s.value === perSeriesAligner)}
|
||||
|
@@ -24,6 +24,7 @@ export const AlignmentPeriod: FC<Props> = ({ templateVariableOptions, onChange,
|
||||
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={selectWidth}
|
||||
onChange={({ value }) => onChange({ ...query, alignmentPeriod: value! })}
|
||||
value={[...options, ...templateVariableOptions].find((s) => s.value === query.alignmentPeriod)}
|
||||
|
@@ -71,6 +71,7 @@ export class ConfigEditor extends PureComponent<Props> {
|
||||
<FieldSet>
|
||||
<InlineField label="Authentication type" labelWidth={20}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={40}
|
||||
value={authTypes.find((x) => x.value === jsonData.authenticationType) || authTypes[0]}
|
||||
options={authTypes}
|
||||
|
@@ -22,6 +22,7 @@ export const VariableQueryField: FC<VariableQueryFieldProps> = ({
|
||||
return (
|
||||
<InlineField label={label} labelWidth={20}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={25}
|
||||
allowCustomValue={allowCustomValue}
|
||||
value={value}
|
||||
|
@@ -32,6 +32,7 @@ export const GroupBy: FunctionComponent<Props> = ({
|
||||
tooltip="You can reduce the amount of data returned for a metric by combining different time series. To combine multiple time series, you can specify a grouping and a function. Grouping is done on the basis of labels. The grouping function is used to combine the time series in the group into a single time series."
|
||||
>
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
width={INPUT_WIDTH}
|
||||
placeholder="Choose label"
|
||||
options={options}
|
||||
|
@@ -54,6 +54,7 @@ export const LabelFilter: FunctionComponent<Props> = ({
|
||||
const AddFilter = () => {
|
||||
return (
|
||||
<Select
|
||||
menuShouldPortal
|
||||
allowCustomValue
|
||||
options={[variableOptionGroup, ...labelsToGroupedOptions(Object.keys(labels))]}
|
||||
onChange={({ value: key = '' }) =>
|
||||
@@ -77,6 +78,7 @@ export const LabelFilter: FunctionComponent<Props> = ({
|
||||
{filters.map(({ key, operator, value, condition }, index) => (
|
||||
<HorizontalGroup key={index} spacing="xs" width="auto">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
allowCustomValue
|
||||
formatCreateLabel={(v) => `Use label key: ${v}`}
|
||||
@@ -91,6 +93,7 @@ export const LabelFilter: FunctionComponent<Props> = ({
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
value={operator}
|
||||
options={operators.map(toOption)}
|
||||
onChange={({ value: operator = '=' }) =>
|
||||
@@ -100,6 +103,7 @@ export const LabelFilter: FunctionComponent<Props> = ({
|
||||
renderControl={OperatorButton}
|
||||
/>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
formatCreateLabel={(v) => `Use label value: ${v}`}
|
||||
allowCustomValue
|
||||
|
@@ -123,6 +123,7 @@ export function Metrics(props: Props) {
|
||||
<QueryEditorRow>
|
||||
<QueryEditorField labelWidth={LABEL_WIDTH} label="Service">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
onChange={onServiceChange}
|
||||
value={[...services, ...templateVariableOptions].find((s) => s.value === service)}
|
||||
@@ -138,6 +139,7 @@ export function Metrics(props: Props) {
|
||||
</QueryEditorField>
|
||||
<QueryEditorField label="Metric name" labelWidth={INNER_LABEL_WIDTH}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
onChange={onMetricTypeChange}
|
||||
value={[...metrics, ...templateVariableOptions].find((s) => s.value === metricType)}
|
||||
|
@@ -29,6 +29,7 @@ export function Project({ projectName, datasource, onChange, templateVariableOpt
|
||||
return (
|
||||
<QueryEditorRow label="Project">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
allowCustomValue
|
||||
formatCreateLabel={(v) => `Use project: ${v}`}
|
||||
|
@@ -76,6 +76,7 @@ export class QueryEditor extends PureComponent<Props> {
|
||||
}
|
||||
>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
value={queryType}
|
||||
options={QUERY_TYPES}
|
||||
|
@@ -36,6 +36,7 @@ export const SLO: React.FC<Props> = ({ query, templateVariableOptions, onChange,
|
||||
return (
|
||||
<QueryEditorRow label="SLO">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
allowCustomValue
|
||||
value={query?.sloId && { value: query?.sloId, label: query?.sloName || query?.sloId }}
|
||||
|
@@ -17,6 +17,7 @@ export const Selector: React.FC<Props> = ({ query, templateVariableOptions, onCh
|
||||
return (
|
||||
<QueryEditorRow label="Selector">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
allowCustomValue
|
||||
value={[...SELECTORS, ...templateVariableOptions].find((s) => s.value === query?.selectorName ?? '')}
|
||||
|
@@ -36,6 +36,7 @@ export const Service: React.FC<Props> = ({ query, templateVariableOptions, onCha
|
||||
return (
|
||||
<QueryEditorRow label="Service">
|
||||
<Select
|
||||
menuShouldPortal
|
||||
width={SELECT_WIDTH}
|
||||
allowCustomValue
|
||||
value={query?.serviceId && { value: query?.serviceId, label: query?.serviceName || query?.serviceId }}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user