Skip to content

Commit

Permalink
Added custom input for Select, Multi select (#507)
Browse files Browse the repository at this point in the history
* custom element added

* add test for custom values editor

* options update

* rename allowCustomValue

* add migration

* Updates

---------

Co-authored-by: Mikhail Volkov <[email protected]>
  • Loading branch information
vitPinchuk and mikhail-vl authored Oct 3, 2024
1 parent 4ee439b commit 91b24fb
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features / Enhancements

- Updated Autosize Code Editor toolbar (#506)
- Added custom input for Select, Multi select (#507)

## 4.6.0 (2024-09-28)

Expand Down
2 changes: 1 addition & 1 deletion src/__mocks__/@grafana/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const TimeOfDayPicker = jest.fn(TimeOfDayPickerMock);
/**
* Mock Select component
*/
const SelectMock = ({ options, onChange, value, isMulti, isClearable, ...restProps }: any) => (
const SelectMock = ({ options, onChange, value, isMulti, isClearable, allowCustomValue, ...restProps }: any) => (
<select
onChange={(event: any) => {
if (onChange) {
Expand Down
13 changes: 13 additions & 0 deletions src/components/ElementEditor/ElementEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
CODE_LANGUAGE_OPTIONS,
CUSTOM_BUTTON_SIZE_OPTIONS,
CUSTOM_BUTTON_VARIANT_OPTIONS,
CUSTOM_VALUE_OPTIONS,
FORM_ELEMENT_TYPE_OPTIONS,
FormElementType,
LINK_TARGET_OPTIONS,
Expand Down Expand Up @@ -590,6 +591,18 @@ export const ElementEditor: React.FC<Props> = ({ element, onChange, onChangeOpti
isFormElementType(element, FormElementType.DISABLED) ||
isFormElementType(element, FormElementType.CHECKBOX_LIST)) && (
<>
<InlineField label="Custom values" labelWidth={14}>
<RadioButtonGroup
options={CUSTOM_VALUE_OPTIONS}
value={!!element.allowCustomValue}
onChange={(value) => {
onChange({
...element,
allowCustomValue: value,
});
}}
/>
</InlineField>
<InlineFieldRow>
<InlineField label="Options Source" labelWidth={14}>
<RadioButtonGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const SelectElement: React.FC<Props> = ({ element, onChange, highlightCla
isMulti={element.type === FormElementType.MULTISELECT}
aria-label={TEST_IDS.formElements.fieldSelect}
value={element.value !== undefined ? element.value : null}
allowCustomValue={element.allowCustomValue}
onChange={(event) => {
onChange<typeof element>({
...element,
Expand Down
31 changes: 31 additions & 0 deletions src/components/FormElementsEditor/FormElementsEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,37 @@ describe('Form Elements Editor', () => {
});
});

it('Should allow custom values', async () => {
const element = {
...FORM_ELEMENT_DEFAULT,
id: 'id',
type: FormElementType.SELECT,
optionsSource: undefined,
};
const elements = [element];

render(
getComponent({
value: elements,
onChange,
context: {
data: [],
},
})
);

/**
* Open id element
*/
const elementSelectors = openElement(element.id, element.type);

expect(elementSelectors.optionsCustomValues(false, 'Allow')).not.toBeChecked();

await act(() => fireEvent.click(elementSelectors.optionsCustomValues(false, 'Allow')));

expect(elementSelectors.optionsCustomValues(false, 'Allow')).toBeChecked();
});

it('Should update Width', async () => {
const elements = [{ ...FORM_ELEMENT_DEFAULT, id: 'id', width: 100 }];

Expand Down
1 change: 1 addition & 0 deletions src/constants/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export const TEXTAREA_DEFAULT: TextareaOptions = {
*/
export const SELECT_DEFAULT: SelectOptions = {
options: [],
allowCustomValue: false,
optionsSource: OptionsSource.CUSTOM,
};

Expand Down
18 changes: 18 additions & 0 deletions src/constants/form-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,21 @@ export const CUSTOM_BUTTON_VARIANT_OPTIONS: SelectableValue[] = [
label: 'Custom',
},
];

/**
* Custom Value Options
*/
export const CUSTOM_VALUE_OPTIONS = [
{
label: 'Allow',
value: true,
ariaLabel: TEST_IDS.formElementsEditor.optionsCustomValues('Allow'),
icon: 'keyboard',
},
{
label: 'Disallow',
value: false,
ariaLabel: TEST_IDS.formElementsEditor.optionsCustomValues('Disallow'),
icon: 'times',
},
];
1 change: 1 addition & 0 deletions src/constants/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export const TEST_IDS = {
fieldLinkText: 'data-testid form-elements-editor field-link-text',
fieldGetOptions: 'form-elements-editor field-get-options',
fileMultipleOption: (name: unknown) => `form-elements-editor file-multiple-option-${name}`,
optionsCustomValues: (value: unknown) => `form-elements-editor options-source-option-${value}`,
},
formElementsSection: {
sectionContent: (id: string, name: string) => `data-testid form-elements-section section-content-${id}-${name}`,
Expand Down
42 changes: 42 additions & 0 deletions src/migration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,4 +671,46 @@ describe('Migration', () => {
});
});
});

it('Should return allowCustom property for select and multiselect elements if not specified', () => {
const options: Partial<PanelOptions> = {
sync: true,
initial: {} as any,
update: {} as any,
resetAction: {} as any,
elements: [
{
type: FormElementType.MULTISELECT,
},
{
type: FormElementType.SELECT,
},
{
allowCustomValue: true,
type: FormElementType.SELECT,
},
{
allowCustomValue: false,
type: FormElementType.MULTISELECT,
},
{
type: FormElementType.STRING,
},
] as any,
};

const result = getMigratedOptions({
options: options as any,
} as any);

const elements: any = result.elements;

expect(elements[0].allowCustomValue).toBeDefined();
expect(elements[0].allowCustomValue).toEqual(false);
expect(elements[1].allowCustomValue).toBeDefined();
expect(elements[1].allowCustomValue).toEqual(false);
expect(elements[2].allowCustomValue).toEqual(true);
expect(elements[3].allowCustomValue).toEqual(false);
expect(elements[4].allowCustomValue).toBeUndefined();
});
});
14 changes: 14 additions & 0 deletions src/migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,19 @@ export const getMigratedOptions = (panel: PanelModel<OutdatedPanelOptions>): Pan
);
}

/**
* Normalize allowCustomValue for Select and Multiselect Type
*/
if (options.elements && options.elements.length > 0) {
options.elements.forEach((element) => {
if (
(element.type === FormElementType.SELECT || element.type === FormElementType.MULTISELECT) &&
!element.hasOwnProperty('allowCustomValue')
) {
element.allowCustomValue = false;
}
});
}

return options as PanelOptions;
};
7 changes: 7 additions & 0 deletions src/types/form-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,13 @@ export interface SelectOptions {
* @type {string}
*/
getOptions?: string;

/**
* Allow Custom values
*
* @type {boolean}
*/
allowCustomValue: boolean;
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/utils/form-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export const getElementWithNewType = (
return {
...baseValues,
...selectOptions,
allowCustomValue: false,
type: newType,
value: Array.isArray(baseValues) ? baseValues : [],
};
Expand All @@ -143,6 +144,7 @@ export const getElementWithNewType = (
return {
...baseValues,
...selectOptions,
allowCustomValue: false,
type: newType,
};
}
Expand Down Expand Up @@ -462,7 +464,7 @@ export const convertToElementValue = (
case FormElementType.LINK: {
return {
...element,
value: typeof value === 'string' ? value : value?.toString() ?? '',
value: typeof value === 'string' ? value : (value?.toString() ?? ''),
};
}
case FormElementType.NUMBER:
Expand Down

0 comments on commit 91b24fb

Please sign in to comment.