diff --git a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
index 32373301f6..72b22d4f28 100644
--- a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
@@ -154,6 +154,12 @@ const fakeDatabaseApiResult = {
],
};
+const fakeDatabaseApiResultInReverseOrder = {
+ ...fakeDatabaseApiResult,
+ ids: [2, 1],
+ result: [...fakeDatabaseApiResult.result].reverse(),
+};
+
const fakeSchemaApiResult = {
count: 2,
result: ['information_schema', 'public'],
@@ -168,7 +174,8 @@ const fakeFunctionNamesApiResult = {
function_names: [],
};
-const databaseApiRoute = 'glob:*/api/v1/database/?*';
+const databaseApiRoute =
+ 'glob:*/api/v1/database/?*order_column:database_name,order_direction*';
const catalogApiRoute = 'glob:*/api/v1/database/*/catalogs/?*';
const schemaApiRoute = 'glob:*/api/v1/database/*/schemas/?*';
const tablesApiRoute = 'glob:*/api/v1/database/*/tables/*';
@@ -241,6 +248,30 @@ test('Should database select display options', async () => {
expect(await screen.findByText('test-mysql')).toBeInTheDocument();
});
+test('should display options in order of the api response', async () => {
+ fetchMock.get(databaseApiRoute, fakeDatabaseApiResultInReverseOrder, {
+ overwriteRoutes: true,
+ });
+ const props = createProps();
+ render(, {
+ useRedux: true,
+ store,
+ });
+ const select = screen.getByRole('combobox', {
+ name: 'Select database or type to search databases',
+ });
+ expect(select).toBeInTheDocument();
+ userEvent.click(select);
+ const options = await screen.findAllByRole('option');
+
+ expect(options[0]).toHaveTextContent(
+ `${fakeDatabaseApiResultInReverseOrder.result[0].id}`,
+ );
+ expect(options[1]).toHaveTextContent(
+ `${fakeDatabaseApiResultInReverseOrder.result[1].id}`,
+ );
+});
+
test('Should fetch the search keyword when total count exceeds initial options', async () => {
fetchMock.get(
databaseApiRoute,
diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx b/superset-frontend/src/components/DatabaseSelector/index.tsx
index 14753c6dfe..308ebc34bd 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -16,8 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React, { ReactNode, useState, useMemo, useEffect, useRef } from 'react';
+import React, {
+ ReactNode,
+ useState,
+ useMemo,
+ useEffect,
+ useRef,
+ useCallback,
+} from 'react';
import { styled, SupersetClient, t } from '@superset-ui/core';
+import type { LabeledValue as AntdLabeledValue } from 'antd/lib/select';
import rison from 'rison';
import { AsyncSelect, Select } from 'src/components';
import Label from 'src/components/Label';
@@ -124,6 +132,10 @@ const SelectLabel = ({
const EMPTY_CATALOG_OPTIONS: CatalogOption[] = [];
const EMPTY_SCHEMA_OPTIONS: SchemaOption[] = [];
+interface AntdLabeledValueWithOrder extends AntdLabeledValue {
+ order: number;
+}
+
export default function DatabaseSelector({
db,
formMode = false,
@@ -153,6 +165,11 @@ export default function DatabaseSelector({
const schemaRef = useRef(schema);
schemaRef.current = schema;
const { addSuccessToast } = useToasts();
+ const sortComparator = useCallback(
+ (itemA: AntdLabeledValueWithOrder, itemB: AntdLabeledValueWithOrder) =>
+ itemA.order - itemB.order,
+ [],
+ );
const loadDatabases = useMemo(
() =>
@@ -165,7 +182,7 @@ export default function DatabaseSelector({
totalCount: number;
}> => {
const queryParams = rison.encode({
- order_columns: 'database_name',
+ order_column: 'database_name',
order_direction: 'asc',
page,
page_size: pageSize,
@@ -191,7 +208,8 @@ export default function DatabaseSelector({
if (result.length === 0) {
if (onEmptyResults) onEmptyResults(search);
}
- const options = result.map((row: DatabaseObject) => ({
+
+ const options = result.map((row: DatabaseObject, order: number) => ({
label: (
,
null,
);