Unverified Commit a47078c2 authored by Marcos Iglesias's avatar Marcos Iglesias Committed by GitHub

Fixes DataSource or Dashboard Icons not rendering on Firefox (#443)

* Updates resource icons with background image approach

* Adds a main landmark to the application

* Adds labels to filter inputs

* Adds language to the html tag and some other missing meta tags (to complete)

* Update styling on Dashboard and Data Store detail pages

* De-duplicates main landmark tag

* DRYs up the creation of icon classes by using Sass maps and @each

* Updating Table Detail landmark tag
parent a14c4dde
@import 'variables'; @import "variables";
$icon-size: 24px;
$icon-small-size: 16px;
// Map of Database names and icon paths
$data-stores: (
database: "/static/images/icons/Database.svg", // Default
hive: "/static/images/icons/logo-hive.svg",
bigquery: "/static/images/icons/logo-bigquery.svg",
druid: "/static/images/icons/logo-druid.svg",
presto: "/static/images/icons/logo-presto.svg",
postgres: "/static/images/icons/logo-postgres.svg",
redshift: "/static/images/icons/logo-redshift.svg"
);
// Map of Dashboard names and icon paths
$dashboards: (
dashboard: "/static/images/icons/dashboard.svg", //Default
mode: "/static/images/icons/logo-mode.svg"
);
// Given a Map of key/value pairs, generates a new class
@mixin iconBackgrounds($map){
@each $name, $url in $map {
&.icon-#{$name} {
background: transparent url($url) center center / contain no-repeat;
}
}
}
span.icon {
background-color: $icon-bg;
border: none;
height: $icon-size;
margin: auto 16px auto 0;
min-width: $icon-size;
width: $icon-size;
display: inline-block;
vertical-align: middle;
// Generate Icons
@include iconBackgrounds($data-stores);
@include iconBackgrounds($dashboards);
}
img.icon { img.icon {
background-color: $icon-bg; background-color: $icon-bg;
border: none; border: none;
height: 24px; height: $icon-size;
margin: -3px 4px -3px 0px; margin: -3px 4px -3px 0;
-webkit-mask-repeat: no-repeat; -webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat; mask-repeat: no-repeat;
min-width: 24px; min-width: $icon-size;
width: 24px; width: $icon-size;
-webkit-mask-size: contain; -webkit-mask-size: contain;
mask-size: contain; mask-size: contain;
&.icon-small { &.icon-small {
height: 16px; height: $icon-small-size;
width: 16px; width: $icon-small-size;
min-width: 16px; min-width: $icon-small-size;
-webkit-mask-size: 16px 16px; -webkit-mask-size: $icon-small-size $icon-small-size;
mask-size: 16px 16px; mask-size: $icon-small-size $icon-small-size;
} }
&.icon-color { &.icon-color {
...@@ -29,163 +73,118 @@ img.icon { ...@@ -29,163 +73,118 @@ img.icon {
} }
&.icon-alert { &.icon-alert {
-webkit-mask-image: url('/static/images/icons/Alert-Triangle.svg'); -webkit-mask-image: url("/static/images/icons/Alert-Triangle.svg");
mask-image: url('/static/images/icons/Alert-Triangle.svg'); mask-image: url("/static/images/icons/Alert-Triangle.svg");
} }
&.icon-bigquery { &.icon-bookmark {
content: url('/static/images/icons/logo-bigquery.svg'); -webkit-mask-image: url("/static/images/icons/Favorite.svg");
background-color: transparent; mask-image: url("/static/images/icons/Favorite.svg");
} }
&.icon-bookmark { &.icon-bookmark-filled {
-webkit-mask-image: url('/static/images/icons/Favorite.svg'); -webkit-mask-image: url("/static/images/icons/Favorite-Filled.svg");
mask-image: url('/static/images/icons/Favorite.svg'); mask-image: url("/static/images/icons/Favorite-Filled.svg");
}
&.icon-bookmark-filled {
-webkit-mask-image: url('/static/images/icons/Favorite-Filled.svg');
mask-image: url('/static/images/icons/Favorite-Filled.svg');
} }
&.icon-delete { &.icon-delete {
-webkit-mask-image: url('/static/images/icons/Trash.svg'); -webkit-mask-image: url("/static/images/icons/Trash.svg");
mask-image: url('/static/images/icons/Trash.svg'); mask-image: url("/static/images/icons/Trash.svg");
}
&.icon-database {
-webkit-mask-image: url('/static/images/icons/Database.svg');
mask-image: url('/static/images/icons/Database.svg');
} }
&.icon-red-triangle-warning { &.icon-red-triangle-warning {
-webkit-mask-image: url('/static/images/icons/DataQualityWarning.svg'); -webkit-mask-image: url("/static/images/icons/DataQualityWarning.svg");
mask-image: url('/static/images/icons/DataQualityWarning.svg'); mask-image: url("/static/images/icons/DataQualityWarning.svg");
}
&.icon-dashboard {
-webkit-mask-image: url('/static/images/icons/dashboard.svg');
mask-image: url('/static/images/icons/dashboard.svg');
} }
&.icon-down { &.icon-down {
-webkit-mask-image: url('/static/images/icons/Down.svg'); -webkit-mask-image: url("/static/images/icons/Down.svg");
mask-image: url('/static/images/icons/Down.svg'); mask-image: url("/static/images/icons/Down.svg");
} }
&.icon-edit { &.icon-edit {
-webkit-mask-image: url('/static/images/icons/Edit.svg'); -webkit-mask-image: url("/static/images/icons/Edit.svg");
mask-image: url('/static/images/icons/Edit.svg'); mask-image: url("/static/images/icons/Edit.svg");
} }
&.icon-help { &.icon-help {
-webkit-mask-image: url('/static/images/icons/Help-Circle.svg'); -webkit-mask-image: url("/static/images/icons/Help-Circle.svg");
mask-image: url('/static/images/icons/Help-Circle.svg'); mask-image: url("/static/images/icons/Help-Circle.svg");
}
&.icon-hive {
content: url('/static/images/icons/logo-hive.svg');
background-color: transparent;
}
&.icon-druid {
content: url('/static/images/icons/logo-druid.svg');
background-color: transparent;
} }
&.icon-github { &.icon-github {
-webkit-mask-image: url('/static/images/icons/github.svg'); -webkit-mask-image: url("/static/images/icons/github.svg");
mask-image: url('/static/images/icons/github.svg'); mask-image: url("/static/images/icons/github.svg");
} }
&.icon-left { &.icon-left {
-webkit-mask-image: url('/static/images/icons/Left.svg'); -webkit-mask-image: url("/static/images/icons/Left.svg");
mask-image: url('/static/images/icons/Left.svg'); mask-image: url("/static/images/icons/Left.svg");
} }
&.icon-loading { &.icon-loading {
-webkit-mask-image: url('/static/images/icons/Loader.svg'); -webkit-mask-image: url("/static/images/icons/Loader.svg");
mask-image: url('/static/images/icons/Loader.svg'); mask-image: url("/static/images/icons/Loader.svg");
} }
&.icon-mail { &.icon-mail {
-webkit-mask-image: url('/static/images/icons/mail.svg'); -webkit-mask-image: url("/static/images/icons/mail.svg");
mask-image: url('/static/images/icons/mail.svg'); mask-image: url("/static/images/icons/mail.svg");
}
&.icon-mode {
content: url('/static/images/icons/logo-mode.svg');
background-color: transparent;
} }
&.icon-plus { &.icon-plus {
-webkit-mask-image: url('/static/images/icons/plus.svg'); -webkit-mask-image: url("/static/images/icons/plus.svg");
mask-image: url('/static/images/icons/plus.svg'); mask-image: url("/static/images/icons/plus.svg");
} }
&.icon-plus-circle { &.icon-plus-circle {
-webkit-mask-image: url('/static/images/icons/Plus-Circle.svg'); -webkit-mask-image: url("/static/images/icons/Plus-Circle.svg");
mask-image: url('/static/images/icons/Plus-Circle.svg'); mask-image: url("/static/images/icons/Plus-Circle.svg");
}
&.icon-postgres {
content: url('/static/images/icons/logo-postgres.svg');
background-color: transparent;
}
&.icon-presto {
content: url('/static/images/icons/logo-presto.svg');
background-color: transparent;
} }
&.icon-preview { &.icon-preview {
-webkit-mask-image: url('/static/images/icons/Preview.svg'); -webkit-mask-image: url("/static/images/icons/Preview.svg");
mask-image: url('/static/images/icons/Preview.svg'); mask-image: url("/static/images/icons/Preview.svg");
}
&.icon-redshift {
content: url('/static/images/icons/logo-redshift.svg');
background-color: transparent;
} }
&.icon-refresh { &.icon-refresh {
-webkit-mask-image: url('/static/images/icons/Refresh-cw.svg'); -webkit-mask-image: url("/static/images/icons/Refresh-cw.svg");
mask-image: url('/static/images/icons/Refresh-cw.svg'); mask-image: url("/static/images/icons/Refresh-cw.svg");
} }
&.icon-right { &.icon-right {
-webkit-mask-image: url('/static/images/icons/Right.svg'); -webkit-mask-image: url("/static/images/icons/Right.svg");
mask-image: url('/static/images/icons/Right.svg'); mask-image: url("/static/images/icons/Right.svg");
} }
&.icon-search { &.icon-search {
-webkit-mask-image: url('/static/images/icons/Search.svg'); -webkit-mask-image: url("/static/images/icons/Search.svg");
mask-image: url('/static/images/icons/Search.svg'); mask-image: url("/static/images/icons/Search.svg");
} }
&.icon-send { &.icon-send {
-webkit-mask-image: url('/static/images/icons/Send.svg'); -webkit-mask-image: url("/static/images/icons/Send.svg");
mask-image: url('/static/images/icons/Send.svg'); mask-image: url("/static/images/icons/Send.svg");
} }
&.icon-slack { &.icon-slack {
-webkit-mask-image: url('/static/images/icons/slack.svg'); -webkit-mask-image: url("/static/images/icons/slack.svg");
mask-image: url('/static/images/icons/slack.svg'); mask-image: url("/static/images/icons/slack.svg");
} }
&.icon-up { &.icon-up {
-webkit-mask-image: url('/static/images/icons/Up.svg'); -webkit-mask-image: url("/static/images/icons/Up.svg");
mask-image: url('/static/images/icons/Up.svg'); mask-image: url("/static/images/icons/Up.svg");
} }
&.icon-users { &.icon-users {
-webkit-mask-image: url('/static/images/icons/users.svg'); -webkit-mask-image: url("/static/images/icons/users.svg");
mask-image: url('/static/images/icons/users.svg'); mask-image: url("/static/images/icons/users.svg");
} }
&.icon-more { &.icon-more {
-webkit-mask-image: url('/static/images/icons/More.svg'); -webkit-mask-image: url("/static/images/icons/More.svg");
mask-image: url('/static/images/icons/More.svg'); mask-image: url("/static/images/icons/More.svg");
} }
} }
......
...@@ -145,7 +145,7 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard ...@@ -145,7 +145,7 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard
<header className="resource-header"> <header className="resource-header">
<div className="header-section"> <div className="header-section">
<Breadcrumb /> <Breadcrumb />
<img className={`icon icon-header ${getSourceIconClass(dashboard.product, ResourceType.dashboard)}`}/> <span className={`icon icon-header ${getSourceIconClass(dashboard.product, ResourceType.dashboard)}`}/>
</div> </div>
<div className="header-section header-title"> <div className="header-section header-title">
<h3 className="header-title-text truncated"> <h3 className="header-title-text truncated">
...@@ -171,7 +171,7 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard ...@@ -171,7 +171,7 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard
className="btn btn-default btn-lg">Open Dashboard</a> className="btn btn-default btn-lg">Open Dashboard</a>
</div> </div>
</header> </header>
<main 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> <div className="section-title title-3">Description</div>
{ {
...@@ -256,7 +256,7 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard ...@@ -256,7 +256,7 @@ export class DashboardPage extends React.Component<DashboardPageProps, Dashboard
<section className="right-panel"> <section className="right-panel">
{ this.renderTabs() } { this.renderTabs() }
</section> </section>
</main> </article>
</div> </div>
); );
} }
......
...@@ -44,6 +44,7 @@ export class FilterSection extends React.Component<FilterSectionProps> { ...@@ -44,6 +44,7 @@ export class FilterSection extends React.Component<FilterSectionProps> {
renderFilterComponent = () => { renderFilterComponent = () => {
const { categoryId, options, type } = this.props; const { categoryId, options, type } = this.props;
if (type === FilterType.INPUT_SELECT) { if (type === FilterType.INPUT_SELECT) {
return ( return (
<InputFilter <InputFilter
...@@ -63,11 +64,12 @@ export class FilterSection extends React.Component<FilterSectionProps> { ...@@ -63,11 +64,12 @@ export class FilterSection extends React.Component<FilterSectionProps> {
render = () => { render = () => {
const { categoryId, hasValue, helpText, title } = this.props; const { categoryId, hasValue, helpText, title } = this.props;
return ( return (
<div className="search-filter-section"> <div className="search-filter-section">
<div className="search-filter-section-header"> <div className="search-filter-section-header">
<div className="search-filter-section-title"> <div className="search-filter-section-title">
<div className="title-2">{ title }</div> <label className="title-2" htmlFor={categoryId}>{ title }</label>
{ {
helpText && helpText &&
<InfoButton <InfoButton
......
...@@ -64,6 +64,7 @@ export class InputFilter extends React.Component<InputFilterProps, InputFilterSt ...@@ -64,6 +64,7 @@ export class InputFilter extends React.Component<InputFilterProps, InputFilterSt
type="text" type="text"
className="form-control" className="form-control"
name={ categoryId } name={ categoryId }
id={ categoryId }
onChange={ this.onInputChange } onChange={ this.onInputChange }
value={ this.state.value } value={ this.state.value }
/> />
......
...@@ -125,7 +125,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps ...@@ -125,7 +125,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps
<header className="resource-header"> <header className="resource-header">
<div className="header-section"> <div className="header-section">
<Breadcrumb /> <Breadcrumb />
<img className={"icon icon-header " + getSourceIconClass(data.database, ResourceType.table)} /> <span className={"icon icon-header " + getSourceIconClass(data.database, ResourceType.table)} />
</div> </div>
<div className="header-section header-title"> <div className="header-section header-title">
<h3 className="header-title-text truncated"> <h3 className="header-title-text truncated">
...@@ -157,7 +157,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps ...@@ -157,7 +157,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps
<ExploreButton tableData={ data }/> <ExploreButton tableData={ data }/>
</div> </div>
</header> </header>
<main className="column-layout-1"> <article className="column-layout-1">
<section className="left-panel"> <section className="left-panel">
{} {}
<EditableSection title="Description"> <EditableSection title="Description">
...@@ -228,7 +228,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps ...@@ -228,7 +228,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps
<section className="right-panel"> <section className="right-panel">
<ColumnList columns={ data.columns }/> <ColumnList columns={ data.columns }/>
</section> </section>
</main> </article>
</div> </div>
); );
} }
......
...@@ -71,7 +71,8 @@ describe('DashboardListItem', () => { ...@@ -71,7 +71,8 @@ describe('DashboardListItem', () => {
}); });
it('renders start correct icon', () => { it('renders start correct icon', () => {
const startIcon = resourceInfo.find('img'); const startIcon = resourceInfo.find('.resource-icon');
expect(startIcon.exists()).toBe(true); expect(startIcon.exists()).toBe(true);
expect(startIcon.props().className).toEqual(`icon resource-icon ${MOCK_ICON_CLASS}`); expect(startIcon.props().className).toEqual(`icon resource-icon ${MOCK_ICON_CLASS}`);
expect(ConfigUtils.getSourceIconClass).toHaveBeenCalledWith(props.dashboard.product, props.dashboard.type); expect(ConfigUtils.getSourceIconClass).toHaveBeenCalledWith(props.dashboard.product, props.dashboard.type);
......
...@@ -8,7 +8,7 @@ import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon'; ...@@ -8,7 +8,7 @@ import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils'; import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils';
import { DashboardResource } from 'interfaces'; import { ResourceType, DashboardResource } from 'interfaces';
import { formatDate } from 'utils/dateUtils'; import { formatDate } from 'utils/dateUtils';
...@@ -29,13 +29,17 @@ class DashboardListItem extends React.Component<DashboardListItemProps, {}> { ...@@ -29,13 +29,17 @@ class DashboardListItem extends React.Component<DashboardListItemProps, {}> {
return `/dashboard?uri=${dashboard.uri}&index=${logging.index}&source=${logging.source}`; return `/dashboard?uri=${dashboard.uri}&index=${logging.index}&source=${logging.source}`;
}; };
generateResourceIconClass = (dashboardId: string, dashboardType: ResourceType): string => {
return `icon resource-icon ${getSourceIconClass(dashboardId, dashboardType)}`;
};
render() { render() {
const { dashboard } = this.props; const { dashboard } = this.props;
return ( return (
<li className="list-group-item clickable"> <li className="list-group-item clickable">
<Link className="resource-list-item table-list-item" to={ this.getLink() }> <Link className="resource-list-item table-list-item" to={ this.getLink() }>
<div className="resource-info"> <div className="resource-info">
<img className={`icon resource-icon ${getSourceIconClass(dashboard.product, dashboard.type)}`} /> <span className={this.generateResourceIconClass(dashboard.product, dashboard.type)} />
<div className="resource-info-text my-auto"> <div className="resource-info-text my-auto">
<div className="resource-name title-2"> <div className="resource-name title-2">
<div className="dashboard-group"> <div className="dashboard-group">
......
...@@ -38,7 +38,7 @@ class TableListItem extends React.Component<TableListItemProps, {}> { ...@@ -38,7 +38,7 @@ class TableListItem extends React.Component<TableListItemProps, {}> {
<li className="list-group-item clickable"> <li className="list-group-item clickable">
<Link className="resource-list-item table-list-item" to={ this.getLink() }> <Link className="resource-list-item table-list-item" to={ this.getLink() }>
<div className="resource-info"> <div className="resource-info">
<img className={this.generateResourceIconClass(table.database, table.type)} /> <span className={this.generateResourceIconClass(table.database, table.type)} />
<div className="resource-info-text my-auto"> <div className="resource-info-text my-auto">
<div className="resource-name title-2"> <div className="resource-name title-2">
<div className="truncated"> <div className="truncated">
......
...@@ -60,11 +60,13 @@ describe('TableListItem', () => { ...@@ -60,11 +60,13 @@ describe('TableListItem', () => {
const testValue = 'noEffectOnTest'; const testValue = 'noEffectOnTest';
const givenResource = ResourceType.table; const givenResource = ResourceType.table;
const iconClass = wrapper.instance().generateResourceIconClass(testValue, givenResource); const iconClass = wrapper.instance().generateResourceIconClass(testValue, givenResource);
expect(getDBIconClassSpy).toHaveBeenCalledWith(testValue, givenResource); expect(getDBIconClassSpy).toHaveBeenCalledWith(testValue, givenResource);
}); });
it('returns the default classes with the correct icon class appended', () => { it('returns the default classes with the correct icon class appended', () => {
const iconClass = wrapper.instance().generateResourceIconClass('noEffectOnTest'); const iconClass = wrapper.instance().generateResourceIconClass('noEffectOnTest');
expect(iconClass).toEqual(`icon resource-icon test-class`); expect(iconClass).toEqual(`icon resource-icon test-class`);
}); });
}); });
...@@ -78,6 +80,7 @@ describe('TableListItem', () => { ...@@ -78,6 +80,7 @@ describe('TableListItem', () => {
props = setupResult.props; props = setupResult.props;
wrapper = setupResult.wrapper; wrapper = setupResult.wrapper;
}); });
it('renders item as Link', () => { it('renders item as Link', () => {
expect(wrapper.find(Link).exists()).toBeTruthy(); expect(wrapper.find(Link).exists()).toBeTruthy();
}); });
...@@ -89,7 +92,8 @@ describe('TableListItem', () => { ...@@ -89,7 +92,8 @@ describe('TableListItem', () => {
}); });
it('renders start correct icon', () => { it('renders start correct icon', () => {
const startIcon = resourceInfo.find('img'); const startIcon = resourceInfo.find('.resource-icon');
expect(startIcon.exists()).toBe(true); expect(startIcon.exists()).toBe(true);
expect(startIcon.props().className).toEqual(wrapper.instance().generateResourceIconClass(props.table.database)); expect(startIcon.props().className).toEqual(wrapper.instance().generateResourceIconClass(props.table.database));
}); });
......
...@@ -35,7 +35,7 @@ ReactDOM.render( ...@@ -35,7 +35,7 @@ ReactDOM.render(
<DocumentTitle title="Amundsen - Data Discovery Portal"> <DocumentTitle title="Amundsen - Data Discovery Portal">
<Provider store={store}> <Provider store={store}>
<Router history={BrowserHistory}> <Router history={BrowserHistory}>
<div id="main"> <main id="main">
<Preloader/> <Preloader/>
<Route component={NavBar} /> <Route component={NavBar} />
<Switch> <Switch>
...@@ -49,7 +49,7 @@ ReactDOM.render( ...@@ -49,7 +49,7 @@ ReactDOM.render(
<Route path="/" component={HomePage} /> <Route path="/" component={HomePage} />
</Switch> </Switch>
<Footer /> <Footer />
</div> </main>
</Router> </Router>
</Provider> </Provider>
</DocumentTitle> </DocumentTitle>
......
<html> <!doctype html>
<html lang="en-US">
<head> <head>
{% if <%= htmlWebpackPlugin.options.config.google.enabled%> %} {% if <%= htmlWebpackPlugin.options.config.google.enabled%> %}
{% include 'fragments/google-analytics-loader.html' %} {% include 'fragments/google-analytics-loader.html' %}
{% endif %} {% endif %}
<meta charset="utf-8"> <meta charset="utf-8">
<title>Amundsen - Data Discovery Portal</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/static/images/favicon.png"> <link rel="shortcut icon" href="/static/images/favicon.png">
<link href="/static/dist/main.css" type="text/css" rel="stylesheet"/> <link href="/static/dist/main.css" type="text/css" rel="stylesheet"/>
<title>Amundsen - Data Discovery Portal</title>
<meta name="theme-color" content="#2B1B81">
</head> </head>
<body> <body>
<div id="content"/> <div id="content"/>
......
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