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

Dashboard URL does not show new name when dashboard name is updated #1009

Open
wants to merge 10 commits into
base: master
from
@@ -15,7 +15,7 @@ import ItemsTable, { Columns } from '@/components/items-list/components/ItemsTab

import Layout from '@/components/layouts/ContentWithSidebar';

import { Dashboard } from '@/services/dashboard';
import { Dashboard, urlForDashboard } from '@/services/dashboard';
import { routesToAngularRoutes } from '@/lib/utils';

import DashboardListEmptyState from './DashboardListEmptyState';
@@ -45,7 +45,7 @@ class DashboardList extends React.Component {
Columns.favorites({ className: 'p-r-0' }),
Columns.custom.sortable((text, item) => (
<React.Fragment>
<a className="table-main-title" href={'dashboard/' + item.slug} data-test={item.slug}>{ item.name }</a>
<a className="table-main-title" href={urlForDashboard(item)} data-test={item.slug}>{ item.name }</a>
<DashboardTagsControl
className="d-block"
tags={item.tags}
@@ -3,7 +3,7 @@
<div class="page-title col-xs-8 col-sm-7 col-lg-7 p-l-0">
<favorites-control item="$ctrl.dashboard"></favorites-control>
<h3>
<edit-in-place class="edit-in-place" is-editable="$ctrl.layoutEditing" on-done="$ctrl.saveName" ignore-blanks="true" value="$ctrl.dashboard.name" editor="'input'"></edit-in-place>
<edit-in-place class="edit-in-place" is-editable="$ctrl.layoutEditing" on-done="$ctrl.saveName" ignore-blanks="true" value="$ctrl.dashboard.name" editor="'input'" data-test="DashboardName"></edit-in-place>
</h3>

<img ng-src="{{$ctrl.dashboard.user.profile_image_url}}" class="profile__image_thumb--dashboard" alt="{{$ctrl.dashboard.user.name}}">
@@ -33,6 +33,7 @@ <h3>
<span ng-switch-default>
<span class="save-status">Saved</span>
<button class="btn btn-primary btn-sm"
data-test="DoneEditingDashboard"
ng-disabled="$ctrl.isGridDisabled"
ng-click="$ctrl.editLayout(false)">
<i class="fa fa-check"></i> Done Editing
@@ -6,7 +6,7 @@ import {
editableMappingsToParameterMappings,
synchronizeWidgetTitles,
} from '@/components/ParameterMappingInput';
import { collectDashboardFilters } from '@/services/dashboard';
import { collectDashboardFilters, urlForDashboard } from '@/services/dashboard';
import { durationHumanize } from '@/filters';
import template from './dashboard.html';
import ShareDashboardDialog from './ShareDashboardDialog';
@@ -160,7 +160,7 @@ function DashboardCtrl(

this.loadDashboard = _.throttle((force) => {
Dashboard.get(
{ slug: $routeParams.dashboardSlug },
{ slug: $routeParams.dashboardSlug || parseInt($routeParams.dashboardId, 10) },
(dashboard) => {
this.dashboard = dashboard;
this.isDashboardOwner = currentUser.id === dashboard.user.id || currentUser.hasPermission('admin');
@@ -283,7 +283,7 @@ function DashboardCtrl(
slug: this.dashboard.id,
version: this.dashboard.version,
});
Dashboard.save(
return Dashboard.save(
data,
(dashboard) => {
_.extend(this.dashboard, _.pick(dashboard, _.keys(data)));
@@ -303,7 +303,9 @@ function DashboardCtrl(
};

this.saveName = (name) => {
updateDashboard({ name });
updateDashboard({ name }).$promise.then(() => {
$location.path(urlForDashboard(this.dashboard));
});
};

this.saveTags = (tags) => {
@@ -413,6 +415,10 @@ export default function init(ngModule) {
template: '<dashboard-page></dashboard-page>',
reloadOnSearch: false,
},
'/dashboards/:dashboardId': {
template: '<dashboard-page></dashboard-page>',
reloadOnSearch: false,
},
};
}

@@ -4,6 +4,8 @@ import { Widget } from './widget';

export let Dashboard = null; // eslint-disable-line import/no-mutable-exports

export const urlForDashboard = ({ id, slug }) => `dashboards/${id}-${slug}`;

export function collectDashboardFilters(dashboard, queryResults, urlParams) {
const filters = {};
_.each(queryResults, (queryResult) => {
@@ -36,8 +36,8 @@ describe('Dashboard', () => {
});

it('archives dashboard', () => {
createDashboard('Foo Bar').then(({ slug }) => {
cy.visit(`/dashboard/${slug}`);
createDashboard('Foo Bar').then(({ id, slug }) => {
cy.visit(`/dashboard/${id}-${slug}`);

cy.getByTestId('DashboardMoreMenu')
.click()
@@ -54,18 +54,39 @@ describe('Dashboard', () => {

cy.visit('/dashboards');
cy.getByTestId('DashboardLayoutContent').within(() => {
cy.getByTestId(slug).should('not.exist');
cy.getByTestId(`${id}-${slug}`).should('not.exist');
});
});
});

it('renames dashboard', () => {
createDashboard('Foo Bar').then(({ id, slug }) => {
cy.visit(`/dashboard/${id}-${slug}`);

cy.getByTestId('DashboardMoreMenu')
.click()
.within(() => {
cy.get('li')
.contains('Edit')
.click();
});

cy.getByTestId('DashboardName').click().within(() => {
cy.get('input').clear().type('Baz Qux');
});

cy.getByTestId('DoneEditingDashboard').click();
cy.url().should('include', 'baz-qux');
});
});

context('viewport width is at 800px', () => {
before(function () {
cy.login();
createDashboard('Foo Bar')
.then(({ slug, id }) => {
this.dashboardUrl = `/dashboard/${slug}`;
this.dashboardEditUrl = `/dashboard/${slug}?edit`;
this.dashboardUrl = `/dashboard/${id}-${slug}`;
this.dashboardEditUrl = `${this.dashboardUrl}?edit`;
return addTextbox(id, 'Hello World!').then(getWidgetTestId);
})
.then((elTestId) => {
@@ -119,8 +140,8 @@ describe('Dashboard', () => {
context('viewport width is at 767px', () => {
before(function () {
cy.login();
createDashboard('Foo Bar').then(({ slug }) => {
this.dashboardUrl = `/dashboard/${slug}`;
createDashboard('Foo Bar').then(({ id, slug }) => {
this.dashboardUrl = `/dashboard/${id}-${slug}`;
});
});

@@ -10,7 +10,7 @@ describe('Grid compliant widgets', () => {
cy.viewport(1215, 800);
createDashboard('Foo Bar')
.then(({ slug, id }) => {
this.dashboardUrl = `/dashboard/${slug}`;
this.dashboardUrl = `/dashboard/${id}-${slug}`;
return addTextbox(id, 'Hello World!').then(getWidgetTestId);
})
.then((elTestId) => {
@@ -8,7 +8,7 @@ describe('Dashboard Sharing', () => {
cy.login();
createDashboard('Foo Bar').then(({ slug, id }) => {
this.dashboardId = id;
this.dashboardUrl = `/dashboard/${slug}`;
this.dashboardUrl = `/dashboard/${id}-${slug}`;
});
});

@@ -8,7 +8,7 @@ describe('Textbox', () => {
cy.login();
createDashboard('Foo Bar').then(({ slug, id }) => {
this.dashboardId = id;
this.dashboardUrl = `/dashboard/${slug}`;
this.dashboardUrl = `/dashboard/${id}-${slug}`;
});
});

@@ -8,7 +8,7 @@ describe('Widget', () => {
cy.login();
createDashboard('Foo Bar').then(({ slug, id }) => {
this.dashboardId = id;
this.dashboardUrl = `/dashboard/${slug}`;
this.dashboardUrl = `/dashboard/${id}-${slug}`;
});
});

@@ -146,7 +146,8 @@ def get(self, dashboard_slug=None):
:>json string widget.created_at: ISO format timestamp for widget creation
:>json string widget.updated_at: ISO format timestamp for last widget modification
"""
dashboard = get_object_or_404(models.Dashboard.get_by_slug_and_org, dashboard_slug, self.current_org)
fn = models.Dashboard.get_by_id_and_org if dashboard_slug.isdigit() else models.Dashboard.get_by_slug_and_org
dashboard = get_object_or_404(fn, dashboard_slug, self.current_org)
response = serialize_dashboard(dashboard, with_widgets=True, user=self.current_user)

api_key = models.ApiKey.get_by_object(dashboard)
@@ -867,6 +867,10 @@ class Dashboard(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model
def __str__(self):
return u"%s=%s" % (self.id, self.name)

@property
def name_as_slug(self):
return utils.slugify(self.name)

@classmethod
def all(cls, org, group_ids, user_id):
query = (
@@ -207,7 +207,7 @@ def serialize_dashboard(obj, with_widgets=False, user=None, with_favorite_state=

d = {
'id': obj.id,
'slug': obj.slug,
'slug': obj.name_as_slug,
'name': obj.name,
'user_id': obj.user_id,
# TODO: we should properly load the users
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.