Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

De-angularize field-name directive #40744

Merged
merged 13 commits into from
Jul 15, 2019
46 changes: 24 additions & 22 deletions src/legacy/core_plugins/kbn_doc_views/public/__tests__/doc_views.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,15 @@ describe('docViews', function () {

it('should have the field name in the first column', function () {
_.each(_.keys(flattened), function (field) {
expect($elem.find('td[title="' + field + '"]').length).to.be(1);
expect($elem.find('[data-test-subj="tableDocViewRow-' + field + '"]').length).to.be(1);
});
});

it('should have the a value for each field', function () {
_.each(_.keys(flattened), function (field) {
const cellValue = $elem.find('td[title="' + field + '"]').siblings().find('.kbnDocViewer__value').text();
const cellValue = $elem
.find('[data-test-subj="tableDocViewRow-' + field + '"]')
.find('.kbnDocViewer__value').text();

// This sucks, but testing the filter chain is too hairy ATM
expect(cellValue.length).to.be.greaterThan(0);
Expand All @@ -117,48 +119,48 @@ describe('docViews', function () {

describe('filtering', function () {
it('should apply a filter when clicking filterable fields', function () {
const cell = $elem.find('td[title="bytes"]').prev();
const row = $elem.find('[data-test-subj="tableDocViewRow-bytes"]');

cell.find('.fa-search-plus').first().click();
row.find('.fa-search-plus').first().click();
expect($scope.filter.calledOnce).to.be(true);
cell.find('.fa-search-minus').first().click();
row.find('.fa-search-minus').first().click();
expect($scope.filter.calledTwice).to.be(true);
cell.find('.fa-asterisk').first().click();
row.find('.fa-asterisk').first().click();
expect($scope.filter.calledThrice).to.be(true);
});

it('should NOT apply a filter when clicking non-filterable fields', function () {
const cell = $elem.find('td[title="area"]').prev();
const row = $elem.find('[data-test-subj="tableDocViewRow-area"]');

cell.find('.fa-search-plus').first().click();
row.find('.fa-search-plus').first().click();
expect($scope.filter.calledOnce).to.be(false);
cell.find('.fa-search-minus').first().click();
row.find('.fa-search-minus').first().click();
expect($scope.filter.calledTwice).to.be(false);
cell.find('.fa-asterisk').first().click();
row.find('.fa-asterisk').first().click();
expect($scope.filter.calledOnce).to.be(true);
});
});

describe('warnings', function () {
it('displays a warning about field name starting with underscore', function () {
const cells = $elem.find('td[title="_underscore"]').siblings();
expect(cells.find('.kbnDocViewer__underscore').length).to.be(1);
expect(cells.find('.kbnDocViewer__noMapping').length).to.be(0);
expect(cells.find('.kbnDocViewer__objectArray').length).to.be(0);
const row = $elem.find('[data-test-subj="tableDocViewRow-_underscore"]');
expect(row.find('.kbnDocViewer__underscore').length).to.be(1);
expect(row.find('.kbnDocViewer__noMapping').length).to.be(0);
expect(row.find('.kbnDocViewer__objectArray').length).to.be(0);
});

it('displays a warning about missing mappings', function () {
const cells = $elem.find('td[title="noMapping"]').siblings();
expect(cells.find('.kbnDocViewer__underscore').length).to.be(0);
expect(cells.find('.kbnDocViewer__noMapping').length).to.be(1);
expect(cells.find('.kbnDocViewer__objectArray').length).to.be(0);
const row = $elem.find('[data-test-subj="tableDocViewRow-noMapping"]');
expect(row.find('.kbnDocViewer__underscore').length).to.be(0);
expect(row.find('.kbnDocViewer__noMapping').length).to.be(1);
expect(row.find('.kbnDocViewer__objectArray').length).to.be(0);
});

it('displays a warning about objects in arrays', function () {
const cells = $elem.find('td[title="objectArray"]').siblings();
expect(cells.find('.kbnDocViewer__underscore').length).to.be(0);
expect(cells.find('.kbnDocViewer__noMapping').length).to.be(0);
expect(cells.find('.kbnDocViewer__objectArray').length).to.be(1);
const row = $elem.find('[data-test-subj="tableDocViewRow-objectArray"]');
expect(row.find('.kbnDocViewer__underscore').length).to.be(0);
expect(row.find('.kbnDocViewer__noMapping').length).to.be(0);
expect(row.find('.kbnDocViewer__objectArray').length).to.be(1);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,8 @@ discover-app {
color: $euiColorDarkShade;
}

// SASSTODO: replace the margin-right value with a variables
.dscField__icon {
margin-right: 5px;
margin-right: $euiSizeS;
text-align: center;
display: inline-block;
width: $euiSizeM;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
kbn-accessible-click
class="sidebar-item-title dscSidebarItem"
>
<field-name
class="dscField dscSidebarItem__label"
field="field"
></field-name>
<div class="dscField dscSidebarItem__label">
<field-name
field="field"
></field-name>
</div>

<button
ng-if="field.name !== '_source'"
Expand Down
137 changes: 14 additions & 123 deletions src/legacy/ui/public/directives/field_name.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,131 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/

import $ from 'jquery';
import { i18n } from '@kbn/i18n';
import { template } from 'lodash';
import { shortenDottedString } from '../../../core_plugins/kibana/common/utils/shorten_dotted_string';
import booleanFieldNameIcon from './field_name_icons/boolean_field_name_icon.html';
import conflictFieldNameIcon from './field_name_icons/conflict_field_name_icon.html';
import dateFieldNameIcon from './field_name_icons/date_field_name_icon.html';
import geoPointFieldNameIcon from './field_name_icons/geo_point_field_name_icon.html';
import ipFieldNameIcon from './field_name_icons/ip_field_name_icon.html';
import murmur3FieldNameIcon from './field_name_icons/murmur3_field_name_icon.html';
import numberFieldNameIcon from './field_name_icons/number_field_name_icon.html';
import sourceFieldNameIcon from './field_name_icons/source_field_name_icon.html';
import stringFieldNameIcon from './field_name_icons/string_field_name_icon.html';
import unknownFieldNameIcon from './field_name_icons/unknown_field_name_icon.html';

import { FieldName } from './field_name/field_name';
import { uiModules } from '../modules';
import { wrapInI18nContext } from 'ui/i18n';
const module = uiModules.get('kibana');

const compiledBooleanFieldNameIcon = template(booleanFieldNameIcon);
const compiledConflictFieldNameIcon = template(conflictFieldNameIcon);
const compiledDateFieldNameIcon = template(dateFieldNameIcon);
const compiledGeoPointFieldNameIcon = template(geoPointFieldNameIcon);
const compiledIpFieldNameIcon = template(ipFieldNameIcon);
const compiledMurmur3FieldNameIcon = template(murmur3FieldNameIcon);
const compiledNumberFieldNameIcon = template(numberFieldNameIcon);
const compiledSourceFieldNameIcon = template(sourceFieldNameIcon);
const compiledStringFieldNameIcon = template(stringFieldNameIcon);
const compiledUnknownFieldNameIcon = template(unknownFieldNameIcon);

module.directive('fieldName', function ($rootScope, config) {
return {
restrict: 'AE',
scope: {
'field': '=',
'fieldName': '=',
'fieldType': '='
},
link: function ($scope, $el) {
const typeToIconMap = {
boolean: compiledBooleanFieldNameIcon({
booleanFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.booleanAriaLabel', {
defaultMessage: 'Boolean field'
}),
}),
conflict: compiledConflictFieldNameIcon({
conflictingFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.conflictFieldAriaLabel', {
defaultMessage: 'Conflicting field'
}),
}),
date: compiledDateFieldNameIcon({
dateFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.dateFieldAriaLabel', {
defaultMessage: 'Date field'
}),
}),
geo_point: compiledGeoPointFieldNameIcon({
geoPointFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.geoPointFieldAriaLabel', {
defaultMessage: 'Date field'
}),
}),
ip: compiledIpFieldNameIcon({
ipAddressFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.ipAddressFieldAriaLabel', {
defaultMessage: 'IP address field'
}),
}),
murmur3: compiledMurmur3FieldNameIcon({
murmur3FieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.murmur3FieldAriaLabel', {
defaultMessage: 'Murmur3 field'
}),
}),
number: compiledNumberFieldNameIcon({
numberFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.numberFieldAriaLabel', {
defaultMessage: 'Number field'
}),
}),
source: compiledSourceFieldNameIcon({
sourceFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.sourceFieldAriaLabel', {
defaultMessage: 'Source field'
}),
}),
string: compiledStringFieldNameIcon({
stringFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.stringFieldAriaLabel', {
defaultMessage: 'String field'
}),
}),
};

function typeIcon(fieldType) {
if (typeToIconMap.hasOwnProperty(fieldType)) {
return typeToIconMap[fieldType];
}

return compiledUnknownFieldNameIcon({
unknownFieldAriaLabel: i18n.translate('common.ui.directives.fieldNameIcons.unknownFieldAriaLabel', {
defaultMessage: 'Unknown field'
}),
});
}

$rootScope.$watchMulti.call($scope, [
'field',
'fieldName',
'fieldType',
'field.rowCount'
], function () {

const type = $scope.field ? $scope.field.type : $scope.fieldType;
const name = $scope.field ? $scope.field.name : $scope.fieldName;
const results = $scope.field ? !$scope.field.rowCount && !$scope.field.scripted : false;
const scripted = $scope.field ? $scope.field.scripted : false;


const isShortDots = config.get('shortDots:enable');
const displayName = isShortDots ? shortenDottedString(name) : name;

$el
.attr('title', name)
.toggleClass('dscField--noResults', results)
.toggleClass('scripted', scripted)
.prepend(typeIcon(type))
.append($('<span>')
.text(displayName)
.addClass('dscFieldName')
);
});
module.directive('fieldName', function (config, reactDirective) {
return reactDirective(
wrapInI18nContext(FieldName),
[
['field', { watchDepth: 'collection' }],
['fieldName', { watchDepth: 'reference' }],
['fieldType', { watchDepth: 'reference' }],
],
{ restrict: 'AE' },
{
useShortDots: config.get('shortDots:enable'),
}
};
);
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions src/legacy/ui/public/directives/field_name/field_name.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render } from 'enzyme';
import { FieldName } from './field_name';

// Note that it currently provides just 2 basic tests, there should be more, but
// the components involved will soon change
test('FieldName renders a string field by providing fieldType and fieldName', () => {
const component = render(<FieldName fieldType="string" fieldName="test" />);
expect(component).toMatchSnapshot();
});

test('FieldName renders a number field by providing a field record, useShortDots is set to false', () => {
const field = {
type: 'number',
name: 'test.test.test',
rowCount: 100,
scripted: false,
};
const component = render(<FieldName field={field} />);
expect(component).toMatchSnapshot();
});

test('FieldName renders a geo field, useShortDots is set to true', () => {
const field = {
type: 'geo_point',
name: 'test.test.test',
rowCount: 0,
scripted: false,
};
const component = render(<FieldName field={field} useShortDots={true} />);
expect(component).toMatchSnapshot();
});
Loading