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

Table: Option to validate and pass error message for each table cell #2813

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
42 changes: 40 additions & 2 deletions stencil-workspace/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import { ButtonColor, ButtonSize, ButtonStyle, ButtonType } from "./components/m
import { ButtonGroupSelectionType } from "./components/modus-button-group/modus-button-group.models";
import { ModusDataTableCellLink, ModusDataTableDisplayOptions, ModusDataTableRowAction, ModusDataTableRowActionClickEvent, ModusDataTableSelectionOptions, ModusDataTableSortEvent, ModusDataTableSortOptions, TCell, TColumn, TRow } from "./components/modus-data-table/modus-data-table.models";
import { ModusDateInputEventDetails, ModusDateInputType } from "./components/modus-date-input/utils/modus-date-input.models";
import { Placement } from "@popperjs/core";
import { ModusIconName } from "./icons/ModusIconUtilities";
import { ModusNavbarApp } from "./components/modus-navbar/apps-menu/modus-navbar-apps-menu";
import { ModusNavbarButton, ModusNavbarDropdownItem, ModusNavbarDropdownOptions, ModusNavbarLogoOptions, ModusNavbarProfileMenuLink, ModusNavbarTooltip, ModusProfileMenuOptions } from "./components/modus-navbar/modus-navbar.models";
import { ModusNavbarApp as ModusNavbarApp1 } from "./components/modus-navbar/apps-menu/modus-navbar-apps-menu";
import { RadioButton } from "./components/modus-radio-group/modus-radio-button";
import { ModusSentimentScaleType } from "./components/modus-sentiment-scale/modus-sentiment-scale.models";
import { ModusSideNavigationItemInfo } from "./components/modus-side-navigation/modus-side-navigation.models";
import { ModusTableCellEditorArgs, ModusTableCellLink, ModusTableCellValueChange, ModusTableColumn, ModusTableColumnOrderState, ModusTableColumnSizingState, ModusTableColumnSort, ModusTableColumnsVisibilityOptions, ModusTableColumnVisibilityState, ModusTableDisplayOptions, ModusTableExpandedState, ModusTableManualPaginationOptions, ModusTableManualSortingOptions, ModusTablePaginationState, ModusTableRowAction, ModusTableRowActionClick, ModusTableRowSelectionOptions, ModusTableSortingState, ModusTableToolbarOptions } from "./components/modus-table/models/modus-table.models";
import { ModusTableCellEditorArgs, ModusTableCellLink, ModusTableCellValueChange, ModusTableColumn, ModusTableColumnOrderState, ModusTableColumnSizingState, ModusTableColumnSort, ModusTableColumnsVisibilityOptions, ModusTableColumnVisibilityState, ModusTableDisplayOptions, ModusTableErrors, ModusTableExpandedState, ModusTableManualPaginationOptions, ModusTableManualSortingOptions, ModusTablePaginationState, ModusTableRowAction, ModusTableRowActionClick, ModusTableRowSelectionOptions, ModusTableSortingState, ModusTableToolbarOptions } from "./components/modus-table/models/modus-table.models";
import { Cell, Column, Row } from "@tanstack/table-core";
import { TableCellEdited, TableContext } from "./components/modus-table/models/table-context.models";
import { Tab } from "./components/modus-tabs/modus-tabs";
Expand All @@ -36,14 +37,15 @@ export { ButtonColor, ButtonSize, ButtonStyle, ButtonType } from "./components/m
export { ButtonGroupSelectionType } from "./components/modus-button-group/modus-button-group.models";
export { ModusDataTableCellLink, ModusDataTableDisplayOptions, ModusDataTableRowAction, ModusDataTableRowActionClickEvent, ModusDataTableSelectionOptions, ModusDataTableSortEvent, ModusDataTableSortOptions, TCell, TColumn, TRow } from "./components/modus-data-table/modus-data-table.models";
export { ModusDateInputEventDetails, ModusDateInputType } from "./components/modus-date-input/utils/modus-date-input.models";
export { Placement } from "@popperjs/core";
export { ModusIconName } from "./icons/ModusIconUtilities";
export { ModusNavbarApp } from "./components/modus-navbar/apps-menu/modus-navbar-apps-menu";
export { ModusNavbarButton, ModusNavbarDropdownItem, ModusNavbarDropdownOptions, ModusNavbarLogoOptions, ModusNavbarProfileMenuLink, ModusNavbarTooltip, ModusProfileMenuOptions } from "./components/modus-navbar/modus-navbar.models";
export { ModusNavbarApp as ModusNavbarApp1 } from "./components/modus-navbar/apps-menu/modus-navbar-apps-menu";
export { RadioButton } from "./components/modus-radio-group/modus-radio-button";
export { ModusSentimentScaleType } from "./components/modus-sentiment-scale/modus-sentiment-scale.models";
export { ModusSideNavigationItemInfo } from "./components/modus-side-navigation/modus-side-navigation.models";
export { ModusTableCellEditorArgs, ModusTableCellLink, ModusTableCellValueChange, ModusTableColumn, ModusTableColumnOrderState, ModusTableColumnSizingState, ModusTableColumnSort, ModusTableColumnsVisibilityOptions, ModusTableColumnVisibilityState, ModusTableDisplayOptions, ModusTableExpandedState, ModusTableManualPaginationOptions, ModusTableManualSortingOptions, ModusTablePaginationState, ModusTableRowAction, ModusTableRowActionClick, ModusTableRowSelectionOptions, ModusTableSortingState, ModusTableToolbarOptions } from "./components/modus-table/models/modus-table.models";
export { ModusTableCellEditorArgs, ModusTableCellLink, ModusTableCellValueChange, ModusTableColumn, ModusTableColumnOrderState, ModusTableColumnSizingState, ModusTableColumnSort, ModusTableColumnsVisibilityOptions, ModusTableColumnVisibilityState, ModusTableDisplayOptions, ModusTableErrors, ModusTableExpandedState, ModusTableManualPaginationOptions, ModusTableManualSortingOptions, ModusTablePaginationState, ModusTableRowAction, ModusTableRowActionClick, ModusTableRowSelectionOptions, ModusTableSortingState, ModusTableToolbarOptions } from "./components/modus-table/models/modus-table.models";
export { Cell, Column, Row } from "@tanstack/table-core";
export { TableCellEdited, TableContext } from "./components/modus-table/models/table-context.models";
export { Tab } from "./components/modus-tabs/modus-tabs";
Expand Down Expand Up @@ -559,6 +561,10 @@ export namespace Components {
* (optional) Label for the field.
*/
"label": string;
/**
* (optional) The placement of the calendar popup
*/
"position": Placement;
}
interface ModusDivider {
}
Expand Down Expand Up @@ -1243,6 +1249,7 @@ export namespace Components {
* (Optional) To control display options of table.
*/
"displayOptions"?: ModusTableDisplayOptions;
"errors": ModusTableErrors;
"fullWidth": boolean;
/**
* Returns data of a column.
Expand Down Expand Up @@ -1333,6 +1340,7 @@ export namespace Components {
interface ModusTableCellEditor {
"args": ModusTableCellEditorArgs;
"dataType": string;
"inputValueChangeHandler": (newValue: string) => void;
"keyDown": (e: KeyboardEvent, newValue: string) => void;
"type": string;
"value": unknown;
Expand Down Expand Up @@ -1751,6 +1759,10 @@ export namespace Components {
* (optional) Disable usage of `tab` key to focus elements inside a tree view. Use `Arrow Up/Down` for focussing a tree item and `Shift + Arrow Right` for focussing a checkbox inside the item.
*/
"disableTabbing": boolean;
/**
* (optional) Sets draggable state to be true to all the children
*/
"enableReordering": boolean;
/**
* (optional) Set expanded tree items
*/
Expand Down Expand Up @@ -1953,6 +1965,10 @@ export interface ModusTableCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLModusTableElement;
}
export interface ModusTableCellMainCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLModusTableCellMainElement;
}
export interface ModusTableRowActionsCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLModusTableRowActionsElement;
Expand Down Expand Up @@ -2622,7 +2638,18 @@ declare global {
prototype: HTMLModusTableCellEditorElement;
new (): HTMLModusTableCellEditorElement;
};
interface HTMLModusTableCellMainElementEventMap {
"cellInputValueChange": TableCellEdited;
}
interface HTMLModusTableCellMainElement extends Components.ModusTableCellMain, HTMLStencilElement {
addEventListener<K extends keyof HTMLModusTableCellMainElementEventMap>(type: K, listener: (this: HTMLModusTableCellMainElement, ev: ModusTableCellMainCustomEvent<HTMLModusTableCellMainElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLModusTableCellMainElementEventMap>(type: K, listener: (this: HTMLModusTableCellMainElement, ev: ModusTableCellMainCustomEvent<HTMLModusTableCellMainElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLModusTableCellMainElement: {
prototype: HTMLModusTableCellMainElement;
Expand Down Expand Up @@ -3490,6 +3517,10 @@ declare namespace LocalJSX {
* (optional) Label for the field.
*/
"label"?: string;
/**
* (optional) The placement of the calendar popup
*/
"position"?: Placement;
}
interface ModusDivider {
}
Expand Down Expand Up @@ -4284,6 +4315,7 @@ declare namespace LocalJSX {
* (Optional) To control display options of table.
*/
"displayOptions"?: ModusTableDisplayOptions;
"errors"?: ModusTableErrors;
"fullWidth"?: boolean;
/**
* (Optional) To enable row hover in table.
Expand Down Expand Up @@ -4395,6 +4427,7 @@ declare namespace LocalJSX {
interface ModusTableCellEditor {
"args"?: ModusTableCellEditorArgs;
"dataType"?: string;
"inputValueChangeHandler"?: (newValue: string) => void;
"keyDown"?: (e: KeyboardEvent, newValue: string) => void;
"type"?: string;
"value"?: unknown;
Expand All @@ -4404,6 +4437,7 @@ declare namespace LocalJSX {
"cell"?: Cell<unknown, unknown>;
"context"?: TableContext;
"hasRowsExpandable"?: boolean;
"onCellInputValueChange"?: (event: ModusTableCellMainCustomEvent<TableCellEdited>) => void;
"valueChange"?: (props: TableCellEdited) => void;
}
interface ModusTableColumnsVisibility {
Expand Down Expand Up @@ -4819,6 +4853,10 @@ declare namespace LocalJSX {
* (optional) Disable usage of `tab` key to focus elements inside a tree view. Use `Arrow Up/Down` for focussing a tree item and `Shift + Arrow Right` for focussing a checkbox inside the item.
*/
"disableTabbing"?: boolean;
/**
* (optional) Sets draggable state to be true to all the children
*/
"enableReordering"?: boolean;
/**
* (optional) Set expanded tree items
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,10 @@ export interface ModusTableManualSortingOptions {
}

export type ModusTableColumnSort = ColumnSort;

export interface ModusTableErrors {
[rowIndex: number]: {
// if id is present in row data, use that, otherwise use index
[accessorKey: string]: string;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ModusTableSortingState,
ModusTablePaginationState,
ModusTableManualPaginationOptions,
ModusTableErrors,
} from './modus-table.models';
import { Row, Table, Updater } from '@tanstack/table-core';
import ModusTableCore from '../modus-table.core';
Expand All @@ -26,6 +27,8 @@ export interface TableContext {

columns: ModusTableColumn<unknown>[];

errors: ModusTableErrors;

columnResize: boolean;

columnReorder: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ table {
left: 46.25px;
}

td.error {
border: 2px solid var(--modus-input-validation-error-color, #da212c) !important;
border-radius: unset;
box-shadow: 0 0 0 1px var(--modus-input-validation-error-color, #da212c);
}

thead {
position: sticky;
top: 0;
Expand Down
4 changes: 4 additions & 0 deletions stencil-workspace/src/components/modus-table/modus-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
ModusTableSortingState,
ModusTableRowWithId,
ModusTableColumnSort,
ModusTableErrors,
} from './models/modus-table.models';
import ColumnDragState from './models/column-drag-state.model';
import {
Expand Down Expand Up @@ -137,6 +138,8 @@ export class ModusTable {
cellVerticalBorderless: false,
};

@Prop() errors: ModusTableErrors;

/** (Optional) To enable row hover in table. */
@Prop() hover = false;

Expand Down Expand Up @@ -511,6 +514,7 @@ export class ModusTable {
rowSelectionOptions: this.rowSelectionOptions,
rowsExpandable: this.rowsExpandable,
columns: this.columns,
errors: this.errors,
columnReorder: this.columnReorder,
columnResize: this.columnResize,
rowSelectionChange: this.rowSelectionChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ table.density-compact {
z-index: 99;
}

.error .editor::part(input-container) {
border: 1px solid var(--modus-input-validation-error-color, #da212c);
border-radius: unset;
box-shadow: 2px solid var(--modus-input-border-active-color, #da212c);
z-index: 99;
}

.error-tooltip {
background-color: var(--modus-input-validation-error-color, #da212c);
border-radius: 2px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// eslint-disable-next-line
import { Host, JSX, Component, Prop, h, Listen, State } from '@stencil/core';
import { Host, JSX, Component, Prop, h, Listen } from '@stencil/core';
import {
KEYBOARD_ENTER,
CELL_EDIT_TYPE_AUTOCOMPLETE,
Expand All @@ -18,7 +18,6 @@ import {
ModusTableCellEditorArgs,
} from '../../../models/modus-table.models';
import { ModusDateInputEventDetails } from '../../../../modus-date-input/utils/modus-date-input.models';
import { createPopper, Instance } from '@popperjs/core';

@Component({
tag: 'modus-table-cell-editor',
Expand All @@ -31,13 +30,10 @@ export class ModusTableCellEditor {
@Prop() type: string;
@Prop() valueChange: (newValue: string) => void;
@Prop() keyDown: (e: KeyboardEvent, newValue: string) => void;

@State() errorMessage: string;
@Prop() inputValueChangeHandler: (newValue: string) => void;

private editedValue: unknown;
private inputElement: HTMLElement;
private errorTooltip: HTMLElement;
private popperInstance: Instance;

connectedCallback(): void {
this.editedValue = this.value;
Expand All @@ -47,11 +43,6 @@ export class ModusTableCellEditor {
if (this.inputElement['focusInput']) {
this.inputElement['focusInput']();
}
this.createErrorTooltip();
}

disconnectedCallback(): void {
this.destroyErrorTooltip();
}

@Listen('click', { target: 'document' })
Expand All @@ -68,76 +59,18 @@ export class ModusTableCellEditor {

handleBlur: () => void = () => {
this.valueChange(this.editedValue as string);
this.destroyErrorTooltip();
};

handleKeyDown: (e: KeyboardEvent) => void = (e) => {
this.keyDown(e, this.editedValue as string);
};

handleError = (e: CustomEvent<string>) => {
this.errorMessage = e.detail;
this.showErrorTooltip();
};

getDefaultProps = (ariaLabel) => ({
'aria-label': ariaLabel,
class: 'editor',
ref: (ref) => (this.inputElement = ref),
});

createErrorTooltip(): void {
if (!this.errorTooltip) {
this.errorTooltip = document.createElement('div');
this.errorTooltip.className = 'error-tooltip';
this.inputElement.getRootNode().appendChild(this.errorTooltip); // Append to the same parent element as input
this.popperInstance = createPopper(this.inputElement, this.errorTooltip, {
placement: 'bottom-start',
modifiers: [
{
name: 'offset',
options: {
offset: [-0.5, 0], // Offset from the element
},
},
{
name: 'preventOverflow',
options: {
boundary: 'viewport', // Prevents tooltip from overflowing the viewport
},
},
],
});
}
}

showErrorTooltip(): void {
if (this.errorTooltip) {
this.errorTooltip.innerText = this.errorMessage;
this.errorTooltip.style.display = 'block';
if (this.popperInstance) {
this.popperInstance.update();
}
}
}

hideErrorTooltip(): void {
if (this.errorTooltip) {
this.errorTooltip.style.display = 'none';
}
}

destroyErrorTooltip(): void {
if (this.popperInstance) {
this.popperInstance.destroy();
this.popperInstance = null;
}
if (this.errorTooltip) {
this.errorTooltip.remove();
this.errorTooltip = null;
}
}

renderNumberInput(): JSX.Element[] {
function handleArrowKeys(e: KeyboardEvent, callback: (e: KeyboardEvent) => void) {
const code = e.key.toLowerCase();
Expand All @@ -163,7 +96,10 @@ export class ModusTableCellEditor {
<modus-text-input
{...this.getDefaultProps('Text input')}
value={this.value as string}
onValueChange={(e: CustomEvent<string>) => (this.editedValue = e.detail)}
onValueChange={(e: CustomEvent<string>) => {
this.editedValue = e.detail;
this.inputValueChangeHandler(e.detail as string);
}}
onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown}
autoFocusInput
Expand Down Expand Up @@ -229,9 +165,7 @@ export class ModusTableCellEditor {
value={this.value as string}
onValueChange={(e: CustomEvent<ModusDateInputEventDetails>) => {
this.editedValue = e.detail[valueKey];
this.hideErrorTooltip();
}}
onValueError={(e: CustomEvent<string>) => this.handleError(e)}></modus-date-input>
}}></modus-date-input>
</modus-date-picker>
);
}
Expand Down
Loading