Skip to content

Commit b13d197

Browse files
[Enterprise Search] Migrate util and components from ent-search (#76051)
* Migrate useDidUpdateEffect hook Migrates https://github.com/elastic/ent-search/blob/master/app/javascript/shared/utils/useDidUpdateEffect.ts with test added. * Migrate TruncateContent component * Migrate TableHeader component * Remove unused deps * Fix test name * Remove custom type in favor of DependencyList * Add stylesheet for truncated content * Actually import stylesheet 🤦🏼‍♂️ * Replace legacy mixin Co-authored-by: Elastic Machine <[email protected]>
1 parent bfa87d2 commit b13d197

File tree

11 files changed

+260
-0
lines changed

11 files changed

+260
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export { TableHeader } from './table_header';
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
import { shallow } from 'enzyme';
9+
import { EuiTableHeader, EuiTableHeaderCell } from '@elastic/eui';
10+
11+
import { TableHeader } from './table_header';
12+
13+
const headerItems = ['foo', 'bar', 'baz'];
14+
15+
describe('TableHeader', () => {
16+
it('renders', () => {
17+
const wrapper = shallow(<TableHeader headerItems={headerItems} />);
18+
19+
expect(wrapper.find(EuiTableHeader)).toHaveLength(1);
20+
expect(wrapper.find(EuiTableHeaderCell)).toHaveLength(3);
21+
});
22+
23+
it('renders extra cell', () => {
24+
const wrapper = shallow(<TableHeader headerItems={headerItems} extraCell />);
25+
26+
expect(wrapper.find(EuiTableHeader)).toHaveLength(1);
27+
expect(wrapper.find(EuiTableHeaderCell)).toHaveLength(4);
28+
});
29+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
9+
import { EuiTableHeader, EuiTableHeaderCell } from '@elastic/eui';
10+
11+
interface ITableHeaderProps {
12+
headerItems: string[];
13+
extraCell?: boolean;
14+
}
15+
16+
export const TableHeader: React.FC<ITableHeaderProps> = ({ headerItems, extraCell }) => (
17+
<EuiTableHeader>
18+
{headerItems.map((item, i) => (
19+
<EuiTableHeaderCell key={i}>{item}</EuiTableHeaderCell>
20+
))}
21+
{extraCell && <EuiTableHeaderCell />}
22+
</EuiTableHeader>
23+
);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export { truncate, truncateBeginning } from './truncate';
8+
export { TruncatedContent } from './truncated_content';
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
import { shallow } from 'enzyme';
9+
10+
import { TruncatedContent } from './';
11+
12+
const content = 'foobarbaz';
13+
14+
describe('TruncatedContent', () => {
15+
it('renders with no truncation', () => {
16+
const wrapper = shallow(<TruncatedContent length={4} content="foo" />);
17+
18+
expect(wrapper.find('span.truncated-content')).toHaveLength(0);
19+
expect(wrapper.text()).toEqual('foo');
20+
});
21+
22+
it('renders with truncation at the end', () => {
23+
const wrapper = shallow(<TruncatedContent tooltipType="title" length={4} content={content} />);
24+
const element = wrapper.find('span.truncated-content');
25+
26+
expect(element).toHaveLength(1);
27+
expect(element.prop('title')).toEqual(content);
28+
expect(wrapper.text()).toEqual('foob…');
29+
expect(wrapper.find('span.truncated-content__tooltip')).toHaveLength(0);
30+
});
31+
32+
it('renders with truncation at the beginning', () => {
33+
const wrapper = shallow(
34+
<TruncatedContent tooltipType="title" beginning length={4} content={content} />
35+
);
36+
37+
expect(wrapper.find('span.truncated-content')).toHaveLength(1);
38+
expect(wrapper.text()).toEqual('…rbaz');
39+
});
40+
41+
it('renders with inline tooltip', () => {
42+
const wrapper = shallow(<TruncatedContent beginning length={4} content={content} />);
43+
44+
expect(wrapper.find('span.truncated-content').prop('title')).toEqual('');
45+
expect(wrapper.find('span.truncated-content__tooltip')).toHaveLength(1);
46+
});
47+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export function truncate(text: string, length: number) {
8+
return `${text.substring(0, length)}…`;
9+
}
10+
11+
export function truncateBeginning(text: string, length: number) {
12+
return `…${text.substring(text.length - length)}`;
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
.truncated-content {
8+
position: relative;
9+
z-index: 2;
10+
display: inline-block;
11+
white-space: nowrap;
12+
13+
&__tooltip {
14+
position: absolute;
15+
top: 50%;
16+
transform: translateY(-50%);
17+
left: -3px;
18+
margin-top: -1px;
19+
background: $euiColorEmptyShade;
20+
border-radius: 2px;
21+
width: calc(100% + 4px);
22+
height: calc(100% + 4px);
23+
padding: 0 2px;
24+
display: none;
25+
align-items: center;
26+
box-shadow: 0 1px 3px rgba(black, 0.1);
27+
border: 1px solid $euiBorderColor;
28+
width: auto;
29+
white-space: nowrap;
30+
31+
.truncated-content:hover & {
32+
display: flex;
33+
}
34+
}
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
9+
import { truncate, truncateBeginning } from './';
10+
11+
import './truncated_content.scss';
12+
13+
interface ITruncatedContentProps {
14+
content: string;
15+
length: number;
16+
beginning?: boolean;
17+
tooltipType?: 'inline' | 'title';
18+
}
19+
20+
export const TruncatedContent: React.FC<ITruncatedContentProps> = ({
21+
content,
22+
length,
23+
beginning = false,
24+
tooltipType = 'inline',
25+
}) => {
26+
if (content.length <= length) return <>{content}</>;
27+
28+
const inline = tooltipType === 'inline';
29+
return (
30+
<span className="truncated-content" title={!inline ? content : ''}>
31+
{beginning ? truncateBeginning(content, length) : truncate(content, length)}
32+
{inline && <span className="truncated-content__tooltip">{content}</span>}
33+
</span>
34+
);
35+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export { useDidUpdateEffect } from './use_did_update_effect';
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React, { useState } from 'react';
8+
import { mount } from 'enzyme';
9+
10+
import { EuiLink } from '@elastic/eui';
11+
12+
import { useDidUpdateEffect } from './use_did_update_effect';
13+
14+
const fn = jest.fn();
15+
16+
const TestHook = ({ value }: { value: number }) => {
17+
const [inputValue, setValue] = useState(value);
18+
useDidUpdateEffect(fn, [inputValue]);
19+
return <EuiLink onClick={() => setValue(2)} />;
20+
};
21+
22+
const wrapper = mount(<TestHook value={1} />);
23+
24+
describe('useDidUpdateEffect', () => {
25+
it('should not fire function when value unchanged', () => {
26+
expect(fn).not.toHaveBeenCalled();
27+
});
28+
29+
it('should fire function when value changed', () => {
30+
wrapper.find(EuiLink).simulate('click');
31+
expect(fn).toHaveBeenCalled();
32+
});
33+
});

0 commit comments

Comments
 (0)