Skip to content

Commit

Permalink
feat(ui): show item status with badges in table and cards (#591)
Browse files Browse the repository at this point in the history
  • Loading branch information
spaenleh authored Mar 24, 2023
1 parent aa0cfa1 commit bde9dd9
Show file tree
Hide file tree
Showing 18 changed files with 345 additions and 156 deletions.
23 changes: 12 additions & 11 deletions cypress/e2e/item/hide/hideItem.cy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
HIDDEN_ITEM_BUTTON_CLASS,
buildHideButtonId,
buildItemMenu,
buildItemMenuButtonId,
} from '../../../../src/config/selectors';
Expand All @@ -10,13 +11,15 @@ import {
HIDDEN_ITEM,
ITEMS_SETTINGS,
} from '../../../fixtures/items';
import { TABLE_ITEM_RENDER_TIME } from '../../../support/constants';

const toggleHideButton = (itemId) => {
cy.wait(TABLE_ITEM_RENDER_TIME);
const toggleHideButton = (itemId, isHidden = false) => {
const menuSelector = `#${buildItemMenuButtonId(itemId)}`;
cy.get(menuSelector).click();
cy.get(`#${buildItemMenu(itemId)} .${HIDDEN_ITEM_BUTTON_CLASS}`).click();

cy.wait('@getItemTags');
cy.get(`#${buildItemMenu(itemId)} .${HIDDEN_ITEM_BUTTON_CLASS}`)
.should('have.attr', 'data-cy', buildHideButtonId(isHidden))
.click();
};

const HIDDEN_ITEM_TAG_ID = Cypress.env('HIDDEN_ITEM_TAG_ID');
Expand All @@ -31,7 +34,7 @@ describe('Hiding Item', () => {
cy.visit(HOME_PATH);
const item = ITEMS_SETTINGS.items[1];

toggleHideButton(item.id);
toggleHideButton(item.id, false);

cy.wait(`@postItemTag`).then(
({
Expand All @@ -48,8 +51,8 @@ describe('Hiding Item', () => {
cy.visit(HOME_PATH);
const item = HIDDEN_ITEM;

cy.wait(5000);
toggleHideButton(item.id);
// make sure to wait for the tags to be fetched
toggleHideButton(item.id, true);

cy.wait('@deleteItemTag').then(({ request: { url } }) => {
expect(url).to.contain(item.tags[1].id);
Expand All @@ -58,7 +61,6 @@ describe('Hiding Item', () => {

it('Cannot hide child of hidden item', () => {
cy.visit(buildItemPath(HIDDEN_ITEM.id));
cy.wait(TABLE_ITEM_RENDER_TIME);
cy.get(`#${buildItemMenuButtonId(CHILD_HIDDEN_ITEM.id)}`).click();
cy.get(
`#${buildItemMenu(CHILD_HIDDEN_ITEM.id)} .${HIDDEN_ITEM_BUTTON_CLASS}`,
Expand All @@ -80,7 +82,7 @@ describe('Hiding Item', () => {
cy.switchMode(ITEM_LAYOUT_MODES.GRID);
const item = ITEMS_SETTINGS.items[1];

toggleHideButton(item.id);
toggleHideButton(item.id, false);

cy.wait(`@postItemTag`).then(
({
Expand All @@ -98,7 +100,7 @@ describe('Hiding Item', () => {
cy.switchMode(ITEM_LAYOUT_MODES.GRID);
const item = ITEMS_SETTINGS.items[0];

toggleHideButton(item.id);
toggleHideButton(item.id, true);

cy.wait('@deleteItemTag').then(({ request: { url } }) => {
expect(url).to.contain(item.tags[1].id);
Expand All @@ -109,7 +111,6 @@ describe('Hiding Item', () => {
cy.visit(buildItemPath(HIDDEN_ITEM.id));
cy.switchMode(ITEM_LAYOUT_MODES.GRID);

cy.wait(TABLE_ITEM_RENDER_TIME);
cy.get(`#${buildItemMenuButtonId(CHILD_HIDDEN_ITEM.id)}`).click();
cy.get(
`#${buildItemMenu(CHILD_HIDDEN_ITEM.id)} .${HIDDEN_ITEM_BUTTON_CLASS}`,
Expand Down
20 changes: 13 additions & 7 deletions cypress/e2e/item/view/viewFolder.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ import { SAMPLE_ITEMS, generateOwnItems } from '../../../fixtures/items';
import { GRAASP_LINK_ITEM } from '../../../fixtures/links';
import { CURRENT_USER } from '../../../fixtures/members';
import { SHARED_ITEMS } from '../../../fixtures/sharedItems';
import { NAVIGATION_LOAD_PAUSE } from '../../../support/constants';
import {
NAVIGATION_LOAD_PAUSE,
TABLE_ITEM_RENDER_TIME,
} from '../../../support/constants';
import { expectFolderViewScreenLayout } from '../../../support/viewUtils';

const translateBuilder = (key) => i18n.t(key, { ns: namespaces.builder });
Expand All @@ -53,6 +56,7 @@ describe('View Folder', () => {

// should get own items
cy.wait('@getOwnItems').then(({ response: { body } }) => {
cy.wait('@getItemMemberships');
// check item is created and displayed
for (const item of body) {
cy.get(`#${buildItemCard(item.id)}`).should('exist');
Expand All @@ -64,12 +68,14 @@ describe('View Folder', () => {
cy.goToItemInGrid(childId);

// should get children
cy.wait('@getChildren').then(({ response: { body } }) => {
// check item is created and displayed
for (const item of body) {
cy.get(`#${buildItemCard(item.id)}`).should('exist');
}
});
cy.wait('@getChildren', { timeout: TABLE_ITEM_RENDER_TIME }).then(
({ response: { body } }) => {
// check item is created and displayed
for (const item of body) {
cy.get(`#${buildItemCard(item.id)}`).should('exist');
}
},
);

// root title
cy.get(`#${NAVIGATION_ROOT_ID}`).should(
Expand Down
3 changes: 3 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
mockGetItemValidationReviewStatuses,
mockGetItemValidationStatuses,
mockGetItems,
mockGetItemsTags,
mockGetMember,
mockGetMemberMentions,
mockGetMembers,
Expand Down Expand Up @@ -234,6 +235,8 @@ Cypress.Commands.add(

mockGetItemTags(items);

mockGetItemsTags(items);

mockPostItemTag(items, postItemTagError);

mockDeleteItemTag(deleteItemTagError);
Expand Down
23 changes: 23 additions & 0 deletions cypress/support/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const {
buildGetItemMembershipsForItemsRoute,
buildGetPublicItemMembershipsForItemsRoute,
buildGetItemTagsRoute,
buildGetItemsTagsRoute,
GET_TAGS_ROUTE,
buildPutItemLoginSchema,
buildPostItemTagRoute,
Expand Down Expand Up @@ -1041,6 +1042,28 @@ export const mockGetItemTags = (items) => {
).as('getItemTags');
};

export const mockGetItemsTags = (items) => {
cy.intercept(
{
method: DEFAULT_GET.method,
url: `${API_HOST}/items/tags?id=*`,
},
({ reply, url }) => {
let { id: ids } = qs.parse(url.split('?')[1]);
if (typeof ids === 'string') {
ids = [ids];
}
const result = ids?.map(
(itemId) =>
items.find(({ id }) => id === itemId)?.tags || [
{ statusCode: StatusCodes.NOT_FOUND },
],
);
reply(result);
},
).as('getItemsTags');
};

export const mockGetTags = (tags) => {
cy.intercept(
{
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
"@emotion/styled": "11.10.5",
"@graasp/chatbox": "1.1.0",
"@graasp/query-client": "0.4.0",
"@graasp/sdk": "0.9.2",
"@graasp/translations": "1.8.0",
"@graasp/sdk": "0.9.3",
"@graasp/translations": "1.10.0",
"@graasp/ui": "2.2.0",
"@mui/icons-material": "5.11.0",
"@mui/lab": "5.0.0-alpha.117",
"@mui/material": "5.11.6",
"@sentry/react": "7.39.0",
"@sentry/tracing": "7.39.0",
"@sentry/react": "7.44.2",
"@sentry/tracing": "7.44.2",
"@uppy/core": "3.0.4",
"@uppy/dashboard": "3.2.0",
"@uppy/drag-drop": "3.0.1",
Expand Down Expand Up @@ -77,7 +77,7 @@
"check": "yarn prettier:check && yarn lint && yarn type-check",
"prettier:check": "prettier --check src/**/*.{js,ts,tsx,json}",
"prettier:write": "prettier --write src/**/*.{js,ts,tsx,json}",
"cypress:open": "env-cmd -f ./.env.test cypress open",
"cypress:open": "env-cmd -f ./.env.local cypress open",
"cypress": "concurrently \"yarn start:test\" \"wait-on http://localhost:3111 && yarn cypress:run\"",
"cypress:run": "env-cmd -f ./.env.test cypress run --headless --browser chrome --spec \"cypress/**/*.cy.js\"",
"postinstall": "husky install",
Expand Down Expand Up @@ -147,6 +147,7 @@
"@mui/icons-material": "5.11.0",
"@mui/material": "5.11.6",
"immer": "9.0.6",
"@graasp/sdk": "0.9.3",
"immutable": "4.2.4",
"@svgr/webpack": "6.5.1"
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/FavoriteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const FavoriteButton: FC<Props> = ({ item, size, type, onClick }) => {

return (
<GraaspFavoriteButton
key={text}
isFavorite={isFavorite}
className={FAVORITE_ITEM_BUTTON_CLASS}
ariaLabel={text}
handleUnfavorite={handleUnfavorite}
Expand Down
6 changes: 5 additions & 1 deletion src/components/common/HideButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import { ActionButton, ActionButtonVariant } from '@graasp/ui';
import { HIDDEN_ITEM_TAG_ID } from '../../config/constants';
import { useBuilderTranslation } from '../../config/i18n';
import { hooks, useMutation } from '../../config/queryClient';
import { HIDDEN_ITEM_BUTTON_CLASS } from '../../config/selectors';
import {
HIDDEN_ITEM_BUTTON_CLASS,
buildHideButtonId,
} from '../../config/selectors';

type Props = {
item: DiscriminatedItem;
Expand Down Expand Up @@ -77,6 +80,7 @@ const HideButton = ({
onClick={handleToggleHide}
className={HIDDEN_ITEM_BUTTON_CLASS}
disabled={!isOriginalHiddenItem}
data-cy={buildHideButtonId(Boolean(hiddenTag))}
>
<ListItemIcon>{icon}</ListItemIcon>
{text}
Expand Down
27 changes: 17 additions & 10 deletions src/components/main/Item.tsx → src/components/main/ItemCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { CSSProperties, FC, PropsWithChildren } from 'react';
import { Link } from 'react-router-dom';

import { DiscriminatedItem, ItemType, getEmbeddedLinkExtra } from '@graasp/sdk';
import { ItemMembershipRecord, ItemRecord } from '@graasp/sdk/frontend';
import {
ItemMembershipRecord,
ItemRecord,
TagRecord,
} from '@graasp/sdk/frontend';
import { Card as GraaspCard, Thumbnail } from '@graasp/ui';

import { DESCRIPTION_MAX_LENGTH } from '../../config/constants';
Expand All @@ -18,6 +22,7 @@ import { isItemUpdateAllowedForUser } from '../../utils/membership';
import EditButton from '../common/EditButton';
import FavoriteButton from '../common/FavoriteButton';
import { useCurrentUserContext } from '../context/CurrentUserContext';
import BadgesCellRenderer from '../table/BadgesCellRenderer';
import DownloadButton from './DownloadButton';
import ItemMenu from './ItemMenu';

Expand All @@ -39,12 +44,11 @@ const NameWrapper = ({
type Props = {
item: ItemRecord;
memberships: List<ItemMembershipRecord>;
tagList?: List<TagRecord>;
};

const ItemComponent: FC<Props> = ({ item, memberships }) => {
const { id, name, description } = item;

const alt = name;
const ItemComponent: FC<Props> = ({ item, memberships, tagList }) => {
const alt = item.name;
const defaultValueComponent = (
<img
style={{
Expand Down Expand Up @@ -90,19 +94,22 @@ const ItemComponent: FC<Props> = ({ item, memberships }) => {
item.toJS() as DiscriminatedItem
}
/>
<DownloadButton id={id} name={name} />
<DownloadButton id={item.id} name={item.name} />
</>
)}
</>
);
// here we use the same component as the table this is why it is instantiated a bit weirdly
const Badges = BadgesCellRenderer({ tagList });

return (
<GraaspCard
description={truncate(stripHtml(description), {
description={truncate(stripHtml(item.description), {
length: DESCRIPTION_MAX_LENGTH,
})}
Actions={Actions}
name={name}
Badges={<Badges data={item} />}
name={item.name}
creator={member?.name}
ItemMenu={
<ItemMenu
Expand All @@ -111,9 +118,9 @@ const ItemComponent: FC<Props> = ({ item, memberships }) => {
/>
}
Thumbnail={ThumbnailComponent}
cardId={buildItemCard(id)}
cardId={buildItemCard(item.id)}
NameWrapper={NameWrapper({
id,
id: item.id,
style: {
textDecoration: 'none',
color: 'inherit',
Expand Down
7 changes: 5 additions & 2 deletions src/components/main/Items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useItemSearch } from '../item/ItemSearch';
import ItemsGrid from './ItemsGrid';
import ItemsTable from './ItemsTable';

const { useManyItemMemberships } = hooks;
const { useManyItemMemberships, useTags } = hooks;

type Props = {
id: string;
Expand Down Expand Up @@ -56,12 +56,13 @@ const Items = ({
? itemSearch?.results?.map(({ id: itemId }) => itemId).toArray()
: [],
);
const { data: tagList, isLoading: isLoadingTagList } = useTags();
// todo: disable depending on showCreator
const { data: creators } = hooks.useMembers(
Object.keys(items?.groupBy(({ creator }) => creator)?.toJS() ?? []),
);

if (isMembershipsLoading) {
if (isMembershipsLoading || isLoadingTagList) {
return <Loader />;
}

Expand All @@ -73,6 +74,7 @@ const Items = ({
title={title}
items={itemSearch.results}
manyMemberships={manyMemberships}
tagList={tagList}
// This enables the possiblity to display messages (item is empty, no search result)
itemSearch={itemSearch}
headerElements={[itemSearch.input, ...headerElements]}
Expand All @@ -89,6 +91,7 @@ const Items = ({
defaultSortedColumn={defaultSortedColumn}
items={itemSearch.results}
manyMemberships={manyMemberships}
tagList={tagList}
headerElements={[itemSearch.input, ...headerElements]}
isSearching={Boolean(itemSearch.text)}
ToolbarActions={ToolbarActions}
Expand Down
Loading

0 comments on commit bde9dd9

Please sign in to comment.