Skip to content

Commit

Permalink
Added highlightAll prop to highlight all matches in EuiHighlight (#2957)
Browse files Browse the repository at this point in the history
* Added es2020 support

* Added props to highlight all match in EuiHighlight

* Updated changelog

* removed usage of matchAll

* Added switch to highlight all in docs

* Activated props tab on EuiHighlight

* moved destructuring to highlight function

* replaced any[] with EuiHighlightChunk

* removed export

* replaced ternary
  • Loading branch information
ashikmeerankutty authored Mar 6, 2020
1 parent 7bf3a99 commit cbbcc9e
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [`master`](https://github.com/elastic/eui/tree/master)

- Added `highlightAll` prop to `EuiHighlight` to highlight all matches ([#2957](https://github.com/elastic/eui/pull/2957))
- Added `showOnFocus` prop to `EuiScreenReaderOnly` to force display on keyboard focus ([#2976](https://github.com/elastic/eui/pull/2976))
- Added `EuiSkipLink` component ([#2976](https://github.com/elastic/eui/pull/2976))
- Created `EuiBadgeGroup` component ([#2921](https://github.com/elastic/eui/pull/2921))
Expand Down
17 changes: 14 additions & 3 deletions src-docs/src/views/highlight/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
EuiHighlight,
EuiFieldSearch,
EuiSpacer,
EuiSwitch,
} from '../../../../src/components';

export class Highlight extends Component {
Expand All @@ -12,6 +13,7 @@ export class Highlight extends Component {

this.state = {
searchValue: 'jumped over',
isHighlightAll: false,
};
}

Expand All @@ -22,15 +24,24 @@ export class Highlight extends Component {
});
};

setHighlightAll = e => {
this.setState({ isHighlightAll: e.target.checked });
};

render() {
const { searchValue } = this.state;
const { searchValue, isHighlightAll } = this.state;
return (
<Fragment>
<EuiFieldSearch value={searchValue} onChange={this.onSearchChange} />

<EuiSpacer size="m" />

<EuiHighlight search={searchValue}>
<EuiSwitch
label="Highlight all"
checked={isHighlightAll}
onChange={e => this.setHighlightAll(e)}
/>
<EuiSpacer size="m" />
<EuiHighlight search={searchValue} highlightAll={isHighlightAll}>
The quick brown fox jumped over the lazy dog
</EuiHighlight>
</Fragment>
Expand Down
1 change: 1 addition & 0 deletions src-docs/src/views/highlight/highlight_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const HighlightExample = {
string, typically in response to user input.
</p>
),
props: { EuiHighlight },
components: { EuiHighlight },
demo: <Highlight />,
},
Expand Down
89 changes: 86 additions & 3 deletions src/components/highlight/highlight.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import React, { Fragment, HTMLAttributes, FunctionComponent } from 'react';
import { CommonProps } from '../common';

interface EuiHighlightChunk {
/**
* Start of the chunk
*/
start: number;
/**
* End of the chunk
*/
end: number;
/**
* Whether to highlight chunk or not
*/
highlight?: boolean;
}

export type EuiHighlightProps = HTMLAttributes<HTMLSpanElement> &
CommonProps & {
children: string;
Expand All @@ -14,12 +29,18 @@ export type EuiHighlightProps = HTMLAttributes<HTMLSpanElement> &
* Should the search be strict or not
*/
strict?: boolean;

/**
* Should highlight all matches
*/
highlightAll?: boolean;
};

const highlight = (
searchSubject: string,
searchValue: string,
isStrict: boolean = false
isStrict: boolean,
highlightAll: boolean
) => {
if (!searchValue) {
return searchSubject;
Expand All @@ -29,6 +50,22 @@ const highlight = (
return null;
}

if (highlightAll) {
const chunks = getHightlightWords(searchSubject, searchValue, isStrict);
return (
<Fragment>
{chunks.map(chunk => {
const { end, highlight, start } = chunk;
const value = searchSubject.substr(start, end - start);
if (highlight) {
return <strong key={start}>{value}</strong>;
}
return value;
})}
</Fragment>
);
}

const normalizedSearchSubject: string = isStrict
? searchSubject
: searchSubject.toLowerCase();
Expand Down Expand Up @@ -58,16 +95,62 @@ const highlight = (
);
};

const getHightlightWords = (
searchSubject: string,
searchValue: string,
isStrict: boolean
) => {
const regex = new RegExp(searchValue, isStrict ? 'g' : 'gi');
const matches = [];
let match;
while ((match = regex.exec(searchSubject)) !== null) {
matches.push({
start: match.index,
end: (match.index || 0) + match[0].length,
});
}
return fillInChunks(matches, searchSubject.length);
};

const fillInChunks = (
chunksToHighlight: EuiHighlightChunk[],
totalLength: number
) => {
const allChunks: EuiHighlightChunk[] = [];
const append = (start: number, end: number, highlight: boolean) => {
if (end - start > 0) {
allChunks.push({
start,
end,
highlight,
});
}
};
if (chunksToHighlight.length === 0) {
append(0, totalLength, false);
} else {
let lastIndex = 0;
chunksToHighlight.forEach(chunk => {
append(lastIndex, chunk.start, false);
append(chunk.start, chunk.end, true);
lastIndex = chunk.end;
});
append(lastIndex, totalLength, false);
}
return allChunks;
};

export const EuiHighlight: FunctionComponent<EuiHighlightProps> = ({
children,
className,
search,
strict,
strict = false,
highlightAll = false,
...rest
}) => {
return (
<span className={className} {...rest}>
{highlight(children, search, strict)}
{highlight(children, search, strict, highlightAll)}
</span>
);
};

0 comments on commit cbbcc9e

Please sign in to comment.