Skip to content

Commit

Permalink
feat: show modal when changing visibility of published item
Browse files Browse the repository at this point in the history
  • Loading branch information
ReidyT committed Jul 22, 2024
1 parent 50b2ae9 commit 4fa2a8f
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 7 deletions.
83 changes: 77 additions & 6 deletions cypress/e2e/item/share/changeVisibility.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ItemLoginSchemaType,
ItemTagType,
PackedFolderItemFactory,
PublicationStatus,
} from '@graasp/sdk';

import { buildItemPath } from '@/config/paths';
Expand All @@ -10,8 +11,11 @@ import { SETTINGS } from '../../../../src/config/constants';
import {
SHARE_ITEM_PSEUDONYMIZED_SCHEMA_ID,
SHARE_ITEM_VISIBILITY_SELECT_ID,
UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON,
buildDataCyWrapper,
buildShareButtonId,
} from '../../../../src/config/selectors';
import { PublishedItemFactory } from '../../../fixtures/items';

const changeVisibility = (value: string): void => {
cy.get(`#${SHARE_ITEM_VISIBILITY_SELECT_ID}`).click();
Expand All @@ -25,12 +29,12 @@ describe('Visibility of an Item', () => {
cy.visit(buildItemPath(item.id));
cy.get(`#${buildShareButtonId(item.id)}`).click();

const visiblitySelect = cy.get(
const visibilitySelect = cy.get(
`#${SHARE_ITEM_VISIBILITY_SELECT_ID} + input`,
);

// visibility select default value
visiblitySelect.should('have.value', SETTINGS.ITEM_PRIVATE.name);
visibilitySelect.should('have.value', SETTINGS.ITEM_PRIVATE.name);

// change private -> public
changeVisibility(SETTINGS.ITEM_PUBLIC.name);
Expand All @@ -47,12 +51,12 @@ describe('Visibility of an Item', () => {
cy.visit(buildItemPath(item.id));
cy.get(`#${buildShareButtonId(item.id)}`).click();
cy.wait(1000);
const visiblitySelect = cy.get(
const visibilitySelect = cy.get(
`#${SHARE_ITEM_VISIBILITY_SELECT_ID} + input`,
);

// visibility select default value
visiblitySelect.should('have.value', SETTINGS.ITEM_PUBLIC.name);
visibilitySelect.should('have.value', SETTINGS.ITEM_PUBLIC.name);

// change public -> private
changeVisibility(SETTINGS.ITEM_PRIVATE.name);
Expand All @@ -69,12 +73,12 @@ describe('Visibility of an Item', () => {
cy.visit(buildItemPath(item.id));
cy.get(`#${buildShareButtonId(item.id)}`).click();
cy.wait(1000);
const visiblitySelect = cy.get(
const visibilitySelect = cy.get(
`#${SHARE_ITEM_VISIBILITY_SELECT_ID} + input`,
);

// visibility select default value
visiblitySelect.should('have.value', SETTINGS.ITEM_PUBLIC.name);
visibilitySelect.should('have.value', SETTINGS.ITEM_PUBLIC.name);

// change public -> item login
changeVisibility(SETTINGS.ITEM_LOGIN.name);
Expand Down Expand Up @@ -125,4 +129,71 @@ describe('Visibility of an Item', () => {
expect(url).to.include(item.id);
});
});

describe('Change visibility of published item', () => {
it('User should validate the change to private', () => {
const item = PublishedItemFactory(
PackedFolderItemFactory({}, { publicTag: {} }),
);
cy.setUpApi({
items: [item],
itemPublicationStatus: PublicationStatus.Published,
});
cy.visit(buildItemPath(item.id));
cy.get(`#${buildShareButtonId(item.id)}`).click();
const visibilitySelect = cy.get(
`#${SHARE_ITEM_VISIBILITY_SELECT_ID} + input`,
);

// visibility select default value
visibilitySelect.should('have.value', SETTINGS.ITEM_PUBLIC.name);

// try to change public -> private
changeVisibility(SETTINGS.ITEM_PRIVATE.name);
// the user have to confirm that changing visibility will remove the publication
cy.get(
`${buildDataCyWrapper(UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON)}`,
).click();
cy.wait(`@deleteItemTag-${ItemTagType.Public}`).then(
({ request: { url } }) => {
expect(url).to.contain(item.id);
},
);
});

it('User should validate the change to item login', () => {
const item = PublishedItemFactory(
PackedFolderItemFactory({}, { publicTag: {} }),
);
cy.setUpApi({
items: [item],
itemPublicationStatus: PublicationStatus.Published,
});
cy.visit(buildItemPath(item.id));
cy.get(`#${buildShareButtonId(item.id)}`).click();
const visibilitySelect = cy.get(
`#${SHARE_ITEM_VISIBILITY_SELECT_ID} + input`,
);

// visibility select default value
visibilitySelect.should('have.value', SETTINGS.ITEM_PUBLIC.name);

// try to change public -> item login
changeVisibility(SETTINGS.ITEM_LOGIN.name);
// the user have to confirm that changing visibility will remove the publication
cy.get(
`${buildDataCyWrapper(UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON)}`,
).click();
cy.wait([
`@deleteItemTag-${ItemTagType.Public}`,
'@putItemLoginSchema',
]).then((data) => {
const {
request: { url },
} = data[0];
expect(url).to.contain(item.id);
expect(url).to.contain(ItemTagType.Public); // originally item login
});
});
});
});
79 changes: 79 additions & 0 deletions src/components/item/sharing/UpdateVisibilityModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Typography,
} from '@mui/material';

import { useBuilderTranslation } from '@/config/i18n';
import {
UPDATE_VISIBILITY_MODAL_CANCEL_BUTTON,
UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON,
} from '@/config/selectors';
import { BUILDER } from '@/langs/constants';

export type Visibility = {
name: string;
value: string;
};

type Props = {
isOpen: boolean;
newVisibility?: Visibility;
onClose: () => void;
onValidate: (visibility: string) => void;
};

export const UpdateVisibilityModal = ({
isOpen,
newVisibility,
onClose,
onValidate,
}: Props): JSX.Element | null => {
const { t } = useBuilderTranslation();

if (!newVisibility) {
return null;
}

const handleValidate = async () => {
onValidate(newVisibility.value);
};

return (
<Dialog open={isOpen}>
<DialogTitle>
<Typography variant="h3">
{t(BUILDER.UPDATE_VISIBILITY_MODAL_TITLE)}
</Typography>
</DialogTitle>
<DialogContent>
<Typography>
{t(BUILDER.UPDATE_VISIBILITY_MODAL_DESCRIPTION)}
</Typography>
</DialogContent>
<DialogActions>
<Button
data-cy={UPDATE_VISIBILITY_MODAL_CANCEL_BUTTON}
onClick={onClose}
variant="outlined"
>
{t(BUILDER.CANCEL_BUTTON)}
</Button>
<Button
data-cy={UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON}
onClick={handleValidate}
variant="contained"
>
{t(BUILDER.UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON, {
visibility: newVisibility.name,
})}
</Button>
</DialogActions>
</Dialog>
);
};

export default UpdateVisibilityModal;
82 changes: 82 additions & 0 deletions src/components/item/sharing/VisibilitySelect.hook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useState } from 'react';

import { PublicationStatus } from '@graasp/sdk';

import { hooks } from '@/config/queryClient';

import { SETTINGS } from '../../../config/constants';
import { useBuilderTranslation } from '../../../config/i18n';
import { BUILDER } from '../../../langs/constants';
import { Visibility } from './UpdateVisibilityModal';

const { usePublicationStatus } = hooks;

type Props = {
itemId: string;
visibility?: string;
updateVisibility: (newVisibility: string) => void | Promise<void>;
};

type UseVisibilitySelect = {
isModalOpen: boolean;
pendingVisibility: Visibility | undefined;
onCloseModal: () => void;
onValidateModal: (newVisibility: string) => void;
onVisibilityChange: (newVisibility: string) => void;
};

const useVisibilitySelect = ({
itemId,
visibility,
updateVisibility,
}: Props): UseVisibilitySelect => {
const { t: translateBuilder } = useBuilderTranslation();
const { data: publicationStatus } = usePublicationStatus(itemId);

// The visibility value is temporary and awaits user confirmation through the dialog.
const [pendingVisibility, setPendingVisibility] = useState<
Visibility | undefined
>();

const translatedVisibilities = {
[SETTINGS.ITEM_LOGIN.name]: translateBuilder(
BUILDER.ITEM_SETTINGS_VISIBILITY_PSEUDONYMIZED_LABEL,
),
[SETTINGS.ITEM_PUBLIC.name]: translateBuilder(
BUILDER.ITEM_SETTINGS_VISIBILITY_PUBLIC_INFORMATIONS,
),
[SETTINGS.ITEM_PRIVATE.name]: translateBuilder(
BUILDER.ITEM_SETTINGS_VISIBILITY_PRIVATE_LABEL,
),
};

const onVisibilityChange = (newVisibility: string) => {
if (
visibility === SETTINGS.ITEM_PUBLIC.name &&
publicationStatus === PublicationStatus.Published
) {
setPendingVisibility({
name: translatedVisibilities[newVisibility],
value: newVisibility,
});
} else {
updateVisibility(newVisibility);
}
};

const onCloseModal = () => setPendingVisibility(undefined);
const onValidateModal = (newVisibility: string) => {
onCloseModal();
updateVisibility(newVisibility);
};

return {
isModalOpen: Boolean(pendingVisibility),
pendingVisibility,
onCloseModal,
onValidateModal,
onVisibilityChange,
};
};

export default useVisibilitySelect;
24 changes: 23 additions & 1 deletion src/components/item/sharing/VisibilitySelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { useBuilderTranslation } from '../../../config/i18n';
import { SHARE_ITEM_VISIBILITY_SELECT_ID } from '../../../config/selectors';
import { BUILDER } from '../../../langs/constants';
import ItemLoginSchemaSelect from './ItemLoginSchemaSelect';
import UpdateVisibilityModal from './UpdateVisibilityModal';
import useVisibilitySelect from './VisibilitySelect.hook';

type Props = {
item: PackedItem;
Expand All @@ -28,6 +30,18 @@ const VisibilitySelect = ({ item, edit }: Props): JSX.Element | null => {
updateVisibility,
} = useVisibility(item);

const {
isModalOpen,
pendingVisibility,
onCloseModal,
onValidateModal,
onVisibilityChange,
} = useVisibilitySelect({
itemId: item.id,
visibility,
updateVisibility,
});

if (isLoading) {
return <Loader />;
}
Expand Down Expand Up @@ -68,10 +82,18 @@ const VisibilitySelect = ({ item, edit }: Props): JSX.Element | null => {

return (
<>
{isModalOpen && (
<UpdateVisibilityModal
isOpen={isModalOpen}
newVisibility={pendingVisibility}
onClose={onCloseModal}
onValidate={onValidateModal}
/>
)}
{edit && (
<Select
value={visibility}
onChange={(e) => updateVisibility(e.target.value)}
onChange={(e) => onVisibilityChange(e.target.value)}
disabled={isDisabled}
id={SHARE_ITEM_VISIBILITY_SELECT_ID}
sx={{ mr: 1 }}
Expand Down
4 changes: 4 additions & 0 deletions src/config/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,10 @@ export const buildPublicationStatus = (status: PublicationStatus): string =>

export const PUBLIC_VISIBILITY_MODAL_VALIDATE_BUTTON =
'publicVisbilityModalValidateButton';
export const UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON =
'updateVisbilityModalValidateButton';
export const UPDATE_VISIBILITY_MODAL_CANCEL_BUTTON =
'updateVisbilityModalCancelButton';

export const DEBOUNCED_TEXT_FIELD_ID = 'debouncedTextfield';

Expand Down
5 changes: 5 additions & 0 deletions src/langs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ export const BUILDER = {
PUBLIC_VISIBILITY_MODAL_VALIDATE_BUTTON:
'PUBLIC_VISIBILITY_MODAL_VALIDATE_BUTTON',

UPDATE_VISIBILITY_MODAL_TITLE: 'UPDATE_VISIBILITY_MODAL_TITLE',
UPDATE_VISIBILITY_MODAL_DESCRIPTION: 'UPDATE_VISIBILITY_MODAL_DESCRIPTION',
UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON:
'UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON',

PUBLISHED_ITEMS_TITLE: 'PUBLISHED_ITEMS_TITLE',

SHARE_ITEM_CSV_IMPORT_MODAL_TITLE: 'SHARE_ITEM_CSV_IMPORT_MODAL_TITLE',
Expand Down
3 changes: 3 additions & 0 deletions src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@
"PUBLIC_VISIBILITY_MODAL_TITLE": "Item visibility",
"PUBLIC_VISIBILITY_MODAL_DESCRIPTION": "The visibility of this item is not Public. To publish it in the Library, you must set its visibility to pubic. This action will allow any user to have access to your item. Do you want to continue?",
"PUBLIC_VISIBILITY_MODAL_VALIDATE_BUTTON": "Make it public",
"UPDATE_VISIBILITY_MODAL_TITLE": "Change visibility of published item",
"UPDATE_VISIBILITY_MODAL_DESCRIPTION": "This element is currently published in the Graasp library. If you change its visibility, the item will be unpublished and will no longer be available in the library. Would you like to continue?",
"UPDATE_VISIBILITY_MODAL_VALIDATE_BUTTON": "Make it {{visibility}}",
"LINK_DEFAULT_NAME": "My Link",
"MOVE_BUTTON": "Move",
"MOVE_BUTTON_zero": "Move",
Expand Down

0 comments on commit 4fa2a8f

Please sign in to comment.