From 8a7b920c1fcaa5bd60bd8c63aa0ef8186915e9aa Mon Sep 17 00:00:00 2001 From: Anthony Le Courric Date: Fri, 8 Sep 2023 17:12:51 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20replace=20gromme?= =?UTF-8?q?t=20Select=20with=20Cunnigham=20Select?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - replace the Grommet Select component with the Cunningham Select component --- CHANGELOG.md | 4 + .../Dashboard/DashboardInstructor/index.tsx | 26 ++--- .../components/MarkdownWizard/index.spec.tsx | 2 +- .../components/MarkdownWizard/index.tsx | 2 + .../Manage/VideoCreateForm.spec.tsx | 30 +++-- .../components/CreatePlaylistForm.spec.tsx | 18 ++- .../Playlist/components/PlaylistForm.spec.tsx | 107 +++++++---------- .../Playlist/components/PlaylistForm.tsx | 108 ++++++++--------- .../Playlist/components/PlaylistPage.tsx | 109 ++++++++++-------- .../api/useUpdatePlaylistAccess/index.ts | 10 +- .../components/AddUserAccessForm.spec.tsx | 8 +- .../components/AddUserAccessForm.tsx | 26 ++--- .../components/PlaylistUserList.spec.tsx | 6 +- .../components/UpdatePlaylistPage.spec.tsx | 27 ++--- .../components/UserListRow.spec.tsx | 26 ++--- .../UpdatePlaylist/components/UserListRow.tsx | 55 ++++++--- .../components/UserRoleOptions.spec.tsx | 6 +- .../components/UserRoleOptions.tsx | 6 +- .../lib_common/src/cunningham-style.css | 31 +++-- .../lib_common/src/cunningham-tokens.css | 13 +++ .../src/utils/tests/factories.ts | 2 +- .../LanguageSelector/index.spec.tsx | 8 +- .../src/components/LanguageSelector/index.tsx | 26 +++-- .../components/MarkdownEditor/index.spec.tsx | 28 +++-- .../src/components/MarkdownEditor/index.tsx | 15 +-- .../components/MarkdownViewer/index.spec.tsx | 6 +- .../LanguageSelect/index.spec.tsx | 78 ++++--------- .../LanguageSelect/index.tsx | 28 +++-- .../index.spec.tsx | 12 +- .../common/VideoWidgetProvider/index.spec.tsx | 13 ++- .../widgets/DownloadVideo/index.spec.tsx | 9 +- .../widgets/DownloadVideo/index.tsx | 43 +++---- .../widgets/LicenseManager/index.spec.tsx | 47 ++++---- .../widgets/LicenseManager/index.tsx | 72 +++++++----- .../widgets/LiveJoinMode/index.spec.tsx | 57 ++++----- .../widgets/LiveJoinMode/index.tsx | 31 ++--- .../CreateVOD/LicenseSelect/index.spec.tsx | 73 ++++-------- .../CreateVOD/LicenseSelect/index.tsx | 37 +++--- .../VideoWizard/CreateVOD/index.spec.tsx | 54 +++++---- .../common/VideoWizard/CreateVOD/index.tsx | 1 + 40 files changed, 651 insertions(+), 609 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6c580de24..bd9765421d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed + +- Replace grommet Select by Cunningham Select (#2400) + ## [4.4.0] - 2023-09-08 ### Added diff --git a/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardInstructor/index.tsx b/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardInstructor/index.tsx index a2c5940ddf..5bd781f8b1 100644 --- a/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardInstructor/index.tsx +++ b/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardInstructor/index.tsx @@ -1,4 +1,5 @@ -import { Box, Heading, Pagination, Paragraph, Select, Text } from 'grommet'; +import { Select } from '@openfun/cunningham-react'; +import { Box, Heading, Pagination, Paragraph, Text } from 'grommet'; import { Maybe } from 'lib-common'; import { FileDepository, Loader } from 'lib-components'; import React, { FocusEvent, useState } from 'react'; @@ -10,6 +11,8 @@ import { useUpdateFileDepository, } from 'apps/deposit/data/queries'; +type SelectProps = React.ComponentPropsWithoutRef; + const PAGE_SIZE = 10; const messages = defineMessages({ @@ -77,7 +80,7 @@ export const DashboardInstructor = ({ const [indices, setIndices] = useState([0, PAGE_SIZE]); const [readFilter, setReadFilter] = useState>(undefined); - const { data, isError, isLoading, refetch } = useDepositedFiles( + const { data, isError, isLoading } = useDepositedFiles( fileDepository.id, { limit: `${PAGE_SIZE}`, @@ -103,9 +106,8 @@ export const DashboardInstructor = ({ }, ]; - const onReadFilterChange = (event: React.ChangeEvent) => { - setReadFilter(event.target.value); - refetch(); + const onReadFilterChange: SelectProps['onChange'] = (event) => { + setReadFilter(event.target.value as Maybe); }; const { mutate } = useUpdateFileDepository(fileDepository.id); @@ -190,17 +192,11 @@ export const DashboardInstructor = ({ pad="medium" > { - if (!organizationResponse) { - return; - } - - if (organizations.length < organizationResponse.count) { - setCurrentOrganizationPage( - (currentPage) => currentPage + 1, - ); - } - }} - labelKey="name" - valueKey={{ key: 'id', reduce: true }} - /> - - - {intl.formatMessage(messages.organizationHelper)} - + disabled={!isEditable} + options={organizations.map((organization) => ({ + label: organization.name, + value: organization.id, + }))} + onChange={(e) => { + setFormValues((value) => ({ + ...value, + organizationId: e.target.value as string, + })); + }} + fullWidth + text={intl.formatMessage(messages.organizationHelper)} + value={formValues.organizationId} + /> - - { - setRoleValue(option); + onChange={(evt) => { + setRoleValue( + options?.find((option) => option.value === evt.target.value), + ); }} + fullWidth + value={roleValue?.value} + clearable={false} /> ', () => { render(); - expect(await screen.findByText('sbire 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('sbire 1')).toBeInTheDocument(); + }); expect(screen.getByText('sbire 2')).toBeInTheDocument(); expect(screen.getByText('sbire 3')).toBeInTheDocument(); }); diff --git a/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UpdatePlaylistPage.spec.tsx b/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UpdatePlaylistPage.spec.tsx index 87cb57bef3..cb5009fb1e 100644 --- a/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UpdatePlaylistPage.spec.tsx +++ b/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UpdatePlaylistPage.spec.tsx @@ -1,4 +1,4 @@ -import { screen, waitFor } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import fetchMock from 'fetch-mock'; import { Playlist } from 'lib-components'; @@ -78,13 +78,20 @@ describe('', () => { expect( await screen.findByRole('heading', { name: 'Main informations' }), ).toBeInTheDocument(); + expect(await screen.findByText('first orga')).toBeInTheDocument(); expect( - await screen.findByRole('button', { - name: 'Open Drop; Selected: id orga', + screen.queryByRole('combobox', { + name: 'Organization', }), ).not.toBeDisabled(); expect(screen.getByDisplayValue('playlist title')).not.toBeDisabled(); - expect(screen.getByDisplayValue('1 year')).not.toBeDisabled(); + + expect(screen.getByText('1 year')).toBeInTheDocument(); + expect( + screen.queryByRole('combobox', { + name: 'Retention duration', + }), + ).not.toBeDisabled(); expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument(); @@ -103,12 +110,6 @@ describe('', () => { await userEvent.click(screen.getByRole('button', { name: 'Save' })); - await waitFor(() => - expect( - screen.getByRole('button', { name: 'Open Drop; Selected: id orga' }), - ).toBeDisabled(), - ); - fetchMock.mock('/api/playlists/some-id/', playlist, { overwriteRoutes: true, }); @@ -118,12 +119,6 @@ describe('', () => { await screen.findByText('Playlist updated with success.'), ).toBeInTheDocument(); - await waitFor(() => - expect( - screen.getByRole('button', { name: 'Open Drop; Selected: id orga' }), - ).not.toBeDisabled(), - ); - expect( screen.getByRole('button', { name: 'Delete playlist' }), ).toBeInTheDocument(); diff --git a/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.spec.tsx b/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.spec.tsx index 0b88e519ff..68418f7777 100644 --- a/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.spec.tsx +++ b/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.spec.tsx @@ -75,18 +75,13 @@ describe('', () => { expect( screen.getByText('my full name (my-email@openfun.fr)'), ).toBeInTheDocument(); - expect( - screen.getByRole('button', { name: /Open Drop/ }), - ).toBeInTheDocument(); - expect(screen.getByRole('textbox')).toHaveValue('Administrator'); expect( screen.getByRole('button', { name: 'Delete user.' }), ).toBeInTheDocument(); - await userEvent.click(screen.getByRole('button', { name: /Open Drop/ })); - + await userEvent.click(screen.getByText('Administrator')); expect( - await screen.findByRole('option', { name: 'Instructor' }), + screen.getByRole('option', { name: 'Instructor' }), ).toBeInTheDocument(); expect(screen.getByRole('option', { name: 'Student' })).toBeInTheDocument(); expect( @@ -109,8 +104,7 @@ describe('', () => { />, ); - await userEvent.click(screen.getByRole('button', { name: /Open Drop/ })); - + await userEvent.click(screen.getByText('Administrator')); const instructorOption = screen.getByRole('option', { name: 'Instructor', }); @@ -118,7 +112,7 @@ describe('', () => { await userEvent.click(instructorOption); - expect(screen.getByRole('textbox')).toHaveValue('Instructor'); + expect(screen.getByText('Instructor')).toBeInTheDocument(); expect( await screen.findByText('Right has been updated with success.'), ).toBeInTheDocument(); @@ -144,8 +138,9 @@ describe('', () => { />, ); - await userEvent.click(screen.getByRole('button', { name: /Open Drop/ })); + expect(screen.queryByText('Instructor')).not.toBeInTheDocument(); + await userEvent.click(screen.getByText('Administrator')); const instructorOption = screen.getByRole('option', { name: 'Instructor', }); @@ -153,7 +148,7 @@ describe('', () => { await userEvent.click(instructorOption); - expect(screen.getByRole('textbox')).toHaveValue('Instructor'); + expect(screen.getByText('Instructor')).toBeInTheDocument(); deferred.reject(); @@ -161,7 +156,7 @@ describe('', () => { await screen.findByText('An error occurred while updating the right.'), ).toBeInTheDocument(); - expect(screen.getByRole('textbox')).toHaveValue('Administrator'); + expect(await screen.findByText('Administrator')).toBeInTheDocument(); }); it('calls for delete', async () => { @@ -260,8 +255,9 @@ describe('', () => { expect( screen.getByText('my full name (my-email@openfun.fr)'), ).toBeInTheDocument(); - expect(screen.getByRole('button', { name: /Open Drop/ })).toBeDisabled(); - expect(screen.getByRole('textbox')).toHaveValue('Administrator'); + const select = screen.getByRole('combobox'); + expect(select.contains(screen.getByText('Administrator'))).toBeTruthy(); + expect(select.hasAttribute('disabled')).toBeTruthy(); expect(screen.getByRole('button', { name: 'Delete user.' })).toBeDisabled(); }); }); diff --git a/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.tsx b/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.tsx index 240b6af976..312a20e0bb 100644 --- a/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.tsx +++ b/src/frontend/apps/standalone_site/src/features/Playlist/features/UpdatePlaylist/components/UserListRow.tsx @@ -1,4 +1,5 @@ -import { Box, Button, Select, Text } from 'grommet'; +import { Select } from '@openfun/cunningham-react'; +import { Box, Button, Text } from 'grommet'; import { AnonymousUser, BinSVG, @@ -13,12 +14,17 @@ import { toast } from 'react-hot-toast'; import { defineMessages, useIntl } from 'react-intl'; import { useDeletePlaylistAccess } from '../api/useDeletePlaylistAccess'; -import { useUpdatePlaylistAcess } from '../api/useUpdatePlaylistAccess'; +import { useUpdatePlaylistAccess } from '../api/useUpdatePlaylistAccess'; import { PlaylistAccess, PlaylistRole } from '../types/playlistAccess'; import { userRoleOptions } from './UserRoleOptions'; const messages = defineMessages({ + labelSelectRole: { + defaultMessage: 'Role', + description: 'The label on the select role input.', + id: 'features.Playlist.features.UpdatePlaylist.components.UserListRow.labelSelectRole', + }, anonymousUser: { defaultMessage: 'Anonymous', description: 'Update page, playlist access row for user without full name.', @@ -66,9 +72,6 @@ const messages = defineMessages({ }, }); -type Option = { label: string; key: PlaylistRole }; -type SelectOnChangeEvent = { option: Option }; - interface UserLabelColumnProps { user: PlaylistAccess['user']; } @@ -84,7 +87,17 @@ export const UserLabelColumn = ({ user }: UserLabelColumnProps) => { userLabel = user.email; } - return {userLabel}; + return ( + + {userLabel} + + ); }; interface UserRolesColumnProps { @@ -101,16 +114,16 @@ export const UserRolesColumn = ({ const intl = useIntl(); const options = userRoleOptions(intl); - const initialOption = options.find((option) => option.key === role); + const initialRole = options.find((option) => option.value === role)?.value; + const [userRole, setUserRole] = useState(initialRole); - const [userRole, setUserRole] = useState(initialOption); - const { mutate: updateMutation } = useUpdatePlaylistAcess(playlistAccessId, { + const { mutate: updateMutation } = useUpdatePlaylistAccess(playlistAccessId, { onSuccess: () => { toast.success(intl.formatMessage(messages.updatePlaylistAccessSuccess)); }, onError: () => { toast.error(intl.formatMessage(messages.updatePlaylistAccessError)); - setUserRole(initialOption); + setUserRole(initialRole); }, }); const { currentUser } = useCurrentUser(); @@ -122,15 +135,23 @@ export const UserRolesColumn = ({ return ( ({ - label: intl.formatDisplayName(lang, { type: 'language' }), + label: intl.formatDisplayName(lang, { type: 'language' }) as string, value: lang, }))} - onChange={({ option }: { option: { label: string; value: string } }) => { - onLanguageChange(option.value); + fullWidth={fullWidth} + value={currentLanguage} + onChange={(evt) => { + if (evt.target.value !== currentLanguage) { + onLanguageChange(evt.target.value as string); + } }} + clearable={false} + disabled={disabled} /> ); }; diff --git a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx index 1e92ee628d..e136315928 100644 --- a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx @@ -159,11 +159,14 @@ describe('', () => { act(() => documentDeferred.resolve(markdownDocument)); await screen.findByDisplayValue(markdownDocument.translations[0].title); + + const selectLanguage = screen.getByRole('combobox', { + name: /Select language/i, + }); + expect(selectLanguage).toBeInTheDocument(); expect( - screen.getByRole('button', { - name: /Select language; Selected: French/i, - }), - ).toBeVisible(); + await within(selectLanguage).findByText('French'), + ).toBeInTheDocument(); await waitFor(() => expect( screen.getAllByText(markdownDocument.translations[0].content).length, @@ -251,7 +254,7 @@ describe('', () => { // Change language to fr await userEvent.click( - screen.getByRole('button', { name: /Select language/i }), + screen.getByRole('combobox', { name: /Select language/i }), ); await userEvent.click( await screen.findByRole('option', { name: /French/i }), @@ -591,7 +594,12 @@ describe('', () => { ).toHaveTextContent(`[//]: # (${markdownImageId})`), ); - expect(screen.getByRole('status')).toHaveTextContent('cats.gif0%'); + const status = await screen.findByRole('status', { + description: (_, element) => + element?.textContent?.includes('cats.gif') ?? false, + }); + + expect(status).toHaveTextContent('cats.gif0%'); // Image is uploaded fetchMock.get( @@ -610,15 +618,11 @@ describe('', () => { }); await waitFor(() => - expect(screen.getByRole('status')).toHaveTextContent( - 'Processing cats.gif', - ), + expect(status).toHaveTextContent('Processing cats.gif'), ); // Image is processed - expect(await screen.findByRole('status')).toHaveTextContent( - 'Uploaded cats.gif', - ); + await waitFor(() => expect(status).toHaveTextContent('Uploaded cats.gif')); await waitFor(() => expect( container.querySelector('div[class="cm-line"]')!, diff --git a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx index 914a987241..0402f4efdd 100644 --- a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx @@ -294,17 +294,18 @@ export const MarkdownEditor = ({ markdownDocumentId }: MarkdownEditorProps) => { /> - + - - + + + diff --git a/src/frontend/packages/lib_markdown/src/components/MarkdownViewer/index.spec.tsx b/src/frontend/packages/lib_markdown/src/components/MarkdownViewer/index.spec.tsx index 24d736192c..33cbbbb82d 100644 --- a/src/frontend/packages/lib_markdown/src/components/MarkdownViewer/index.spec.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MarkdownViewer/index.spec.tsx @@ -45,7 +45,7 @@ describe('', () => { // Change language to fr await userEvent.click( - screen.getByRole('button', { name: /Select language/i }), + screen.getByRole('combobox', { name: /Select language/i }), ); await userEvent.click( await screen.findByRole('option', { name: /French/i }), @@ -75,7 +75,7 @@ describe('', () => { // Change language to fr await userEvent.click( - screen.getByRole('button', { name: /Select language/i }), + screen.getByRole('combobox', { name: /Select language/i }), ); await userEvent.click( await screen.findByRole('option', { name: /French/i }), @@ -127,7 +127,7 @@ describe('', () => { // Change language to fr await userEvent.click( - screen.getByRole('button', { name: /Select language/i }), + screen.getByRole('combobox', { name: /Select language/i }), ); await userEvent.click( await screen.findByRole('option', { name: /French/i }), diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.spec.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.spec.tsx index 21d5d87e3a..88c9eab100 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.spec.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.spec.tsx @@ -1,4 +1,4 @@ -import { screen, waitFor } from '@testing-library/react'; +import { screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import fetchMock from 'fetch-mock'; import { @@ -26,8 +26,6 @@ const languageChoices = [ ]; describe('', () => { - //jest.spyOn(console, 'error').mockImplementation(() => jest.fn()); - afterEach(() => { fetchMock.restore(); }); @@ -49,14 +47,10 @@ describe('', () => { }), ); - screen.getByRole('button', { - name: 'Select the language for which you want to upload a timed text file; Selected: fr', + const button = await screen.findByRole('combobox', { + name: 'Choose the language', }); - expect( - screen.getByRole('textbox', { - name: 'Select the language for which you want to upload a timed text file, fr', - }), - ).toHaveValue('French'); + expect(within(button).getByText('French')).toBeInTheDocument(); }); it('renders the component with instructor local language unavailable', async () => { @@ -76,14 +70,10 @@ describe('', () => { }), ); - screen.getByRole('button', { - name: 'Select the language for which you want to upload a timed text file; Selected: en', + const button = await screen.findByRole('combobox', { + name: 'Choose the language', }); - expect( - screen.getByRole('textbox', { - name: 'Select the language for which you want to upload a timed text file, en', - }), - ).toHaveValue('English'); + expect(within(button).getByText('English')).toBeInTheDocument(); }); it('renders the component with some languages already having some subtitles uploaded', async () => { @@ -114,17 +104,12 @@ describe('', () => { }), ); - expect( - screen.getByRole('textbox', { - name: 'Select the language for which you want to upload a timed text file, fr', - }), - ).toHaveValue('French'); + const button = await screen.findByRole('combobox', { + name: 'Choose the language', + }); + expect(within(button).getByText('French')).toBeInTheDocument(); - await userEvent.click( - screen.getByRole('button', { - name: 'Select the language for which you want to upload a timed text file; Selected: fr', - }), - ); + await userEvent.click(button); screen.getByRole('option', { name: 'English' }); screen.getByRole('option', { name: 'French' }); @@ -162,14 +147,12 @@ describe('', () => { }), ).not.toBeInTheDocument(); - screen.getByRole('button', { - name: 'Select the language for which you want to upload a timed text file; Selected: error', + const button = await screen.findByRole('combobox', { + name: 'Choose the language', }); expect( - screen.getByRole('textbox', { - name: 'Select the language for which you want to upload a timed text file, error', - }), - ).toHaveValue('No language availables'); + within(button).getByText('No language availables'), + ).toBeInTheDocument(); }); it('changes the selected language', async () => { @@ -189,29 +172,12 @@ describe('', () => { }), ); - expect( - await screen.findByRole('textbox', { - name: 'Select the language for which you want to upload a timed text file, fr', - }), - ).toHaveValue('French'); - - await userEvent.click( - screen.getByRole('button', { - name: 'Select the language for which you want to upload a timed text file; Selected: fr', - }), - ); - - const englishButtonOption = screen.getByRole('option', { name: 'English' }); - - await userEvent.click(englishButtonOption); - - screen.getByRole('button', { - name: 'Select the language for which you want to upload a timed text file; Selected: en', + const button = await screen.findByRole('combobox', { + name: 'Choose the language', }); - expect( - screen.getByRole('textbox', { - name: 'Select the language for which you want to upload a timed text file, en', - }), - ).toHaveValue('English'); + expect(within(button).getByText('French')).toBeInTheDocument(); + await userEvent.click(button); + await userEvent.click(screen.getByRole('option', { name: 'English' })); + expect(within(button).getByText('English')).toBeInTheDocument(); }); }); diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.tsx index 3aa1b81521..850c3a6e62 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/LocalizedTimedTextTrackUpload/LanguageSelect/index.tsx @@ -1,4 +1,5 @@ -import { Select, timedTextMode, useTimedTextTrack } from 'lib-components'; +import { Select } from '@openfun/cunningham-react'; +import { timedTextMode, useTimedTextTrack } from 'lib-components'; import { useEffect, useMemo, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -6,12 +7,18 @@ import { LanguageChoice } from '@lib-video/types/SelectOptions'; const messages = defineMessages({ selectLanguageLabel: { - defaultMessage: - 'Select the language for which you want to upload a timed text file', + defaultMessage: 'Choose the language', description: 'The label of the select used for choosing the language for which the user wants to upload a file.', id: 'components.LanguageSelect.selectLanguageLabel', }, + selectLanguageInfo: { + defaultMessage: + 'The language for which you want to upload a timed text file', + description: + 'The text under the select used for choosing the language for which the user wants to upload a file.', + id: 'components.LanguageSelect.selectLanguageInfo', + }, noLanguageAvailableLabel: { defaultMessage: 'No language availables', description: @@ -98,14 +105,19 @@ export const LanguageSelect = ({ return ( ( - - {`${label}`} - - )} - onChange={({ - option, - }: { - option: { label: string; value: string }; - }) => { - setSelectedOption(option); + fullWidth + value={selectedQuality} + onChange={(evt) => { + setSelectedQuality(evt.target.value as string); }} - title={intl.formatMessage(messages.selectQualityLabel)} + clearable={false} + text={intl.formatMessage(messages.selectQualityInfo)} /> { label={intl.formatMessage(messages.downloadButtonLabel)} href={ video.urls - ? video.urls.mp4[Number(selectedOption.value) as videoSize] + ? video.urls.mp4[Number(selectedQuality) as videoSize] : undefined } target="_blank" diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.spec.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.spec.tsx index 4d36d10c13..8f1b54728d 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.spec.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.spec.tsx @@ -1,4 +1,4 @@ -import { screen, waitFor } from '@testing-library/react'; +import { screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import fetchMock from 'fetch-mock'; import { @@ -62,10 +62,12 @@ describe('', () => { ); expect(screen.getByText('License')).toBeInTheDocument(); - const textbox = await screen.findByRole('textbox', { - name: 'Select the license under which you want to publish your video, NO_CC', + const selectButton = await screen.findByRole('combobox', { + name: 'Select the license', }); - expect(textbox).toHaveValue('All rights reserved'); + expect( + await within(selectButton).findByText('All rights reserved'), + ).toBeInTheDocument(); }); it('renders the component with no license', async () => { @@ -83,10 +85,11 @@ describe('', () => { ); expect(screen.getByText('License')).toBeInTheDocument(); - const textbox = await screen.findByRole('textbox', { - name: 'Select the license under which you want to publish your video', - }); - expect(textbox).toHaveValue(''); + expect( + await screen.findByRole('combobox', { + name: 'Select the license', + }), + ).toBeInTheDocument(); }); it('renders the component with a license and successfully updates it', async () => { @@ -105,21 +108,20 @@ describe('', () => { ); expect(screen.getByText('License')).toBeInTheDocument(); - const selectButton = await screen.findByRole('button', { - name: 'Select the license under which you want to publish your video; Selected: NO_CC', + const selectButton = await screen.findByRole('combobox', { + name: 'Select the license', }); + expect( + await within(selectButton).findByText('All rights reserved'), + ).toBeInTheDocument(); await userEvent.click(selectButton); const CreativeCommonButtonOption = screen.getByRole('option', { name: 'Creative Common By Attribution', }); await userEvent.click(CreativeCommonButtonOption); - await waitFor(() => - expect( - screen.getByRole('textbox', { - name: 'Select the license under which you want to publish your video, CC_BY', - }), - ).toHaveValue('Creative Common By Attribution'), - ); + expect( + await within(selectButton).findByText('Creative Common By Attribution'), + ).toBeInTheDocument(); }); it('renders the component with no license choice', async () => { @@ -139,8 +141,8 @@ describe('', () => { ); expect(screen.getByText('License')).toBeInTheDocument(); - const selectButton = await screen.findByRole('button', { - name: 'Select the license under which you want to publish your video; Selected: All rights reserved', + const selectButton = await screen.findByRole('combobox', { + name: 'Select the license', }); await userEvent.click(selectButton); await screen.findByText('No license available'); @@ -161,9 +163,12 @@ describe('', () => { ); expect(screen.getByText('License')).toBeInTheDocument(); - const selectButton = await screen.findByRole('button', { - name: 'Select the license under which you want to publish your video; Selected: NO_CC', + const selectButton = await screen.findByRole('combobox', { + name: 'Select the license', }); + expect( + await within(selectButton).findByText('All rights reserved'), + ).toBeInTheDocument(); await userEvent.click(selectButton); const CreativeCommonButtonOption = screen.getByRole('option', { name: 'Creative Common By Attribution', diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.tsx index af63cbc6ff..8d86694f98 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LicenseManager/index.tsx @@ -1,6 +1,5 @@ -import { Select } from 'grommet'; -import { Nullable } from 'lib-common'; -import { FoldableItem } from 'lib-components'; +import { Select } from '@openfun/cunningham-react'; +import { CenterLoader, FoldableItem } from 'lib-components'; import React, { useEffect, useMemo, useState } from 'react'; import toast from 'react-hot-toast'; import { defineMessages, useIntl } from 'react-intl'; @@ -8,7 +7,6 @@ import { defineMessages, useIntl } from 'react-intl'; import { useUpdateVideo } from '@lib-video/api/useUpdateVideo'; import { useVideoMetadata } from '@lib-video/api/useVideoMetadata'; import { useCurrentVideo } from '@lib-video/hooks/useCurrentVideo'; -import { LicenseChoice } from '@lib-video/types'; const messages = defineMessages({ info: { @@ -38,18 +36,24 @@ const messages = defineMessages({ id: 'components.LicenseManager.noLicenseAvailableLabel', }, selectLicenseLabel: { - defaultMessage: - 'Select the license under which you want to publish your video', + defaultMessage: 'Select the license', description: 'The label of the select used for choosing the license under which the instructor wants to publish your video', id: 'components.LicenseManager.selectLicenseLabel', }, + selectLicenseInfo: { + defaultMessage: + 'Select the license under which you want to publish your video', + description: + 'The text under the select used for choosing the license under which the instructor wants to publish your video', + id: 'components.LicenseManager.selectLicenseInfo', + }, }); export const LicenseManager = () => { const intl = useIntl(); const video = useCurrentVideo(); - const { data } = useVideoMetadata(intl.locale); + const { data, isLoading } = useVideoMetadata(intl.locale); const choices = useMemo(() => { return data?.actions.POST.license.choices?.map((choice) => ({ label: choice.display_name, @@ -57,15 +61,19 @@ export const LicenseManager = () => { })); }, [data?.actions.POST.license.choices]); - const [selectedLicense, setSelectedLicense] = useState< - Nullable - >(video.license ? { label: video.license, value: video.license } : null); + const [selectedLicense, setSelectedLicense] = useState( + video.license ? video.license : undefined, + ); useEffect(() => { + if (isLoading) { + return; + } + return setSelectedLicense( - choices?.find((choice) => choice.label === video.license) || null, + choices?.find((choice) => choice.value === video.license)?.value, ); - }, [choices, video.license]); + }, [choices, isLoading, video.license]); const errorLicenseChoice = { label: intl.formatMessage(messages.noLicenseAvailableLabel), @@ -85,28 +93,36 @@ export const LicenseManager = () => { }, }); - function onChange(option: LicenseChoice) { - videoMutation.mutate({ license: option.value }); - setSelectedLicense(option); - } - return ( - { + if (selectedLicense === evt.target.value) { + return; + } + + setSelectedLicense(evt.target.value as string); + + if (evt.target.value !== errorLicenseChoice.value) { + videoMutation.mutate({ license: evt.target.value as string }); + } + }} + fullWidth + clearable={false} + text={intl.formatMessage(messages.selectLicenseInfo)} + /> + ) : ( + + )} ); }; diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.spec.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.spec.tsx index 9f6736cdd7..50f22510f2 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.spec.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.spec.tsx @@ -46,11 +46,12 @@ describe('', () => { ); screen.getByText('Join the discussion'); - const button = screen.getByRole('button', { - name: /select join the discussion mode/i, + const button = screen.getByRole('combobox', { + name: 'Join the discussion mode', }); - const select = within(button).getByRole('textbox'); - expect(select).toHaveValue('Accept joining the discussion after approval'); + expect( + within(button).getByText('Accept joining the discussion after approval'), + ).toBeInTheDocument(); }); it('selects join mode denied', async () => { @@ -72,11 +73,12 @@ describe('', () => { ), ); - const button = screen.getByRole('button', { - name: /select join the discussion mode/i, + const button = screen.getByRole('combobox', { + name: 'Join the discussion mode', }); - const select = within(button).getByRole('textbox'); - expect(select).toHaveValue('Accept joining the discussion after approval'); + expect( + within(button).getByText('Accept joining the discussion after approval'), + ).toBeInTheDocument(); await userEvent.click(button); await userEvent.click(screen.getByText(/not allowed/i)); @@ -106,7 +108,7 @@ describe('', () => { { ...mockedVideo, join_mode: JoinMode.DENIED }, ), ); - expect(within(button).getByRole('textbox')).toHaveValue('Not allowed'); + expect(within(button).getByText('Not allowed')).toBeInTheDocument(); }); it('selects join mode ask for approval', async () => { @@ -128,11 +130,10 @@ describe('', () => { ), ); - const button = screen.getByRole('button', { - name: /select join the discussion mode/i, + const button = screen.getByRole('combobox', { + name: 'Join the discussion mode', }); - const select = within(button).getByRole('textbox'); - expect(select).toHaveValue('Not allowed'); + expect(within(button).getByText('Not allowed')).toBeInTheDocument(); await userEvent.click(button); await userEvent.click( @@ -164,9 +165,9 @@ describe('', () => { { ...mockedVideo, join_mode: JoinMode.APPROVAL }, ), ); - expect(within(button).getByRole('textbox')).toHaveValue( - 'Accept joining the discussion after approval', - ); + expect( + within(button).getByText('Accept joining the discussion after approval'), + ).toBeInTheDocument(); }); it('selects join mode forced', async () => { @@ -188,11 +189,12 @@ describe('', () => { ), ); - const button = screen.getByRole('button', { - name: /select join the discussion mode/i, + const button = screen.getByRole('combobox', { + name: 'Join the discussion mode', }); - const select = within(button).getByRole('textbox'); - expect(select).toHaveValue('Accept joining the discussion after approval'); + expect( + within(button).getByText('Accept joining the discussion after approval'), + ).toBeInTheDocument(); await userEvent.click(button); await userEvent.click( @@ -224,9 +226,9 @@ describe('', () => { { ...mockedVideo, join_mode: JoinMode.FORCED }, ), ); - expect(within(button).getByRole('textbox')).toHaveValue( - 'Everybody will join the discussion', - ); + expect( + within(button).getByText('Everybody will join the discussion'), + ).toBeInTheDocument(); }); it('selects join mode denied, but backend returns an error', async () => { @@ -245,11 +247,12 @@ describe('', () => { ), ); - const button = screen.getByRole('button', { - name: /select join the discussion mode/i, + const button = screen.getByRole('combobox', { + name: 'Join the discussion mode', }); - const select = within(button).getByRole('textbox'); - expect(select).toHaveValue('Accept joining the discussion after approval'); + expect( + within(button).getByText('Accept joining the discussion after approval'), + ).toBeInTheDocument(); await userEvent.click(button); await userEvent.click(screen.getByText(/not allowed/i)); diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.tsx index 9362265091..d60b652b36 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWidgetProvider/widgets/LiveJoinMode/index.tsx @@ -1,4 +1,5 @@ -import { Box, Select } from 'grommet'; +import { Select } from '@openfun/cunningham-react'; +import { Box } from 'grommet'; import { FoldableItem, JoinMode, report } from 'lib-components'; import React from 'react'; import { toast } from 'react-hot-toast'; @@ -22,10 +23,15 @@ const messages = defineMessages({ id: 'components.LiveJoinMode.title', }, selectLabel: { - defaultMessage: 'Select join the discussion mode', + defaultMessage: 'Join the discussion mode', description: 'The label for the select to set join modes.', id: 'components.LiveJoinMode.selectLabel', }, + selectLabelInfo: { + defaultMessage: 'Choose the mode when someone join the discussion', + description: 'The text under the select to set join modes.', + id: 'components.LiveJoinMode.selectLabelInfo', + }, approval: { defaultMessage: 'Accept joining the discussion after approval', description: 'The label associated to the approval join mode.', @@ -95,20 +101,19 @@ export const LiveJoinMode = () => { { - setSelectedLicense(option); - onChange(option); - }} + label={intl.formatMessage(messages.selectLicenseLabel)} options={choices ?? [errorLicenseChoice]} - replace={false} + fullWidth value={selectedLicense.value} - valueKey={{ key: 'value', reduce: true }} + onChange={(evt) => { + if (evt.target.value !== selectedLicense.value) { + const choice = + choices?.find((option) => option.value === evt.target.value) || + errorLicenseChoice; + + setSelectedLicense(choice); + onChange(choice); + } + }} + clearable={false} + disabled={disabled} + text={intl.formatMessage(messages.selectLicenseInfo)} /> ); }; diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.spec.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.spec.tsx index 0e7fd6751a..f1803384e5 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.spec.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.spec.tsx @@ -1,6 +1,6 @@ /* eslint-disable testing-library/no-node-access */ /* eslint-disable testing-library/no-container */ -import { screen, waitFor } from '@testing-library/react'; +import { screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import fetchMock from 'fetch-mock'; import { @@ -121,13 +121,14 @@ describe('', () => { screen.getByRole('textbox', { name: 'Enter title of your video here' }); screen.getByText('Add a video or drag & drop it'); screen.getByTestId('input-video-test-id'); - await waitFor(() => - expect( - screen.getByRole('textbox', { - name: 'Select the license under which you want to publish your video, CC_BY', - }), - ).toHaveValue('Creative Common By Attribution'), - ); + const rerenderedLicense = await screen.findByRole('combobox', { + name: 'Select the license', + }); + expect( + await within(rerenderedLicense).findByText( + 'Creative Common By Attribution', + ), + ).toBeInTheDocument(); const goBackButton = screen.getByRole('button', { name: 'Go back' }); const createVideoButton = screen.getByRole('button', { @@ -200,13 +201,14 @@ describe('', () => { ).toBeInTheDocument(); screen.getByTestId('input-video-test-id'); - await waitFor(() => - expect( - screen.getByRole('textbox', { - name: 'Select the license under which you want to publish your video, CC_BY', - }), - ).toHaveValue('Creative Common By Attribution'), - ); + const rerenderedLicense = await screen.findByRole('combobox', { + name: 'Select the license', + }); + expect( + await within(rerenderedLicense).findByText( + 'Creative Common By Attribution', + ), + ).toBeInTheDocument(); const goBackButton = screen.getByRole('button', { name: 'Go back' }); const createVideoButton = screen.getByRole('button', { @@ -354,9 +356,14 @@ describe('', () => { const rerenderedTitle = screen.getByRole('textbox', { name: 'Enter title of your video here', }); - const rerenderedLicense = screen.getByRole('textbox', { - name: 'Select the license under which you want to publish your video, CC_BY', + const rerenderedLicense = await screen.findByRole('combobox', { + name: 'Select the license', }); + expect( + await within(rerenderedLicense).findByText( + 'Creative Common By Attribution', + ), + ).toBeInTheDocument(); const rerenderedCreateVideoButton = screen.getByRole('button', { name: 'Create a video', }); @@ -364,7 +371,7 @@ describe('', () => { name: 'Go back', }); expect(rerenderedTitle).toBeDisabled(); - expect(rerenderedLicense).toBeDisabled(); + expect(rerenderedLicense.hasAttribute('disabled')).toBeTruthy(); expect(rerenderedGoBackButton).toBeDisabled(); expect(rerenderedCreateVideoButton).toBeDisabled(); @@ -607,9 +614,14 @@ describe('', () => { const rerenderedTitle = screen.getByRole('textbox', { name: 'Enter title of your video here', }); - const rerenderedLicense = screen.getByRole('textbox', { - name: 'Select the license under which you want to publish your video, CC_BY', + const rerenderedLicense = await screen.findByRole('combobox', { + name: 'Select the license', }); + expect( + await within(rerenderedLicense).findByText( + 'Creative Common By Attribution', + ), + ).toBeInTheDocument(); const rerenderedCreateVideoButton = screen.getByRole('button', { name: 'Create a video', }); @@ -618,7 +630,7 @@ describe('', () => { }); screen.getByText('50 %'); expect(rerenderedTitle).toBeDisabled(); - expect(rerenderedLicense).toBeDisabled(); + expect(rerenderedLicense.hasAttribute('disabled')).toBeTruthy(); expect(rerenderedGoBackButton).toBeDisabled(); expect(rerenderedCreateVideoButton).toBeDisabled(); diff --git a/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.tsx b/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.tsx index 6ad6e3e380..768c5aef3f 100644 --- a/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.tsx +++ b/src/frontend/packages/lib_video/src/components/common/VideoWizard/CreateVOD/index.tsx @@ -181,6 +181,7 @@ export const CreateVOD = ({ }) } value={wizardedVideo.title || ''} + fullWidth />