Dashboards: Implement natural sort for query variables (#78024)

This commit is contained in:
Ivan Babrou 2023-11-21 01:17:38 -08:00 committed by GitHub
parent b2d94f3d85
commit 860b3bbce7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 45 additions and 2 deletions

View File

@ -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<br/>`0`: Never refresh the variable<br/>`1`: Queries the data source every time the dashboard loads.<br/>`2`: Queries the data source when the dashboard time range changes.<br/>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<br/>Accepted values are:<br/>`0`: No sorting<br/>`1`: Alphabetical ASC<br/>`2`: Alphabetical DESC<br/>`3`: Numerical ASC<br/>`4`: Numerical DESC<br/>`5`: Alphabetical Case Insensitive ASC<br/>`6`: Alphabetical Case Insensitive DESC<br/>Possible values are: `0`, `1`, `2`, `3`, `4`, `5`, `6`. |
| `sort` | integer | No | | Sort variable options<br/>Accepted values are:<br/>`0`: No sorting<br/>`1`: Alphabetical ASC<br/>`2`: Alphabetical DESC<br/>`3`: Numerical ASC<br/>`4`: Numerical DESC<br/>`5`: Alphabetical Case Insensitive ASC<br/>`6`: Alphabetical Case Insensitive DESC<br/>`7`: Natural ASC<br/>`8`: Natural DESC<br/>Possible values are: `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`. |
### VariableOption

View File

@ -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: {

View File

@ -36,6 +36,8 @@ export enum VariableSort {
numericalDesc,
alphabeticalCaseInsensitiveAsc,
alphabeticalCaseInsensitiveDesc,
naturalAsc,
naturalDesc,
}
export enum VariableHide {

View File

@ -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,
}

View File

@ -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

View File

@ -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<Props>) {

View File

@ -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', () => {

View File

@ -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) {