Unverified Commit 79135345 authored by Daniel's avatar Daniel Committed by GitHub

Add badges to table details (#356)

- Added badge support for tables
- Added BadgeConfig to customize display names/styles of defined badges
parent 57f05b10
......@@ -36,6 +36,7 @@ def marshall_table_full(table: Dict) -> Dict:
"""
# Filter and parse the response dictionary from the metadata service
fields = [
'badges',
'columns',
'cluster',
'database',
......
......@@ -10,6 +10,7 @@ import { getTableData } from 'ducks/tableMetadata/reducer';
import { GetTableDataRequest } from 'ducks/tableMetadata/types';
import AppConfig from 'config/config';
import BadgeList from 'components/common/BadgeList';
import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
import Breadcrumb from 'components/common/Breadcrumb';
import DataPreviewButton from 'components/TableDetail/DataPreviewButton';
......@@ -28,11 +29,10 @@ import TagInput from 'components/Tags/TagInput';
import { TableMetadata } from 'interfaces/TableMetadata';
import { EditableSection } from 'components/TableDetail/EditableSection';
import RequestDescriptionText from './RequestDescriptionText';
import { getDatabaseDisplayName, getDatabaseIconClass, notificationsEnabled } from 'config/config-utils';
import './styles';
import RequestDescriptionText from './RequestDescriptionText';
import RequestMetadataForm from './RequestMetadataForm';
export interface StateFromProps {
......@@ -124,8 +124,14 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps
{ getDatabaseDisplayName(data.database) }
&nbsp;&bull;&nbsp;
{ data.cluster }
&nbsp;
{
data.badges.length > 0 &&
<BadgeList badges={ data.badges } />
}
{
data.is_view && <Flag text="Table View" labelStyle="primary"/>
data.is_view &&
<Flag text="table view" labelStyle="warning"/>
}
</div>
</div>
......
import * as React from 'react';
import Flag from 'components/common/Flag';
import { getBadgeConfig } from 'config/config-utils';
import { Badge } from 'interfaces/Tags';
export interface BadgeListProps {
badges: Badge[];
}
const BadgeList: React.SFC<BadgeListProps> = ({ badges }) => {
return (
<span className="badge-list">
{
badges.map((badge, index) => {
const badgeConfig = getBadgeConfig(badge.tag_name);
return <Flag text={ badgeConfig.displayName }
labelStyle={ badgeConfig.style }
key={`badge-${index}`}/>;
})
}
</span>
);
};
export default BadgeList;
import * as React from 'react';
import { shallow } from 'enzyme';
import BadgeList from '../'
import Flag from 'components/common/Flag';
import { BadgeStyle } from 'config/config-types';
import * as ConfigUtils from 'config/config-utils';
import { Badge, TagType } from 'interfaces/Tags';
describe('BadgeList', () => {
const getBadgeConfigSpy = jest.spyOn(ConfigUtils, 'getBadgeConfig');
getBadgeConfigSpy.mockImplementation((badgeName: string) => {
return {
displayName: badgeName + " test name",
style: BadgeStyle.PRIMARY,
};
});
describe('BadgeList function component', () => {
const badges: Badge[] = [
{
tag_name: 'test_1',
tag_type: TagType.BADGE,
},
{
tag_name: 'test_3',
tag_type: TagType.BADGE,
},
];
const badgeList = shallow(<BadgeList badges={ badges } />);
it('renders a badge-list element', () => {
const container = badgeList.find('.badge-list')
expect(container.exists()).toBe(true);
});
it('renders a <Flag> for each badge in the input', () => {
expect(badgeList.find(Flag).length).toEqual(badges.length);
});
it('passes the correct props to the flag', () => {
badges.forEach((badge, index) => {
const flag = badgeList.childAt(index);
const flagProps = flag.props();
const badgeConfig = ConfigUtils.getBadgeConfig(badge.tag_name);
expect(flagProps.text).toEqual(badgeConfig.displayName);
expect(flagProps.labelStyle).toEqual(badgeConfig.style);
});
});
});
});
......@@ -3,7 +3,7 @@
.flag {
height: 20px;
display: inline-block;
margin: 0px 8px;
margin: 0 4px;
font-size: 14px;
border-radius: 5px;
}
......
import { AppConfig } from './config-types';
const configDefault: AppConfig = {
badges: {},
browse: {
curatedTags: [],
showAllTags: true,
......
......@@ -5,6 +5,7 @@
*/
export interface AppConfig {
badges: BadgeConfig;
browse: BrowseConfig;
editableText: EditableTextConfig;
google: GoogleAnalyticsConfig;
......@@ -18,6 +19,7 @@ export interface AppConfig {
}
export interface AppConfigCustom {
badges?: BadgeConfig;
browse?: BrowseConfig;
editableText?: EditableTextConfig;
google?: GoogleAnalyticsConfig
......@@ -52,6 +54,29 @@ interface BrowseConfig {
showAllTags: boolean;
}
export enum BadgeStyle {
DANGER = "danger",
DEFAULT = "default",
INFO = "info",
PRIMARY = "primary",
SUCCESS = "success",
WARNING = "warning",
}
export interface BadgeStyleConfig {
style: BadgeStyle;
displayName?: string;
}
/**
* BadgeConfig - Configure badge colors
*
* An object that maps badges to BadgeStyleConfigs
*/
interface BadgeConfig {
[badge: string]: BadgeStyleConfig;
}
/** ResourceConfig - For customizing values related to how various resources
* are displayed in the UI.
*
......
import AppConfig from 'config/config';
import { BadgeStyleConfig, BadgeStyle } from 'config/config-types';
export const DEFAULT_DATABASE_ICON_CLASS = 'icon-database icon-color';
......@@ -31,6 +32,21 @@ export function getDatabaseIconClass(databaseId: string): string {
return databaseConfig.iconClass;
}
/**
* Given a badge name, this will return a badge style and a display name.
* If these are not specified by config, it will default to some simple rules:
* use BadgeStyle.DEFAULT and replace '-' and '_' with spaces for display name.
*/
export function getBadgeConfig(badgeName: string): BadgeStyleConfig {
const config = AppConfig.badges[badgeName] || {};
return {
style: BadgeStyle.DEFAULT,
displayName: badgeName.replace(/[-_]/g, ' '),
...config,
};
}
/**
* Returns whether or not feedback features should be enabled
*/
......
import AppConfig from 'config/config';
import * as ConfigUtils from 'config/config-utils';
import { BadgeStyle } from 'config/config-types';
describe('getDatabaseDisplayName', () => {
it('returns given id if no config for that id exists', () => {
......@@ -27,6 +28,33 @@ describe('getDatabaseIconClass', () => {
})
});
describe('getBadgeConfig', () => {
AppConfig.badges = {
'test_1': {
style: BadgeStyle.DANGER,
displayName: 'badge display value 1',
},
'test_2': {
style: BadgeStyle.DANGER,
displayName: 'badge display value 2',
}
};
it('Returns the badge config for a given badge', () => {
const config = ConfigUtils.getBadgeConfig('test_1');
const expectedConfig = AppConfig.badges['test_1'];
expect(config.style).toEqual(expectedConfig.style);
expect(config.displayName).toEqual(expectedConfig.displayName);
});
it('Returns default badge config for unspecified badges', () => {
const badgeName = 'not_configured_badge';
const badgeConfig = ConfigUtils.getBadgeConfig(badgeName);
expect(badgeConfig.style).toEqual(BadgeStyle.DEFAULT);
expect(badgeConfig.displayName).toEqual('not configured badge');
});
});
describe('feedbackEnabled', () => {
it('returns whether or not the feaadback feature is enabled', () => {
expect(ConfigUtils.feedbackEnabled()).toBe(AppConfig.mailClientFeatures.feedbackEnabled);
......
......@@ -179,6 +179,7 @@ export const initialPreviewState = {
};
export const initialTableDataState: TableMetadata = {
badges: [],
cluster: '',
columns: [],
database: '',
......
......@@ -109,6 +109,7 @@ const globalState: GlobalState = {
},
statusCode: null,
tableData: {
badges: [],
cluster: '',
columns: [],
database: '',
......
import { UpdateMethod } from './Enums';
import { User } from './User';
import { Badge } from 'interfaces/Tags';
interface PartitionData {
is_partitioned: boolean;
......@@ -68,6 +69,7 @@ export interface TableOwners {
}
export interface TableMetadata {
badges: Badge[];
cluster: string;
columns: TableColumn[];
database: string;
......
......@@ -5,8 +5,18 @@ export interface UpdateTagData {
tagName: string;
}
export enum TagType {
TAG = 'default',
BADGE = 'badge',
}
export interface Tag {
tag_count: number;
tag_count?: number;
tag_name: string;
tag_type?: TagType.TAG;
}
export interface Badge {
tag_name: string;
tag_type?: string;
tag_type: TagType.BADGE;
}
......@@ -4,6 +4,11 @@ This document describes how to leverage the frontend service's application confi
**NOTE: This document is a work in progress and does not include 100% of features. We welcome PRs to complete this document**
## Badge Config
Badges are a special type of tag that cannot be edited through the UI.
`BadgeConfig` can be used to customize the text and color of badges. This config defines a mapping of badge name to a `BadgeStyle` and optional `displayName`. Badges that are not defined will default to use the `BadgeStyle.default` style and `displayName` use the badge name with any `_` or `-` characters replaced with a space.
## Browse Tags Feature
_TODO: Please add doc_
......
......@@ -78,6 +78,7 @@ class MetadataTest(unittest.TestCase):
}
self.expected_parsed_metadata = {
'key': 'table_key',
'badges': [],
'cluster': 'test_cluster',
'database': 'test_db',
'schema': 'test_schema',
......
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