diff --git a/docs/sources/developers/kinds/core/dashboard/schema-reference.md b/docs/sources/developers/kinds/core/dashboard/schema-reference.md
index 4b6f67f6c34..b3773b104ab 100644
--- a/docs/sources/developers/kinds/core/dashboard/schema-reference.md
+++ b/docs/sources/developers/kinds/core/dashboard/schema-reference.md
@@ -648,7 +648,7 @@ A variable is a placeholder for a value. You can use variables in metric queries
| `query` | | No | | Query used to fetch values for a variable |
| `refresh` | integer | No | | Options to config when to refresh a variable
`0`: Never refresh the variable
`1`: Queries the data source every time the dashboard loads.
`2`: Queries the data source when the dashboard time range changes.
Possible values are: `0`, `1`, `2`. |
| `skipUrlSync` | boolean | No | `false` | Whether the variable value should be managed by URL query params or not |
-| `sort` | integer | No | | Sort variable options
Accepted values are:
`0`: No sorting
`1`: Alphabetical ASC
`2`: Alphabetical DESC
`3`: Numerical ASC
`4`: Numerical DESC
`5`: Alphabetical Case Insensitive ASC
`6`: Alphabetical Case Insensitive DESC
Possible values are: `0`, `1`, `2`, `3`, `4`, `5`, `6`. |
+| `sort` | integer | No | | Sort variable options
Accepted values are:
`0`: No sorting
`1`: Alphabetical ASC
`2`: Alphabetical DESC
`3`: Numerical ASC
`4`: Numerical DESC
`5`: Alphabetical Case Insensitive ASC
`6`: Alphabetical Case Insensitive DESC
`7`: Natural ASC
`8`: Natural DESC
Possible values are: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`. |
### VariableOption
diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue
index 5fb6347ee3d..4964b029a54 100644
--- a/kinds/dashboard/dashboard_kind.cue
+++ b/kinds/dashboard/dashboard_kind.cue
@@ -243,7 +243,9 @@ lineage: schemas: [{
// `4`: Numerical DESC
// `5`: Alphabetical Case Insensitive ASC
// `6`: Alphabetical Case Insensitive DESC
- #VariableSort: 0 | 1 | 2 | 3 | 4 | 5 | 6 @cuetsy(kind="enum",memberNames="disabled|alphabeticalAsc|alphabeticalDesc|numericalAsc|numericalDesc|alphabeticalCaseInsensitiveAsc|alphabeticalCaseInsensitiveDesc")
+ // `7`: Natural ASC
+ // `8`: Natural DESC
+ #VariableSort: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 @cuetsy(kind="enum",memberNames="disabled|alphabeticalAsc|alphabeticalDesc|numericalAsc|numericalDesc|alphabeticalCaseInsensitiveAsc|alphabeticalCaseInsensitiveDesc|naturalAsc|naturalDesc")
// Ref to a DataSource instance
#DataSourceRef: {
diff --git a/packages/grafana-data/src/types/templateVars.ts b/packages/grafana-data/src/types/templateVars.ts
index d16daed9ea4..86d5c50594d 100644
--- a/packages/grafana-data/src/types/templateVars.ts
+++ b/packages/grafana-data/src/types/templateVars.ts
@@ -36,6 +36,8 @@ export enum VariableSort {
numericalDesc,
alphabeticalCaseInsensitiveAsc,
alphabeticalCaseInsensitiveDesc,
+ naturalAsc,
+ naturalDesc,
}
export enum VariableHide {
diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts
index 666357753f5..b2e807b1361 100644
--- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts
+++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts
@@ -233,6 +233,8 @@ export enum VariableHide {
* `4`: Numerical DESC
* `5`: Alphabetical Case Insensitive ASC
* `6`: Alphabetical Case Insensitive DESC
+ * `7`: Natural ASC
+ * `8`: Natural DESC
*/
export enum VariableSort {
alphabeticalAsc = 1,
@@ -240,6 +242,8 @@ export enum VariableSort {
alphabeticalCaseInsensitiveDesc = 6,
alphabeticalDesc = 2,
disabled = 0,
+ naturalAsc = 7,
+ naturalDesc = 8,
numericalAsc = 3,
numericalDesc = 4,
}
diff --git a/pkg/kinds/dashboard/dashboard_spec_gen.go b/pkg/kinds/dashboard/dashboard_spec_gen.go
index 7e2a86472dd..072eaf552fb 100644
--- a/pkg/kinds/dashboard/dashboard_spec_gen.go
+++ b/pkg/kinds/dashboard/dashboard_spec_gen.go
@@ -152,6 +152,8 @@ const (
VariableSortN4 VariableSort = 4
VariableSortN5 VariableSort = 5
VariableSortN6 VariableSort = 6
+ VariableSortN7 VariableSort = 7
+ VariableSortN8 VariableSort = 8
)
// Defines values for VariableType.
@@ -960,6 +962,8 @@ type VariableModel struct {
// `4`: Numerical DESC
// `5`: Alphabetical Case Insensitive ASC
// `6`: Alphabetical Case Insensitive DESC
+ // `7`: Natural ASC
+ // `8`: Natural DESC
Sort *VariableSort `json:"sort,omitempty"`
// Dashboard variable type
@@ -1001,6 +1005,8 @@ type VariableRefresh int
// `4`: Numerical DESC
// `5`: Alphabetical Case Insensitive ASC
// `6`: Alphabetical Case Insensitive DESC
+// `7`: Natural ASC
+// `8`: Natural DESC
type VariableSort int
// Dashboard variable type
diff --git a/public/app/features/variables/query/QueryVariableSortSelect.tsx b/public/app/features/variables/query/QueryVariableSortSelect.tsx
index 33e8f116770..c6d0bc38cdb 100644
--- a/public/app/features/variables/query/QueryVariableSortSelect.tsx
+++ b/public/app/features/variables/query/QueryVariableSortSelect.tsx
@@ -19,6 +19,8 @@ const SORT_OPTIONS = [
{ label: 'Numerical (desc)', value: VariableSort.numericalDesc },
{ label: 'Alphabetical (case-insensitive, asc)', value: VariableSort.alphabeticalCaseInsensitiveAsc },
{ label: 'Alphabetical (case-insensitive, desc)', value: VariableSort.alphabeticalCaseInsensitiveDesc },
+ { label: 'Natural (asc)', value: VariableSort.naturalAsc },
+ { label: 'Natural (desc)', value: VariableSort.naturalDesc },
];
export function QueryVariableSortSelect({ onChange, sort }: PropsWithChildren) {
diff --git a/public/app/features/variables/query/reducer.test.ts b/public/app/features/variables/query/reducer.test.ts
index 89643ff82da..e0f273484bd 100644
--- a/public/app/features/variables/query/reducer.test.ts
+++ b/public/app/features/variables/query/reducer.test.ts
@@ -277,6 +277,8 @@ describe('sortVariableValues', () => {
${[{ text: '1' }, { text: null }, { text: '2' }]} | ${VariableSort.numericalDesc} | ${[{ text: '2' }, { text: '1' }, { text: null }]}
${[{ text: 'a' }, { text: null }, { text: 'b' }]} | ${VariableSort.alphabeticalCaseInsensitiveAsc} | ${[{ text: null }, { text: 'a' }, { text: 'b' }]}
${[{ text: 'a' }, { text: null }, { text: 'b' }]} | ${VariableSort.alphabeticalCaseInsensitiveDesc} | ${[{ text: 'b' }, { text: 'a' }, { text: null }]}
+ ${[{ text: '1' }, { text: null }, { text: '2' }]} | ${VariableSort.naturalAsc} | ${[{ text: null }, { text: '1' }, { text: '2' }]}
+ ${[{ text: '1' }, { text: null }, { text: '2' }]} | ${VariableSort.naturalDesc} | ${[{ text: '2' }, { text: '1' }, { text: null }]}
`(
'then it should sort the options correctly without throwing (sortOrder:$sortOrder)',
({ options, sortOrder, expected }) => {
@@ -286,6 +288,19 @@ describe('sortVariableValues', () => {
}
);
});
+
+ describe('when using natural sort', () => {
+ it.each`
+ options | sortOrder | expected
+ ${[{ text: '12-lax01' }, { text: '4-sjc01' }, { text: '21-lhr01' }]} | ${VariableSort.naturalAsc} | ${[{ text: '4-sjc01' }, { text: '12-lax01' }, { text: '21-lhr01' }]}
+ ${[{ text: 'lax01' }, { text: 'sjc01' }, { text: 'sjc02' }, { text: 'lhr01' }]} | ${VariableSort.naturalAsc} | ${[{ text: 'lax01' }, { text: 'lhr01' }, { text: 'sjc01' }, { text: 'sjc02' }]}
+ ${[{ text: '4m10' }, { text: '4m2' }, { text: '4m1' }, { text: '4m4' }]} | ${VariableSort.naturalAsc} | ${[{ text: '4m1' }, { text: '4m2' }, { text: '4m4' }, { text: '4m10' }]}
+ `('then it should sort like humans would naturally sort', ({ options, sortOrder, expected }) => {
+ const result = sortVariableValues(options, sortOrder);
+
+ expect(result).toEqual(expected);
+ });
+ });
});
describe('metricNamesToVariableValues', () => {
diff --git a/public/app/features/variables/query/reducer.ts b/public/app/features/variables/query/reducer.ts
index b59354013b6..a5c623c6699 100644
--- a/public/app/features/variables/query/reducer.ts
+++ b/public/app/features/variables/query/reducer.ts
@@ -56,6 +56,18 @@ export const sortVariableValues = (options: any[], sortOrder: VariableSort) => {
options = sortBy(options, (opt) => {
return toLower(opt.text);
});
+ } else if (sortType === 4) {
+ options.sort((a, b) => {
+ if (!a.text) {
+ return -1;
+ }
+
+ if (!b.text) {
+ return 1;
+ }
+
+ return a.text.localeCompare(b.text, undefined, { numeric: true });
+ });
}
if (reverseSort) {