mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Azure Monitor: Implement logic to allow multiple selection (#61740)
This commit is contained in:
parent
46722679b5
commit
3e86a1b3d9
@ -139,8 +139,8 @@ e2e.scenario({
|
||||
scenario: () => {
|
||||
e2e.flows.addDashboard({
|
||||
timeRange: {
|
||||
from: '2022-10-03 00:00:00',
|
||||
to: '2022-10-03 23:59:59',
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
zone: 'Coordinated Universal Time',
|
||||
},
|
||||
});
|
||||
@ -210,8 +210,8 @@ e2e.scenario({
|
||||
scenario: () => {
|
||||
e2e.flows.addDashboard({
|
||||
timeRange: {
|
||||
from: '2022-10-03 00:00:00',
|
||||
to: '2022-10-03 23:59:59',
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
zone: 'Coordinated Universal Time',
|
||||
},
|
||||
});
|
||||
|
@ -88,4 +88,5 @@ export interface FeatureToggles {
|
||||
disablePrometheusExemplarSampling?: boolean;
|
||||
alertingBacktesting?: boolean;
|
||||
alertingNoNormalState?: boolean;
|
||||
azureMultipleResourcePicker?: boolean;
|
||||
}
|
||||
|
@ -183,6 +183,7 @@ export const resourceTypeDisplayNames: { [k: string]: string } = {
|
||||
'microsoft.hpcworkbench/instances/consortiums': 'Consortiums (preview)',
|
||||
'microsoft.hybridcompute/machines': 'Servers - Azure Arc',
|
||||
'microsoft.hybridcompute/privatelinkscopes': 'Azure Arc Private Link Scopes',
|
||||
'microsoft.hybridcontainerservice/provisionedclusters': 'Provisioned clusters',
|
||||
'microsoft.hybriddata/datamanagers': 'StorSimple Data Managers',
|
||||
'microsoft.hybridnetwork/devices': 'Azure Network Function Manager – Devices',
|
||||
'microsoft.hybridnetwork/networkfunctions': 'Azure Network Function Manager – Network Functions',
|
||||
@ -269,6 +270,7 @@ export const resourceTypeDisplayNames: { [k: string]: string } = {
|
||||
'microsoft.network/networkmanagers': 'Network Managers',
|
||||
'microsoft.network/networksecuritygroups': 'Network security groups',
|
||||
'microsoft.network/networkwatchers': 'Network Watchers',
|
||||
'microsoft.network/networkwatchers/connectionmonitors': 'Connection Monitors',
|
||||
'microsoft.network/networkwatchers/flowlogs': 'NSG Flow Logs',
|
||||
'microsoft.network/privatednszones': 'Private DNS zones',
|
||||
'microsoft.network/privateendpoints': 'Private endpoints',
|
||||
@ -400,3 +402,23 @@ export const resourceTypeDisplayNames: { [k: string]: string } = {
|
||||
'microsoft.security/insights/classification': 'Data Sensitivity Security Insights (Preview)',
|
||||
'microsoft.security/locations/alerts': 'Security Alerts',
|
||||
};
|
||||
|
||||
// This list has been manually written using the Azure Portal as the source.
|
||||
// Visit https://portal.azure.com/#view/Microsoft_Azure_Monitoring/AzureMonitoringBrowseBlade/~/metrics
|
||||
// and go to Select a scope > Resource types > Multi-resource compatible (preview)
|
||||
export const multiResourceCompatibleTypes: { [ns: string]: boolean } = {
|
||||
'microsoft.cache/redis': true, // 'Azure Cache for Redis'
|
||||
'microsoft.dbforpostgresql/flexibleservers': true, // 'Azure Database for PostgreSQL flexible servers'
|
||||
'microsoft.storagecache/amlfilesystems': true, // 'Lustre File Systems'
|
||||
'microsoft.databoxedge/databoxedgedevices': true, // 'Azure Stack Edge / Data Box Gateway'
|
||||
'microsoft.dataprotection/backupvaults': true, // 'Backup vaults'
|
||||
'microsoft.netapp/netappaccounts/capacitypools': true, // 'Capacity pools'
|
||||
'microsoft.network/networkwatchers/connectionmonitors': true, // 'Connection Monitors'
|
||||
'microsoft.keyvault/vaults': true, // 'Key vaults'
|
||||
'microsoft.recoveryservices/vaults': true, // 'Recovery Services vaults'
|
||||
'microsoft.sql/servers/databases': true, // 'SQL databases'
|
||||
'microsoft.sql/servers/elasticpools': true, // 'SQL elastic pools'
|
||||
'microsoft.compute/virtualmachinescalesets': true, // 'Virtual machine scale sets'
|
||||
'microsoft.compute/virtualmachines': true, // 'Virtual machines'
|
||||
'microsoft.signalrservice/webpubsub': true, // 'Web PubSub Service'
|
||||
};
|
||||
|
@ -0,0 +1,121 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import config from 'app/core/config';
|
||||
|
||||
import createMockDatasource from '../../__mocks__/datasource';
|
||||
import createMockQuery from '../../__mocks__/query';
|
||||
import { createMockResourcePickerData } from '../MetricsQueryEditor/MetricsQueryEditor.test';
|
||||
|
||||
import LogsQueryEditor from './LogsQueryEditor';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...(jest.requireActual('@grafana/runtime') as unknown as object),
|
||||
getTemplateSrv: () => ({
|
||||
replace: (val: string) => {
|
||||
return val;
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
const variableOptionGroup = {
|
||||
label: 'Template variables',
|
||||
options: [],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.featureToggles.azureMultipleResourcePicker = true;
|
||||
});
|
||||
|
||||
describe('LogsQueryEdiutor', () => {
|
||||
const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView;
|
||||
|
||||
beforeEach(() => {
|
||||
window.HTMLElement.prototype.scrollIntoView = function () {};
|
||||
});
|
||||
afterEach(() => {
|
||||
window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
|
||||
});
|
||||
|
||||
it('should select multiple resources', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureLogAnalytics?.resources;
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<LogsQueryEditor
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
|
||||
resourcePickerButton.click();
|
||||
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
|
||||
subscriptionButton.click();
|
||||
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
|
||||
resourceGroupButton.click();
|
||||
|
||||
const checkbox = await screen.findByLabelText('web-server');
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
|
||||
const checkbox2 = await screen.findByLabelText('db-server');
|
||||
await userEvent.click(checkbox2);
|
||||
expect(checkbox2).toBeChecked();
|
||||
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
|
||||
|
||||
expect(onChange).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
azureLogAnalytics: expect.objectContaining({
|
||||
resources: [
|
||||
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/web-server',
|
||||
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/db-server',
|
||||
],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should disable other resource types when selecting multiple resources', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureLogAnalytics?.resources;
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<LogsQueryEditor
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
|
||||
resourcePickerButton.click();
|
||||
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
|
||||
subscriptionButton.click();
|
||||
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
|
||||
resourceGroupButton.click();
|
||||
|
||||
const checkbox = await screen.findByLabelText('web-server');
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
|
||||
expect(await screen.findByLabelText('web-server_DataDisk')).toBeDisabled();
|
||||
});
|
||||
});
|
@ -1,12 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import { EditorFieldGroup, EditorRow, EditorRows } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Alert } from '@grafana/ui';
|
||||
|
||||
import Datasource from '../../datasource';
|
||||
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
|
||||
import ResourceField from '../ResourceField';
|
||||
import { ResourceRowType } from '../ResourcePicker/types';
|
||||
import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../ResourcePicker/types';
|
||||
import { parseResourceDetails } from '../ResourcePicker/utils';
|
||||
|
||||
import FormatAsField from './FormatAsField';
|
||||
import QueryField from './QueryField';
|
||||
@ -32,6 +34,23 @@ const LogsQueryEditor: React.FC<LogsQueryEditorProps> = ({
|
||||
hideFormatAs,
|
||||
}) => {
|
||||
const migrationError = useMigrations(datasource, query, onChange);
|
||||
const disableRow = (row: ResourceRow, selectedRows: ResourceRowGroup) => {
|
||||
if (selectedRows.length === 0) {
|
||||
// Only if there is some resource(s) selected we should disable rows
|
||||
return false;
|
||||
}
|
||||
// Disable multiple selection until the feature is ready
|
||||
if (!config.featureToggles.azureMultipleResourcePicker) {
|
||||
return true;
|
||||
}
|
||||
const rowResourceNS = parseResourceDetails(row.uri, row.location).metricNamespace?.toLowerCase();
|
||||
const selectedRowSampleNs = parseResourceDetails(
|
||||
selectedRows[0].uri,
|
||||
selectedRows[0].location
|
||||
).metricNamespace?.toLowerCase();
|
||||
// Only resources with the same metricNamespace can be selected
|
||||
return rowResourceNS !== selectedRowSampleNs;
|
||||
};
|
||||
|
||||
return (
|
||||
<span data-testid="azure-monitor-logs-query-editor-with-experimental-ui">
|
||||
@ -55,6 +74,7 @@ const LogsQueryEditor: React.FC<LogsQueryEditorProps> = ({
|
||||
]}
|
||||
resources={query.azureLogAnalytics?.resources ?? []}
|
||||
queryType="logs"
|
||||
disableRow={disableRow}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
|
@ -3,6 +3,8 @@ import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
|
||||
|
||||
import config from 'app/core/config';
|
||||
|
||||
import createMockDatasource from '../../__mocks__/datasource';
|
||||
import { createMockInstanceSetttings } from '../../__mocks__/instanceSettings';
|
||||
import createMockPanelData from '../../__mocks__/panelData';
|
||||
@ -31,6 +33,10 @@ const variableOptionGroup = {
|
||||
options: [],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.featureToggles.azureMultipleResourcePicker = true;
|
||||
});
|
||||
|
||||
export function createMockResourcePickerData() {
|
||||
const mockDatasource = createMockDatasource();
|
||||
const mockResourcePicker = new ResourcePickerData(
|
||||
@ -170,6 +176,100 @@ describe('MetricsQueryEditor', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should select multiple resources', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureMonitor?.resources;
|
||||
delete query?.azureMonitor?.metricNamespace;
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
|
||||
resourcePickerButton.click();
|
||||
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
|
||||
subscriptionButton.click();
|
||||
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
|
||||
resourceGroupButton.click();
|
||||
|
||||
const checkbox = await screen.findByLabelText('web-server');
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
|
||||
const checkbox2 = await screen.findByLabelText('db-server');
|
||||
await userEvent.click(checkbox2);
|
||||
expect(checkbox2).toBeChecked();
|
||||
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
|
||||
|
||||
expect(onChange).toBeCalledTimes(1);
|
||||
expect(onChange).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
subscription: 'def-456',
|
||||
azureMonitor: expect.objectContaining({
|
||||
metricNamespace: 'microsoft.compute/virtualmachines',
|
||||
resources: [
|
||||
expect.objectContaining({
|
||||
resourceGroup: 'dev-3',
|
||||
resourceName: 'web-server',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
resourceGroup: 'dev-3',
|
||||
resourceName: 'db-server',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should disable other resource types when selecting multiple resources', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureMonitor?.resources;
|
||||
delete query?.azureMonitor?.metricNamespace;
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
|
||||
resourcePickerButton.click();
|
||||
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
|
||||
subscriptionButton.click();
|
||||
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
|
||||
resourceGroupButton.click();
|
||||
|
||||
const checkbox = await screen.findByLabelText('web-server');
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
|
||||
expect(await screen.findByLabelText('web-server_DataDisk')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should change the metric name when selected', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const onChange = jest.fn();
|
||||
|
@ -2,11 +2,14 @@ import React from 'react';
|
||||
|
||||
import { PanelData } from '@grafana/data/src/types';
|
||||
import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { multiResourceCompatibleTypes } from '../../azureMetadata';
|
||||
import type Datasource from '../../datasource';
|
||||
import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish } from '../../types';
|
||||
import ResourceField from '../ResourceField';
|
||||
import { ResourceRowType } from '../ResourcePicker/types';
|
||||
import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../ResourcePicker/types';
|
||||
import { parseResourceDetails } from '../ResourcePicker/utils';
|
||||
|
||||
import AggregationField from './AggregationField';
|
||||
import DimensionFields from './DimensionFields';
|
||||
@ -45,6 +48,31 @@ const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
|
||||
resourceName: r.resourceName,
|
||||
region: query.azureMonitor?.region,
|
||||
})) ?? [];
|
||||
const disableRow = (row: ResourceRow, selectedRows: ResourceRowGroup) => {
|
||||
if (selectedRows.length === 0) {
|
||||
// Only if there is some resource(s) selected we should disable rows
|
||||
return false;
|
||||
}
|
||||
if (!config.featureToggles.azureMultipleResourcePicker) {
|
||||
// Disable multiple selection until the feature is ready
|
||||
return true;
|
||||
}
|
||||
|
||||
const rowResource = parseResourceDetails(row.uri, row.location);
|
||||
const selectedRowSample = parseResourceDetails(selectedRows[0].uri, selectedRows[0].location);
|
||||
// Only resources:
|
||||
// - in the same subscription
|
||||
// - in the same region
|
||||
// - with the same metric namespace
|
||||
// - with a metric namespace that is compatible with multi-resource queries
|
||||
return (
|
||||
rowResource.subscription !== selectedRowSample.subscription ||
|
||||
rowResource.region !== selectedRowSample.region ||
|
||||
rowResource.metricNamespace?.toLocaleLowerCase() !== selectedRowSample.metricNamespace?.toLocaleLowerCase() ||
|
||||
!multiResourceCompatibleTypes[rowResource.metricNamespace?.toLocaleLowerCase() ?? '']
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<span data-testid="azure-monitor-metrics-query-editor-with-experimental-ui">
|
||||
<EditorRows>
|
||||
@ -59,6 +87,7 @@ const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
|
||||
selectableEntryTypes={[ResourceRowType.Resource]}
|
||||
resources={resources ?? []}
|
||||
queryType={'metrics'}
|
||||
disableRow={disableRow}
|
||||
/>
|
||||
<MetricNamespaceField
|
||||
metricNamespaces={metricNamespaces}
|
||||
|
@ -10,7 +10,7 @@ import { AzureQueryEditorFieldProps, AzureMetricResource } from '../../types';
|
||||
import { Field } from '../Field';
|
||||
import ResourcePicker from '../ResourcePicker';
|
||||
import getStyles from '../ResourcePicker/styles';
|
||||
import { ResourceRowType } from '../ResourcePicker/types';
|
||||
import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../ResourcePicker/types';
|
||||
import { parseMultipleResourceDetails, setResources } from '../ResourcePicker/utils';
|
||||
|
||||
interface ResourceFieldProps<T> extends AzureQueryEditorFieldProps {
|
||||
@ -19,6 +19,7 @@ interface ResourceFieldProps<T> extends AzureQueryEditorFieldProps {
|
||||
resources: T[];
|
||||
inlineField?: boolean;
|
||||
labelWidth?: number;
|
||||
disableRow: (row: ResourceRow, selectedRows: ResourceRowGroup) => boolean;
|
||||
}
|
||||
|
||||
const ResourceField: React.FC<ResourceFieldProps<string | AzureMetricResource>> = ({
|
||||
@ -30,6 +31,7 @@ const ResourceField: React.FC<ResourceFieldProps<string | AzureMetricResource>>
|
||||
resources,
|
||||
inlineField,
|
||||
labelWidth,
|
||||
disableRow,
|
||||
}) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const [pickerIsOpen, setPickerIsOpen] = useState(false);
|
||||
@ -68,6 +70,7 @@ const ResourceField: React.FC<ResourceFieldProps<string | AzureMetricResource>>
|
||||
onCancel={closePicker}
|
||||
selectableEntryTypes={selectableEntryTypes}
|
||||
queryType={queryType}
|
||||
disableRow={disableRow}
|
||||
/>
|
||||
</Modal>
|
||||
<Field label="Resource" inlineField={inlineField} labelWidth={labelWidth}>
|
||||
|
@ -18,6 +18,7 @@ const defaultProps = {
|
||||
onRowSelectedChange: jest.fn(),
|
||||
selectableEntryTypes: [],
|
||||
scrollIntoView: false,
|
||||
disableRow: jest.fn().mockReturnValue(false),
|
||||
};
|
||||
|
||||
describe('NestedRow', () => {
|
||||
@ -56,4 +57,16 @@ describe('NestedRow', () => {
|
||||
const box = screen.queryByRole('checkbox');
|
||||
expect(box).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should disable a checkbox if specified', () => {
|
||||
render(
|
||||
<table>
|
||||
<tbody>
|
||||
<NestedRow {...defaultProps} selectableEntryTypes={[ResourceRowType.Resource]} disableRow={() => true} />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
const box = screen.queryByRole('checkbox');
|
||||
expect(box).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
@ -15,6 +15,7 @@ interface NestedRowProps {
|
||||
requestNestedRows: (row: ResourceRow) => Promise<void>;
|
||||
onRowSelectedChange: (row: ResourceRow, selected: boolean) => void;
|
||||
selectableEntryTypes: ResourceRowType[];
|
||||
disableRow: (row: ResourceRow, selectedRows: ResourceRowGroup) => boolean;
|
||||
scrollIntoView?: boolean;
|
||||
}
|
||||
|
||||
@ -26,12 +27,13 @@ const NestedRow: React.FC<NestedRowProps> = ({
|
||||
onRowSelectedChange,
|
||||
selectableEntryTypes,
|
||||
scrollIntoView,
|
||||
disableRow,
|
||||
}) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const [rowStatus, setRowStatus] = useState<'open' | 'closed' | 'loading'>('closed');
|
||||
|
||||
const isSelected = !!selectedRows.find((v) => v.uri === row.uri);
|
||||
const isDisabled = selectedRows.length > 0 && !isSelected;
|
||||
const isDisabled = !isSelected && disableRow(row, selectedRows);
|
||||
const isOpen = rowStatus === 'open';
|
||||
|
||||
const onRowToggleCollapse = async () => {
|
||||
@ -92,6 +94,7 @@ const NestedRow: React.FC<NestedRowProps> = ({
|
||||
onRowSelectedChange={onRowSelectedChange}
|
||||
selectableEntryTypes={selectableEntryTypes}
|
||||
scrollIntoView={scrollIntoView}
|
||||
disableRow={disableRow}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
@ -70,6 +70,7 @@ const defaultProps = {
|
||||
ResourceRowType.Variable,
|
||||
],
|
||||
queryType,
|
||||
disableRow: jest.fn(),
|
||||
};
|
||||
|
||||
describe('AzureMonitor ResourcePicker', () => {
|
||||
|
@ -25,6 +25,7 @@ interface ResourcePickerProps<T> {
|
||||
|
||||
onApply: (resources: T[]) => void;
|
||||
onCancel: () => void;
|
||||
disableRow: (row: ResourceRow, selectedRows: ResourceRowGroup) => boolean;
|
||||
}
|
||||
|
||||
const ResourcePicker = ({
|
||||
@ -34,6 +35,7 @@ const ResourcePicker = ({
|
||||
onCancel,
|
||||
selectableEntryTypes,
|
||||
queryType,
|
||||
disableRow,
|
||||
}: ResourcePickerProps<string | AzureMetricResource>) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
@ -203,6 +205,7 @@ const ResourcePicker = ({
|
||||
onRowSelectedChange={handleSelectionChanged}
|
||||
selectableEntryTypes={selectableEntryTypes}
|
||||
scrollIntoView={true}
|
||||
disableRow={disableRow}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
@ -226,6 +229,7 @@ const ResourcePicker = ({
|
||||
requestNestedRows={requestNestedRows}
|
||||
onRowSelectedChange={handleSelectionChanged}
|
||||
selectableEntryTypes={selectableEntryTypes}
|
||||
disableRow={() => false}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
|
Loading…
Reference in New Issue
Block a user