Skip to content

Commit

Permalink
feat(dashbaords): add dashboard favourite to table view (#81776)
Browse files Browse the repository at this point in the history
Add dashboard favourite column to table view:

<img width="1328" alt="Screenshot 2024-12-06 at 12 11 35 PM"
src="https://github.com/user-attachments/assets/0e018345-c8b9-4aab-a454-9958d9615f15">
  • Loading branch information
harshithadurai authored Dec 6, 2024
1 parent e092c1b commit f48ca85
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 15 deletions.
40 changes: 40 additions & 0 deletions static/app/views/dashboards/manage/dashboardTable.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe('Dashboards - DashboardTable', function () {
isEditableByEveryone: false,
teamsWithEditAccess: [1],
},
isFavorited: true,
}),
DashboardListItemFixture({
id: '2',
Expand Down Expand Up @@ -312,4 +313,43 @@ describe('Dashboards - DashboardTable', function () {
await userEvent.click((await screen.findAllByTestId('edit-access-dropdown'))[0]);
expect(screen.getAllByPlaceholderText('Search Teams')[0]).toBeInTheDocument();
});

it('renders favorite column', async function () {
MockApiClient.addMockResponse({
url: '/organizations/org-slug/dashboards/2/favorite/',
method: 'PUT',
body: {isFavorited: false},
});

const organizationWithFavorite = OrganizationFixture({
features: [
'global-views',
'dashboards-basic',
'dashboards-edit',
'discover-query',
'dashboards-table-view',
'dashboards-favourite',
],
});

render(
<DashboardTable
onDashboardsChange={jest.fn()}
organization={organizationWithFavorite}
dashboards={dashboards}
location={router.location}
/>,
{
organization: organizationWithFavorite,
}
);

expect((await screen.findAllByTestId('grid-head-cell')).length).toBe(5);
expect(screen.getByLabelText('Favorite Column')).toBeInTheDocument();
expect(screen.queryAllByLabelText('Favorite')).toHaveLength(1);
expect(screen.queryAllByLabelText('UnFavorite')).toHaveLength(1);

await userEvent.click(screen.queryAllByLabelText('Favorite')[0]);
expect(screen.queryAllByLabelText('UnFavorite')).toHaveLength(2);
});
});
114 changes: 99 additions & 15 deletions static/app/views/dashboards/manage/dashboardTable.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {useState} from 'react';
import styled from '@emotion/styled';
import type {Location} from 'history';
import cloneDeep from 'lodash/cloneDeep';
Expand All @@ -6,10 +7,12 @@ import {
createDashboard,
deleteDashboard,
fetchDashboard,
updateDashboardFavorite,
updateDashboardPermissions,
} from 'sentry/actionCreators/dashboards';
import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
import type {Client} from 'sentry/api';
import Feature from 'sentry/components/acl/feature';
import {ActivityAvatar} from 'sentry/components/activity/item/avatar';
import {Button} from 'sentry/components/button';
import {openConfirmModal} from 'sentry/components/confirm';
Expand All @@ -20,7 +23,7 @@ import GridEditable, {
} from 'sentry/components/gridEditable';
import Link from 'sentry/components/links/link';
import TimeSince from 'sentry/components/timeSince';
import {IconCopy, IconDelete} from 'sentry/icons';
import {IconCopy, IconDelete, IconStar} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Organization} from 'sentry/types/organization';
Expand Down Expand Up @@ -50,6 +53,54 @@ enum ResponseKeys {
OWNER = 'createdBy',
ACCESS = 'permissions',
CREATED = 'dateCreated',
FAVORITE = 'isFavorited',
}

type FavoriteButtonProps = {
api: Client;
dashboardId: string;
isFavorited: boolean;
organization: Organization;
};

function FavoriteButton({
isFavorited,
api,
organization,
dashboardId,
}: FavoriteButtonProps) {
const [favorited, setFavorited] = useState(isFavorited);
return (
<Feature features="dashboards-favourite">
<StyledFavoriteButton
aria-label={t('Favorite Button')}
size="zero"
borderless
icon={
<IconStar
color={favorited ? 'yellow300' : 'gray300'}
isSolid={favorited}
aria-label={favorited ? t('UnFavorite') : t('Favorite')}
size="sm"
/>
}
onClick={async () => {
try {
setFavorited(!favorited);
await updateDashboardFavorite(
api,
organization.slug,
dashboardId,
!favorited
);
} catch (error) {
// If the api call fails, revert the state
setFavorited(favorited);
}
}}
/>
</Feature>
);
}

function DashboardTable({
Expand All @@ -60,20 +111,18 @@ function DashboardTable({
onDashboardsChange,
isLoading,
}: Props) {
const columnOrder = organization.features.includes('dashboards-edit-access')
? [
{key: ResponseKeys.NAME, name: t('Name'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.WIDGETS, name: t('Widgets'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.OWNER, name: t('Owner'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.ACCESS, name: t('Access'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.CREATED, name: t('Created'), width: COL_WIDTH_UNDEFINED},
]
: [
{key: ResponseKeys.NAME, name: t('Name'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.WIDGETS, name: t('Widgets'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.OWNER, name: t('Owner'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.CREATED, name: t('Created'), width: COL_WIDTH_UNDEFINED},
];
const columnOrder: GridColumnOrder<ResponseKeys>[] = [
...(organization.features.includes('dashboards-favourite')
? [{key: ResponseKeys.FAVORITE, name: t('Favorite'), width: 50}]
: []),
{key: ResponseKeys.NAME, name: t('Name'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.WIDGETS, name: t('Widgets'), width: COL_WIDTH_UNDEFINED},
{key: ResponseKeys.OWNER, name: t('Owner'), width: COL_WIDTH_UNDEFINED},
...(organization.features.includes('dashboards-edit-access')
? [{key: ResponseKeys.ACCESS, name: t('Access'), width: COL_WIDTH_UNDEFINED}]
: []),
{key: ResponseKeys.CREATED, name: t('Created'), width: COL_WIDTH_UNDEFINED},
];

function handleDelete(dashboard: DashboardListItem) {
deleteDashboard(api, organization.slug, dashboard.id)
Expand Down Expand Up @@ -121,6 +170,17 @@ function DashboardTable({
column: GridColumnOrder<string>,
dataRow: DashboardListItem
) => {
if (column.key === ResponseKeys.FAVORITE) {
return (
<FavoriteButton
isFavorited={dataRow[ResponseKeys.FAVORITE] ?? false}
api={api}
organization={organization}
dashboardId={dataRow.id}
/>
);
}

if (column.key === ResponseKeys.NAME) {
return (
<Link
Expand Down Expand Up @@ -232,6 +292,20 @@ function DashboardTable({
columnSortBy={[]}
grid={{
renderBodyCell,
renderHeadCell: column => {
if (column.key === ResponseKeys.FAVORITE) {
return (
<Feature features="dashboards-favourite">
<StyledIconStar
color="yellow300"
isSolid
aria-label={t('Favorite Column')}
/>
</Feature>
);
}
return column.name;
},
}}
isLoading={isLoading}
emptyMessage={
Expand Down Expand Up @@ -273,3 +347,13 @@ const StyledButton = styled(Button)`
border: none;
box-shadow: none;
`;

const StyledFavoriteButton = styled(Button)`
padding: 0;
width: 16px;
border: 3px solid transparent;
`;

const StyledIconStar = styled(IconStar)`
margin-left: ${space(0.5)};
`;

0 comments on commit f48ca85

Please sign in to comment.