Unverified Commit 675a29de authored by Daniel's avatar Daniel Committed by GitHub

Update EditableText component (#299)

* Fix some styling issues with react-markdown
* Update EditableText to use React Refs instead of ref callbacks
* Add configs for table and column description max lengths
parent 1945ca62
import * as React from 'react';
import moment from 'moment-timezone';
import { OverlayTrigger, Popover } from 'react-bootstrap';
import moment from 'moment-timezone';
import AppConfig from 'config/config';
import ColumnDescEditableText from 'components/TableDetail/ColumnDescEditableText';
import { logClick } from 'ducks/utilMethods';
import { TableColumn } from 'interfaces';
......@@ -132,6 +132,7 @@ class DetailListItem extends React.Component<DetailListItemProps, DetailListItem
columnIndex={this.props.index}
editable={metadata.is_editable}
value={metadata.description}
maxLength={AppConfig.editableText.columnDescLength}
/>
</div>
{
......
......@@ -17,9 +17,8 @@
padding-right: 32px;
}
// TODO - move to common styles
.truncated,
.truncated .editable-text {
.truncated .editable-text,
.truncated .editable-text p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
......
......@@ -356,6 +356,7 @@ export class TableDetail extends React.Component<TableDetailProps & RouteCompone
<TableDescEditableText
value={ data.table_description }
editable={ data.is_editable }
maxLength={ AppConfig.editableText.tableDescLength }
/>
</div>
<div className="col-xs-12 col-md-5 float-md-right col-lg-4">
......
import autosize from 'autosize';
import * as React from 'react';
import ReactDOM from 'react-dom';
import { Overlay, Tooltip } from 'react-bootstrap';
import * as ReactMarkdown from 'react-markdown';
import { Overlay, Popover, Tooltip } from 'react-bootstrap';
import autosize from 'autosize';
// TODO: Use css-modules instead of 'import'
// TODO: Outdated approach (lines 148, 168). Replace with React.createRef(). See more at https://reactjs.org/docs/refs-and-the-dom.html
import './styles.scss';
export interface StateFromProps {
......@@ -34,12 +32,12 @@ interface EditableTextState {
}
class EditableText extends React.Component<EditableTextProps, EditableTextState> {
private textAreaTarget: HTMLTextAreaElement;
private editAnchorTarget: HTMLAnchorElement;
private textAreaRef;
private editAnchorRef;
public static defaultProps: EditableTextProps = {
editable: true,
maxLength: 4000,
maxLength: 250,
onSubmitValue: null,
getLatestValue: null,
value: '',
......@@ -52,6 +50,9 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
constructor(props) {
super(props);
this.textAreaRef = React.createRef();
this.editAnchorRef = React.createRef();
this.state = {
editable: props.editable,
inEditMode: false,
......@@ -63,21 +64,18 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
componentDidUpdate() {
const { isDisabled, inEditMode, refreshValue, value } = this.state;
if (inEditMode) {
autosize(this.textAreaTarget);
const textArea = this.textAreaRef.current;
if (!inEditMode) return;
autosize(textArea);
if (refreshValue && refreshValue !== value && !isDisabled) {
// disable the component if a refresh is needed
this.setState({ isDisabled: true })
}
else {
} else if (textArea) {
// when entering edit mode, place focus in the textarea
const textArea = ReactDOM.findDOMNode(this.textAreaTarget);
if (textArea) {
textArea.focus();
}
}
}
}
exitEditMode = () => {
this.setState({ isDisabled: false, inEditMode: false, refreshValue: '' });
......@@ -97,28 +95,27 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
};
updateText = () => {
const newValue = ReactDOM.findDOMNode(this.textAreaTarget).value;
const newValue = this.textAreaRef.current.value;
const onSuccessCallback = () => { this.setState({value: newValue, inEditMode: false, refreshValue: undefined }); };
const onFailureCallback = () => { this.exitEditMode(); };
this.props.onSubmitValue(newValue, onSuccessCallback, onFailureCallback);
};
getTarget(type) {
if (type === 'editAnchor') {
return ReactDOM.findDOMNode(this.editAnchorTarget);
}
if (type === 'textArea') {
return ReactDOM.findDOMNode(this.textAreaTarget)
}
}
getAnchorTarget = () => {
return this.editAnchorRef.current;
};
getTextAreaTarget = () => {
return this.textAreaRef.current;
};
render() {
if (!this.state.editable) {
return (
<div id='editable-container' className='editable-container'>
<div id='editable-text' className='editable-text'>
<ReactMarkdown source={this.state.value}/>
<ReactMarkdown source={ this.state.value }/>
</div>
</div>
);
......@@ -128,26 +125,24 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
<div id='editable-container' className='editable-container'>
<Overlay
placement='top'
show={this.state.isDisabled}
target={this.getTarget.bind(this,'editAnchor')}
show={ this.state.isDisabled }
target={ this.getAnchorTarget }
>
<Tooltip id='error-tooltip'>
<div className="error-tooltip">
<text>This text is out of date, please refresh the component</text>
<button onClick={this.refreshText} className="btn btn-flat-icon">
<button onClick={ this.refreshText } className="btn btn-flat-icon">
<img className='icon icon-refresh'/>
</button>
</div>
</Tooltip>
</Overlay>
<div id='editable-text' className={"editable-text"}>
<ReactMarkdown source={this.state.value}/>
<div id='editable-text' className="editable-text">
<ReactMarkdown source={ this.state.value }/>
<a className={ "edit-link" + (this.state.value ? "" : " no-value") }
href="JavaScript:void(0)"
onClick={ this.enterEditMode }
ref={ anchor => {
this.editAnchorTarget = anchor;
}}
ref={ this.editAnchorRef }
>
{
this.state.value ? "edit" : "Add Description"
......@@ -163,21 +158,17 @@ class EditableText extends React.Component<EditableTextProps, EditableTextState>
<textarea
id='editable-textarea'
className='editable-textarea'
rows={2}
maxLength={this.props.maxLength}
ref={textarea => {
this.textAreaTarget = textarea;
}}
>
{this.state.value}
</textarea>
rows={ 2 }
maxLength={ this.props.maxLength }
ref={ this.textAreaRef }
defaultValue={ this.state.value }
/>
<Overlay
placement='top'
show={true}
target={this.getTarget.bind(this,'textArea')}
show={ true }
target={ this.getTextAreaTarget }
>
<Tooltip>
<Tooltip id='save-tooltip'>
<button id='cancel' onClick={this.exitEditMode}>Cancel</button>
<button id='save' onClick={this.updateText}>Save</button>
</Tooltip>
......
......@@ -25,6 +25,12 @@
.editable-text {
flex-grow: 0.1;
word-break: break-word;
// React-Markdown wraps editable text with a paragraph
> p {
margin: 0;
display: inline;
}
}
.editable-textarea {
......
......@@ -5,11 +5,18 @@ const configDefault: AppConfig = {
curatedTags: [],
showAllTags: true,
},
editableText: {
tableDescLength: 750,
columnDescLength: 250,
},
google: {
enabled: false,
key: 'default-key',
sampleRate: 100,
},
indexUsers: {
enabled: false,
},
logoPath: null,
navLinks: [
{
......@@ -40,9 +47,6 @@ const configDefault: AppConfig = {
return `https://DEFAULT_EXPLORE_URL?schema=${schema}&cluster=${cluster}&db=${database}&table=${table}`;
}
},
indexUsers: {
enabled: false,
}
};
export default configDefault;
......@@ -6,22 +6,24 @@
export interface AppConfig {
browse: BrowseConfig;
editableText: EditableTextConfig;
google: GoogleAnalyticsConfig;
indexUsers: IndexUsersConfig;
logoPath: string | null;
navLinks: Array<LinkConfig>;
tableLineage: TableLineageConfig;
tableProfile: TableProfileConfig;
indexUsers: indexUsersConfig;
}
export interface AppConfigCustom {
browse?: BrowseConfig;
editableText?: EditableTextConfig;
google?: GoogleAnalyticsConfig
indexUsers?: IndexUsersConfig;
logoPath?: string;
navLinks?: Array<LinkConfig>;
tableLineage?: TableLineageConfig;
tableProfile?: TableProfileConfig;
indexUsers?: indexUsersConfig;
}
/**
......@@ -83,6 +85,23 @@ export interface LinkConfig {
use_router: boolean;
}
interface indexUsersConfig {
/**
* IndexUsersConfig - When enabled, the IndexUsers feature will index users as searchable resources. This requires
* user objects are ingested via Databuilder
*
* enabled - Enables/disables this feature in the frontend only
*/
interface IndexUsersConfig {
enabled: boolean;
}
/**
* EditableTextConfig - Configure max length limits for editable fields
*
* tableDescLength - maxlength for table descriptions
* columnDescLength - maxlength for column descriptions
*/
interface EditableTextConfig {
tableDescLength: number;
columnDescLength: number;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment