Bug 1715790 - Alerts View - Test name is too long (#7185)

* Add platform column, move options into tags
* Add sort for platform column, remove platform from title
* Add unit tests, refactor tags options column
This commit is contained in:
esanuandra 2021-06-26 02:02:32 +03:00 коммит произвёл GitHub
Родитель ffbff08476
Коммит 382b3cfe8e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 381 добавлений и 156 удалений

Просмотреть файл

@ -45,7 +45,7 @@
"id": 1945375,
"framework_id": 1,
"signature_hash": "461af9d92db3f2d97dc6e4c4d47e7ad256356861",
"machine_platform": "linux64-shippable-qr",
"machine_platform": "windows10-64-qr",
"suite": "tp5o_webext",
"test": "responsiveness",
"tags": ["benchmark", "warm"],
@ -74,7 +74,7 @@
"id": 1945376,
"framework_id": 1,
"signature_hash": "461af9d92db3f2d97dc6e4c4d47e7ad256356861",
"machine_platform": "linux64-shippable-qr",
"machine_platform": "macosx1015-64-shippable",
"suite": "tp5a_webext",
"test": "responsiveness",
"tags": ["cold", "live"],

Просмотреть файл

@ -24,10 +24,15 @@ const frameworks = [
},
];
const alertTableRowTest = (tags, alert = testAlert) => {
const alertTableRowTest = (tags, alert = testAlert, options) => {
if (tags) {
testAlert.series_signature.tags = [...tags];
}
if (options) {
testAlert.series_signature.extra_options = [...options];
}
return render(
<table>
<tbody>
@ -48,6 +53,26 @@ const alertTableRowTest = (tags, alert = testAlert) => {
afterEach(cleanup);
test('Test column contains only suite and test name', async () => {
const { getByTestId } = alertTableRowTest(false, testAlert);
const { suite, test } = testAlert.series_signature;
const alertTitle = await waitFor(() =>
getByTestId(`alert ${testAlert.id} title`),
);
expect(alertTitle.textContent).toBe(`${suite} ${test}`);
});
test(`Platform column contains alerts's platform`, async () => {
const { getByTestId } = alertTableRowTest(false, testAlert);
const { machine_platform: machinePlatform } = testAlert.series_signature;
const alertPlatform = await waitFor(() => getByTestId(`alert-platform`));
expect(alertPlatform.textContent).toBe(machinePlatform);
});
test("Alert item with no tags displays 'No tags'", async () => {
const { getByText } = alertTableRowTest(['']);
@ -66,22 +91,25 @@ test('Alert item with 2 tags displays 2 tags', async () => {
test("Alert item with more than 2 tags displays '...' button", async () => {
const testTags = ['tag1', 'tag2', 'tag3'];
const { getByText } = alertTableRowTest(testTags);
const { getByTestId } = alertTableRowTest(testTags);
const showMoreButton = await waitFor(() => getByText('...'));
const showMoreButton = await waitFor(() => getByTestId('show-more-tags'));
expect(showMoreButton).toBeInTheDocument();
expect(showMoreButton.textContent).toBe('...');
});
test("Button '...' displays all the tags for an alert item", async () => {
const testTags = ['tag1', 'tag2', 'tag3'];
const { getByText, getAllByTestId } = alertTableRowTest(testTags);
const { getByTestId, getAllByTestId } = alertTableRowTest(testTags);
let visibleTags = await waitFor(() => getAllByTestId(`alert-tag`));
expect(visibleTags).toHaveLength(2);
const showMoreButton = await waitFor(() => getByText('...'));
const showMoreButton = await waitFor(() => getByTestId('show-more-tags'));
expect(showMoreButton.textContent).toBe('...');
fireEvent.click(showMoreButton);
visibleTags = await waitFor(() => getAllByTestId(`alert-tag`));
@ -89,6 +117,54 @@ test("Button '...' displays all the tags for an alert item", async () => {
expect(visibleTags).toHaveLength(testTags.length);
});
test("Alert item with no options displays 'No options'", async () => {
const { getByText } = alertTableRowTest(false, testAlert, ['']);
const message = await waitFor(() => getByText('No options'));
expect(message).toBeInTheDocument();
});
test('Alert item with 2 options displays 2 options', async () => {
const testOptions = ['option1', 'option2'];
const { getAllByTestId } = alertTableRowTest(false, testAlert, testOptions);
const options = await waitFor(() => getAllByTestId(`alert-option`));
expect(options).toHaveLength(testOptions.length);
});
test("Alert item with more than 2 options displays '...' button", async () => {
const testOptions = ['option1', 'option2', 'option3'];
const { getByTestId } = alertTableRowTest(false, testAlert, testOptions);
const showMoreButton = await waitFor(() => getByTestId('show-more-options'));
expect(showMoreButton.textContent).toBe('...');
});
test("Button '...' displays all options for an alert item", async () => {
const testOptions = ['option1', 'option2', 'option3'];
const { getByTestId, getAllByTestId } = alertTableRowTest(
false,
testAlert,
testOptions,
);
let visibleOptions = await waitFor(() => getAllByTestId(`alert-option`));
expect(visibleOptions).toHaveLength(2);
const showMoreButton = await waitFor(() => getByTestId('show-more-options'));
expect(showMoreButton.textContent).toBe('...');
fireEvent.click(showMoreButton);
visibleOptions = await waitFor(() => getAllByTestId(`alert-option`));
expect(visibleOptions).toHaveLength(testOptions.length);
});
test('Documentation link is available for talos framework', async () => {
const { getByTestId } = alertTableRowTest();
expect(getByTestId('docs')).toHaveAttribute(

Просмотреть файл

@ -592,7 +592,7 @@ test('No tags are displayed if there is no active tag', async () => {
// TODO should write tests for alert summary dropdown menu actions performed in StatusDropdown
// (adding notes or marking as 'fixed', etc)
test('table data can be sorted in descending order by test and platform', async () => {
test(`table data can be sorted in descending order by 'Test'`, async () => {
const {
getAllByLabelText,
getByTestId,
@ -613,7 +613,7 @@ test('table data can be sorted in descending order by test and platform', async
expect(alertTableRows[2]).toContainElement(alert2);
const sortByTest = await waitFor(() =>
getAllByTitle('Sorted in default order by test and platform'),
getAllByTitle('Sorted in default order by test'),
);
// firing the sort button once triggers ascending sort
@ -628,6 +628,50 @@ test('table data can be sorted in descending order by test and platform', async
expect(alertTableRows[2]).toContainElement(alert3);
});
test(`table data can be sorted in ascending order by 'Platform'`, async () => {
const {
getByTestId,
getAllByLabelText,
getAllByTitle,
} = alertsViewControls();
let alertTableRows = await waitFor(() =>
getAllByLabelText('Alert table row'),
);
const alert1 = await waitFor(() => getByTestId('69344'));
const alert2 = await waitFor(() => getByTestId('69345'));
const alert3 = await waitFor(() => getByTestId('69346'));
// alerts are sorted in a default manner without clicking on sort buttons
expect(alertTableRows[0]).toContainElement(alert3);
expect(alertTableRows[1]).toContainElement(alert1);
expect(alertTableRows[2]).toContainElement(alert2);
const sortByPlatform = await waitFor(() =>
getAllByTitle('Sorted in default order by platform'),
);
// firing the sort button once triggers ascending sort
fireEvent.click(sortByPlatform[0]);
alertTableRows = await waitFor(() => getAllByLabelText('Alert table row'));
expect(alertTableRows[0]).toContainElement(alert1);
expect(alertTableRows[1]).toContainElement(alert3);
expect(alertTableRows[2]).toContainElement(alert2);
});
test(`table data cannot be sorted by 'Tags & Options'`, async () => {
const { getAllByTitle } = alertsViewControls();
const sortByTags = await waitFor(() =>
getAllByTitle('Sorted by tags & options disabled'),
);
expect(sortByTags[0]).toHaveClass('disabled-button');
});
test(`table data can be sorted in ascending order by 'Confidence'`, async () => {
const {
getAllByLabelText,
@ -662,40 +706,6 @@ test(`table data can be sorted in ascending order by 'Confidence'`, async () =>
expect(alertTableRows[2]).toContainElement(alert3);
});
test(`table data can be sorted in ascending order by 'Tags'`, async () => {
const {
getAllByLabelText,
getByTestId,
getAllByTitle,
} = alertsViewControls();
let alertTableRows = await waitFor(() =>
getAllByLabelText('Alert table row'),
);
const alert1 = await waitFor(() => getByTestId('69344'));
const alert2 = await waitFor(() => getByTestId('69345'));
const alert3 = await waitFor(() => getByTestId('69346'));
// alerts are sorted in a default manner without clicking on sort buttons
expect(alertTableRows[0]).toContainElement(alert3);
expect(alertTableRows[1]).toContainElement(alert1);
expect(alertTableRows[2]).toContainElement(alert2);
const sortByTags = await waitFor(() =>
getAllByTitle('Sorted in default order by tags'),
);
// firing the sort button once triggers ascending sort
fireEvent.click(sortByTags[0]);
alertTableRows = await waitFor(() => getAllByLabelText('Alert table row'));
expect(alertTableRows[0]).toContainElement(alert2);
expect(alertTableRows[1]).toContainElement(alert1);
expect(alertTableRows[2]).toContainElement(alert3);
});
test('test data can be sorted only by one column', async () => {
const {
getAllByLabelText,
@ -716,12 +726,12 @@ test('test data can be sorted only by one column', async () => {
expect(alertTableRows[1]).toContainElement(alert1);
expect(alertTableRows[2]).toContainElement(alert2);
const sortByTags = await waitFor(() =>
getAllByTitle('Sorted in default order by tags'),
const sortByPlatform = await waitFor(() =>
getAllByTitle('Sorted in default order by platform'),
);
// firing the sort button once triggers ascending sort
fireEvent.click(sortByTags[0]);
expect(sortByTags[0].title).toBe('Sorted in ascending order by tags');
fireEvent.click(sortByPlatform[0]);
expect(sortByPlatform[0].title).toBe('Sorted in ascending order by platform');
const sortByConfidence = await waitFor(() =>
getAllByTitle('Sorted in default order by confidence'),
@ -730,7 +740,7 @@ test('test data can be sorted only by one column', async () => {
expect(sortByConfidence[0].title).toBe(
'Sorted in ascending order by confidence',
);
expect(sortByTags[0].title).toBe('Sorted in default order by tags');
expect(sortByPlatform[0].title).toBe('Sorted in default order by platform');
alertTableRows = await waitFor(() => getAllByLabelText('Alert table row'));

Просмотреть файл

@ -11,13 +11,13 @@ import {
import projects from '../../mock/repositories';
import CompareTableControls from '../../../../ui/perfherder/compare/CompareTableControls';
import CompareTable from '../../../../ui/perfherder/compare/CompareTable';
import SortButton from '../../../../ui/perfherder/shared/SortButton';
import ComparePageTitle from '../../../../ui/shared/ComparePageTitle';
import {
compareTableText,
filterText,
} from '../../../../ui/perfherder/perf-helpers/constants';
import JobModel from '../../../../ui/models/job';
import TableColumnHeader from '../../../../ui/perfherder/shared/TableColumnHeader';
// TODO addtional tests:
// 1) that the table is receiving the correct data structure after data
@ -694,7 +694,7 @@ test(`measurement unit is passed in the header name for Base and New`, async ()
expect(queryByText('Base (ms)')).toBeInTheDocument();
});
test(`SortButton shows the title as expected`, async () => {
test(`TableColumnHeader shows the title as expected`, async () => {
const defaultProps = {
onChangeSort: jest.fn(),
column: {
@ -702,7 +702,7 @@ test(`SortButton shows the title as expected`, async () => {
currentSort: 'default',
},
};
const { queryByText } = render(<SortButton {...defaultProps} />);
const { queryByText } = render(<TableColumnHeader {...defaultProps} />);
expect(queryByText('New (score)')).not.toBeInTheDocument();
expect(queryByText('New')).toBeInTheDocument();

Просмотреть файл

@ -544,3 +544,10 @@ li.pagination-active.active > button {
.legend-docs a:visited {
color: #337ab7;
}
.disabled-button {
border: 1px solid transparent;
box-sizing: border-box;
cursor: not-allowed;
opacity: 0.65;
}

Просмотреть файл

@ -16,12 +16,13 @@ import {
} from '../perf-helpers/helpers';
import TruncatedText from '../../shared/TruncatedText';
import ErrorBoundary from '../../shared/ErrorBoundary';
import SortButton from '../shared/SortButton';
import TableColumnHeader from '../shared/TableColumnHeader';
import SortButtonDisabled from '../shared/SortButtonDisabled';
import { tableSort, getNextSort, sort, sortTables } from '../perf-helpers/sort';
import AlertTableRow from './AlertTableRow';
import AlertHeader from './AlertHeader';
import StatusDropdown from './StatusDropdown';
import AlertTableRow from './AlertTableRow';
import DownstreamSummary from './DownstreamSummary';
import AlertActionPanel from './AlertActionPanel';
import SelectAlertsDropdown from './SelectAlertsDropdown';
@ -37,13 +38,18 @@ export default class AlertTable extends React.Component {
allSelected: false,
selectedAlerts: [],
tableConfig: {
TestAndPlatform: {
name: 'Test and platform',
Test: {
name: 'Test',
sortValue: 'title',
currentSort: tableSort.default,
},
Tags: {
name: 'Tags',
Platform: {
name: 'Platform',
sortValue: 'machine_platform',
currentSort: tableSort.default,
},
TagsOptions: {
name: 'Tags & Options',
sortValue: 'tags',
currentSort: tableSort.default,
},
@ -347,47 +353,48 @@ export default class AlertTable extends React.Component {
<th> </th>
<th> </th>
<th className="align-bottom">
<SortButton
column={tableConfig.TestAndPlatform}
<TableColumnHeader
column={tableConfig.Test}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="align-bottom">
{' '}
<SortButton
column={tableConfig.Tags}
<TableColumnHeader
column={tableConfig.Platform}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="align-bottom">
{' '}
<SortButton
{tableConfig.TagsOptions.name}
<SortButtonDisabled column={tableConfig.TagsOptions} />
</th>
<th className="align-bottom">
<TableColumnHeader
column={tableConfig.PreviousValue}
onChangeSort={this.onChangeSort}
/>
</th>
<th> </th>
<th className="align-bottom">
{' '}
<SortButton
<TableColumnHeader
column={tableConfig.NewValue}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="align-bottom">
<SortButton
<TableColumnHeader
column={tableConfig.AbsoluteDifference}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="align-bottom">
<SortButton
<TableColumnHeader
column={tableConfig.Magnitude}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="align-bottom">
<SortButton
<TableColumnHeader
column={tableConfig.Confidence}
onChangeSort={this.onChangeSort}
/>

Просмотреть файл

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import Badge from 'reactstrap/lib/Badge';
export default class AlertTablePlatform extends React.PureComponent {
render() {
const { platform } = this.props;
return (
<Badge color="light" data-testid="alert-platform">
{platform}
</Badge>
);
}
}
AlertTablePlatform.propTypes = {
platform: PropTypes.string.isRequired,
};

Просмотреть файл

@ -2,14 +2,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Button,
FormGroup,
Input,
Label,
Badge,
UncontrolledTooltip,
} from 'reactstrap';
import { Button, FormGroup, Input, Label } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faStar as faStarSolid,
@ -38,15 +31,15 @@ import {
phTimeRanges,
} from '../perf-helpers/constants';
import AlertTablePlatform from './AlertTablePlatform';
import AlertTableTagsOptions from './AlertTableTagsOptions';
export default class AlertTableRow extends React.Component {
constructor(props) {
super(props);
const { tags } = this.props.alert.series_signature;
this.state = {
starred: this.props.alert.starred,
checkboxSelected: false,
displayAllTags: false,
tags,
};
}
@ -183,12 +176,8 @@ export default class AlertTableRow extends React.Component {
frameworkName,
);
const { title } = alert;
const { suite } = alert.series_signature;
const { url, remainingTestName } = getSplitTestTitle(
title,
suite,
frameworkName,
);
const { suite, test } = alert.series_signature;
const { url } = getSplitTestTitle(title, suite, frameworkName);
return (
<span>
<span
@ -197,14 +186,16 @@ export default class AlertTableRow extends React.Component {
title={alert.backfill_record ? backfillRetriggeredTitle : ''}
>
{hasDocumentation && alert.title ? (
<div className="alert-docs">
<div className="alert-docs" data-testid={`alert ${alert.id} title`}>
<a data-testid="docs" href={url}>
{suite}
</a>{' '}
{remainingTestName}
{test}
</div>
) : (
<div>{alert.title}</div>
<div data-testid={`alert ${alert.id} title`}>
{suite} {test}
</div>
)}
</span>{' '}
{this.renderAlertStatus(alert, alertStatus, statusColor)}{' '}
@ -230,50 +221,6 @@ export default class AlertTableRow extends React.Component {
);
};
showTags = (tags) => {
return tags.map((item) => (
<Badge color="light" key={`${item}`} data-testid="alert-tag">
{item}
</Badge>
));
};
getTags = (alert) => {
const { displayAllTags, tags } = this.state;
const visibleTags = 2;
if (tags.length && tags[0] !== '') {
return (
<React.Fragment>
{this.showTags(tags.slice(0, visibleTags))}
{!displayAllTags && tags.length > visibleTags && (
<Button
color="link"
size="sm"
id={`alert-${alert.id}-tags`}
onClick={() =>
this.setState((prevState) => ({
displayAllTags: !prevState.displayAllTags,
}))
}
>
<span>...</span>
<UncontrolledTooltip
placement="top"
target={`alert-${alert.id}-tags`}
>
Show more tags
</UncontrolledTooltip>
</Button>
)}
{displayAllTags && this.showTags(tags.slice(visibleTags))}
</React.Fragment>
);
}
return <Badge color="light">No tags</Badge>;
};
// arbitrary scale from 0-20% multiplied by 5, capped
// at 100 (so 20% regression === 100% bad)
getCappedMagnitude = (percent) => Math.min(Math.abs(percent) * 5, 100);
@ -297,6 +244,9 @@ export default class AlertTableRow extends React.Component {
const { user, alert, alertSummary } = this.props;
const { starred, checkboxSelected } = this.state;
const { tags, extra_options: options } = alert.series_signature;
const items = { tags, options };
const alertStatus = getStatus(alert.status, alertStatusMap);
const tooltipText = alert.classifier_email
? `Classified by ${alert.classifier_email}`
@ -357,7 +307,14 @@ export default class AlertTableRow extends React.Component {
this.getTitleText(alert, alertStatus)
)}
</td>
<td className="table-width-md">{this.getTags(alert)}</td>
<td className="table-width-md">
<AlertTablePlatform
platform={alert.series_signature.machine_platform}
/>
</td>
<td className="table-width-md">
<AlertTableTagsOptions alertId={alert.id} items={items} />
</td>
<td className="table-width-md">{formatNumber(alert.prev_value)}</td>
<td className="table-width-sm">
<span

Просмотреть файл

@ -0,0 +1,97 @@
import React from 'react';
import PropTypes from 'prop-types';
import UncontrolledTooltip from 'reactstrap/lib/UncontrolledTooltip';
import Button from 'reactstrap/lib/Button';
import Badge from 'reactstrap/lib/Badge';
export default class AlertTableTagsOptions extends React.Component {
itemsType = { tags: 'tags', options: 'options' };
visibleItems = {
tags: 2,
options: 2,
};
constructor(props) {
super(props);
const { tags, options } = this.props.items;
this.state = {
displayAllItems: {
tags: false,
options: false,
},
options,
tags,
};
}
showItems = (items, type) => {
const badgeId = {
tags: 'alert-tag',
options: 'alert-option',
};
return items.map((item) => (
<Badge color="light" key={`${item}`} data-testid={badgeId[type]}>
{item}
</Badge>
));
};
displayItems = (items, type) => {
const { alertId } = this.props;
const { displayAllItems } = this.state;
return items.length && items[0] !== '' ? (
<div>
{this.showItems(items.slice(0, this.visibleItems[type]), type)}
{!displayAllItems[type] && items.length > this.visibleItems[type] && (
<Button
color="link"
size="sm"
id={`alert-${alertId}-${type}`}
onClick={() =>
this.setState((prevState) => ({
displayAllItems: {
...prevState.displayAllItems,
[type]: !prevState.displayAllItems[type],
},
}))
}
>
<span data-testid={`show-more-${type}`}>...</span>
<UncontrolledTooltip
placement="top"
target={`alert-${alertId}-${type}`}
>
Show more {type}
</UncontrolledTooltip>
</Button>
)}
{displayAllItems[type] &&
this.showItems(items.slice(this.visibleItems[type]), type)}
</div>
) : (
<Badge color="light">No {type}</Badge>
);
};
render() {
const { options, tags } = this.state;
return (
<React.Fragment>
{this.displayItems(tags, this.itemsType.tags)}
{this.displayItems(options, this.itemsType.options)}
</React.Fragment>
);
}
}
AlertTableTagsOptions.propTypes = {
items: PropTypes.shape({
tags: PropTypes.array.isRequired,
options: PropTypes.array.isRequired,
}).isRequired,
alertId: PropTypes.number.isRequired,
};

Просмотреть файл

@ -8,7 +8,7 @@ import { getHashBasedId, getSplitTestTitle } from '../perf-helpers/helpers';
import { testDocumentationFrameworks } from '../perf-helpers/constants';
import { hashFunction } from '../../helpers/utils';
import { tableSort, getNextSort, sort, sortTables } from '../perf-helpers/sort';
import SortButton from '../shared/SortButton';
import TableColumnHeader from '../shared/TableColumnHeader';
import RetriggerButton from './RetriggerButton';
import CompareTableRow from './CompareTableRow';
@ -167,14 +167,14 @@ export default class CompareTable extends React.Component {
<FontAwesomeIcon icon={faHashtag} />
</Button>
)}
<SortButton
<TableColumnHeader
column={tableConfig.TestName}
onChangeSort={this.onChangeSort}
/>
</div>
</th>
<th className="table-width-lg">
<SortButton
<TableColumnHeader
column={tableConfig.Base}
onChangeSort={this.onChangeSort}
/>
@ -182,25 +182,25 @@ export default class CompareTable extends React.Component {
{/* empty for less than/greater than data */}
<th className="table-width-sm" aria-label="Comparison" />
<th className="table-width-lg">
<SortButton
<TableColumnHeader
column={tableConfig.New}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="table-width-lg">
<SortButton
<TableColumnHeader
column={tableConfig.Delta}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="table-width-lg">
<SortButton
<TableColumnHeader
column={tableConfig.Magnitude}
onChangeSort={this.onChangeSort}
/>
</th>
<th className="table-width-lg">
<SortButton
<TableColumnHeader
column={tableConfig.Confidence}
onChangeSort={this.onChangeSort}
/>

Просмотреть файл

@ -14,12 +14,16 @@ export const sortTypes = {
sortByString: (value) => (a, b) => a[value].localeCompare(b[value]),
sortByStrFirstElement: (value) => (a, b) =>
a.series_signature[value][0].localeCompare(b.series_signature[value][0]),
sortBySeriesSignatureValue: (value) => (a, b) =>
a.series_signature[value].localeCompare(b.series_signature[value]),
sortByValue: (value) => (a, b) => a[value] - b[value],
},
[tableSort.descending]: {
sortByString: (value) => (a, b) => b[value].localeCompare(a[value]),
sortByStrFirstElement: (value) => (a, b) =>
b.series_signature[value][0].localeCompare(a.series_signature[value][0]),
sortBySeriesSignatureValue: (value) => (a, b) =>
b.series_signature[value].localeCompare(a.series_signature[value]),
sortByValue: (value) => (a, b) => b[value] - a[value],
},
};
@ -49,14 +53,18 @@ export const sort = (sortValue, sortType, data, table) => {
} else {
validData = data;
}
const { sortByString, sortByValue, sortByStrFirstElement } = sortTypes[
sortType
];
const {
sortByString,
sortByValue,
sortByStrFirstElement,
sortBySeriesSignatureValue,
} = sortTypes[sortType];
const getSortType = {
title: sortByString,
name: sortByString,
tags: sortByStrFirstElement,
machine_platform: sortBySeriesSignatureValue,
};
let doSort = getSortType[sortValue];

Просмотреть файл

@ -27,17 +27,14 @@ export default class SortButton extends React.Component {
const { column, onChangeSort } = this.props;
const { name, currentSort } = column;
return (
<div className="d-flex align-items-end">
<div>{name === 'Test name' ? '' : `${name}`}</div>
<Badge
className="mx-1 btn btn-darker-secondary"
role="button"
title={`Sorted in ${currentSort} order by ${name.toLowerCase()}`}
onClick={() => onChangeSort(column)}
>
<FontAwesomeIcon icon={this.sortTypes[currentSort].icon} />
</Badge>
</div>
<Badge
className="mx-1 btn btn-darker-secondary"
role="button"
title={`Sorted in ${currentSort} order by ${name.toLowerCase()}`}
onClick={() => onChangeSort(column)}
>
<FontAwesomeIcon icon={this.sortTypes[currentSort].icon} />
</Badge>
);
}
}

Просмотреть файл

@ -0,0 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Badge } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSort } from '@fortawesome/free-solid-svg-icons';
export default class SortButtonDisabled extends React.PureComponent {
render() {
const {
column: { name },
} = this.props;
return (
<Badge
className="mx-1 disabled-button"
aria-disabled="true"
title={`Sorted by ${name.toLowerCase()} disabled`}
>
<FontAwesomeIcon icon={faSort} />
</Badge>
);
}
}
SortButtonDisabled.propTypes = { column: PropTypes.shape({}).isRequired };

Просмотреть файл

@ -0,0 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';
import SortButton from './SortButton';
// eslint-disable-next-line react/prefer-stateless-function
export default class TableColumnHeader extends React.Component {
render() {
const { column, onChangeSort } = this.props;
const { name } = column;
return (
<div className="d-flex align-items-end">
<div>{name === 'Test name' ? '' : `${name}`}</div>
<SortButton column={column} onChangeSort={onChangeSort} />
</div>
);
}
}
TableColumnHeader.propTypes = {
column: PropTypes.shape({}).isRequired,
onChangeSort: PropTypes.func.isRequired,
};