Skip to content
This repository has been archived by the owner on Jan 23, 2025. It is now read-only.

feat: factor out create link modal #1566

Merged
merged 3 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
83 changes: 53 additions & 30 deletions cypress/e2e/item/create/createLink.cy.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
import { PackedFolderItemFactory } from '@graasp/sdk';

import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import { ITEM_FORM_CONFIRM_BUTTON_ID } from '../../../../src/config/selectors';
import {
GRAASP_LINK_ITEM,
GRAASP_LINK_ITEM_NO_PROTOCOL,
INVALID_LINK_ITEM,
LINK_ITEM_WITH_BLANK_NAME,
} from '../../../fixtures/links';
CREATE_ITEM_BUTTON_ID,
CREATE_ITEM_LINK_ID,
ITEM_FORM_CONFIRM_BUTTON_ID,
ITEM_FORM_LINK_INPUT_ID,
ITEM_FORM_NAME_INPUT_ID,
} from '../../../../src/config/selectors';
import { CREATE_ITEM_PAUSE } from '../../../support/constants';
import { createLink } from '../../../support/createUtils';

const openLinkModal = () => {
cy.get(`#${CREATE_ITEM_BUTTON_ID}`).click();
cy.get(`#${CREATE_ITEM_LINK_ID}`).click();
};

const createLink = ({ url }: { url: string }): void => {
openLinkModal();

cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).clear().type(url);
// wait for iframely to fill fields
cy.get(`[role=dialog]`).should('contain', 'Page title');
};

describe('Create Link', () => {
it('create link on Home', () => {
cy.setUpApi();
cy.visit(HOME_PATH);

// create
createLink(GRAASP_LINK_ITEM);
createLink({ url: 'https://graasp.org' });
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.wait('@postItem').then(() => {
// check item is created and displayed
Expand All @@ -33,7 +46,8 @@ describe('Create Link', () => {
cy.visit(HOME_PATH);

// create
createLink(GRAASP_LINK_ITEM_NO_PROTOCOL);
createLink({ url: 'graasp.org' });
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.wait('@postItem').then(() => {
// check item is created and displayed
Expand All @@ -44,6 +58,21 @@ describe('Create Link', () => {
});
});

it('enter valid link, then reset link', () => {
cy.setUpApi();
cy.visit(HOME_PATH);

// enter valid data
createLink({ url: 'graasp.org' });
cy.get(`#${ITEM_FORM_NAME_INPUT_ID} input`).should('not.be.empty');

// type a wrong link and cannot save
cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).clear().type('something');
cy.get(`#${ITEM_FORM_NAME_INPUT_ID} input`).should('not.be.empty');
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).should('be.disabled');
});

it('create link in item', () => {
const FOLDER = PackedFolderItemFactory();
const CHILD = PackedFolderItemFactory({ parentItem: FOLDER });
Expand All @@ -55,7 +84,8 @@ describe('Create Link', () => {
cy.visit(buildItemPath(id));

// create
createLink(GRAASP_LINK_ITEM);
createLink({ url: 'https://graasp.org' });
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.wait('@postItem').then(({ request: { url } }) => {
expect(url).to.contain(FOLDER.id);
Expand All @@ -69,17 +99,14 @@ describe('Create Link', () => {

describe('Error handling', () => {
it('cannot add an invalid link', () => {
const FOLDER = PackedFolderItemFactory();
cy.setUpApi({ items: [FOLDER] });
const { id } = FOLDER;
cy.setUpApi();
cy.visit(HOME_PATH);

// go to children item
cy.visit(buildItemPath(id));

// create
createLink(INVALID_LINK_ITEM, {
confirm: false,
});
// fill link and name
openLinkModal();
cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).type('invalid');
cy.get(`#${ITEM_FORM_NAME_INPUT_ID}`).type('name');
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).should(
'have.prop',
Expand All @@ -89,17 +116,13 @@ describe('Create Link', () => {
});

it('cannot have an empty name', () => {
const FOLDER = PackedFolderItemFactory();
cy.setUpApi({ items: [FOLDER] });
const { id } = FOLDER;

// go to children item
cy.visit(buildItemPath(id));
cy.setUpApi();
cy.visit(HOME_PATH);

// create
createLink(LINK_ITEM_WITH_BLANK_NAME, {
confirm: false,
});
// fill link and clear name
createLink({ url: 'https://graasp.org' });
cy.get(`#${ITEM_FORM_NAME_INPUT_ID}`).clear();
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).should(
'have.prop',
Expand Down
38 changes: 0 additions & 38 deletions cypress/fixtures/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,6 @@ export const GRAASP_LINK_ITEM: LinkItemType = PackedLinkItemFactory({
}),
});

export const GRAASP_LINK_ITEM_NO_PROTOCOL: LinkItemType = PackedLinkItemFactory(
{
creator: CURRENT_USER,
extra: buildLinkExtra({
url: 'graasp.eu',
html: '',
thumbnails: ['https://graasp.eu/img/epfl/logo-tile.png'],
icons: [
'https://graasp.eu/cdn/img/epfl/favicons/favicon-32x32.png?v=yyxJ380oWY',
],
}),
},
);

export const GRAASP_LINK_ITEM_IFRAME_ONLY: LinkItemType = PackedLinkItemFactory(
{
...GRAASP_LINK_ITEM,
Expand All @@ -59,27 +45,3 @@ export const YOUTUBE_LINK_ITEM: LinkItemType = PackedLinkItemFactory({
showLinkIframe: true,
},
});

export const INVALID_LINK_ITEM: LinkItemType = PackedLinkItemFactory({
creator: CURRENT_USER,
name: 'graasp youtube link',
description: 'a description for graasp youtube link',
extra: buildLinkExtra({
url: 'wrong link',
html: '',
thumbnails: [],
icons: [],
}),
});

export const LINK_ITEM_WITH_BLANK_NAME: LinkItemType = PackedLinkItemFactory({
creator: CURRENT_USER,
name: '',
description: 'a description for graasp youtube link',
extra: buildLinkExtra({
url: 'https://www.youtube.com/watch?v=FmiEgBMTPLo',
html: '',
thumbnails: [],
icons: [],
}),
});
22 changes: 1 addition & 21 deletions cypress/support/commands/item.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
DiscriminatedItem,
ItemType,
getAppExtra,
getDocumentExtra,
} from '@graasp/sdk';
import { DiscriminatedItem, getAppExtra, getDocumentExtra } from '@graasp/sdk';

import {
CUSTOM_APP_CYPRESS_ID,
Expand All @@ -13,7 +8,6 @@ import {
ITEM_FORM_APP_URL_ID,
ITEM_FORM_CONFIRM_BUTTON_ID,
ITEM_FORM_DOCUMENT_TEXT_SELECTOR,
ITEM_FORM_LINK_INPUT_ID,
ITEM_FORM_NAME_INPUT_ID,
ITEM_MEMBERSHIP_PERMISSION_SELECT_CLASS,
MY_GRAASP_ITEM_PATH,
Expand Down Expand Up @@ -122,20 +116,6 @@ Cypress.Commands.add(
},
);

Cypress.Commands.add(
'fillLinkModal',
({ extra = {} }, { confirm = true } = {}) => {
cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).type(
// first select all the text and then remove it to have a clear field, then type new text
`{selectall}{backspace}${extra?.[ItemType.LINK]?.url}`,
);

if (confirm) {
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();
}
},
);

Cypress.Commands.add(
'fillDocumentModal',
({ name = '', extra }, { confirm = true } = {}) => {
Expand Down
15 changes: 0 additions & 15 deletions cypress/support/createUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
DiscriminatedItem,
DocumentItemType,
ItemType,
LinkItemType,
} from '@graasp/sdk';

import {
Expand All @@ -13,7 +12,6 @@ import {
CREATE_ITEM_DOCUMENT_ID,
CREATE_ITEM_FILE_ID,
CREATE_ITEM_H5P_ID,
CREATE_ITEM_LINK_ID,
CREATE_ITEM_ZIP_ID,
DASHBOARD_UPLOADER_ID,
H5P_DASHBOARD_UPLOADER_ID,
Expand Down Expand Up @@ -49,15 +47,6 @@ export const createFolder = (
cy.fillFolderModal(payload, options);
};

export const createLink = (
payload: LinkItemType,
options?: { confirm?: boolean },
): void => {
cy.get(`#${CREATE_ITEM_BUTTON_ID}`).click();
cy.get(`#${CREATE_ITEM_LINK_ID}`).click();
cy.fillLinkModal(payload, options);
};

export const createFile = (
payload: FileItemForTest,
options?: { confirm?: boolean },
Expand Down Expand Up @@ -91,10 +80,6 @@ export const createItem = (
cy.get(`#${CREATE_ITEM_BUTTON_ID}`).click();

switch (payload.type) {
case ItemType.LINK:
cy.get(`#${CREATE_ITEM_LINK_ID}`).click();
cy.fillLinkModal(payload, options);
break;
case ItemType.S3_FILE:
case ItemType.LOCAL_FILE: {
const { confirm = true } = options;
Expand Down
5 changes: 0 additions & 5 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
AppItemExtra,
DiscriminatedItem,
DocumentItemExtra,
ItemType,
PermissionLevel,
} from '@graasp/sdk';

Expand Down Expand Up @@ -53,10 +52,6 @@ declare global {

goToHome(): void;

fillLinkModal(
payload?: { extra?: { [ItemType.LINK]: { url?: string } } },
options?: { confirm?: boolean },
): void;
fillDocumentModal(
payload: {
name: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/item/form/ItemNameField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ export const ItemNameField = ({
const handleClearClick = () => {
reset({ name: '' });
};

return (
<TextField
variant="standard"
autoFocus={autoFocus}
id={ITEM_FORM_NAME_INPUT_ID}
label={translateBuilder(BUILDER.CREATE_NEW_ITEM_NAME_LABEL)}
required={required}
// always shrink because setting name from defined app does not shrink automatically
slotProps={{
inputLabel: { shrink: true },
Expand Down
67 changes: 36 additions & 31 deletions src/components/item/form/link/LinkDescriptionField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UseFormRegisterReturn } from 'react-hook-form';
import { useFormContext } from 'react-hook-form';

import { IconButton, TextField } from '@mui/material';

Expand All @@ -9,48 +9,53 @@ import { BUILDER } from '@/langs/constants';

type LinkDescriptionFieldProps = {
onRestore: () => void;
onClear: () => void;
showRestoreButton?: boolean;
form: UseFormRegisterReturn;
showClearButton?: boolean;
showRestoreButton: boolean;
};
const LinkDescriptionField = ({
form,
onRestore,
onClear,
showRestoreButton,
showClearButton,
}: LinkDescriptionFieldProps): JSX.Element => {
const {
register,
setValue,
getValues,
formState: { errors },
} = useFormContext<{ description: string }>();
const { t } = useBuilderTranslation();
const { description } = getValues();
return (
<TextField
label={t(BUILDER.DESCRIPTION_LABEL)}
variant="standard"
InputLabelProps={{ shrink: true }}
{...form}
InputProps={{
endAdornment: (
<>
<IconButton
onClick={onRestore}
sx={{
visibility: showRestoreButton ? 'visible' : 'hidden',
}}
>
<Undo2Icon size="20" />
</IconButton>
slotProps={{
inputLabel: { shrink: true },
input: {
endAdornment: (
<>
<IconButton
onClick={onRestore}
sx={{
visibility: showRestoreButton ? 'visible' : 'hidden',
}}
>
<Undo2Icon size="20" />
</IconButton>

<IconButton
onClick={onClear}
sx={{
visibility: showClearButton ? 'visible' : 'hidden',
}}
>
<XIcon size="20" />
</IconButton>
</>
),
<IconButton
onClick={() => setValue('description', '')}
sx={{
visibility: description ? 'visible' : 'hidden',
}}
>
<XIcon size="20" />
</IconButton>
</>
),
},
}}
error={Boolean(errors.description)}
helperText={errors.description?.message}
{...register('description')}
/>
);
};
Expand Down
Loading