Skip to content

Commit

Permalink
feat: custom header by passing html element (#1955)
Browse files Browse the repository at this point in the history
* feat: custom header by passing html element

* test: add test

* fix: remove unnecessary characters
  • Loading branch information
jajugoguma committed Aug 28, 2023
1 parent 1a4036c commit 98c2dcc
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 30 deletions.
50 changes: 50 additions & 0 deletions packages/toast-ui.grid/cypress/integration/column.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,3 +483,53 @@ describe('move column', () => {
});
});
});

describe('customHeader', () => {
let customHeader: HTMLElement;

const CUSTOM_HEADER_CLASS_NAME = 'custom-header';
const data = [
{ name: 'Kim', age: 10, price: 100 },
{ name: 'Lee', age: 20, price: 200 },
{ name: 'Ryu', age: 30, price: 300 },
{ name: 'Han', age: 40, price: 400 },
];

beforeEach(() => {
customHeader = document.createElement('div');
customHeader.className = CUSTOM_HEADER_CLASS_NAME;
customHeader.textContent = 'Custom Header';
});

it('should render the given HTML element to the header.', () => {
const columns = [
{ name: 'name', header: 'Name', customHeader },
{ name: 'age', header: 'Age' },
{ name: 'price', header: 'Price' },
];

cy.createGrid({
data,
columns,
});

cy.getHeaderCell('name').click().should('contain.html', customHeader.outerHTML);
});

it('should sets the textContent of the customHeader to the value of the header property if no header property is passed.', () => {
const columns = [
{ name: 'name', customHeader },
{ name: 'age', header: 'Age' },
{ name: 'price', header: 'Price' },
];

cy.createGrid({
data,
columns,
});

cy.gridInstance()
.invoke('getColumn', 'name')
.should('contain', { header: customHeader.textContent });
});
});
2 changes: 2 additions & 0 deletions packages/toast-ui.grid/src/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ if ((module as any).hot) {
* the text line is broken by fitting to the column's width and new line characters.(This option will be deprecated)
* @param {boolean} [options.columns.rowSpan=false] - If set to true, apply dynamic rowspan to column data.
* If it is not a top-level relational column of a column relationship or the grid has tree data, dynamic rowspan is not applied.
* @param {HTMLElement} [options.columns.customHeader] - If set HTML element, the element will render in header cell.
* If set without header property, the text content of element will be set to header property.
* @param {Object} [options.summary] - The object for configuring summary area.
* @param {number} [options.summary.height] - The height of the summary area.
* @param {string} [options.summary.position='bottom'] - The position of the summary area. ('bottom', 'top')
Expand Down
36 changes: 19 additions & 17 deletions packages/toast-ui.grid/src/store/column.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
import {
OptColumn,
OptRowHeader,
OptTree,
Dictionary,
OptCellEditor,
OptCellRenderer,
OptColumn,
OptColumnHeaderInfo,
OptComplexColumnInfo,
Dictionary,
OptFilter,
OptRowHeader,
OptRowHeaderColumn,
OptTree,
} from '@t/options';
import {
ColumnOptions,
AlignType,
VAlignType,
Relations,
ColumnInfo,
CellRendererOptions,
CellEditorOptions,
CellRendererOptions,
ClipboardCopyOptions,
ColumnFilterOption,
Column,
ColumnFilterOption,
ColumnHeaderInfo,
ColumnInfo,
ColumnOptions,
Relations,
VAlignType,
} from '@t/store/column';
import { FilterOptionType } from '@t/store/filterLayerState';
import { observable } from '../helper/observable';
import { isRowNumColumn } from '../helper/column';
import {
createMapFromArray,
findIndex,
findProp,
includes,
omit,
isString,
isEmpty,
isFunction,
isNumber,
isObject,
isString,
isUndefined,
isNumber,
findProp,
omit,
uniq,
isEmpty,
findIndex,
} from '../helper/common';
import { DefaultRenderer } from '../renderer/default';
import { editorMap } from '../editor/manager';
Expand Down Expand Up @@ -244,6 +244,7 @@ export function createColumn(
filter,
className,
comparator,
customHeader,
} = column;

const editorOptions = createEditorOptions(editor);
Expand All @@ -262,7 +263,7 @@ export function createColumn(
return observable({
name,
escapeHTML,
header: header || name,
header: header || customHeader?.textContent || name,
hidden: Boolean(hidden),
resizable: isUndefined(resizable) ? Boolean(columnOptions.resizable) : Boolean(resizable),
align: align || 'left',
Expand Down Expand Up @@ -295,6 +296,7 @@ export function createColumn(
comparator,
autoResizing: width === 'auto',
rowSpan,
customHeader,
});
}

Expand Down
32 changes: 26 additions & 6 deletions packages/toast-ui.grid/src/view/columnHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { h, Component } from 'preact';
import { Component, h } from 'preact';
import { cls } from '../helper/dom';
import { HeaderCheckbox } from './headerCheckbox';
import { SortingButton } from './sortingButton';
import { SortingOrder } from './sortingOrder';
import { FilterButton } from './filterButton';
import { isRowHeader, isCheckboxColumn } from '../helper/column';
import { HeaderRenderer, ColumnHeaderInfo } from '@t/renderer';
import { isCheckboxColumn, isRowHeader } from '../helper/column';
import { ColumnHeaderInfo, HeaderRenderer } from '@t/renderer';
import Grid from '../grid';
import { isFunction } from '../helper/common';
import { isDraggableColumn } from '../query/column';
Expand All @@ -28,15 +28,35 @@ export class ColumnHeader extends Component<Props> {

private getElement(type: string) {
const { columnInfo } = this.props;
const { name, sortable, sortingType, filter, headerRenderer, header } = columnInfo;
const {
name,
sortable,
sortingType,
filter,
headerRenderer,
header,
customHeader,
} = columnInfo;

if (headerRenderer) {
return null;
}

switch (type) {
case 'checkbox':
return isCheckboxColumn(name) ? <HeaderCheckbox /> : header;
case 'checkbox': {
if (isCheckboxColumn(name)) {
return <HeaderCheckbox />;
}

if (this.el && customHeader) {
this.el.appendChild(customHeader);

return null;
}

return header;
}

case 'sortingBtn':
return sortable && <SortingButton columnName={name} sortingType={sortingType} />;
case 'sortingOrder':
Expand Down
9 changes: 7 additions & 2 deletions packages/toast-ui.grid/src/view/headerCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { h, Component } from 'preact';
import { Component, h } from 'preact';
import { connect } from './hoc';
import { DispatchProps } from '../dispatch/create';
import { ColumnInfo } from '@t/store/column';

interface StoreProps {
header: string;
customHeader: ColumnInfo['customHeader'];
checkedAllRows: boolean;
disabled: boolean;
}
Expand Down Expand Up @@ -63,8 +65,11 @@ export const HeaderCheckbox = connect<StoreProps>((store) => {
column: { allColumnMap },
} = store;

const { header, customHeader } = allColumnMap._checked;

return {
header: allColumnMap._checked.header,
header,
customHeader,
checkedAllRows,
disabled: disabledAllCheckbox,
};
Expand Down
7 changes: 4 additions & 3 deletions packages/toast-ui.grid/types/renderer/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import TuiGrid from '../index';
import { CellRenderData, RowKey } from '../store/data';
import {
ColumnInfo,
AlignType,
VAlignType,
SortingType,
ColumnFilterOption,
ColumnInfo,
SortingType,
VAlignType,
} from '../store/column';

export type CellRendererProps = CellRenderData & {
Expand All @@ -29,6 +29,7 @@ export interface CellRendererClass {
export interface ColumnHeaderInfo {
name: string;
header: string;
customHeader?: ColumnInfo['customHeader'];
headerAlign?: AlignType;
headerVAlign?: VAlignType;
sortable?: boolean;
Expand Down
5 changes: 3 additions & 2 deletions packages/toast-ui.grid/types/store/column.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Side } from './focus';
import { CellValue, Row } from './data';
import { OptTree, Dictionary, OptColumnHeaderInfo, GridEventListener } from '../options';
import { HeaderRendererClass, CellRendererClass, CellRendererProps } from '../renderer';
import { Dictionary, GridEventListener, OptColumnHeaderInfo, OptTree } from '../options';
import { CellRendererClass, CellRendererProps, HeaderRendererClass } from '../renderer';
import { CellEditorClass } from '../editor';
import { FilterOptionType, OperatorType } from './filterLayerState';

Expand Down Expand Up @@ -125,6 +125,7 @@ export interface InvalidColumn {

export interface CommonColumnInfo {
header: string;
customHeader?: HTMLElement;
hidden: boolean;
align: AlignType;
valign: VAlignType;
Expand Down

0 comments on commit 98c2dcc

Please sign in to comment.