logs: make sure log-row-react-keys are always unique (#71279)

This commit is contained in:
Gábor Farkas 2023-07-13 08:01:55 +02:00 committed by GitHub
parent 8a38b5ea3c
commit 1f55003db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 2 deletions

View File

@ -0,0 +1,41 @@
import { UniqueKeyMaker } from './UniqueKeyMaker';
describe('UniqueKeyMaker', () => {
const expectKeys = (testData: Array<[string, string]>) => {
const k = new UniqueKeyMaker();
testData.forEach(([input, output]) => {
expect(k.getKey(input)).toBe(output);
});
// we also make a check that all the output-values are unique
const outputs = testData.map(([i, o]) => o);
const uniqueOutputLength = new Set(outputs).size;
expect(uniqueOutputLength).toBe(outputs.length);
};
it('should handle already unique keys', () => {
expectKeys([
['one', 'k_one'],
['two', 'k_two'],
['three', 'k_three'],
]);
});
it('should handle duplicate keys', () => {
expectKeys([
['one', 'k_one'],
['one', 'i_2'],
['one', 'i_3'],
]);
});
it('should handle a mix of unique and duplicate keys', () => {
expectKeys([
['one', 'k_one'],
['two', 'k_two'],
['one', 'i_3'],
['two', 'i_4'],
['three', 'k_three'],
]);
});
});

View File

@ -0,0 +1,25 @@
// this class generates react-keys that are guaranteed to be unique.
// it will try to use the provided `maybeId`, but if that's a duplicate,
// it will use an index-based key.
// NOTE: it will always add a prefix to the string, this is necessary
// to avoid the problem if your proposed key would conflict with
// the index-based names
export class UniqueKeyMaker {
seen: Set<string>;
count: number;
constructor() {
this.seen = new Set();
this.count = 0;
}
getKey(proposedKey: string) {
this.count += 1;
const maybeKey = `k_${proposedKey}`;
if (this.seen.has(maybeKey)) {
return `i_${this.count}`;
} else {
this.seen.add(maybeKey);
return maybeKey;
}
}
}

View File

@ -13,6 +13,7 @@ import {
} from '@grafana/data';
import { withTheme2, Themeable2 } from '@grafana/ui';
import { UniqueKeyMaker } from '../UniqueKeyMaker';
import { sortLogRows } from '../utils';
//Components
@ -122,13 +123,15 @@ class UnThemedLogRows extends PureComponent<Props, State> {
// React profiler becomes unusable if we pass all rows to all rows and their labels, using getter instead
const getRows = this.makeGetRows(orderedRows);
const keyMaker = new UniqueKeyMaker();
return (
<table className={styles.logsRowsTable}>
<tbody>
{hasData &&
firstRows.map((row) => (
<LogRow
key={row.uid}
key={keyMaker.getKey(row.uid)}
getRows={getRows}
row={row}
showDuplicates={showDuplicates}
@ -148,7 +151,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
renderAll &&
lastRows.map((row) => (
<LogRow
key={row.uid}
key={keyMaker.getKey(row.uid)}
getRows={getRows}
row={row}
showDuplicates={showDuplicates}