Skip to content
This repository has been archived by the owner on Jun 4, 2024. It is now read-only.

Issue 807 - Customizable Markdown options #808

Merged
merged 5 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- [#808](https://github.com/plotly/dash-table/pull/808)Fix a regression introduced with [#787](https://github.com/plotly/dash-table/pull/787) making it impossible to open markdown links in the current tab.
- Adds a new `markdown_options` property that supports:
- `linkTarget` nested prop with values `_blank`, `_parent`, `_self`, `_top` (default: `_blank`)
Marc-Andre-Rivet marked this conversation as resolved.
Show resolved Hide resolved

### Fixed
- [#806](https://github.com/plotly/dash-table/pull/806) Fix a bug where fixed rows a misaligned after navigating or editing cells [#803](https://github.com/plotly/dash-table/issues/803)

Expand Down
16 changes: 16 additions & 0 deletions src/core/objPropsToCamel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { reduce, toPairs, assoc } from 'ramda';
import { toCamelCase } from 'dash-table/derived/style/py2jsCssProperties';

const objPropsToCamel = (value: any): any => (value !== null && typeof value === 'object') ?
reduce((
acc,
[key, pValue]: [string, any]
) => assoc(toCamelCase(key.split('_')), objPropsToCamel(pValue), acc),
{} as any,
toPairs(value)
) :
Array.isArray(value) ?
value.map(objPropsToCamel, value) :
value;

export default objPropsToCamel;
16 changes: 13 additions & 3 deletions src/dash-table/components/CellFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { CSSProperties } from 'react';
import { matrixMap2, matrixMap3 } from 'core/math/matrixZipMap';
import { arrayMap2 } from 'core/math/arrayZipMap';

import { ICellFactoryProps } from 'dash-table/components/Table/props';
import { ICellFactoryProps, IMarkdownOptions } from 'dash-table/components/Table/props';
import derivedCellWrappers from 'dash-table/derived/cell/wrappers';
import derivedCellContents from 'dash-table/derived/cell/contents';
import derivedCellOperations from 'dash-table/derived/cell/operations';
Expand All @@ -14,6 +14,7 @@ import { derivedRelevantCellStyles } from 'dash-table/derived/style';
import { IEdgesMatrices } from 'dash-table/derived/edges/type';
import { memoizeOne } from 'core/memoizer';
import memoizerCache from 'core/cache/memoizer';
import Markdown from 'dash-table/utils/Markdown';

export default class CellFactory {

Expand All @@ -33,6 +34,10 @@ export default class CellFactory {
private readonly relevantStyles = derivedRelevantCellStyles()
) { }

private getMarkdown = memoizeOne((
options: IMarkdownOptions
) => new Markdown(options));

public createCells(dataEdges: IEdgesMatrices | undefined, dataOpEdges: IEdgesMatrices | undefined) {
const {
active_cell,
Expand All @@ -44,6 +49,7 @@ export default class CellFactory {
id,
is_focused,
loading_state,
markdown_options,
row_deletable,
row_selectable,
selected_cells,
Expand Down Expand Up @@ -121,13 +127,16 @@ export default class CellFactory {
selected_cells
);

const markdown = this.getMarkdown(markdown_options);

const partialCellContents = this.cellContents.partialGet(
visibleColumns,
virtualized.data,
virtualized.offset,
!!is_focused,
dropdowns,
loading_state
loading_state,
markdown
);

const cellContents = this.cellContents.get(
Expand All @@ -139,7 +148,8 @@ export default class CellFactory {
virtualized.offset,
!!is_focused,
dropdowns,
loading_state
loading_state,
markdown
);

const ops = this.getDataOpCells(
Expand Down
8 changes: 5 additions & 3 deletions src/dash-table/components/CellMarkdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ interface IProps {
active: boolean;
applyFocus: boolean;
className: string;
markdown: Markdown;
value: any;
}

export default class CellMarkdown extends PureComponent<IProps, {}> {

getMarkdown = memoizeOne((value: string, _ready: any) => ({
getMarkdown = memoizeOne((value: any, md: Markdown, _ready: any) => ({
dangerouslySetInnerHTML: {
__html: Markdown.render(String(value))
__html: md.render(String(value))
}
}));

Expand All @@ -41,13 +42,14 @@ export default class CellMarkdown extends PureComponent<IProps, {}> {
render() {
const {
className,
markdown,
value
} = this.props;

return (<div
ref='el'
className={[className, 'cell-markdown'].join(' ')}
{...this.getMarkdown(value, Markdown.isReady)}
{...this.getMarkdown(value, markdown, Markdown.isReady)}
/>);
}

Expand Down
6 changes: 6 additions & 0 deletions src/dash-table/components/Table/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ export interface INumberLocale {
separate_4digits?: boolean;
}

export interface IMarkdownOptions {
link_target: '_blank' | '_parent' | '_self' | '_top' | string;
}

export type NumberFormat = ({
locale: INumberLocale;
nully: any;
Expand Down Expand Up @@ -315,6 +319,7 @@ export interface IProps {
hidden_columns?: string[];
include_headers_on_copy_paste?: boolean;
locale_format: INumberLocale;
markdown_options: IMarkdownOptions;
merge_duplicate_headers?: boolean;
fixed_columns?: Fixed;
fixed_rows?: Fixed;
Expand Down Expand Up @@ -491,6 +496,7 @@ export interface ICellFactoryProps {
id: string;
is_focused?: boolean;
loading_state: boolean;
markdown_options: IMarkdownOptions;
paginator: IPaginator;
row_deletable: boolean;
row_selectable: Selection;
Expand Down
22 changes: 22 additions & 0 deletions src/dash-table/dash/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export const defaultProps = {
data: 0
},

markdown_options: {
link_target: '_blank'
},

tooltip: {},
tooltip_conditional: [],
tooltip_data: [],
Expand Down Expand Up @@ -423,6 +427,24 @@ export const propTypes = {
separate_4digits: PropTypes.bool
}),

/**
* The `markdown_options` property allows customization of the markdown cells behavior.
* 'link_target': (default: '_blank') the link's behavior (_blank opens the link in a
* new tab, _parent opens the link in the parent frame, _self opens the link in the
* current tab, and _top opens the link in the top frame) or a string
*/
markdown_options: PropTypes.exact({
link_target: PropTypes.oneOfType([
PropTypes.string,
PropTypes.oneOf([
'_blank',
'_parent',
'_self',
'_top'
])
]).isRequired
}),

/**
* The `css` property is a way to embed CSS selectors and rules
* onto the page.
Expand Down
29 changes: 23 additions & 6 deletions src/dash-table/derived/cell/contents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { memoizeOne } from 'core/memoizer';
import getFormatter from 'dash-table/type/formatter';
import { shallowClone } from 'core/math/matrixZipMap';
import CellMarkdown from 'dash-table/components/CellMarkdown';
import Markdown from 'dash-table/utils/Markdown';

const mapData = R.addIndex<Datum, JSX.Element[]>(R.map);
const mapRow = R.addIndex<IColumn, JSX.Element>(R.map);
Expand Down Expand Up @@ -68,7 +69,8 @@ class Contents {
_offset: IViewportOffset,
isFocused: boolean,
dropdowns: (IDropdown | undefined)[][],
data_loading: boolean
data_loading: boolean,
markdown: Markdown
): JSX.Element[][] => {
const formatters = R.map(getFormatter, columns);

Expand All @@ -84,7 +86,8 @@ class Contents {
rowIndex,
datum,
formatters,
data_loading
data_loading,
markdown
), columns), data);
});

Expand All @@ -97,7 +100,8 @@ class Contents {
offset: IViewportOffset,
isFocused: boolean,
dropdowns: (IDropdown | undefined)[][],
data_loading: boolean
data_loading: boolean,
markdown: Markdown
): JSX.Element[][] => {
if (!activeCell) {
return contents;
Expand All @@ -124,13 +128,26 @@ class Contents {
iActive,
data[i],
formatters,
data_loading
data_loading,
markdown
);

return contents;
});

private getContent(active: boolean, applyFocus: boolean, isFocused: boolean, column: IColumn, dropdown: IDropdown | undefined, columnIndex: number, rowIndex: number, datum: any, formatters: ((value: any) => any)[], data_loading: boolean) {
private getContent(
active: boolean,
applyFocus: boolean,
isFocused: boolean,
column: IColumn,
dropdown: IDropdown | undefined,
columnIndex: number,
rowIndex: number,
datum: any,
formatters: ((value: any) => any)[],
data_loading: boolean,
markdown: Markdown
) {

const className = [
...(active ? ['input-active'] : []),
Expand All @@ -139,7 +156,6 @@ class Contents {
].join(' ');

const cellType = getCellType(active, column.editable, dropdown && dropdown.options, column.presentation, data_loading);

switch (cellType) {
case CellType.Dropdown:
return (<CellDropdown
Expand Down Expand Up @@ -170,6 +186,7 @@ class Contents {
active={active}
applyFocus={applyFocus}
className={className}
markdown={markdown}
value={datum[column.id]}
/>);
case CellType.DropdownLabel:
Expand Down
2 changes: 1 addition & 1 deletion src/dash-table/derived/style/py2jsCssProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import cssProperties from './cssProperties';

export type StyleProperty = string | number;

const toCamelCase = (fragments: string[]) => fragments.map((f, i) => i ?
export const toCamelCase = (fragments: string[]) => fragments.map((f, i) => i ?
f.charAt(0).toUpperCase() + f.substring(1) :
f
).join('');
Expand Down
36 changes: 21 additions & 15 deletions src/dash-table/utils/Markdown.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import { Remarkable } from 'remarkable';
import objPropsToCamel from 'core/objPropsToCamel';
import LazyLoader from 'dash-table/LazyLoader';
import { IMarkdownOptions } from 'dash-table/components/Table/props';

export default class Markdown {

static isReady: Promise<boolean> | true = new Promise<boolean>(resolve => {
Markdown.hljsResolve = resolve;
});

static render = (value: string) => {
return Markdown.md.render(value);
}

private static hljsResolve: () => any;

private static hljs: any;

private static readonly md: Remarkable = new Remarkable({
private readonly md: Remarkable = new Remarkable({
highlight: (str: string, lang: string) => {
if (Markdown.hljs) {
if (lang && Markdown.hljs.getLanguage(lang)) {
Expand All @@ -32,12 +22,28 @@ export default class Markdown {
}
return '';
},
linkTarget:'_blank'
...objPropsToCamel(this.options)
});

constructor(private readonly options: IMarkdownOptions) {

}

public render = (value: string) => this.md.render(value);

public static get isReady() {
return Markdown._isReady;
}

private static hljs: any;
private static hljsResolve: () => any;
private static _isReady: Promise<boolean> | true = new Promise<boolean>(resolve => {
Markdown.hljsResolve = resolve;
});

private static async loadhljs() {
Markdown.hljs = await LazyLoader.hljs;
Markdown.hljsResolve();
Markdown.isReady = true;
Markdown._isReady = true;
}
}
Loading