diff --git a/.eslintrc.js b/.eslintrc.js index 8944cac..d51b712 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -117,7 +117,7 @@ module.exports = { "max-params": ["warn"], "brace-style": ["warn", "1tbs", { "allowSingleLine": true }], "prefer-const": ["warn"], - + "class-methods-use-this":["warn"], // plugin:react "react/jsx-indent": ["warn", 2], "react/jsx-indent-props": ["warn", 2], @@ -130,7 +130,8 @@ module.exports = { "react/jsx-no-literals": ["off"], "react/jsx-max-depth": ["off"], // rule throws exception in single-dimension-measure "react/jsx-filename-extension": ["warn"], - "react/prefer-stateless-function": ["warn"] + "react/prefer-stateless-function": ["warn"], + "react/no-set-state": ["warn"] }, extends: [ "eslint:all", diff --git a/src/data-table/data-cell.jsx b/src/data-table/data-cell.jsx index d176fc8..f137a03 100644 --- a/src/data-table/data-cell.jsx +++ b/src/data-table/data-cell.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { ApplyPreMask } from '../masking'; import { addSeparators } from '../utilities'; - +import Tooltip from '../tooltip/index.jsx'; function formatMeasurementValue (measurement, styling) { // TODO: measurement.name is a horrible propertyname, it's actually the column header const isColumnPercentageBased = measurement.parents.measurement.header.substring(0, 1) === '%'; @@ -61,11 +61,9 @@ function getSemaphoreColors (measurement, semaphoreColors) { } return semaphoreColors.statusColors.normal; } - class DataCell extends React.PureComponent { constructor (props) { super(props); - this.handleSelect = this.handleSelect.bind(this); } @@ -84,7 +82,14 @@ class DataCell extends React.PureComponent { } render () { - const { data, general, measurement, styleBuilder, styling } = this.props; + const { + data, + general, + measurement, + styleBuilder, + styling + } = this.props; + const isColumnPercentageBased = measurement.name.substring(0, 1) === '%'; let formattedMeasurementValue = formatMeasurementValue(measurement, styling); if (styleBuilder.hasComments()) { @@ -99,27 +104,29 @@ class DataCell extends React.PureComponent { let cellStyle = { fontFamily: styling.options.fontFamily, ...styleBuilder.getStyle(), - paddingLeft: '4px', + paddingLeft: '5px', textAlign: textAlignment }; - const { semaphoreColors } = styling; + + const { semaphoreColors, semaphoreColors: { fieldsToApplyTo } } = styling; const isValidSemaphoreValue = !styleBuilder.hasComments() && !isNaN(measurement.value); - const shouldHaveSemaphoreColors = semaphoreColors.fieldsToApplyTo.applyToAll || semaphoreColors.fieldsToApplyTo.specificFields.indexOf(measurement.parents.dimension1.header) !== -1; + const shouldHaveSemaphoreColors = (fieldsToApplyTo.applyToMetric || fieldsToApplyTo.specificFields.indexOf(measurement.parents.dimension1.header) !== -1); if (isValidSemaphoreValue && shouldHaveSemaphoreColors) { const { backgroundColor, color } = getSemaphoreColors(measurement, semaphoreColors); cellStyle = { + ...styleBuilder.getStyle(), backgroundColor, color, fontFamily: styling.options.fontFamily, - fontSize: styleBuilder.getStyle().fontSize, - paddingLeft: '4px', + paddingLeft: '5px', textAlign: textAlignment }; } let cellClass = 'grid-cells'; - const shouldUseSmallCells = isColumnPercentageBased && data.headers.measurements.length > 1; + const hasTwoDimensions = data.headers.dimension2 && data.headers.dimension2.length > 0; + const shouldUseSmallCells = isColumnPercentageBased && data.headers.measurements.length > 1 && hasTwoDimensions; if (shouldUseSmallCells) { cellClass = 'grid-cells-small'; } @@ -130,12 +137,15 @@ class DataCell extends React.PureComponent { onClick={this.handleSelect} style={cellStyle} > - {formattedMeasurementValue} + + {formattedMeasurementValue} + ); } } - DataCell.propTypes = { data: PropTypes.shape({ headers: PropTypes.shape({ diff --git a/src/headers-table/column-header.jsx b/src/headers-table/column-header.jsx index d50d666..e698a82 100644 --- a/src/headers-table/column-header.jsx +++ b/src/headers-table/column-header.jsx @@ -1,10 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; +import Tooltip from '../tooltip/index.jsx'; class ColumnHeader extends React.PureComponent { constructor (props) { super(props); - this.handleSelect = this.handleSelect.bind(this); } @@ -14,7 +14,9 @@ class ColumnHeader extends React.PureComponent { } render () { - const { baseCSS, cellSuffix, colSpan, entry, styling } = this.props; + const { baseCSS, cellSuffix, colSpan, entry, styling, qlik } = this.props; + const inEditState = qlik.inEditState(); + const style = { ...baseCSS, fontSize: `${14 + styling.headerOptions.fontSizeAdjustment} px`, @@ -29,7 +31,12 @@ class ColumnHeader extends React.PureComponent { onClick={this.handleSelect} style={style} > - {entry.displayValue} + + {entry.displayValue} + ); } diff --git a/src/headers-table/index.spec.js b/src/headers-table/index.spec.js index 9c7cb27..c2458a5 100644 --- a/src/headers-table/index.spec.js +++ b/src/headers-table/index.spec.js @@ -13,7 +13,8 @@ describe('', () => { qlik: { backendApi: { selectValues: () => {} - } + }, + inEditState: () => {} }, styling }; diff --git a/src/initialize-transformed.js b/src/initialize-transformed.js index 1e1f063..04084ce 100644 --- a/src/initialize-transformed.js +++ b/src/initialize-transformed.js @@ -260,6 +260,7 @@ async function initializeTransformed ({ $element, layout, component }) { semaphoreColors: { fieldsToApplyTo: { applyToAll: layout.allsemaphores, + applyToMetric: layout.allmetrics, specificFields: [ layout.conceptsemaphore1, layout.conceptsemaphore2, diff --git a/src/main.less b/src/main.less index ee5dbd9..ac01c42 100644 --- a/src/main.less +++ b/src/main.less @@ -3,6 +3,12 @@ @TableBorder: 1px solid #d3d3d3; @KpiTableWidth: 230px; + *, + *::before, + *::after { + box-sizing: border-box; + } + .edit-mode { pointer-events: none; } @@ -18,6 +24,38 @@ z-index: 110; } + .tooltip-wrapper { + min-width: 25px; + position: fixed; + padding: 5px; + padding-top: 15px; + background-color: #404040; + z-index: 100; + pointer-events: none; + border-radius: 5px; + height: 30px; + width: auto; + opacity: 0.9; + text-align: center; + transform: translate(-50%, -110%); + + > p { + color: #fff; + } + } + + .tooltip-wrapper::after { + content: ""; + position: absolute; + bottom: -10px; + left: 50%; + border-width: 10px 10px 0; + border-style: solid; + border-color: #404040 transparent; + margin-left: -10px; + pointer-events: none; + } + .icon-xls { text-align: left; } @@ -102,6 +140,7 @@ /* ***************** */ .grid-cells { + position: relative; ._cell(70px); } @@ -198,13 +237,6 @@ width: 350px; } - /* popups for headers */ - .header-wrapper { - position: absolute; - top: 0; - z-index: 1; - } - .tooltip { position: fixed !important; color: rgb(70, 70, 70); @@ -213,12 +245,6 @@ border: groove; } - .row-wrapper { - height: calc(~"100% - 97px"); - padding: 0; - margin-top: 0; - } - .kpi-table .fdim-cells, .data-table td { line-height: 1em !important; @@ -274,6 +300,8 @@ .kpi-table .row-wrapper, .data-table .header-wrapper, .data-table .row-wrapper { + /* stylelint-disable-next-line property-no-unknown */ + scrollbar-width: none; -ms-overflow-style: none; // IE 10+ -moz-overflow: -moz-scrollbars-none; // Firefox diff --git a/src/paint.jsx b/src/paint.jsx index 6d9fe26..50586f4 100644 --- a/src/paint.jsx +++ b/src/paint.jsx @@ -1,4 +1,3 @@ -import $ from 'jquery'; // eslint-disable-line id-length import initializeStore from './store'; import React from 'react'; import ReactDOM from 'react-dom'; @@ -20,27 +19,4 @@ export default async function paint ($element, layout, component) { ); ReactDOM.render(jsx, $element[0]); - - // TODO: fixing tooltips has a seperate issue, make sure to remove this as part of that issue - $(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).hover(function () { - $(`[tid="${layout.qInfo.qId}"] .tooltip`).delay(500) - .show(0); - $(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).children(`[tid="${layout.qInfo.qId}"] .tooltip`) - .remove(); - - const element = $(this); - const offset = element.offset(); - const toolTip = $('
'); - - toolTip.css({ - left: offset.left, - top: offset.top - }); - - toolTip.text(element.text()); - $(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).append(toolTip); - }, () => { - $(`[tid="${layout.qInfo.qId}"] .tooltip`).delay(0) - .hide(0); - }); } diff --git a/src/root.spec.js b/src/root.spec.js index 42214c9..6cf20e9 100644 --- a/src/root.spec.js +++ b/src/root.spec.js @@ -10,7 +10,8 @@ describe('', () => { qlik: { backendApi: { selectValues: () => {} - } + }, + inEditState: () => {} }, state }; diff --git a/src/tooltip/index.jsx b/src/tooltip/index.jsx new file mode 100644 index 0000000..336c6a1 --- /dev/null +++ b/src/tooltip/index.jsx @@ -0,0 +1,65 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const handleCalculateTooltipPosition = (event) => { + const tooltipClassName = 'tooltip-wrapper'; + const tooltip = document.getElementsByClassName(tooltipClassName); + + tooltip[0].style.left = event.clientX + 'px'; + tooltip[0].style.top = event.clientY + 'px'; +}; +class Tooltip extends React.Component { + constructor (props) { + super(props); + this.state = { + showTooltip: false + }; + this.handleRenderTooltip = this.handleRenderTooltip.bind(this); + } + + shouldComponentUpdate (nextProps, nextState) { + const { showTooltip } = this.state; + if (nextState.showTooltip === showTooltip) { + return false; + } + return true; + } + + handleRenderTooltip () { + const { showTooltip } = this.state; + this.setState({ showTooltip: !showTooltip }); + } + + + render () { + const { children, tooltipText } = this.props; + const { showTooltip } = this.state; + + return ( +
+ {children} + + {showTooltip + ? ( +
+

+ {tooltipText} +

+
+ ) : null} +
+ ); + } +} + +Tooltip.propTypes = { + children: PropTypes.string.isRequired, + tooltipText: PropTypes.string.isRequired +}; +export default Tooltip;