Added color options for highlighting search terms

Summary:
This diff builds on the previous ones by enabling other colors to be used as highlights for the search terms. Current color options are: yellow(default), red, blue, green. Possible extensions to this feature could include allow the user to enter a custom hex-color string and use that as the highlight color.

Changelog: DataTable will now have option to have its search terms highlighted in the search results by toggling and customizing the highlight colors in the menu bar

Reviewed By: mweststrate

Differential Revision: D37383163

fbshipit-source-id: c81e383c0570ef5efbf3171b92b81a8fb2e55ea7
This commit is contained in:
Feiyu Wong
2022-06-29 10:36:52 -07:00
committed by Facebook GitHub Bot
parent 2f39ede6f7
commit f46cf2b0ce
10 changed files with 106 additions and 32 deletions

View File

@@ -7,7 +7,6 @@
* @format
*/
import styled from '@emotion/styled';
import React, {
useEffect,
memo,
@@ -19,19 +18,20 @@ import React, {
import {debounce} from 'lodash';
import {theme} from './theme';
const Highlighted = styled.span({
backgroundColor: theme.searchHighlightBackground,
});
export interface HighlightManager {
setFilter(text: string | undefined): void;
render(text: string): React.ReactNode;
setHighlightColor(color: string | undefined): void;
}
function createHighlightManager(initialText: string = ''): HighlightManager {
function createHighlightManager(
initialText: string = '',
initialHighlightColor: string = theme.searchHighlightBackground.yellow,
): HighlightManager {
const callbacks = new Set<(prev: string, next: string) => void>();
let matches = 0;
let currentFilter = initialText;
let currHighlightColor = initialHighlightColor;
const Highlight: React.FC<{text: string}> = memo(({text}) => {
const [, setUpdate] = useState(0);
@@ -65,9 +65,9 @@ function createHighlightManager(initialText: string = ''): HighlightManager {
) : (
<>
{text.substr(0, index)}
<Highlighted>
<span style={{backgroundColor: currHighlightColor}}>
{text.substr(index, currentFilter.length)}
</Highlighted>
</span>
{text.substr(index + currentFilter.length)}
</>
)}
@@ -87,6 +87,12 @@ function createHighlightManager(initialText: string = ''): HighlightManager {
render(text: string) {
return <Highlight text={text} />;
},
setHighlightColor(color: string) {
if (color !== currHighlightColor) {
currHighlightColor = color;
callbacks.forEach((cb) => cb(currentFilter, currentFilter));
}
},
};
}
@@ -98,21 +104,32 @@ export const HighlightContext = createContext<HighlightManager>({
// stub implementation in case we render a component without a Highlight context
return text;
},
setHighlightColor(_color: string) {
throw new Error('Cannot set the color of a stub highlight manager');
},
});
export function HighlightProvider({
text,
highlightColor,
children,
}: {
text: string | undefined;
highlightColor?: string | undefined;
children: React.ReactElement;
}) {
const [highlightManager] = useState(() => createHighlightManager(text));
const [highlightManager] = useState(() =>
createHighlightManager(text, highlightColor),
);
useEffect(() => {
highlightManager.setFilter(text);
}, [text, highlightManager]);
useEffect(() => {
highlightManager.setHighlightColor(highlightColor);
}, [highlightColor, highlightManager]);
return (
<HighlightContext.Provider value={highlightManager}>
{children}

View File

@@ -115,9 +115,7 @@ test.local('can filter for data', async () => {
expect(element.parentElement).toMatchInlineSnapshot(`
<span>
"j
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
>
<span>
son
</span>
"

View File

@@ -609,6 +609,10 @@ export function DataTable<T extends object>(
tableState.highlightSearchSetting.highlightEnabled
? tableState.searchValue
: ''
}
highlightColor={
tableState.highlightSearchSetting.color ||
theme.searchHighlightBackground.yellow
}>
{mainSection}
</HighlightProvider>

View File

@@ -12,6 +12,7 @@ import {Percentage} from '../../utils/widthUtils';
import {MutableRefObject, Reducer} from 'react';
import {DataSource, DataSourceVirtualizer} from '../../data-source/index';
import produce, {castDraft, immerable, original} from 'immer';
import {theme} from '../theme';
export type OnColumnResize = (id: string, size: number | Percentage) => void;
export type Sorting<T = any> = {
@@ -106,7 +107,8 @@ type DataManagerActions<T> =
| Action<'setAutoScroll', {autoScroll: boolean}>
| Action<'toggleSearchValue'>
| Action<'clearSearchHistory'>
| Action<'toggleHighlightSearch'>;
| Action<'toggleHighlightSearch'>
| Action<'setSearchHighlightColor', {color: string}>;
type DataManagerConfig<T> = {
dataSource: DataSource<T, T[keyof T]>;
@@ -309,6 +311,12 @@ export const dataTableManagerReducer = produce<
!draft.highlightSearchSetting.highlightEnabled;
break;
}
case 'setSearchHighlightColor': {
if (draft.highlightSearchSetting.color !== action.color) {
draft.highlightSearchSetting.color = action.color;
}
break;
}
default: {
throw new Error('Unknown action ' + (action as any).type);
}
@@ -341,6 +349,7 @@ export type DataTableManager<T> = {
dataSource: DataSource<T, T[keyof T]>;
toggleSearchValue(): void;
toggleHighlightSearch(): void;
setSearchHighlightColor(color: string): void;
};
export function createDataTableManager<T>(
@@ -393,6 +402,9 @@ export function createDataTableManager<T>(
toggleHighlightSearch() {
dispatch({type: 'toggleHighlightSearch'});
},
setSearchHighlightColor(color) {
dispatch({type: 'setSearchHighlightColor', color});
},
dataSource,
};
}
@@ -440,7 +452,7 @@ export function createInitialState<T>(
autoScroll: prefs?.autoScroll ?? config.autoScroll ?? false,
highlightSearchSetting: prefs?.highlightSearchSetting ?? {
highlightEnabled: false,
color: '',
color: theme.searchHighlightBackground.yellow,
},
};
// @ts-ignore

View File

@@ -8,7 +8,7 @@
*/
import {CopyOutlined, FilterOutlined, TableOutlined} from '@ant-design/icons';
import {Checkbox, Menu, Switch} from 'antd';
import {Badge, Checkbox, Menu, Select, Switch} from 'antd';
import {Layout} from 'flipper-plugin';
import {
DataTableDispatch,
@@ -25,8 +25,10 @@ import {toFirstUpper} from '../../utils/toFirstUpper';
import {DataSource} from '../../data-source/index';
import {renderColumnValue} from './TableRow';
import {textContent} from '../../utils/textContent';
import {theme} from '../theme';
const {Item, SubMenu} = Menu;
const {Option} = Select;
export function tableContextMenuFactory<T>(
datasource: DataSource<T, T[keyof T]>,
@@ -197,6 +199,7 @@ export function tableContextMenuFactory<T>(
e.stopPropagation();
e.preventDefault();
}}>
Highlight search terms
<Switch
checked={highlightSearchSetting.highlightEnabled}
size="small"
@@ -206,7 +209,42 @@ export function tableContextMenuFactory<T>(
});
}}
/>
Highlight search terms
</Layout.Horizontal>
</Menu.Item>
<Menu.Item key="highlight search color">
<Layout.Horizontal
gap
center
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}>
Highlight search color
<Select
style={{width: '7em'}}
defaultValue={highlightSearchSetting.color}
onChange={(color: string) => {
dispatch({
type: 'setSearchHighlightColor',
color: color,
});
}}>
{Object.entries(theme.searchHighlightBackground).map(
([colorName, color]) => (
<Option key={colorName} value={color}>
<Badge
text={
<span style={{backgroundColor: color}}>
{colorName.charAt(0).toUpperCase() +
colorName.slice(1)}
</span>
}
color={color}
/>
</Option>
),
)}
</Select>
</Layout.Horizontal>
</Menu.Item>
</SubMenu>

View File

@@ -59,7 +59,7 @@ test('update and append', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
test DataTable
</span>
@@ -70,7 +70,7 @@ test('update and append', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
true
</span>
@@ -123,7 +123,7 @@ test('column visibility', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
test DataTable
</span>
@@ -134,7 +134,7 @@ test('column visibility', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
true
</span>
@@ -160,7 +160,7 @@ test('column visibility', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
test DataTable
</span>

View File

@@ -52,7 +52,7 @@ test('update and append', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
test DataTable
</span>
@@ -63,7 +63,7 @@ test('update and append', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
true
</span>

View File

@@ -118,7 +118,7 @@ class PartialHighlight extends PureComponent<{
content: string;
}> {
static HighlightedText = styled.span<{selected: boolean}>((props) => ({
backgroundColor: theme.searchHighlightBackground,
backgroundColor: theme.searchHighlightBackground.yellow,
color: props.selected ? `${theme.textColorPrimary} !important` : 'auto',
}));

View File

@@ -21,7 +21,12 @@ export const theme = {
textColorSecondary: 'var(--flipper-text-color-secondary)',
textColorPlaceholder: 'var(--flipper-text-color-placeholder)',
textColorActive: 'var(--light-color-button-active)',
searchHighlightBackground: antColors.yellow[3],
searchHighlightBackground: {
yellow: antColors.yellow[3],
red: antColors.red[3],
green: antColors.green[3],
blue: antColors.blue[3],
} as const,
selectionBackgroundColor: 'var(--flipper-primary-background-wash)',
disabledColor: 'var(--flipper-disabled-color)',
backgroundDefault: 'var(--flipper-background-default)',

View File

@@ -129,7 +129,7 @@ test('It can render rows', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
00:00:00.000
</span>
@@ -140,7 +140,7 @@ test('It can render rows', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
Android Phone
</span>
@@ -151,7 +151,7 @@ test('It can render rows', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
FB4A
</span>
@@ -162,7 +162,7 @@ test('It can render rows', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
unique-string
</span>
@@ -173,7 +173,7 @@ test('It can render rows', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
</span>
</div>
@@ -183,7 +183,7 @@ test('It can render rows', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
</span>
</div>
@@ -193,7 +193,7 @@ test('It can render rows', async () => {
>
<span>
<span
class="css-1cfwmd7-Highlighted eiud9hg0"
style="background-color: rgb(255, 245, 102);"
/>
toClient:send
</span>