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:
Ashley Harrison
2021-08-04 15:47:53 +01:00
committed by GitHub
parent a0f94866b4
commit 8aa3845f70
153 changed files with 347 additions and 71 deletions

View File

@@ -116,6 +116,22 @@ Weve 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).
Wed 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.

View File

@@ -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) {

View File

@@ -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}

View File

@@ -193,6 +193,7 @@ export class Cascader extends React.PureComponent<CascaderProps, CascaderState>
<div>
{isSearching ? (
<Select
menuShouldPortal
allowCustomValue={allowCustomValue}
placeholder={placeholder}
autoFocus={!focusCascade}

View File

@@ -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}

View File

@@ -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}

View File

@@ -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>
);

View File

@@ -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>

View File

@@ -43,6 +43,7 @@ export const withSelect = () => {
return (
<InlineField label="Select option">
<Select
menuShouldPortal
width={16}
onChange={action('item selected')}
options={[

View File

@@ -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>
);

View File

@@ -69,6 +69,7 @@ export const Basic: Story = (args) => {
{(value, updateValue) => {
return (
<Select
menuShouldPortal
{...args}
onChange={(value: SelectableValue<string>) => {
action('onChanged fired')(value);

View File

@@ -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';

View File

@@ -28,6 +28,7 @@ export const FieldNamePicker: React.FC<StandardEditorProps<string, FieldNamePick
return (
<>
<Select
menuShouldPortal
value={selectedOption}
options={selectOptions}
onChange={onSelectChange}

View File

@@ -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';

View File

@@ -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';

View File

@@ -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';

View File

@@ -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 {

View File

@@ -44,6 +44,7 @@ export function SegmentSelect<T>({
return (
<div {...rest} ref={ref}>
<Select
menuShouldPortal
width={width}
noOptionsMessage={noOptionsMessage}
placeholder={placeholder}

View File

@@ -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) => {

View File

@@ -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();

View File

@@ -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',

View File

@@ -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;

View File

@@ -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}

View File

@@ -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} />

View File

@@ -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}

View File

@@ -56,6 +56,7 @@ export function ValuePicker<T>({
{isPicking && (
<span>
<Select
menuShouldPortal
placeholder={label}
options={options}
aria-label={selectors.components.ValuePicker.select(label)}

View File

@@ -134,6 +134,7 @@ const ScaleDistributionEditor: React.FC<FieldOverrideEditorProps<ScaleDistributi
/>
{value.type === ScaleDistribution.Log && (
<Select
menuShouldPortal
allowCustomValue={false}
autoFocus
options={LOG_DISTRIBUTION_OPTIONS}

View File

@@ -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>
) : (

View File

@@ -50,6 +50,7 @@ export function FolderFilter({ onChange: propsOnChange, maxMenuHeight }: FolderF
</span>
)}
<AsyncMultiSelect
menuShouldPortal
{...selectOptions}
isLoading={loading}
loadOptions={debouncedLoadOptions}

View File

@@ -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>
);
};

View File

@@ -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}

View File

@@ -25,6 +25,7 @@ export default class DisabledPermissionListItem extends Component<Props, any> {
<td>
<div className="gf-form">
<Select
menuShouldPortal
options={dashboardPermissionLevels}
onChange={() => {}}
disabled={true}

View File

@@ -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}

View File

@@ -35,6 +35,7 @@ export const DashboardPicker: FC<Props> = ({ onChange, value, width, isClearable
return (
<AsyncSelect
menuShouldPortal
width={width}
isClearable={isClearable}
defaultOptions={true}

View File

@@ -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}

View File

@@ -24,6 +24,7 @@ export const MetricSelect: FC<Props> = (props) => {
return (
<Select
menuShouldPortal
className={className}
isMulti={false}
isClearable={false}

View File

@@ -48,6 +48,7 @@ export class OrgPicker extends PureComponent<Props, State> {
return (
<AsyncSelect
menuShouldPortal
className={className}
isLoading={isLoading}
defaultOptions={true}

View File

@@ -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}

View File

@@ -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}

View File

@@ -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}

View File

@@ -10,6 +10,7 @@ exports[`FolderPicker should render 1`] = `
defaultValue={null}
loadOptions={[Function]}
loadingMessage="Loading folders..."
menuShouldPortal={true}
onChange={[Function]}
onCreateOption={[Function]}
value={null}

View File

@@ -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}

View File

@@ -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>
);
};

View File

@@ -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)}

View File

@@ -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)}

View File

@@ -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}

View File

@@ -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}

View File

@@ -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"

View File

@@ -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}

View File

@@ -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>
);

View File

@@ -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"

View File

@@ -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:

View File

@@ -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}

View File

@@ -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.
`,

View File

@@ -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)}

View File

@@ -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()}

View File

@@ -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 }}
/>

View File

@@ -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} />
)}
/>
);

View File

@@ -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}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 }}

View File

@@ -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}

View File

@@ -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}

View File

@@ -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)}

View File

@@ -47,6 +47,7 @@ export const ConditionField: FC = () => {
name="condition"
render={({ field: { onChange, ref, ...field } }) => (
<Select
menuShouldPortal
{...field}
width={42}
options={options}

View File

@@ -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} />;
};

View File

@@ -55,6 +55,7 @@ export const SelectWithAdd: FC<Props> = ({
} else {
return (
<Select
menuShouldPortal
width={width}
options={_options}
value={value}

View File

@@ -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}

View File

@@ -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>
</>
)}

View File

@@ -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)}

View File

@@ -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} />;
};

View File

@@ -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)"

View File

@@ -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"

View File

@@ -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">

View File

@@ -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"

View File

@@ -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>

View File

@@ -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}

View File

@@ -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>
);

View File

@@ -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>
</>

View File

@@ -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}

View File

@@ -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}>

View File

@@ -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}

View File

@@ -86,6 +86,7 @@ export default function Browse({ route }: GrafanaRouteComponentProps): ReactElem
</div>
<div>
<Select
menuShouldPortal
width={24}
value={sortBy}
onChange={onSortByChange}

View File

@@ -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)}

View File

@@ -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={

View File

@@ -33,6 +33,7 @@ export function VariableSelectField({
</InlineFormLabel>
<div aria-label={ariaLabel}>
<Select
menuShouldPortal
onChange={onChange}
value={value}
width={width ?? 25}

View File

@@ -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}

View File

@@ -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)}

View File

@@ -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)}

View File

@@ -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}

View File

@@ -22,6 +22,7 @@ export const VariableQueryField: FC<VariableQueryFieldProps> = ({
return (
<InlineField label={label} labelWidth={20}>
<Select
menuShouldPortal
width={25}
allowCustomValue={allowCustomValue}
value={value}

View File

@@ -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}

View File

@@ -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

View File

@@ -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)}

View File

@@ -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}`}

View File

@@ -76,6 +76,7 @@ export class QueryEditor extends PureComponent<Props> {
}
>
<Select
menuShouldPortal
width={SELECT_WIDTH}
value={queryType}
options={QUERY_TYPES}

View File

@@ -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 }}

View File

@@ -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 ?? '')}

View File

@@ -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