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

fix: allow multiple tooltips per grid cell #318

Merged
merged 1 commit into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@
"/src/slickgrid-react"
],
"dependencies": {
"@slickgrid-universal/common": "~4.6.0",
"@slickgrid-universal/custom-footer-component": "~4.6.0",
"@slickgrid-universal/empty-warning-component": "~4.6.0",
"@slickgrid-universal/event-pub-sub": "~4.6.0",
"@slickgrid-universal/pagination-component": "~4.6.0",
"@slickgrid-universal/common": "~4.6.1",
"@slickgrid-universal/custom-footer-component": "~4.6.1",
"@slickgrid-universal/empty-warning-component": "~4.6.1",
"@slickgrid-universal/event-pub-sub": "~4.6.1",
"@slickgrid-universal/pagination-component": "~4.6.1",
"dequal": "^2.0.3",
"font-awesome": "^4.7.0",
"i18next": "^23.10.1",
Expand All @@ -107,13 +107,13 @@
"@fnando/sparkline": "^0.3.10",
"@popperjs/core": "^2.11.8",
"@release-it/conventional-changelog": "^8.0.1",
"@slickgrid-universal/composite-editor-component": "~4.6.0",
"@slickgrid-universal/custom-tooltip-plugin": "~4.6.0",
"@slickgrid-universal/excel-export": "~4.6.0",
"@slickgrid-universal/graphql": "~4.6.0",
"@slickgrid-universal/odata": "~4.6.0",
"@slickgrid-universal/rxjs-observable": "~4.6.0",
"@slickgrid-universal/text-export": "~4.6.0",
"@slickgrid-universal/composite-editor-component": "~4.6.1",
"@slickgrid-universal/custom-tooltip-plugin": "~4.6.1",
"@slickgrid-universal/excel-export": "~4.6.1",
"@slickgrid-universal/graphql": "~4.6.3",
"@slickgrid-universal/odata": "~4.6.1",
"@slickgrid-universal/rxjs-observable": "~4.6.1",
"@slickgrid-universal/text-export": "~4.6.1",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.2",
"@testing-library/user-event": "^14.5.2",
Expand Down
6 changes: 4 additions & 2 deletions src/examples/slickgrid/Example30.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ExcelExportService } from '@slickgrid-universal/excel-export';
import { SlickCustomTooltip } from '@slickgrid-universal/custom-tooltip-plugin';
import { SlickCompositeEditor, SlickCompositeEditorComponent } from '@slickgrid-universal/composite-editor-component';
import React from 'react';

Expand Down Expand Up @@ -138,7 +139,8 @@ export default class Example30 extends React.Component<Props, State> {
defineGrids() {
const columnDefinitions: Column[] = [
{
id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, minWidth: 75,
id: 'title', name: '<span title="Task must always be followed by a number" class="color-warning-dark fa fa-exclamation-triangle"></span> Title <span title="Title is always rendered as UPPERCASE" class="fa fa-info-circle"></span>',
field: 'title', sortable: true, type: FieldType.string, minWidth: 75,
cssClass: 'text-uppercase fw-bold', columnGroup: 'Common Factor',
filterable: true, filter: { model: Filters.compoundInputText },
editor: {
Expand Down Expand Up @@ -405,7 +407,7 @@ export default class Example30 extends React.Component<Props, State> {
excelExportOptions: {
exportWithFormatter: false
},
externalResources: [new ExcelExportService(), this.compositeEditorInstance],
externalResources: [new ExcelExportService(), new SlickCustomTooltip(), this.compositeEditorInstance],
enableFiltering: true,
rowSelectionOptions: {
// True (Single Selection), False (Multiple Selections)
Expand Down
17 changes: 12 additions & 5 deletions src/examples/slickgrid/Example33.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { SlickCustomTooltip } from '@slickgrid-universal/custom-tooltip-plugin';
import React from 'react';

import BaseSlickGridState from './state-slick-grid-base';
import './example33.scss';

const NB_ITEMS = 500;

Expand Down Expand Up @@ -117,6 +118,7 @@ export default class Example32 extends React.Component<Props, State> {
// define tooltip options here OR for the entire grid via the grid options (cell tooltip options will have precedence over grid options)
customTooltip: {
useRegularTooltip: true, // note regular tooltip will try to find a "title" attribute in the cell formatter (it won't work without a cell formatter)
useRegularTooltipFromCellTextOnly: true,
},
},
{
Expand Down Expand Up @@ -165,7 +167,12 @@ export default class Example32 extends React.Component<Props, State> {
formatter: Formatters.percentCompleteBar,
sortable: true, filterable: true,
filter: { model: Filters.slider, operator: '>=' },
customTooltip: { useRegularTooltip: true, },
customTooltip: {
position: 'center',
formatter: (_row, _cell, value) => typeof value === 'string' && value.includes('%') ? value : `${value}%`,
headerFormatter: undefined,
headerRowFormatter: undefined
},
},
{
id: 'start', name: 'Start', field: 'start', sortable: true,
Expand Down Expand Up @@ -456,12 +463,12 @@ export default class Example32 extends React.Component<Props, State> {

tooltipFormatter(row: number, cell: number, value: any, column: Column, dataContext: any, grid: SlickGrid) {
const tooltipTitle = 'Custom Tooltip';
const effortDrivenHtml = Formatters.checkmarkMaterial(row, cell, dataContext.effortDriven, column, dataContext, grid);
const effortDrivenHtml = Formatters.checkmarkMaterial(row, cell, dataContext.effortDriven, column, dataContext, grid) as HTMLElement;

return `<div class="header-tooltip-title">${tooltipTitle}</div>
<div class="tooltip-2cols-row"><div>Id:</div> <div>${dataContext.id}</div></div>
<div class="tooltip-2cols-row"><div>Title:</div> <div>${dataContext.title}</div></div>
<div class="tooltip-2cols-row"><div>Effort Driven:</div> <div>${effortDrivenHtml}</div></div>
<div class="tooltip-2cols-row"><div>Effort Driven:</div> <div>${effortDrivenHtml.outerHTML || ''}</div></div>
<div class="tooltip-2cols-row"><div>Completion:</div> <div>${this.loadCompletionIcons(dataContext.percentComplete)}</div></div>
`;
}
Expand All @@ -471,9 +478,9 @@ export default class Example32 extends React.Component<Props, State> {

// use a 2nd Formatter to get the percent completion
// any properties provided from the `asyncPost` will end up in the `__params` property (unless a different prop name is provided via `asyncParamsPropName`)
const completionBar = Formatters.percentCompleteBarWithText(row, cell, dataContext.percentComplete, column, dataContext, grid);
const completionBar = Formatters.percentCompleteBarWithText(row, cell, dataContext.percentComplete, column, dataContext, grid) as HTMLElement;
const out = `<div class="color-sf-primary-dark header-tooltip-title">${tooltipTitle}</div>
<div class="tooltip-2cols-row"><div>Completion:</div> <div>${completionBar}</div></div>
<div class="tooltip-2cols-row"><div>Completion:</div> <div>${completionBar.outerHTML || ''}</div></div>
<div class="tooltip-2cols-row"><div>Lifespan:</div> <div>${dataContext.__params.lifespan.toFixed(2)}</div></div>
<div class="tooltip-2cols-row"><div>Ratio:</div> <div>${dataContext.__params.ratio.toFixed(2)}</div></div>
`;
Expand Down
2 changes: 2 additions & 0 deletions src/examples/slickgrid/Example35.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import i18next, { TFunction } from 'i18next';
import { SlickCustomTooltip } from '@slickgrid-universal/custom-tooltip-plugin';
import {
FieldType,
Editors,
Expand Down Expand Up @@ -228,6 +229,7 @@ class Example35 extends React.Component<Props, State> {
deleteButtonPrompt: 'Are you sure you want to delete this row?',
},
},
externalResources: [new SlickCustomTooltip()],
};

this.setState((state: State) => ({
Expand Down
80 changes: 80 additions & 0 deletions src/examples/slickgrid/example33.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
$slick-button-border-color: #ababab !default;
@import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-bootstrap.scss';

// --
// Custom Tooltips CSS Variables (or SASS equivalent)
// ----------------------------------------------------
// :root {
// --slick-tooltip-background-color: #363636;
// --slick-tooltip-border-color: #252525;
// --slick-tooltip-border: 2px solid #252525;
// --slick-tooltip-color: #ffffff;

// --slick-tooltip-arrow-color: var(--slick-tooltip-border-color);
// --slick-tooltip-arrow-size: 10px;
// --slick-tooltip-arrow-side-margin: 15px;
// }

.editable-field {
background-color: rgba(227, 240, 251, 0.569) !important;
}
.unsaved-editable-field {
background-color: #fbfdd1 !important;
}
.button-style {
cursor: pointer;
background-color: white;
border: 1px solid #{$slick-button-border-color};
border-radius: 2px;
justify-content: center;
text-align: center;
&:hover {
border-color: darken($slick-button-border-color, 10%);
}
}

.header-tooltip-title {
font-weight: bold;
font-size: 14px;
}
.headerrow-tooltip-title {
color: #AD0041;
font-style: italic;
font-size: 13px;
font-weight: bold;
}

// it's preferable to use CSS Variables (or SASS) but if you want to change colors of your tooltip for 1 column in particular you can do it this way
// e.g. change css of 5th column 4 (zero index: l4)
.l4 {
--slick-tooltip-color: #fff;
}
.l4 .header-tooltip-title,
.l4 .headerrow-tooltip-title {
color: #ffffff;
}
.l4.slick-custom-tooltip {
color: #ffffff;
background-color: #363636;
border: 2px solid #252525;
}
.l4.slick-custom-tooltip.arrow-down::after,
.l4.slick-custom-tooltip.arrow-up::after {
border-width: 10px; // arrow size
}
.l4.slick-custom-tooltip.arrow-down::after {
border-top-color: #252525; // arrow down color
}
.l4.slick-custom-tooltip.arrow-up::after {
top: -20px; // arrow size * 2
border-bottom-color: #252525; // arrow up color
}
.l4.slick-custom-tooltip.arrow-left-align::after {
margin-left: 15px;
}
.l4.slick-custom-tooltip.arrow-right-align::after {
margin-left: calc(100% - 20px - 15px); // 20px is (arrow size * 2), 15px is your extra side margin
}
.l6.slick-custom-tooltip.arrow-left-align::after {
margin-left: 4px;
}
29 changes: 23 additions & 6 deletions test/cypress/e2e/example30.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { changeTimezone, zeroPadding } from '../plugins/utilities';

describe('Example 30 Composite Editor Modal', () => {
const fullPreTitles = ['', 'Common Factor', 'Analysis', 'Period', 'Item', ''];
const fullTitles = ['', 'Title', 'Duration', 'Cost', '% Complete', 'Complexity', 'Start', 'Completed', 'Finish', 'Product', 'Country of Origin', 'Action'];
const fullTitles = ['', ' Title ', 'Duration', 'Cost', '% Complete', 'Complexity', 'Start', 'Completed', 'Finish', 'Product', 'Country of Origin', 'Action'];

const GRID_ROW_HEIGHT = 35;
const EDITABLE_CELL_RGB_COLOR = 'rgba(227, 240, 251, 0.57)';
Expand Down Expand Up @@ -33,6 +33,23 @@ describe('Example 30 Composite Editor Modal', () => {
.each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
});

it('should display 2 different tooltips when hovering icons on "Title" column', () => {
cy.get('.slick-column-name').as('title-column');
cy.get('@title-column')
.find('.fa-exclamation-triangle')
.trigger('mouseover');

cy.get('.slick-custom-tooltip').should('be.visible');
cy.get('.slick-custom-tooltip .tooltip-body').contains('Task must always be followed by a number');

cy.get('@title-column')
.find('.fa-info-circle')
.trigger('mouseover');

cy.get('.slick-custom-tooltip').should('be.visible');
cy.get('.slick-custom-tooltip .tooltip-body').contains('Title is always rendered as UPPERCASE');
});

it('should have "TASK 0" (uppercase) incremented by 1 after each row', () => {
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).contains('TASK 0', { matchCase: false })
.should('have.css', 'text-transform', 'uppercase');
Expand Down Expand Up @@ -89,10 +106,10 @@ describe('Example 30 Composite Editor Modal', () => {
});

it('should not be able to change the "Finish" dates on first 2 rows', () => {
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(8)`).should('contain', '').click(); // this date should also always be initially empty
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(8)`).should('contain', '').click({ force: true }); // this date should also always be initially empty
cy.get(`.flatpickr-day.today:visible`).should('not.exist');

cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(8)`).should('contain', '').click(); // this date should also always be initially empty
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(8)`).should('contain', '').click({ force: true }); // this date should also always be initially empty
cy.get(`.flatpickr-day.today:visible`).should('not.exist');
});

Expand Down Expand Up @@ -289,7 +306,7 @@ describe('Example 30 Composite Editor Modal', () => {
cy.get('.slick-editor-modal').should('not.exist');
});

it('should have new TASK 8888 displayed on first row', () => {
it('should have new TASK 8899 displayed on first row', () => {
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(1)`).contains('TASK 8899', { matchCase: false });
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(2)`).should('contain', '33 days');
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(4)`).should('contain', '17');
Expand Down Expand Up @@ -404,10 +421,10 @@ describe('Example 30 Composite Editor Modal', () => {

cy.get('.btn-save')
.click()
.then(() => expect(alertStub.getCall(0)).to.be.calledWith('Sorry we could not detect any changes.'));
.then(() => expect(alertStub.getCall(0)).to.be.calledWith('Sorry we could not detect any changes.'))

cy.get('.btn-cancel')
.click();
.click()
});

it('should have the "Mass Selection" button disabled when no rows are selected', () => {
Expand Down
15 changes: 9 additions & 6 deletions test/cypress/e2e/example33.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ describe('Example 33 - Regular & Custom Tooltips', () => {
cy.get('.slick-custom-tooltip').should('be.visible');
cy.get('.slick-custom-tooltip').contains('Task 2 - (async tooltip)');

cy.get('.tooltip-2cols-row:nth(0)').find('div:nth(0)').contains('Completion:');
cy.get('.tooltip-2cols-row:nth(0)').find('div').should('have.class', 'percent-complete-bar-with-text');

cy.get('.tooltip-2cols-row:nth(1)').find('div:nth(0)').contains('Lifespan:');
cy.get('.tooltip-2cols-row:nth(1)').find('div:nth(1)').contains(/\d+$/); // use regexp to make sure it's a number

Expand Down Expand Up @@ -167,7 +170,7 @@ describe('Example 33 - Regular & Custom Tooltips', () => {

it('should mouse over header-row (filter) 2nd column Title and expect a tooltip to show rendered from an headerRowFormatter', () => {
cy.get(`.slick-headerrow-columns .slick-headerrow-column:nth(1)`).as('checkbox0-filter');
cy.get('@checkbox0-filter').trigger('mouseenter');
cy.get('@checkbox0-filter').trigger('mouseover');

cy.get('.slick-custom-tooltip').should('be.visible');
cy.get('.slick-custom-tooltip').contains('Custom Tooltip - Header Row (filter)');
Expand All @@ -180,15 +183,15 @@ describe('Example 33 - Regular & Custom Tooltips', () => {

it('should mouse over header-row (filter) Finish column and NOT expect any tooltip to show since it is disabled on that column', () => {
cy.get(`.slick-headerrow-columns .slick-headerrow-column:nth(8)`).as('finish-filter');
cy.get('@finish-filter').trigger('mouseenter');
cy.get('@finish-filter').trigger('mouseover');

cy.get('.slick-custom-tooltip').should('not.exist');
cy.get('@finish-filter').trigger('mouseout');
});

it('should mouse over header-row (filter) Prerequisite column and expect to see tooltip of selected filter options', () => {
cy.get(`.slick-headerrow-columns .slick-headerrow-column:nth(10)`).as('checkbox10-header');
cy.get('@checkbox10-header').trigger('mouseenter');
cy.get('@checkbox10-header').trigger('mouseover');

cy.get('.filter-prerequisites .ms-choice span').contains('15 of 500 selected');
cy.get('.slick-custom-tooltip').should('be.visible');
Expand All @@ -199,15 +202,15 @@ describe('Example 33 - Regular & Custom Tooltips', () => {

it('should mouse over header title on 1st column with checkbox and NOT expect any tooltip to show since it is disabled on that column', () => {
cy.get(`.slick-header-columns .slick-header-column:nth(0)`).as('checkbox-header');
cy.get('@checkbox-header').trigger('mouseenter');
cy.get('@checkbox-header').trigger('mouseover');

cy.get('.slick-custom-tooltip').should('not.exist');
cy.get('@checkbox-header').trigger('mouseout');
});

it('should mouse over header title on 2nd column with Title name and expect a tooltip to show rendered from an headerFormatter', () => {
cy.get(`.slick-header-columns .slick-header-column:nth(1)`).as('checkbox0-header');
cy.get('@checkbox0-header').trigger('mouseenter');
cy.get('@checkbox0-header').trigger('mouseover');

cy.get('.slick-custom-tooltip').should('be.visible');
cy.get('.slick-custom-tooltip').contains('Custom Tooltip - Header');
Expand All @@ -220,7 +223,7 @@ describe('Example 33 - Regular & Custom Tooltips', () => {

it('should mouse over header title on 2nd column with Finish name and NOT expect any tooltip to show since it is disabled on that column', () => {
cy.get(`.slick-header-columns .slick-header-column:nth(8)`).as('finish-header');
cy.get('@finish-header').trigger('mouseenter');
cy.get('@finish-header').trigger('mouseover');

cy.get('.slick-custom-tooltip').should('not.exist');
cy.get('@finish-header').trigger('mouseout');
Expand Down
Loading