diff --git a/packages/grafana-ui/src/components/Segment/Segment.story.tsx b/packages/grafana-ui/src/components/Segment/Segment.story.tsx
index 949e3cdadac..9de2b52933d 100644
--- a/packages/grafana-ui/src/components/Segment/Segment.story.tsx
+++ b/packages/grafana-ui/src/components/Segment/Segment.story.tsx
@@ -80,6 +80,38 @@ SegmentStories.add('Grouped Array Options', () => {
);
});
+SegmentStories.add('With custom options allowed', () => {
+ const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption);
+ return (
+
+ {(value, updateValue) => (
+ <>
+
+
+ Segment Name
+
+
) => {
+ updateValue(value);
+ action('Segment value changed')(value);
+ }}
+ />
+ ) => action('New value added')(value)}
+ options={options}
+ />
+
+ >
+ )}
+
+ );
+});
+
const CustomLabelComponent = ({ value }: any) =>
custom({value})
;
SegmentStories.add('Custom Label Field', () => {
diff --git a/packages/grafana-ui/src/components/Segment/Segment.tsx b/packages/grafana-ui/src/components/Segment/Segment.tsx
index 9f064549634..6d4b062fd48 100644
--- a/packages/grafana-ui/src/components/Segment/Segment.tsx
+++ b/packages/grafana-ui/src/components/Segment/Segment.tsx
@@ -13,6 +13,7 @@ export function Segment({
onChange,
Component,
className,
+ allowCustomValue,
}: React.PropsWithChildren>) {
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
@@ -25,6 +26,7 @@ export function Segment({
width={width}
options={options}
onClickOutside={() => setExpanded(false)}
+ allowCustomValue={allowCustomValue}
onChange={value => {
setExpanded(false);
onChange(value);
diff --git a/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx b/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx
index cee3b401528..75aa2ec4c3e 100644
--- a/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx
+++ b/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx
@@ -81,8 +81,39 @@ SegmentStories.add('Grouped Array Options', () => {
);
});
-const CustomLabelComponent = ({ value }: any) => custom({value})
;
+SegmentStories.add('With custom options allowed', () => {
+ const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption);
+ return (
+
+ {(value, updateValue) => (
+ <>
+
+
+ Segment Name
+
+
loadOptions(options)}
+ onChange={value => {
+ updateValue(value);
+ action('Segment value changed')(value);
+ }}
+ />
+ action('New value added')(value)}
+ loadOptions={() => loadOptions(options)}
+ />
+
+ >
+ )}
+
+ );
+});
+const CustomLabelComponent = ({ value }: any) => custom({value})
;
SegmentStories.add('Custom Label Field', () => {
return (
diff --git a/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx b/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx
index cf529cf55c8..599441b0442 100644
--- a/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx
+++ b/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx
@@ -14,6 +14,7 @@ export function SegmentAsync({
loadOptions,
Component,
className,
+ allowCustomValue,
}: React.PropsWithChildren>) {
const [selectPlaceholder, setSelectPlaceholder] = useState('');
const [loadedOptions, setLoadedOptions] = useState>>([]);
@@ -38,6 +39,7 @@ export function SegmentAsync({
width={width}
options={loadedOptions}
noOptionsMessage={selectPlaceholder}
+ allowCustomValue={allowCustomValue}
onClickOutside={() => {
setSelectPlaceholder('');
setLoadedOptions([]);
diff --git a/packages/grafana-ui/src/components/Segment/SegmentSelect.tsx b/packages/grafana-ui/src/components/Segment/SegmentSelect.tsx
index f0c5a643b3d..de3aaa64f46 100644
--- a/packages/grafana-ui/src/components/Segment/SegmentSelect.tsx
+++ b/packages/grafana-ui/src/components/Segment/SegmentSelect.tsx
@@ -10,6 +10,7 @@ export interface Props {
onClickOutside: () => void;
width: number;
noOptionsMessage?: string;
+ allowCustomValue?: boolean;
}
export function SegmentSelect({
@@ -18,6 +19,7 @@ export function SegmentSelect({
onClickOutside,
width,
noOptionsMessage = '',
+ allowCustomValue = false,
}: React.PropsWithChildren>) {
const ref = useRef(null);
@@ -39,6 +41,7 @@ export function SegmentSelect({
isOpen={true}
onChange={({ value }) => onChange(value!)}
options={options}
+ allowCustomValue={allowCustomValue}
/>
);
diff --git a/packages/grafana-ui/src/components/Segment/types.ts b/packages/grafana-ui/src/components/Segment/types.ts
index 792c6dfb23d..89d809c7777 100644
--- a/packages/grafana-ui/src/components/Segment/types.ts
+++ b/packages/grafana-ui/src/components/Segment/types.ts
@@ -5,4 +5,5 @@ export interface SegmentProps {
value?: T;
Component?: ReactElement;
className?: string;
+ allowCustomValue?: boolean;
}
diff --git a/packages/grafana-ui/src/components/Select/Select.story.tsx b/packages/grafana-ui/src/components/Select/Select.story.tsx
index 27601611c5d..6532b250230 100644
--- a/packages/grafana-ui/src/components/Select/Select.story.tsx
+++ b/packages/grafana-ui/src/components/Select/Select.story.tsx
@@ -36,3 +36,30 @@ SelectStories.add('default', () => {
);
});
+
+SelectStories.add('With allowCustomValue', () => {
+ const intialState: SelectableValue = { label: 'A label', value: 'A value' };
+ const value = object>('Selected Value:', intialState);
+ const options = object>>('Options:', [
+ intialState,
+ { label: 'Another label', value: 'Another value' },
+ ]);
+
+ return (
+
+ {(value, updateValue) => {
+ return (
+
+ );
+});
diff --git a/packages/grafana-ui/src/components/Select/Select.tsx b/packages/grafana-ui/src/components/Select/Select.tsx
index 3721ae4ef37..0d7bc2213d3 100644
--- a/packages/grafana-ui/src/components/Select/Select.tsx
+++ b/packages/grafana-ui/src/components/Select/Select.tsx
@@ -4,7 +4,9 @@ import React, { PureComponent } from 'react';
// Ignoring because I couldn't get @types/react-select work wih Torkel's fork
// @ts-ignore
-import { default as ReactSelect } from '@torkelo/react-select';
+import { default as ReactSelect, Creatable } from '@torkelo/react-select';
+// @ts-ignore
+import { Creatable } from '@torkelo/react-select/lib/creatable';
// @ts-ignore
import { default as ReactAsyncSelect } from '@torkelo/react-select/lib/Async';
// @ts-ignore
@@ -48,6 +50,7 @@ export interface CommonProps {
onOpenMenu?: () => void;
onCloseMenu?: () => void;
tabSelectsValue?: boolean;
+ allowCustomValue: boolean;
}
export interface SelectProps extends CommonProps {
@@ -83,6 +86,7 @@ export class Select extends PureComponent> {
backspaceRemovesValue: true,
maxMenuHeight: 300,
tabSelectsValue: true,
+ allowCustomValue: false,
components: {
Option: SelectOption,
SingleValue,
@@ -120,6 +124,7 @@ export class Select extends PureComponent> {
tabSelectsValue,
onCloseMenu,
onOpenMenu,
+ allowCustomValue,
} = this.props;
let widthClass = '';
@@ -127,6 +132,14 @@ export class Select extends PureComponent> {
widthClass = 'width-' + width;
}
+ let SelectComponent: ReactSelect | Creatable = ReactSelect;
+ const creatableOptions: any = {};
+
+ if (allowCustomValue) {
+ SelectComponent = Creatable;
+ creatableOptions.formatCreateLabel = (input: string) => input;
+ }
+
const selectClassNames = classNames('gf-form-input', 'gf-form-input--form-dropdown', widthClass, className);
const selectComponents = { ...Select.defaultProps.components, ...components };
@@ -134,7 +147,7 @@ export class Select extends PureComponent> {
{(onOpenMenuInternal, onCloseMenuInternal) => {
return (
- extends PureComponent> {
onMenuOpen={onOpenMenuInternal}
onMenuClose={onCloseMenuInternal}
tabSelectsValue={tabSelectsValue}
+ {...creatableOptions}
/>
);
}}
diff --git a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
index 501a0829942..3f33ab7bf63 100644
--- a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
+++ b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
@@ -81,6 +81,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
className="gf-form"
>