Unverified Commit e7ae9cd4 authored by Tamika Tannis's avatar Tamika Tannis Committed by GitHub

Update design for uneditable descriptions (#449)

* Move EditableSection to common

* Update EditableSection

* Code cleanup

* Fix merge
parent 9c0edff0
...@@ -87,7 +87,7 @@ ...@@ -87,7 +87,7 @@
background-color: transparent; background-color: transparent;
color: $text-secondary; color: $text-secondary;
box-shadow: none !important; box-shadow: none !important;
padding: 0px; padding: 0;
text-align: left; text-align: left;
&:focus, &:focus,
...@@ -102,6 +102,26 @@ ...@@ -102,6 +102,26 @@
} }
} }
&.btn-flat-icon-dark {
border: none;
background-color: transparent;
color: $text-secondary;
box-shadow: none !important;
padding: 0;
text-align: left;
&:focus,
&:not(.disabled):hover,
&:not([disabled]):hover {
background-color: transparent;
color: $text-primary;
.icon {
background-color: $text-primary;
}
}
}
&.btn-close { &.btn-close {
height: 18px; height: 18px;
width: 18px; width: 18px;
......
...@@ -5,6 +5,7 @@ export const DASHBOARD_OWNER_SOURCE = "dashboard_page_owner" ...@@ -5,6 +5,7 @@ export const DASHBOARD_OWNER_SOURCE = "dashboard_page_owner"
export const NO_OWNER_TEXT = 'No owner'; export const NO_OWNER_TEXT = 'No owner';
export const ADD_DESC_TEXT = 'Add Description in'; export const ADD_DESC_TEXT = 'Add Description in';
export const EDIT_DESC_TEXT = 'Click to edit description in';
export const LAST_RUN_SUCCEEDED = 'succeeded'; export const LAST_RUN_SUCCEEDED = 'succeeded';
export const LAST_RUN_FAILED = 'failed'; export const LAST_RUN_FAILED = 'failed';
...@@ -9,6 +9,7 @@ import AvatarLabel from 'components/common/AvatarLabel'; ...@@ -9,6 +9,7 @@ import AvatarLabel from 'components/common/AvatarLabel';
import Breadcrumb from 'components/common/Breadcrumb'; import Breadcrumb from 'components/common/Breadcrumb';
import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon'; import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
import Flag from 'components/common/Flag'; import Flag from 'components/common/Flag';
import EditableSection from 'components/common/EditableSection';
import LoadingSpinner from 'components/common/LoadingSpinner'; import LoadingSpinner from 'components/common/LoadingSpinner';
import TabsComponent from 'components/common/TabsComponent'; import TabsComponent from 'components/common/TabsComponent';
import { getDashboard } from 'ducks/dashboard/reducer'; import { getDashboard } from 'ducks/dashboard/reducer';
...@@ -23,6 +24,7 @@ import { formatDateTimeShort } from '../../utils/dateUtils'; ...@@ -23,6 +24,7 @@ import { formatDateTimeShort } from '../../utils/dateUtils';
import ResourceList from 'components/common/ResourceList'; import ResourceList from 'components/common/ResourceList';
import { import {
ADD_DESC_TEXT, ADD_DESC_TEXT,
EDIT_DESC_TEXT,
DASHBOARD_OWNER_SOURCE, DASHBOARD_OWNER_SOURCE,
DASHBOARD_SOURCE, DASHBOARD_SOURCE,
LAST_RUN_SUCCEEDED, LAST_RUN_SUCCEEDED,
...@@ -30,7 +32,6 @@ import { ...@@ -30,7 +32,6 @@ import {
TABLES_PER_PAGE TABLES_PER_PAGE
} from 'components/DashboardPage/constants'; } from 'components/DashboardPage/constants';
import TagInput from 'components/Tags/TagInput'; import TagInput from 'components/Tags/TagInput';
import { EditableSection } from 'components/TableDetail/EditableSection';
import { ResourceType } from 'interfaces'; import { ResourceType } from 'interfaces';
import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils'; import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils';
...@@ -158,7 +159,11 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard ...@@ -158,7 +159,11 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard
<a id="dashboard-group-link" <a id="dashboard-group-link"
onClick={ logClick } onClick={ logClick }
href={ dashboard.group_url } href={ dashboard.group_url }
target="_blank">{ dashboard.group_name }</a> target="_blank"
rel="noopener noreferrer"
>
{ dashboard.group_name }
</a>
</div> </div>
</div> </div>
{/* <div className="header-section header-links">links here</div> */} {/* <div className="header-section header-links">links here</div> */}
...@@ -167,28 +172,39 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard ...@@ -167,28 +172,39 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard
target="_blank" target="_blank"
href={ dashboard.url } href={ dashboard.url }
onClick={ logClick } onClick={ logClick }
className="btn btn-default btn-lg">Open Dashboard</a> className="btn btn-default btn-lg"
rel="noopener noreferrer"
>
Open Dashboard
</a>
</div> </div>
</header> </header>
<article className="column-layout-1"> <article className="column-layout-1">
<section className="left-panel"> <section className="left-panel">
<div className="section-title title-3">Description</div> <EditableSection
{ title="Description"
hasDescription && readOnly={ true }
<div> editUrl={ dashboard.url }
{ dashboard.description } editText={`${EDIT_DESC_TEXT} ${getSourceDisplayName(dashboard.product, ResourceType.dashboard)}`}
</div> >
} {
{ hasDescription &&
!hasDescription && <div>
<a { dashboard.description }
className="edit-link body-2" </div>
target="_blank" }
href={ dashboard.url } {
> !hasDescription &&
{`${ADD_DESC_TEXT} ${getSourceDisplayName(dashboard.product, ResourceType.dashboard)}`} <a
</a> className="edit-link body-2"
} target="_blank"
href={ dashboard.url }
rel="noopener noreferrer"
>
{`${ADD_DESC_TEXT} ${getSourceDisplayName(dashboard.product, ResourceType.dashboard)}`}
</a>
}
</EditableSection>
<section className="column-layout-2"> <section className="column-layout-2">
<section className="left-panel"> <section className="left-panel">
<div className="section-title title-3">Owners</div> <div className="section-title title-3">Owners</div>
......
...@@ -12,7 +12,7 @@ import { logClick } from 'ducks/utilMethods'; ...@@ -12,7 +12,7 @@ import { logClick } from 'ducks/utilMethods';
import { RequestMetadataType, TableColumn } from 'interfaces'; import { RequestMetadataType, TableColumn } from 'interfaces';
import './styles.scss'; import './styles.scss';
import { EditableSection } from 'components/TableDetail/EditableSection'; import EditableSection from 'components/common/EditableSection';
interface DispatchFromProps { interface DispatchFromProps {
openRequestDescriptionDialog: (requestMetadataType: RequestMetadataType, columnName: string) => OpenRequestAction; openRequestDescriptionDialog: (requestMetadataType: RequestMetadataType, columnName: string) => OpenRequestAction;
......
...@@ -16,7 +16,7 @@ const DEFAULT_ERROR_TEXT = 'There was a problem with the request, please reload ...@@ -16,7 +16,7 @@ const DEFAULT_ERROR_TEXT = 'There was a problem with the request, please reload
import { GlobalState } from 'ducks/rootReducer'; import { GlobalState } from 'ducks/rootReducer';
import { updateTableOwner } from 'ducks/tableMetadata/owners/reducer'; import { updateTableOwner } from 'ducks/tableMetadata/owners/reducer';
import { EditableSectionChildProps } from 'components/TableDetail/EditableSection'; import { EditableSectionChildProps } from 'components/common/EditableSection';
import { logClick } from 'ducks/utilMethods'; import { logClick } from 'ducks/utilMethods';
export interface DispatchFromProps { export interface DispatchFromProps {
......
...@@ -30,7 +30,7 @@ import WriterLink from 'components/TableDetail/WriterLink'; ...@@ -30,7 +30,7 @@ import WriterLink from 'components/TableDetail/WriterLink';
import TagInput from 'components/Tags/TagInput'; import TagInput from 'components/Tags/TagInput';
import { ResourceType, TableMetadata} from 'interfaces'; import { ResourceType, TableMetadata} from 'interfaces';
import { EditableSection } from 'components/TableDetail/EditableSection'; import EditableSection from 'components/common/EditableSection';
import { getSourceIconClass, issueTrackingEnabled, notificationsEnabled } from 'config/config-utils'; import { getSourceIconClass, issueTrackingEnabled, notificationsEnabled } from 'config/config-utils';
......
...@@ -10,7 +10,7 @@ import { getAllTags, updateTags} from 'ducks/tags/reducer'; ...@@ -10,7 +10,7 @@ import { getAllTags, updateTags} from 'ducks/tags/reducer';
import { GetAllTagsRequest, UpdateTagsRequest} from 'ducks/tags/types'; import { GetAllTagsRequest, UpdateTagsRequest} from 'ducks/tags/types';
import TagInfo from "../TagInfo"; import TagInfo from "../TagInfo";
import { EditableSectionChildProps } from 'components/TableDetail/EditableSection'; import { EditableSectionChildProps } from 'components/common/EditableSection';
import { ResourceType, Tag, UpdateMethod, UpdateTagData } from 'interfaces'; import { ResourceType, Tag, UpdateMethod, UpdateTagData } from 'interfaces';
// TODO: Use css-modules instead of 'import' // TODO: Use css-modules instead of 'import'
import './styles.scss'; import './styles.scss';
......
import * as React from 'react'; import * as React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { EditableSection, EditableSectionProps } from '.'; import { OverlayTrigger, Popover } from 'react-bootstrap';
import EditableSection, { EditableSectionProps } from '.';
import TagInput from 'components/Tags/TagInput'; import TagInput from 'components/Tags/TagInput';
import { ResourceType } from 'interfaces/Resources'; import { ResourceType } from 'interfaces/Resources';
describe("EditableSection", () => { describe("EditableSection", () => {
const setup = (propOverrides?: Partial<EditableSectionProps>, children?) => { const setup = (propOverrides?: Partial<EditableSectionProps>, children?) => {
const props = { const props = {
...@@ -72,13 +73,22 @@ describe("EditableSection", () => { ...@@ -72,13 +73,22 @@ describe("EditableSection", () => {
expect(wrapper.childAt(1).text()).toBe(child); expect(wrapper.childAt(1).text()).toBe(child);
}); });
it("renders button when readOnly=false", () => { it("renders edit button correctly when readOnly=false", () => {
expect(wrapper.find(".edit-button").length).toEqual(1); expect(wrapper.find(".edit-button").props().onClick).toBe(wrapper.instance().toggleEdit);
}); });
it("renders does not add button when readOnly=true", () => { describe("renders edit link correctly when readOnly=true", () => {
const { wrapper } = setup({readOnly: true}, <TagInput resourceType={ResourceType.table} uriKey={"key"}/>); let props;
expect(wrapper.find(".edit-button").length).toEqual(0); let wrapper;
beforeAll(() => {
const setupResult = setup({ readOnly: true, editUrl: 'test', editText: 'hello' }, <div/>);
props = setupResult.props;
wrapper = setupResult.wrapper;
});
it("link links to editUrl", () => {
expect(wrapper.find(".edit-button").props().href).toBe(props.editUrl);
});
}); });
}); });
}); });
import * as React from 'react'; import * as React from 'react';
import { OverlayTrigger, Popover } from 'react-bootstrap';
import './styles.scss'; import './styles.scss';
export interface EditableSectionProps { export interface EditableSectionProps {
title: string; title: string;
readOnly?: boolean; readOnly?: boolean;
/* Should be used when readOnly=true to prompt users with a relevant explanation for the given use case */
editText?: string;
/* Should be used when readOnly=true to link to the source where users can edit the given metadata */
editUrl?: string;
} }
interface EditableSectionState { interface EditableSectionState {
...@@ -37,8 +42,33 @@ export class EditableSection extends React.Component<EditableSectionProps, Edita ...@@ -37,8 +42,33 @@ export class EditableSection extends React.Component<EditableSectionProps, Edita
return str.split(new RegExp('[\\s+_]')).map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()).join(" "); return str.split(new RegExp('[\\s+_]')).map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()).join(" ");
} }
renderButton = (): React.ReactNode => {
return (
<button className={`btn btn-flat-icon edit-button ${(this.state.isEditing ? 'active': '')}`} onClick={ this.toggleEdit }>
<img className={`icon icon-small icon-edit ${(this.state.isEditing ? 'icon-color' : '')}`} />
</button>
);
};
renderReadOnlyButton = (): React.ReactNode => {
const { editText, editUrl } = this.props;
const popoverHoverFocus = (<Popover id="popover-trigger-hover-focus">{ editText }</Popover>);
return (
<OverlayTrigger
trigger={["hover", "focus"]}
placement="top"
overlay={popoverHoverFocus}
>
<a className="btn btn-flat-icon-dark edit-button" href={ editUrl } target="_blank" rel="noopener noreferrer">
<img className="icon icon-small icon-edit" />
</a>
</OverlayTrigger>
);
};
render() { render() {
const childrenWithProps = React.Children.map(this.props.children, child => { const { title, readOnly = false } = this.props;
const childrenWithProps = !readOnly ? React.Children.map(this.props.children, child => {
if (!React.isValidElement(child)) { if (!React.isValidElement(child)) {
return child; return child;
} }
...@@ -46,21 +76,18 @@ export class EditableSection extends React.Component<EditableSectionProps, Edita ...@@ -46,21 +76,18 @@ export class EditableSection extends React.Component<EditableSectionProps, Edita
isEditing: this.state.isEditing, isEditing: this.state.isEditing,
setEditMode: this.setEditMode, setEditMode: this.setEditMode,
}); });
}); }) : this.props.children;
return ( return (
<section className="editable-section"> <section className="editable-section">
<div className="section-title title-3"> <div className="section-title title-3">
{ EditableSection.convertText(this.props.title) } { EditableSection.convertText(title) }
{ { !readOnly ? this.renderButton() : this.renderReadOnlyButton() }
!this.props.readOnly &&
<button className={"btn btn-flat-icon edit-button" + (this.state.isEditing? " active": "")} onClick={ this.toggleEdit }>
<img className={"icon icon-small icon-edit" + (this.state.isEditing? " icon-color" : "")} />
</button>
}
</div> </div>
{ childrenWithProps } { childrenWithProps }
</section> </section>
); );
} }
} }
export default EditableSection;
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
.edit-button { .edit-button {
opacity: 0; opacity: 0;
margin-left: 4px; margin-left: 4px;
img {
margin-bottom: auto;
}
&.active { &.active {
opacity: 1; opacity: 1;
} }
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
REFRESH_MESSAGE, REFRESH_MESSAGE,
UPDATE_BUTTON_TEXT UPDATE_BUTTON_TEXT
} from './constants'; } from './constants';
import { EditableSectionChildProps } from 'components/TableDetail/EditableSection'; import { EditableSectionChildProps } from 'components/common/EditableSection';
export interface StateFromProps { export interface StateFromProps {
refreshValue?: string; refreshValue?: string;
......
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