diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index 3a33a4e40..2c13c0ee0 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -451,17 +451,17 @@ }, "filters": { "buttons": { - "showResults": "Show results ({{count}})" + "search": "Search" }, "errorDialog": { - "description": "Select first the language and level. Click on the SHOW RESULTS button.", + "description": "Select first the language and level. Click on the SEARCH button.", "title": "Select the language and level" }, "errors": { "required": "Selection is required" }, "heading": "Search for tests", - "information": "Search for a suitable YKI test by selecting the language and level. Click on the SHOW RESULTS button to see the results.", + "information": "Search for a suitable YKI test by selecting the language and level. Click on the SEARCH button to see the results.", "selectExamDetails": { "prompt": "Choose test", "required": "(required)" diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index d49744980..c22440fb1 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -451,17 +451,17 @@ }, "filters": { "buttons": { - "showResults": "Näytä tulokset ({{count}})" + "search": "Hae" }, "errorDialog": { - "description": "Valitse ensin tutkinnon kieli ja taso. Paina sen jälkeen uudestaan NÄYTÄ TULOKSET -painiketta.", + "description": "Valitse ensin tutkinnon kieli ja taso. Paina sen jälkeen uudestaan HAE -painiketta.", "title": "Valitse tutkinnon kieli ja taso" }, "errors": { "required": "Valinta on pakollinen" }, "heading": "Etsi yleisiä kielitutkintoja (YKI)", - "information": "Hae tästä sopivaa YKI-testiä. Valitse kieli ja taso. Paina NÄYTÄ TULOKSET -painiketta.", + "information": "Hae tästä sopivaa YKI-testiä. Valitse kieli ja taso. Paina HAE -painiketta.", "selectExamDetails": { "prompt": "Valitse tutkinto", "required": "(pakollinen)" diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 61c4a1cd4..3d33bf770 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -451,17 +451,17 @@ }, "filters": { "buttons": { - "showResults": "Visa resultat ({{count}})" + "search": "Sök" }, "errorDialog": { - "description": "Välj först testets språk och nivå. Tryck på VISA RESULTAT-knappen.", + "description": "Välj först testets språk och nivå. Tryck på SÖK-knappen.", "title": "Välj språk och nivå" }, "errors": { "required": "Obligatoriskt att välja" }, "heading": "Sök allmänna språkexamina (YKI)", - "information": "Sök här för att hitta ett lämpligt YKI-test. Välj språk och nivå. Tryck på VISA RESULTAT-knappen.", + "information": "Sök här för att hitta ett lämpligt YKI-test. Välj språk och nivå. Tryck på SÖK-knappen.", "selectExamDetails": { "prompt": "Välj examen", "required": "(obligatorisk)" diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx index 1cc1b933b..992cc51b3 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx @@ -120,18 +120,17 @@ export const PublicExamSessionListing = ({ keyPrefix: 'yki.pages.registrationPage.examSessionListing', }); const translateCommon = useCommonTranslation(); - const { isPhone } = useWindowProperties(); const { status } = useAppSelector(examSessionsSelector); const listingHeaderRef = useRef(null); useEffect(() => { - if (isPhone) { + if (status === APIResponseStatus.Success) { listingHeaderRef.current?.scrollIntoView({ behavior: 'smooth', inline: 'nearest', }); } - }, [isPhone]); + }, [status]); switch (status) { case APIResponseStatus.NotStarted: @@ -162,6 +161,7 @@ export const PublicExamSessionListing = ({ count: examSessions.length, }, )} + aria-live="assertive" > {translateCommon('component.table.header.searchResults', { count: examSessions.length, diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx index 7551be4fa..f15637f9c 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx @@ -12,9 +12,16 @@ import { ComboBox, CustomButton, LanguageSelect, + LoadingProgressIndicator, Text, } from 'shared/components'; -import { Color, Severity, TextFieldVariant, Variant } from 'shared/enums'; +import { + APIResponseStatus, + Color, + Severity, + TextFieldVariant, + Variant, +} from 'shared/enums'; import { useDialog } from 'shared/hooks'; import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; @@ -22,10 +29,7 @@ import { useAppDispatch, useAppSelector } from 'configs/redux'; import { ExamLanguage, ExamLevel } from 'enums/app'; import { ExamSessionFilters } from 'interfaces/examSessions'; import { setPublicExamSessionFilters } from 'redux/reducers/examSessions'; -import { - examSessionsSelector, - selectFilteredPublicExamSessions, -} from 'redux/selectors/examSessions'; +import { examSessionsSelector } from 'redux/selectors/examSessions'; const municipalityToComboBoxOption = (m: string) => ({ value: m, @@ -200,9 +204,10 @@ export const PublicExamSessionFilters = ({ const { showDialog } = useDialog(); - const filteredExamSessions = useAppSelector(selectFilteredPublicExamSessions); const { language, level, excludeFullSessions, excludeNonOpenSessions } = useAppSelector(examSessionsSelector).filters; + const { status } = useAppSelector(examSessionsSelector); + const isLoading = status === APIResponseStatus.InProgress; const dispatch = useAppDispatch(); const onFilterChange = (filter: Partial) => { @@ -285,18 +290,21 @@ export const PublicExamSessionFilters = ({
- } + - {`${t('filters.buttons.showResults', { - count: filteredExamSessions.length, - })}`} - + } + > + {t('filters.buttons.search')} + +
); diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx index fd90fc4f3..70afa0f7b 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx @@ -1,5 +1,5 @@ import { TableCell, TableRow, Typography } from '@mui/material'; -import dayjs, { Dayjs } from 'dayjs'; +import { Dayjs } from 'dayjs'; import { ReactNode } from 'react'; import { CustomButtonLink, Text } from 'shared/components'; import { Color, Variant } from 'shared/enums'; @@ -61,21 +61,22 @@ const RegistrationUnavailableText = ({ const { t } = usePublicTranslation({ keyPrefix: 'yki.component.registration.registrationUnavailable', }); - const now = dayjs(); - const { start, end } = + const { start } = ExamSessionUtils.getEffectiveRegistrationPeriodDetails(examSession); - if (now.isBefore(start)) { - return ( - <> - {t('admissionOpensOn', { - startDate: DateUtils.formatOptionalDate(start), - })} - - ); - } else if (now.isAfter(end)) { - return <>{t('admissionPeriodIsClosed')}; - } else { + if (examSession.open) { return <>{t('examSessionIsFull')}; + } else { + if (examSession.upcoming_admission || examSession.upcoming_post_admission) { + return ( + <> + {t('admissionOpensOn', { + startDate: DateUtils.formatOptionalDate(start), + })} + + ); + } else { + return <>{t('admissionPeriodIsClosed')}; + } } }; diff --git a/frontend/packages/yki/src/pages/RegistrationPage.tsx b/frontend/packages/yki/src/pages/RegistrationPage.tsx index 003751c42..de6ae2e25 100644 --- a/frontend/packages/yki/src/pages/RegistrationPage.tsx +++ b/frontend/packages/yki/src/pages/RegistrationPage.tsx @@ -21,12 +21,14 @@ export const RegistrationPage: FC = () => { }); const dispatch = useAppDispatch(); - const { status, exam_sessions } = useAppSelector(examSessionsSelector); + const { status } = useAppSelector(examSessionsSelector); const [results, setResults] = useState>([]); + const [updateResults, setUpdateResults] = useState(false); const [showResults, setShowResults] = useState(false); const filteredExamSessions = useAppSelector(selectFilteredPublicExamSessions); const onApplyFilters = () => { - setResults(filteredExamSessions); + dispatch(loadExamSessions()); + setUpdateResults(true); setShowResults(true); setPage(0); }; @@ -39,10 +41,11 @@ export const RegistrationPage: FC = () => { useEffect(() => { if (status === APIResponseStatus.NotStarted) { dispatch(loadExamSessions()); - } else if (status === APIResponseStatus.Success) { - setResults(exam_sessions); + } else if (status === APIResponseStatus.Success && updateResults) { + setResults(filteredExamSessions); + setUpdateResults(false); } - }, [dispatch, status, exam_sessions]); + }, [dispatch, updateResults, status, filteredExamSessions]); return ( diff --git a/frontend/packages/yki/src/redux/sagas/examSession.ts b/frontend/packages/yki/src/redux/sagas/examSession.ts index 47fd24c66..85dcd052e 100644 --- a/frontend/packages/yki/src/redux/sagas/examSession.ts +++ b/frontend/packages/yki/src/redux/sagas/examSession.ts @@ -1,7 +1,6 @@ import { call, put, takeLatest } from '@redux-saga/core/effects'; import { PayloadAction } from '@reduxjs/toolkit'; import { AxiosResponse } from 'axios'; -import dayjs from 'dayjs'; import axiosInstance from 'configs/axios'; import { translateOutsideComponent } from 'configs/i18n'; @@ -26,12 +25,9 @@ import { SerializationUtils } from 'utils/serialization'; function* loadExamSessionsSaga() { const t = translateOutsideComponent(); try { - // TODO Allow passing desired date through redux actions? - const from = dayjs().format('YYYY-MM-DD'); const response: AxiosResponse = yield call( axiosInstance.get, APIEndpoints.ExamSessions, - { params: { from } }, ); yield put( diff --git a/frontend/packages/yki/src/styles/components/registration/_exam-session-filters.scss b/frontend/packages/yki/src/styles/components/registration/_exam-session-filters.scss index 29e4b16bc..805271207 100644 --- a/frontend/packages/yki/src/styles/components/registration/_exam-session-filters.scss +++ b/frontend/packages/yki/src/styles/components/registration/_exam-session-filters.scss @@ -63,11 +63,22 @@ display: flex; gap: 2rem; - & > button { + // Allow container for loading progress indicator to grow + > div { + @include phone { + flex-grow: 1; + } + } + + & button { @include phone { flex-grow: 1; padding: 0.8rem 3rem; } + + @include not-phone { + min-width: 10vw; + } } } diff --git a/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts b/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts index 3306cea04..3109a8905 100644 --- a/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts +++ b/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts @@ -13,7 +13,7 @@ describe('PublicRegistrationPage', () => { describe('allows filtering exams', () => { it('but filter criteria must be selected first', () => { - onPublicRegistrationPage.showResults(); + onPublicRegistrationPage.search(); const dialogHeading = 'Valitse tutkinnon kieli ja taso'; findDialogByText(dialogHeading).should('be.visible'); findDialogByText(dialogHeading) @@ -23,9 +23,9 @@ describe('PublicRegistrationPage', () => { }); it('all results are available initially', () => { - onPublicRegistrationPage.expectResultsCount(10); onPublicRegistrationPage.selectExamLanguage('kaikki kielet'); onPublicRegistrationPage.selectExamLevel('kaikki tasot'); + onPublicRegistrationPage.search(); onPublicRegistrationPage.expectResultsCount(10); }); @@ -33,20 +33,22 @@ describe('PublicRegistrationPage', () => { onPublicRegistrationPage.selectExamLanguage('kaikki kielet'); onPublicRegistrationPage.selectExamLevel('kaikki tasot'); onPublicRegistrationPage.toggleShowOnlyIfAvailablePlaces(); + onPublicRegistrationPage.search(); onPublicRegistrationPage.expectResultsCount(4); onPublicRegistrationPage.toggleShowOnlyIfOngoingAdmission(); + onPublicRegistrationPage.search(); onPublicRegistrationPage.expectResultsCount(3); }); it('can filter by exam language and level', () => { onPublicRegistrationPage.selectExamLanguage('suomi'); onPublicRegistrationPage.selectExamLevel('kaikki tasot'); + onPublicRegistrationPage.search(); onPublicRegistrationPage.expectResultsCount(9); onPublicRegistrationPage.selectExamLevel('ylin taso'); + onPublicRegistrationPage.search(); onPublicRegistrationPage.expectResultsCount(3); - - onPublicRegistrationPage.showResults(); onPublicRegistrationPage.expectResultRowsCount(3); }); }); @@ -57,7 +59,7 @@ describe('PublicRegistrationPage', () => { onPublicRegistrationPage.selectExamLevel('kaikki tasot'); onPublicRegistrationPage.toggleShowOnlyIfAvailablePlaces(); onPublicRegistrationPage.toggleShowOnlyIfOngoingAdmission(); - onPublicRegistrationPage.showResults(); + onPublicRegistrationPage.search(); onPublicRegistrationPage .getResultRows() @@ -71,7 +73,7 @@ describe('PublicRegistrationPage', () => { onPublicRegistrationPage.selectExamLanguage('suomi'); onPublicRegistrationPage.selectExamLevel('perustaso'); onPublicRegistrationPage.toggleShowOnlyIfOngoingAdmission(); - onPublicRegistrationPage.showResults(); + onPublicRegistrationPage.search(); onPublicRegistrationPage .getResultRows() diff --git a/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts b/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts index e14956d81..05e89f93a 100644 --- a/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts +++ b/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts @@ -13,14 +13,21 @@ class PublicRegistrationPage { cy.findByLabelText( 'Näytä vain kielitutkinnot, joihin voi ilmoittautua nyt', ), - showResultsButton: () => cy.contains('Näytä tulokset'), + searchButton: () => cy.findByRole('button', { name: /Hae/ }), title: () => cy.findByTestId('public-registration-page__title-heading'), }; expectResultsCount(count: number) { + const resultsLabelSuffix = + count === 0 + ? 'ei tuloksia' + : count === 1 + ? '1 tulos' + : `${count} tulosta`; this.elements - .showResultsButton() - .should('have.text', `Näytä tulokset (${count})`); + .resultBox() + .findByRole('heading', { name: `Tulokset (${resultsLabelSuffix})` }) + .should('exist'); } expectResultRowsCount(count: number) { @@ -43,8 +50,9 @@ class PublicRegistrationPage { selectComboBoxOptionByName(this.elements.filterByLevel(), level); } - showResults() { - this.elements.showResultsButton().click(); + search() { + this.elements.searchButton().should('not.be.disabled'); + this.elements.searchButton().click(); } toggleShowOnlyIfAvailablePlaces() {