diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/OrderBySection.test.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/OrderBySection.test.tsx
new file mode 100644
index 00000000000..471470aed86
--- /dev/null
+++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/OrderBySection.test.tsx
@@ -0,0 +1,373 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+import {
+ AzureQueryType,
+ BuilderQueryEditorExpressionType,
+ BuilderQueryEditorOrderByExpression,
+ BuilderQueryEditorOrderByOptions,
+ BuilderQueryEditorPropertyType,
+} from '../../dataquery.gen';
+import { AzureMonitorQuery } from '../../types/query';
+
+import { OrderBySection } from './OrderBySection';
+
+describe('OrderBySection', () => {
+ const mockAllColumns = [
+ { name: 'TimeGenerated', type: 'datetime' },
+ { name: 'Level', type: 'string' },
+ { name: 'Count', type: 'int' },
+ { name: 'Duration', type: 'real' },
+ ];
+
+ const createMockQuery = (orderBy?: BuilderQueryEditorOrderByExpression[]): AzureMonitorQuery => ({
+ refId: 'A',
+ queryType: AzureQueryType.LogAnalytics,
+ azureLogAnalytics: {
+ builderQuery: {
+ from: {
+ type: BuilderQueryEditorExpressionType.Property,
+ property: { type: BuilderQueryEditorPropertyType.String, name: 'AppRequests' },
+ },
+ columns: {
+ type: BuilderQueryEditorExpressionType.Property,
+ columns: ['TimeGenerated', 'Level', 'Count'],
+ },
+ orderBy: {
+ type: BuilderQueryEditorExpressionType.Order_by,
+ expressions: orderBy || [],
+ },
+ reduce: {
+ type: BuilderQueryEditorExpressionType.Reduce,
+ expressions: [],
+ },
+ groupBy: {
+ type: BuilderQueryEditorExpressionType.Group_by,
+ expressions: [],
+ },
+ where: {
+ type: BuilderQueryEditorExpressionType.And,
+ expressions: [],
+ },
+ },
+ },
+ });
+
+ const defaultProps = {
+ query: createMockQuery(),
+ allColumns: mockAllColumns,
+ buildAndUpdateQuery: jest.fn(),
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders the order by section', () => {
+ render();
+ expect(screen.getByText('Order By')).toBeInTheDocument();
+ });
+
+ it('renders add button when no order by exists', () => {
+ render();
+ const addButton = screen.getByLabelText('Add order by');
+ expect(addButton).toBeInTheDocument();
+ });
+
+ it('renders existing order by expressions', () => {
+ const existingOrderBy: BuilderQueryEditorOrderByExpression[] = [
+ {
+ property: { name: 'TimeGenerated', type: BuilderQueryEditorPropertyType.String },
+ order: BuilderQueryEditorOrderByOptions.Desc,
+ type: BuilderQueryEditorExpressionType.Order_by,
+ },
+ {
+ property: { name: 'Level', type: BuilderQueryEditorPropertyType.String },
+ order: BuilderQueryEditorOrderByOptions.Asc,
+ type: BuilderQueryEditorExpressionType.Order_by,
+ },
+ ];
+
+ const queryWithOrderBy = createMockQuery(existingOrderBy);
+ render();
+
+ expect(screen.getAllByLabelText('Order by column')).toHaveLength(2);
+ expect(screen.getAllByLabelText('Order Direction')).toHaveLength(2);
+ expect(screen.getByText('TimeGenerated')).toBeInTheDocument();
+ expect(screen.getByText('Level')).toBeInTheDocument();
+ });
+
+ it('calls buildAndUpdateQuery when order by is added', async () => {
+ render();
+
+ const addButton = screen.getByLabelText('Add order by');
+ await userEvent.click(addButton);
+
+ expect(defaultProps.buildAndUpdateQuery).toHaveBeenCalledWith({
+ orderBy: expect.arrayContaining([
+ expect.objectContaining({
+ property: expect.objectContaining({ name: '' }),
+ order: BuilderQueryEditorOrderByOptions.Asc,
+ }),
+ ]),
+ });
+ });
+
+ it('calls buildAndUpdateQuery when column is changed', async () => {
+ const existingOrderBy: BuilderQueryEditorOrderByExpression[] = [
+ {
+ property: { name: 'TimeGenerated', type: BuilderQueryEditorPropertyType.String },
+ order: BuilderQueryEditorOrderByOptions.Desc,
+ type: BuilderQueryEditorExpressionType.Order_by,
+ },
+ ];
+
+ const queryWithOrderBy = createMockQuery(existingOrderBy);
+ render();
+
+ const columnSelect = screen.getByLabelText('Order by column');
+ await userEvent.click(columnSelect);
+
+ const levelOption = await screen.getByText('Level');
+ await userEvent.click(levelOption);
+
+ expect(defaultProps.buildAndUpdateQuery).toHaveBeenCalledWith({
+ orderBy: expect.arrayContaining([
+ expect.objectContaining({
+ property: expect.objectContaining({ name: 'Level' }),
+ }),
+ ]),
+ });
+ });
+
+ it('calls buildAndUpdateQuery when order direction is changed', async () => {
+ const existingOrderBy: BuilderQueryEditorOrderByExpression[] = [
+ {
+ property: { name: 'TimeGenerated', type: BuilderQueryEditorPropertyType.String },
+ order: BuilderQueryEditorOrderByOptions.Asc,
+ type: BuilderQueryEditorExpressionType.Order_by,
+ },
+ ];
+
+ const queryWithOrderBy = createMockQuery(existingOrderBy);
+ render();
+
+ const orderSelect = screen.getByLabelText('Order Direction');
+ await userEvent.click(orderSelect);
+
+ const descOption = await screen.getByText('Descending');
+ await userEvent.click(descOption);
+
+ expect(defaultProps.buildAndUpdateQuery).toHaveBeenCalledWith({
+ orderBy: expect.arrayContaining([
+ expect.objectContaining({
+ order: BuilderQueryEditorOrderByOptions.Desc,
+ }),
+ ]),
+ });
+ });
+
+ it('calls buildAndUpdateQuery when order by is deleted', async () => {
+ const existingOrderBy: BuilderQueryEditorOrderByExpression[] = [
+ {
+ property: { name: 'TimeGenerated', type: BuilderQueryEditorPropertyType.String },
+ order: BuilderQueryEditorOrderByOptions.Desc,
+ type: BuilderQueryEditorExpressionType.Order_by,
+ },
+ {
+ property: { name: 'Level', type: BuilderQueryEditorPropertyType.String },
+ order: BuilderQueryEditorOrderByOptions.Asc,
+ type: BuilderQueryEditorExpressionType.Order_by,
+ },
+ ];
+
+ const queryWithOrderBy = createMockQuery(existingOrderBy);
+ render();
+
+ const removeButtons = screen.getAllByLabelText('Remove order by');
+ await userEvent.click(removeButtons[0]);
+
+ expect(defaultProps.buildAndUpdateQuery).toHaveBeenCalledWith({
+ orderBy: [
+ expect.objectContaining({
+ property: expect.objectContaining({ name: 'Level' }),
+ }),
+ ],
+ });
+ });
+
+ it('uses group by columns when available', async () => {
+ const query = createMockQuery();
+ query.azureLogAnalytics!.builderQuery!.groupBy = {
+ type: BuilderQueryEditorExpressionType.Group_by,
+ expressions: [
+ {
+ property: { name: 'Level', type: BuilderQueryEditorPropertyType.String },
+ type: BuilderQueryEditorExpressionType.Group_by,
+ },
+ ],
+ };
+
+ render();
+
+ const addButton = screen.getByLabelText('Add order by');
+ await userEvent.click(addButton);
+
+ const columnSelect = screen.getByLabelText('Order by column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+ });
+
+ it('uses aggregate columns when available', async () => {
+ const query = createMockQuery();
+ query.azureLogAnalytics!.builderQuery!.reduce = {
+ type: BuilderQueryEditorExpressionType.Reduce,
+ expressions: [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ },
+ property: { name: 'Count', type: BuilderQueryEditorPropertyType.String },
+ },
+ ],
+ };
+
+ render();
+
+ const addButton = screen.getByLabelText('Add order by');
+ await userEvent.click(addButton);
+
+ const columnSelect = screen.getByLabelText('Order by column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('Count')).toBeInTheDocument();
+ });
+
+ it('uses both group by and aggregate columns when available', async () => {
+ const query = createMockQuery();
+ query.azureLogAnalytics!.builderQuery!.groupBy = {
+ type: BuilderQueryEditorExpressionType.Group_by,
+ expressions: [
+ {
+ property: { name: 'Level', type: BuilderQueryEditorPropertyType.String },
+ type: BuilderQueryEditorExpressionType.Group_by,
+ },
+ ],
+ };
+ query.azureLogAnalytics!.builderQuery!.reduce = {
+ type: BuilderQueryEditorExpressionType.Reduce,
+ expressions: [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ },
+ property: { name: 'Count', type: BuilderQueryEditorPropertyType.String },
+ },
+ ],
+ };
+
+ render();
+
+ const addButton = screen.getByLabelText('Add order by');
+ await userEvent.click(addButton);
+
+ const columnSelect = screen.getByLabelText('Order by column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+ expect(await screen.getByText('Count')).toBeInTheDocument();
+ });
+
+ it('does not duplicate available columns', async () => {
+ const query = createMockQuery();
+ query.azureLogAnalytics!.builderQuery!.groupBy = {
+ type: BuilderQueryEditorExpressionType.Group_by,
+ expressions: [
+ {
+ property: { name: 'Level', type: BuilderQueryEditorPropertyType.String },
+ type: BuilderQueryEditorExpressionType.Group_by,
+ },
+ ],
+ };
+ query.azureLogAnalytics!.builderQuery!.reduce = {
+ type: BuilderQueryEditorExpressionType.Reduce,
+ expressions: [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ },
+ property: { name: 'Level', type: BuilderQueryEditorPropertyType.String },
+ },
+ ],
+ };
+
+ render();
+
+ const addButton = screen.getByLabelText('Add order by');
+ await userEvent.click(addButton);
+
+ const columnSelect = screen.getByLabelText('Order by column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+ });
+
+ it('uses selected columns when no group by or aggregates', async () => {
+ const query = createMockQuery();
+ query.azureLogAnalytics!.builderQuery!.columns!.columns = ['TimeGenerated', 'Level'];
+
+ render();
+
+ const addButton = screen.getByLabelText('Add order by');
+ await userEvent.click(addButton);
+
+ const columnSelect = screen.getByLabelText('Order by column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('TimeGenerated')).toBeInTheDocument();
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+ });
+
+ it('falls back to all columns when no other columns available', async () => {
+ const query = createMockQuery();
+ query.azureLogAnalytics!.builderQuery!.columns!.columns = [];
+
+ render();
+
+ const addButton = screen.getByLabelText('Add order by');
+ await userEvent.click(addButton);
+
+ const columnSelect = screen.getByLabelText('Order by column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('TimeGenerated')).toBeInTheDocument();
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+ expect(await screen.getByText('Count')).toBeInTheDocument();
+ expect(await screen.getByText('Duration')).toBeInTheDocument();
+ });
+
+ it('resets order by when table changes', () => {
+ const existingOrderBy: BuilderQueryEditorOrderByExpression[] = [
+ {
+ property: { name: 'TimeGenerated', type: BuilderQueryEditorPropertyType.String },
+ order: BuilderQueryEditorOrderByOptions.Desc,
+ type: BuilderQueryEditorExpressionType.Order_by,
+ },
+ ];
+
+ const queryWithOrderBy = createMockQuery(existingOrderBy);
+ const { rerender } = render();
+
+ const newQuery = createMockQuery(existingOrderBy);
+ newQuery.azureLogAnalytics!.builderQuery!.from!.property.name = 'AppEvents';
+
+ rerender();
+
+ const addButton = screen.getByLabelText('Add order by');
+ expect(addButton).toBeInTheDocument();
+ });
+});
diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/OrderBySection.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/OrderBySection.tsx
index 98587b1401f..ed8cde5ab39 100644
--- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/OrderBySection.tsx
+++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/OrderBySection.tsx
@@ -45,16 +45,35 @@ export const OrderBySection: React.FC = ({ query, allColumn
const aggregateColumns = builderQuery?.reduce?.expressions?.map((r) => r.property?.name) || [];
const selectedColumns = builderQuery?.columns?.columns || [];
- const allAvailableColumns =
- groupByColumns.length > 0
- ? groupByColumns
- : aggregateColumns.length > 0
- ? aggregateColumns
- : selectedColumns.length > 0
- ? selectedColumns
- : allColumns.map((col) => col.name);
+ const allAvailableColumns = new Set();
+ if (groupByColumns.length > 0) {
+ groupByColumns.forEach((col) => {
+ if (col) {
+ allAvailableColumns.add(col);
+ }
+ });
+ }
+ if (aggregateColumns.length > 0) {
+ aggregateColumns.forEach((col) => {
+ if (col) {
+ allAvailableColumns.add(col);
+ }
+ });
+ }
+ if (allAvailableColumns.size === 0 && selectedColumns.length > 0) {
+ selectedColumns.forEach((col) => {
+ if (col) {
+ allAvailableColumns.add(col);
+ }
+ });
+ }
+ if (allAvailableColumns.size === 0) {
+ allColumns.forEach((col) => {
+ allAvailableColumns.add(col.name);
+ });
+ }
- const columnOptions = allAvailableColumns.map((col) => ({
+ const columnOptions = Array.from(allAvailableColumns).map((col) => ({
label: col,
value: col,
}));