Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Dashboards/Queries/Users list pages to React #3381

Merged
merged 40 commits into from
Feb 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8e6d6b6
Refine existing implementation of dashboards/queries/users lists and …
kravets-levko Jan 30, 2019
0dff4a5
Migrate common list page controller to React and refactor it's logic
kravets-levko Jan 31, 2019
ec5d46f
Migrate Dashboard list page to React
kravets-levko Jan 31, 2019
77ce09d
Migrate Queries list page to React
kravets-levko Jan 31, 2019
88297f7
Migrate Users list page to React
kravets-levko Jan 31, 2019
efed0e0
Merge branch 'master' into feature/react-list-controllers
kravets-levko Jan 31, 2019
7c82ab4
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 2, 2019
02bd99d
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 2, 2019
fb8f624
Remove react-timeago dependency
kravets-levko Feb 2, 2019
834f942
Use composition instead of inheritance
kravets-levko Feb 2, 2019
d92df5c
Refine implementation
kravets-levko Feb 2, 2019
a75d63c
Merge sidebar into single component
kravets-levko Feb 3, 2019
683e75a
Refine column definitions
kravets-levko Feb 3, 2019
72c0099
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 3, 2019
78ebb21
Use simple controller instead of React context
kravets-levko Feb 3, 2019
1b013d3
Refine implementation
kravets-levko Feb 3, 2019
ccf3deb
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 3, 2019
5171317
Restore changes from getredash/redash#2888
kravets-levko Feb 3, 2019
01e999a
Tweak Users list page
kravets-levko Feb 3, 2019
2d3aed6
Ability to render dynamically defined components
kravets-levko Feb 4, 2019
ef7436d
Tweak users list page
kravets-levko Feb 4, 2019
aaaf894
User list page for non-admins
kravets-levko Feb 4, 2019
8a57ded
Fix: ItemsTable ignores isAvailable field
kravets-levko Feb 4, 2019
6412859
Refine implementation
kravets-levko Feb 4, 2019
c419340
Refine implementation
kravets-levko Feb 4, 2019
e40b266
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 4, 2019
1785a8c
Implement LiveItemsList as higher order component
kravets-levko Feb 5, 2019
f8445f3
Some fixes
kravets-levko Feb 5, 2019
dcf22cd
Move some definitions to a better place
kravets-levko Feb 5, 2019
e56d2f6
Some fixes
kravets-levko Feb 5, 2019
b0b1873
Refine components
kravets-levko Feb 5, 2019
fcdd864
Refine UsersList page
kravets-levko Feb 5, 2019
706e4bc
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 5, 2019
26bff4a
More comments for a god of comments
kravets-levko Feb 5, 2019
b9fb9d0
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 5, 2019
3b1055c
Fix wrong tables size on smaller screens
kravets-levko Feb 5, 2019
73bbeab
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 5, 2019
64bc61f
Tweak tables
kravets-levko Feb 5, 2019
3fef4ad
Merge branch 'master' into feature/react-list-controllers
arikfr Feb 5, 2019
7e9eea9
Merge branch 'master' into feature/react-list-controllers
kravets-levko Feb 5, 2019
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
32 changes: 32 additions & 0 deletions client/app/assets/less/ant.less
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,35 @@
}
}
}

// Table

.@{table-prefix-cls} {
color: inherit;

* {
transition: none !important;
}

&-thead > tr > th {
padding: @table-padding-vertical * 2 @table-padding-horizontal;
}

.@{table-prefix-cls}-column-sorters {
&:before,
&:hover:before {
content: none;
}
}

&-thead > tr > th {
.@{table-prefix-cls}-column-sorter {
&-up,
&-down {
&.on {
color: @table-header-icon-active-color;
}
}
}
}
}
18 changes: 18 additions & 0 deletions client/app/assets/less/inc/ant-variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,21 @@
@pagination-disabled-bg: fade(@redash-gray, 15%);
@pagination-hover-color: #333;
@pagination-hover-bg: fade(@redash-gray, 25%);


/* --------------------------------------------------------
Table
-----------------------------------------------------------*/

@table-border-radius-base: 0;
@table-header-color: #333;
@table-header-bg: fade(@redash-gray, 3%);
@table-header-icon-color: fade(@text-color, 20%);
@table-header-icon-active-color: @text-color;
@table-header-sort-bg: @table-header-bg;
@table-header-sort-active-bg: @table-header-bg;
@table-header-filter-active-bg: @table-header-bg;
@table-body-sort-bg: transparent;
@table-row-hover-bg: fade(@redash-gray, 5%);
@table-padding-vertical: 7px;
@table-padding-horizontal: 10px;
7 changes: 2 additions & 5 deletions client/app/assets/less/redash/redash-newstyle.less
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,9 @@ body {
box-shadow: inset 3px 0px 0px @brand-primary;
}

.table.table-data {
> tbody > tr > td {
.table-data {
tbody > tr > td {
padding-top: 5px !important;
}

tr:hover {
cursor: pointer;
}

Expand Down
6 changes: 4 additions & 2 deletions client/app/components/BigMessage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';

export function BigMessage({ message, icon, children }) {
export function BigMessage({ message, icon, children, className }) {
return (
<div className="tiled bg-white p-15 text-center">
<div className={'p-15 text-center ' + className}>
<h3 className="m-t-0 m-b-0">
<i className={'fa ' + icon} />
</h3>
Expand All @@ -19,11 +19,13 @@ BigMessage.propTypes = {
message: PropTypes.string,
icon: PropTypes.string.isRequired,
children: PropTypes.node,
className: PropTypes.string,
};

BigMessage.defaultProps = {
message: '',
children: null,
className: 'tiled bg-white',
};

export default function init(ngModule) {
Expand Down
50 changes: 50 additions & 0 deletions client/app/components/DynamicComponent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { isFunction, isString } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';

const componentsRegistry = new Map();
const activeInstances = new Set();

export function registerComponent(name, component) {
if (isString(name) && (name !== '')) {
componentsRegistry[name] = isFunction(component) ? component : null;
// Refresh active DynamicComponent instances which use this component
activeInstances.forEach((dynamicComponent) => {
kravets-levko marked this conversation as resolved.
Show resolved Hide resolved
if (dynamicComponent.props.name === name) {
dynamicComponent.forceUpdate();
}
});
}
}

export function unregisterComponent(name) {
registerComponent(name, null);
}

export default class DynamicComponent extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
children: PropTypes.node,
};

static defaultProps = {
children: null,
};

componentDidMount() {
activeInstances.add(this);
}

componentWillUnmount() {
activeInstances.delete(this);
}

render() {
const { name, children, ...props } = this.props;
const RealComponent = componentsRegistry.get(name);
if (!RealComponent) {
return null;
}
return <RealComponent {...props}>{children}</RealComponent>;
}
}
79 changes: 42 additions & 37 deletions client/app/components/FavoritesControl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,53 @@ import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import { $rootScope } from '@/services/ng';

function toggleItem(event, item, callback) {
event.preventDefault();
event.stopPropagation();
export class FavoritesControl extends React.Component {
static propTypes = {
item: PropTypes.shape({
is_favorite: PropTypes.bool.isRequired,
}).isRequired,
onChange: PropTypes.func,
// Force component update when `item` changes.
// Remove this when `react2angular` will finally go to hell
forceUpdate: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
};

const action = item.is_favorite ? item.$unfavorite.bind(item) : item.$favorite.bind(item);
const savedIsFavorite = item.is_favorite;
static defaultProps = {
onChange: () => {},
forceUpdate: '',
};

action().then(() => {
item.is_favorite = !savedIsFavorite;
$rootScope.$broadcast('reloadFavorites');
callback();
});
}
toggleItem(event, item, callback) {
event.preventDefault();
event.stopPropagation();

export function FavoritesControl({ item, onChange }) {
const icon = item.is_favorite ? 'fa fa-star' : 'fa fa-star-o';
const title = item.is_favorite ? 'Remove from favorites' : 'Add to favorites';
return (
<a
href="javascript:void(0)"
title={title}
className="btn-favourite"
onClick={event => toggleItem(event, item, onChange)}
>
<i className={icon} aria-hidden="true" />
</a>
);
}
const action = item.is_favorite ? item.$unfavorite.bind(item) : item.$favorite.bind(item);
const savedIsFavorite = item.is_favorite;

FavoritesControl.propTypes = {
item: PropTypes.shape({
is_favorite: PropTypes.bool.isRequired,
}).isRequired,
onChange: PropTypes.func,
// Force component update when `item` changes.
// Remove this when `react2angular` will finally go to hell
forceUpdate: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
};
action().then(() => {
item.is_favorite = !savedIsFavorite;
this.forceUpdate();
$rootScope.$broadcast('reloadFavorites');
callback();
});
}

FavoritesControl.defaultProps = {
onChange: () => {},
};
render() {
const { item, onChange } = this.props;
const icon = item.is_favorite ? 'fa fa-star' : 'fa fa-star-o';
const title = item.is_favorite ? 'Remove from favorites' : 'Add to favorites';
return (
<a
href="javascript:void(0)"
title={title}
className="btn-favourite"
onClick={event => this.toggleItem(event, item, onChange)}
>
<i className={icon} aria-hidden="true" />
</a>
);
}
}

export default function init(ngModule) {
ngModule.component('favoritesControlImpl', react2angular(FavoritesControl));
Expand Down
7 changes: 5 additions & 2 deletions client/app/components/NoTaggedObjectsFound.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { react2angular } from 'react2angular';
import { BigMessage } from '@/components/BigMessage';
import TagsControl from '@/components/tags-control/TagsControl';

function NoTaggedObjectsFound({ objectType, tags }) {
export function NoTaggedObjectsFound({ objectType, tags }) {
return (
<BigMessage icon="fa-tags">
No {objectType} found tagged with&nbsp;<TagsControl className="inline-tags-control" tags={Array.from(tags)} />.
Expand All @@ -14,7 +14,10 @@ function NoTaggedObjectsFound({ objectType, tags }) {

NoTaggedObjectsFound.propTypes = {
objectType: PropTypes.string.isRequired,
tags: PropTypes.objectOf(Set).isRequired,
tags: PropTypes.oneOfType([
PropTypes.array,
PropTypes.objectOf(Set),
]).isRequired,
};

export default function init(ngModule) {
Expand Down
3 changes: 2 additions & 1 deletion client/app/components/TagsList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class TagsList extends React.Component {
}
this.forceUpdate();

this.props.onUpdate(this.state.selectedTags);
this.props.onUpdate([...this.state.selectedTags]);
}

render() {
Expand All @@ -63,6 +63,7 @@ export class TagsList extends React.Component {
{map(allTags, tag => (
<a
key={tag.name}
href="javascript:void(0)"
className={classNames('list-group-item', 'max-character', { active: selectedTags.has(tag.name) })}
onClick={event => this.toggleTag(event, tag.name)}
>
Expand Down
Loading