Skip to content

Commit

Permalink
Add code options source for select element (#323)
Browse files Browse the repository at this point in the history
* Add code options source for select element

* Fix build

* Update icons

---------

Co-authored-by: Mikhail Volkov <mikhail@volkovlabs.io>
  • Loading branch information
asimonok and mikhail-vl authored Jan 1, 2024
1 parent 06bd1fe commit 9a409a2
Show file tree
Hide file tree
Showing 15 changed files with 153 additions and 47 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Add disable if code (#321)
- Update reset request visibility if reset button is hidden (#322)
- Add code options source for select element (#323)

## 3.4.0 (2023-12-14)

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 @@ -52,7 +52,7 @@ const Select = jest.fn(({ options, onChange, value, isMulti, isClearable, ...res
if (isMulti) {
onChange(options.filter((option: any) => event.target.values.includes(option.value)));
} else {
onChange(options.find((option: any) => option.value === event.target.value));
onChange(options.find((option: any) => option.value == event.target.value));
}
}
}}
Expand Down
43 changes: 31 additions & 12 deletions src/components/ElementEditor/ElementEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ export const ElementEditor: React.FC<Props> = ({
/>
</InlineField>
</InlineFieldRow>
{element.optionsSource === OptionsSource.QUERY ? (
{element.optionsSource === OptionsSource.QUERY && (
<ElementQueryOptionsEditor
value={element.queryOptions}
onChange={(queryOptions) => {
Expand All @@ -527,22 +527,41 @@ export const ElementEditor: React.FC<Props> = ({
}}
data={data}
/>
) : (
<div className={styles.optionsContainer} data-testid={TEST_IDS.formElementsEditor.options}>
<ElementOptionsEditor
options={element.options}
onChange={(options) =>
)}
{element.optionsSource === OptionsSource.CODE && (
<Field label="Get Options Code" description="Must return array with {label,value} objects">
<AutosizeCodeEditor
value={element.getOptions || ''}
language={CodeLanguage.JAVASCRIPT}
onBlur={(code) => {
onChange({
...element,
options,
})
}
onChangeItem={(updated, original, checkConflict) => {
return onChangeOption(element, updated, original, checkConflict);
getOptions: code,
});
}}
monacoOptions={{ formatOnPaste: true, formatOnType: true }}
showLineNumbers={true}
aria-label={TEST_IDS.formElementsEditor.fieldGetOptions}
/>
</div>
</Field>
)}
{element.optionsSource === OptionsSource.CUSTOM ||
(!element.optionsSource && (
<div className={styles.optionsContainer} data-testid={TEST_IDS.formElementsEditor.options}>
<ElementOptionsEditor
options={element.options}
onChange={(options) =>
onChange({
...element,
options,
})
}
onChangeItem={(updated, original, checkConflict) => {
return onChangeOption(element, updated, original, checkConflict);
}}
/>
</div>
))}
</>
)}

Expand Down
24 changes: 7 additions & 17 deletions src/components/FormElement/FormElement.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cx } from '@emotion/css';
import { DateTime, dateTime, PanelData } from '@grafana/data';
import { DateTime, dateTime } from '@grafana/data';
import {
DatePickerWithInput,
DateTimePicker,
Expand All @@ -17,7 +17,7 @@ import {
} from '@grafana/ui';
import { NumberInput } from '@volkovlabs/components';
import Slider from 'rc-slider';
import React, { ChangeEvent, useMemo } from 'react';
import React, { ChangeEvent } from 'react';

import { BOOLEAN_ELEMENT_OPTIONS, FormElementType, TEST_IDS } from '../../constants';
import { CodeLanguage, LinkTarget, LocalFormElement } from '../../types';
Expand Down Expand Up @@ -45,28 +45,18 @@ interface Props {
* Highlight Class
*/
highlightClass: (element: LocalFormElement) => string;

/**
* Data
*/
data: PanelData;
}

/**
* Form Element
*/
export const FormElement: React.FC<Props> = ({ element, onChange, highlightClass, data }) => {
export const FormElement: React.FC<Props> = ({ element, onChange, highlightClass }) => {
/**
* Styles and Theme
*/
const theme = useTheme2();
const styles = useStyles2(getStyles);

/**
* Options
*/
const options = useMemo(() => element.helpers.getOptions({ data }), [data, element.helpers]);

return (
<InlineFieldRow data-testid={TEST_IDS.formElements.element(element.id, element.type)}>
{element.type === FormElementType.NUMBER && (
Expand Down Expand Up @@ -160,9 +150,9 @@ export const FormElement: React.FC<Props> = ({ element, onChange, highlightClass
>
<Input
value={
!options.length
!element.options?.length
? element.value?.toString() || ''
: options.find((option) => option.value === element.value)?.label
: element.options?.find((option) => option.value === element.value)?.label
}
type="text"
width={applyWidth(element.width)}
Expand Down Expand Up @@ -364,7 +354,7 @@ export const FormElement: React.FC<Props> = ({ element, onChange, highlightClass
});
}}
fullWidth={!element.width}
options={options}
options={element.options}
className={highlightClass(element)}
/>
</InlineField>
Expand All @@ -390,7 +380,7 @@ export const FormElement: React.FC<Props> = ({ element, onChange, highlightClass
});
}}
width={applyWidth(element.width)}
options={options}
options={element.options}
className={highlightClass(element)}
/>
</InlineField>
Expand Down
33 changes: 33 additions & 0 deletions src/components/FormElements/FormElements.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,39 @@ describe('Form Elements', () => {
});
});

describe('Get Options Code', () => {
it('Should use options from code', async () => {
const options = {
submit: {},
initial: { highlightColor: false },
update: {},
reset: {},
elements: [
{
id: 'select',
type: FormElementType.SELECT,
optionsSource: OptionsSource.CODE,
getOptions: `return [{ value: 1, label: '1' }]`,
},
],
};

render(getComponent({ options, onChangeElement }));

/**
* Select
*/
expect(selectors.fieldSelect()).toBeInTheDocument();

/**
* Change value
*/
await act(async () => fireEvent.change(selectors.fieldSelect(), { target: { value: 1 } }));

expect(selectors.fieldSelect()).toHaveValue('1');
});
});

it('Should find unit element', async () => {
const options = {
submit: {},
Expand Down
13 changes: 3 additions & 10 deletions src/components/FormElements/FormElements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ export const FormElements: React.FC<Props> = ({
.map((element) => ({
...element,
disabled: element.helpers.disableIf({ elements, replaceVariables }),
options: element.helpers.getOptions({ elements, replaceVariables, data }),
}));
}, [elements, replaceVariables]);
}, [data, elements, replaceVariables]);

return (
<div data-testid={TEST_IDS.formElements.root}>
Expand All @@ -118,15 +119,7 @@ export const FormElements: React.FC<Props> = ({
/**
* Return
*/
return (
<FormElement
key={index}
element={element}
onChange={onChangeElement}
highlightClass={highlightClass}
data={data}
/>
);
return <FormElement key={index} element={element} onChange={onChangeElement} highlightClass={highlightClass} />;
})}
</div>
);
Expand Down
29 changes: 29 additions & 0 deletions src/components/FormElementsEditor/FormElementsEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2359,6 +2359,35 @@ describe('Form Elements Editor', () => {
});
});

/**
* Get Options Code
*/
describe('Get Options Code', () => {
it('Should update getOptions code value', async () => {
const element = {
...FORM_ELEMENT_DEFAULT,
id: 'select',
type: FormElementType.SELECT,
optionsSource: OptionsSource.CODE,
};
const elements = [element];

render(getComponent({ value: elements, onChange }));

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

/**
* Change Get Options Field
*/
await act(async () => fireEvent.change(elementSelectors.fieldGetOptions(), { target: { value: '123' } }));

expect(elementSelectors.fieldGetOptions()).toHaveValue('123');
});
});

/**
* Layout Section
*/
Expand Down
3 changes: 3 additions & 0 deletions src/constants/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,17 @@ export const BUTTON_ORIENTATION_OPTIONS: SelectableValue[] = [
{
value: ButtonOrientation.LEFT,
label: 'Left',
icon: 'horizontal-align-left',
},
{
value: ButtonOrientation.CENTER,
label: 'Center',
icon: 'horizontal-align-center',
},
{
value: ButtonOrientation.RIGHT,
label: 'Right',
icon: 'horizontal-align-right',
},
];

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

Expand Down
8 changes: 8 additions & 0 deletions src/constants/form-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export const ICON_OPTIONS = getAvailableIcons().map((name) => ({
export enum OptionsSource {
QUERY = 'Query',
CUSTOM = 'Custom',
CODE = 'Code',
}

/**
Expand All @@ -185,6 +186,13 @@ export const OPTIONS_SOURCE_OPTIONS = [
label: 'Custom',
value: OptionsSource.CUSTOM,
ariaLabel: TEST_IDS.formElementsEditor.optionsSourceOption(OptionsSource.CUSTOM),
icon: 'keyboard',
},
{
label: 'Code',
value: OptionsSource.CODE,
ariaLabel: TEST_IDS.formElementsEditor.optionsSourceOption(OptionsSource.CODE),
icon: 'calculator-alt',
},
];

Expand Down
4 changes: 4 additions & 0 deletions src/constants/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ export const LAYOUT_VARIANT_OPTIONS: SelectableValue[] = [
value: LayoutVariant.SINGLE,
description: 'All elements together.',
label: 'Basic',
icon: 'bars',
},
{
value: LayoutVariant.NONE,
description: 'Buttons only.',
label: 'Buttons only',
icon: 'apps',
},
{
value: LayoutVariant.SPLIT,
Expand All @@ -47,10 +49,12 @@ export const LAYOUT_ORIENTATION_OPTIONS: SelectableValue[] = [
value: LayoutOrientation.HORIZONTAL,
description: 'Horizontal Orientation',
label: 'Horizontal',
icon: 'horizontal-align-center',
},
{
value: LayoutOrientation.VERTICAL,
description: 'Vertical Orientation',
label: 'Vertical',
icon: 'vertical-align-center',
},
];
10 changes: 6 additions & 4 deletions src/constants/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const enum RequestMethod {
export const INITIAL_REQUEST_METHOD_OPTIONS: SelectableValue[] = [
{
value: RequestMethod.NONE,
label: 'Custom Code',
label: 'Code',
icon: 'calculator-alt',
},
{
value: RequestMethod.DATASOURCE,
Expand All @@ -43,7 +44,8 @@ export const INITIAL_REQUEST_METHOD_OPTIONS: SelectableValue[] = [
export const UPDATE_REQUEST_METHOD_OPTIONS: SelectableValue[] = [
{
value: RequestMethod.NONE,
label: 'Custom Code',
label: 'Code',
icon: 'calculator-alt',
},
{
value: RequestMethod.DATASOURCE,
Expand Down Expand Up @@ -101,7 +103,7 @@ export const enum PayloadMode {
export const PAYLOAD_MODE_OPTIONS = [
{ label: 'All Elements', value: PayloadMode.ALL },
{ label: 'Updated Only', value: PayloadMode.UPDATED },
{ label: 'Custom Code', value: PayloadMode.CUSTOM },
{ label: 'Code', value: PayloadMode.CUSTOM, icon: 'calculator-alt' },
];

/**
Expand All @@ -117,7 +119,7 @@ export const enum ResetActionMode {
* Reset Action Options
*/
export const RESET_ACTION_OPTIONS = [
{ label: 'Custom Code', value: ResetActionMode.CUSTOM },
{ label: 'Code', value: ResetActionMode.CUSTOM, icon: 'calculator-alt' },
{ label: 'Initial Request', value: ResetActionMode.INITIAL },
{ label: 'Data Source', value: ResetActionMode.DATASOURCE, icon: 'database' },
];
1 change: 1 addition & 0 deletions src/constants/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export const TEST_IDS = {
buttonSetDate: 'data-testid form-elements-editor button-set-date',
buttonRemoveDate: 'data-testid form-elements-editor button-remove-date',
fieldLinkText: 'data-testid form-elements-editor field-link-text',
fieldGetOptions: 'form-elements-editor field-get-options',
},
headerParametersEditor: {
buttonAdd: 'data-testid header-parameters-editor button-add',
Expand Down
Loading

0 comments on commit 9a409a2

Please sign in to comment.