Highlight search terms in logs with yellow when highlight search setting is enabled
Summary: Building on the previous diff which added a setting to enable/disable highlighting search terms in the logs. This diff adds the actual highlighting and connects with the setting. The highlighting currently only supports one color, while the next diff will seek to support a preset of a "custom" colors for the highlighting Reviewed By: mweststrate Differential Revision: D37348441 fbshipit-source-id: 7a2b74b16f239d5e36c213e06ccb86f74eaa8df5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
24a314054e
commit
2f39ede6f7
@@ -20,6 +20,7 @@ import {safeStringify} from '../utils/safeStringify';
|
||||
import {urlRegex} from '../utils/urlRegex';
|
||||
import {useTableRedraw} from '../data-source/index';
|
||||
import {theme} from './theme';
|
||||
import {HighlightManager} from './Highlight';
|
||||
|
||||
/**
|
||||
* A Formatter is used to render an arbitrarily value to React. If a formatter returns 'undefined'
|
||||
@@ -27,48 +28,65 @@ import {theme} from './theme';
|
||||
*
|
||||
* In case further processing by the default formatter is to be avoided, make sure a string is returned from any custom formatter.
|
||||
*/
|
||||
export type Formatter = (value: any) => string | React.ReactElement | any;
|
||||
export type Formatter = (
|
||||
value: any,
|
||||
highlighter?: HighlightManager,
|
||||
) => string | React.ReactElement | any;
|
||||
|
||||
export const DataFormatter = {
|
||||
defaultFormatter(value: any) {
|
||||
defaultFormatter(value: any, highlighter?: HighlightManager) {
|
||||
if (isValidElement(value)) {
|
||||
return value;
|
||||
}
|
||||
let res = '';
|
||||
switch (typeof value) {
|
||||
case 'boolean':
|
||||
return value ? 'true' : 'false';
|
||||
res = value ? 'true' : 'false';
|
||||
break;
|
||||
case 'number':
|
||||
return '' + value;
|
||||
res = '' + value;
|
||||
break;
|
||||
case 'undefined':
|
||||
return '';
|
||||
break;
|
||||
case 'string':
|
||||
return value;
|
||||
res = value;
|
||||
break;
|
||||
case 'object': {
|
||||
if (value === null) return '';
|
||||
if (value === null) break;
|
||||
if (value instanceof Date) {
|
||||
return (
|
||||
res =
|
||||
value.toTimeString().split(' ')[0] +
|
||||
'.' +
|
||||
pad('' + value.getMilliseconds(), 3, '0')
|
||||
);
|
||||
pad('' + value.getMilliseconds(), 3, '0');
|
||||
break;
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
return safeStringify(Array.from(value.entries()));
|
||||
res = safeStringify(Array.from(value.entries()));
|
||||
break;
|
||||
}
|
||||
if (value instanceof Set) {
|
||||
return safeStringify(Array.from(value.values()));
|
||||
res = safeStringify(Array.from(value.values()));
|
||||
break;
|
||||
}
|
||||
return safeStringify(value);
|
||||
res = safeStringify(value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return '<unrenderable value>';
|
||||
res = '<unrenderable value>';
|
||||
}
|
||||
return highlighter?.render(res) ?? res;
|
||||
},
|
||||
|
||||
truncate(maxLength: number) {
|
||||
return (value: any) => {
|
||||
return (value: any, highlighter?: HighlightManager) => {
|
||||
if (typeof value === 'string' && value.length > maxLength) {
|
||||
return <TruncateHelper value={value} maxLength={maxLength} />;
|
||||
return (
|
||||
<TruncateHelper
|
||||
value={value}
|
||||
maxLength={maxLength}
|
||||
textWrapper={highlighter}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
@@ -131,16 +149,20 @@ export const DataFormatter = {
|
||||
return value;
|
||||
},
|
||||
|
||||
format(value: any, formatters?: Formatter[] | Formatter): any {
|
||||
format(
|
||||
value: any,
|
||||
formatters?: Formatter[] | Formatter,
|
||||
highlighter?: HighlightManager,
|
||||
): any {
|
||||
let res = value;
|
||||
if (Array.isArray(formatters)) {
|
||||
for (const formatter of formatters) {
|
||||
res = formatter(res);
|
||||
res = formatter(res, highlighter);
|
||||
}
|
||||
} else if (formatters) {
|
||||
res = formatters(res);
|
||||
res = formatters(res, highlighter);
|
||||
}
|
||||
return DataFormatter.defaultFormatter(res);
|
||||
return DataFormatter.defaultFormatter(res, highlighter);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -148,16 +170,18 @@ export const DataFormatter = {
|
||||
export function TruncateHelper({
|
||||
value,
|
||||
maxLength,
|
||||
textWrapper,
|
||||
}: {
|
||||
value: string;
|
||||
maxLength: number;
|
||||
textWrapper?: HighlightManager; //Could be a generic type
|
||||
}) {
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
const redrawRow = useTableRedraw();
|
||||
|
||||
const message = collapsed ? value.substr(0, maxLength) : value;
|
||||
return (
|
||||
<>
|
||||
{collapsed ? value.substr(0, maxLength) : value}
|
||||
{textWrapper ? textWrapper.render(message) : message}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCollapsed((c) => !c);
|
||||
|
||||
@@ -53,6 +53,7 @@ import {usePluginInstanceMaybe} from '../../plugin/PluginContext';
|
||||
import {debounce} from 'lodash';
|
||||
import {useInUnitTest} from '../../utils/useInUnitTest';
|
||||
import {createDataSource} from '../../state/createDataSource';
|
||||
import {HighlightProvider} from '../Highlight';
|
||||
|
||||
type DataTableBaseProps<T = any> = {
|
||||
columns: DataTableColumn<T>[];
|
||||
@@ -603,7 +604,14 @@ export function DataTable<T extends object>(
|
||||
|
||||
return (
|
||||
<Layout.Container grow={props.scrollable}>
|
||||
{mainSection}
|
||||
<HighlightProvider
|
||||
text={
|
||||
tableState.highlightSearchSetting.highlightEnabled
|
||||
? tableState.searchValue
|
||||
: ''
|
||||
}>
|
||||
{mainSection}
|
||||
</HighlightProvider>
|
||||
{props.enableAutoScroll && (
|
||||
<AutoScroller>
|
||||
<PushpinFilled
|
||||
|
||||
@@ -16,6 +16,7 @@ import {DataFormatter} from '../DataFormatter';
|
||||
import {Dropdown} from 'antd';
|
||||
import {contextMenuTrigger} from '../data-inspector/DataInspectorNode';
|
||||
import {getValueAtPath} from './DataTableManager';
|
||||
import {HighlightManager, useHighlighter} from '../Highlight';
|
||||
|
||||
// heuristic for row estimation, should match any future styling updates
|
||||
export const DEFAULT_ROW_HEIGHT = 24;
|
||||
@@ -109,6 +110,7 @@ export const TableRow = memo(function TableRow<T>({
|
||||
highlighted,
|
||||
config,
|
||||
}: TableRowProps<T>) {
|
||||
const highlighter = useHighlighter();
|
||||
const row = (
|
||||
<TableBodyRowContainer
|
||||
highlighted={highlighted}
|
||||
@@ -127,6 +129,7 @@ export const TableRow = memo(function TableRow<T>({
|
||||
record,
|
||||
highlighted,
|
||||
itemIndex,
|
||||
highlighter,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -157,8 +160,13 @@ export function renderColumnValue<T>(
|
||||
record: T,
|
||||
highlighted: boolean,
|
||||
itemIndex: number,
|
||||
highlighter?: HighlightManager,
|
||||
) {
|
||||
return col.onRender
|
||||
? col.onRender(record, highlighted, itemIndex)
|
||||
: DataFormatter.format(getValueAtPath(record, col.key), col.formatters);
|
||||
: DataFormatter.format(
|
||||
getValueAtPath(record, col.key),
|
||||
col.formatters,
|
||||
highlighter,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,24 +49,34 @@ test('update and append', async () => {
|
||||
{
|
||||
const elem = await rendering.findAllByText('test DataTable');
|
||||
expect(elem.length).toBe(1);
|
||||
expect(elem[0].parentElement).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
test DataTable
|
||||
</div>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
true
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
expect(elem[0].parentElement?.parentElement).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
test DataTable
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
true
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
act(() => {
|
||||
@@ -103,24 +113,34 @@ test('column visibility', async () => {
|
||||
{
|
||||
const elem = await rendering.findAllByText('test DataTable');
|
||||
expect(elem.length).toBe(1);
|
||||
expect(elem[0].parentElement).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
test DataTable
|
||||
</div>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
true
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
expect(elem[0].parentElement?.parentElement).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
test DataTable
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
true
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
// hide done
|
||||
@@ -130,18 +150,23 @@ test('column visibility', async () => {
|
||||
{
|
||||
const elem = await rendering.findAllByText('test DataTable');
|
||||
expect(elem.length).toBe(1);
|
||||
expect(elem[0].parentElement).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
test DataTable
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
expect(elem[0].parentElement?.parentElement).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
test DataTable
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
// reset
|
||||
@@ -151,7 +176,7 @@ test('column visibility', async () => {
|
||||
{
|
||||
const elem = await rendering.findAllByText('test DataTable');
|
||||
expect(elem.length).toBe(1);
|
||||
expect(elem[0].parentElement?.children.length).toBe(2);
|
||||
expect(elem[0].parentElement?.parentElement?.children.length).toBe(2);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ test('update and append', async () => {
|
||||
{
|
||||
const elem = await rendering.findAllByText('test DataTable');
|
||||
expect(elem.length).toBe(1);
|
||||
expect(elem[0].parentElement).toMatchInlineSnapshot(`
|
||||
expect(elem[0].parentElement?.parentElement).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
@@ -50,13 +50,23 @@ test('update and append', async () => {
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
test DataTable
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
test DataTable
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-1baxqcf-TableBodyColumnContainer e1luu51r0"
|
||||
width="50%"
|
||||
>
|
||||
true
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
true
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
@@ -117,8 +117,9 @@ test('It can render rows', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
expect((await renderer.findByText('unique-string')).parentElement)
|
||||
.toMatchInlineSnapshot(`
|
||||
expect(
|
||||
(await renderer.findByText('unique-string')).parentElement?.parentElement,
|
||||
).toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-dropdown-trigger css-1k3kr6b-TableBodyRowContainer e1luu51r1"
|
||||
>
|
||||
@@ -126,39 +127,76 @@ test('It can render rows', async () => {
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
>
|
||||
00:00:00.000
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
00:00:00.000
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
>
|
||||
Android Phone
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
Android Phone
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
>
|
||||
FB4A
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
FB4A
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
>
|
||||
unique-string
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
unique-string
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
/>
|
||||
<div
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
/>
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
>
|
||||
toClient:send
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12luweq-TableBodyColumnContainer e1luu51r0"
|
||||
width="14%"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="css-1cfwmd7-Highlighted eiud9hg0"
|
||||
/>
|
||||
toClient:send
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user