Skip to content

Commit

Permalink
Merge pull request #1244 from openedx/saleem-latif/ENT-8762
Browse files Browse the repository at this point in the history
ENT-8762: Marked strings for the i18n of the first 2 tabs of admin portal settings page.
  • Loading branch information
saleem-latif authored Jun 3, 2024
2 parents 84d81b2 + 3498f52 commit 1bffae3
Show file tree
Hide file tree
Showing 54 changed files with 1,596 additions and 478 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ intl_imports = ./node_modules/.bin/intl-imports.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
transifex_temp = ./temp/babel-plugin-formatjs

shell: ## run a shell on the cookie-cutter container
docker exec -it /bin/bash
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"scripts": {
"build": "fedx-scripts webpack",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"i18n_extract": "fedx-scripts formatjs extract",
"build:with-theme": "THEME=npm:@edx/brand-edx.org@latest npm run install-theme && fedx-scripts webpack",
"check-types": "tsc --noemit",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .; npm run check-types",
Expand Down
11 changes: 5 additions & 6 deletions src/components/ConfirmationModal/ConfirmationModal.test.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
render, screen,
} from '@testing-library/react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';

import ConfirmationModal from './index';
import { renderWithI18nProvider } from '../test/testUtils';

describe('<ConfirmationModal />', () => {
const basicProps = {
Expand All @@ -16,20 +15,20 @@ describe('<ConfirmationModal />', () => {

it('should call onConfirm when confirm button is clicked', () => {
const mockHandleConfirm = jest.fn();
render(<ConfirmationModal {...basicProps} onConfirm={mockHandleConfirm} />);
renderWithI18nProvider(<ConfirmationModal {...basicProps} onConfirm={mockHandleConfirm} />);
userEvent.click(screen.getByText('Confirm'));
expect(mockHandleConfirm).toHaveBeenCalledTimes(1);
});

it('should call onClose when modal is closed', () => {
const mockHandleClose = jest.fn();
render(<ConfirmationModal {...basicProps} onClose={mockHandleClose} />);
renderWithI18nProvider(<ConfirmationModal {...basicProps} onClose={mockHandleClose} />);
userEvent.click(screen.getByText('Cancel'));
expect(mockHandleClose).toHaveBeenCalledTimes(1);
});

it('should show error alert if confirmButtonState = error', () => {
render(<ConfirmationModal {...basicProps} confirmButtonState="errored" />);
renderWithI18nProvider(<ConfirmationModal {...basicProps} confirmButtonState="errored" />);
expect(screen.getByText('Something went wrong')).toBeInTheDocument();
});
});
158 changes: 110 additions & 48 deletions src/components/ConfirmationModal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,49 @@ import {
ModalDialog, ActionRow, Button, StatefulButton, Alert,
} from '@openedx/paragon';
import { Info } from '@openedx/paragon/icons';
import { defineMessages, FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';

export const DEFAULT_TITLE = 'Are you sure?';

const CONFIRM_TEXT = 'Confirm';
const CANCEL_TEXT = 'Cancel';
const LOADING_TEXT = 'Loading...';
const TRY_AGAIN_TEXT = 'Try again';

export const CONFIRM_BUTTON_STATES = {
default: 'default',
pending: 'pending',
errored: 'errored',
};

const messages = defineMessages({
[DEFAULT_TITLE]: {
id: 'adminPortal.confirmationModal.defaultTitle',
defaultMessage: 'Are you sure?',
description: 'Default title for the confirmation modal',
},
[CONFIRM_TEXT]: {
id: 'adminPortal.confirmationModal.confirm',
defaultMessage: 'Confirm',
description: 'Confirm button text for the confirmation modal',
},
[CANCEL_TEXT]: {
id: 'adminPortal.confirmationModal.cancel',
defaultMessage: 'Cancel',
description: 'Cancel button text for the confirmation modal',
},
[LOADING_TEXT]: {
id: 'adminPortal.confirmationModal.loading',
defaultMessage: 'Loading...',
description: 'Loading state text for the confirmation modal',
},
[TRY_AGAIN_TEXT]: {
id: 'adminPortal.confirmationModal.tryAgain',
defaultMessage: 'Try again',
description: 'Try again button text for the confirmation modal',
},
});

const ConfirmationModal = ({
isOpen,
disabled,
Expand All @@ -24,49 +59,76 @@ const ConfirmationModal = ({
confirmText,
cancelText,
...rest
}) => (
<ModalDialog
title="Confirmation Modal"
variant="default"
isOpen={isOpen}
onClose={onClose}
{...rest}
>
<ModalDialog.Header>
<ModalDialog.Title>
{title}
</ModalDialog.Title>
{confirmButtonState === CONFIRM_BUTTON_STATES.errored && (
<Alert
icon={Info}
variant="danger"
>
<Alert.Heading>
Something went wrong
</Alert.Heading>
Please try again.
</Alert>
)}
</ModalDialog.Header>
<ModalDialog.Body>
{body}
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<Button onClick={onClose} variant="outline-primary">
{cancelText}
</Button>
<StatefulButton
labels={confirmButtonLabels}
state={confirmButtonState}
variant="primary"
disabled={disabled}
onClick={onConfirm}
/>
</ActionRow>
</ModalDialog.Footer>
</ModalDialog>
);
}) => {
const intl = useIntl();
const defaultMessage = confirmButtonLabels[CONFIRM_BUTTON_STATES.default];
const pendingMessage = confirmButtonLabels[CONFIRM_BUTTON_STATES.pending];
const erroredMessage = confirmButtonLabels[CONFIRM_BUTTON_STATES.errored];

// This code snippet first checks if the message exists in the messages object, and if it does,
// it formats the message using the intl.formatMessage function. If the message does not exist,
// it uses the defaultMessage value.
const translatedConfirmButtonLabels = {
[CONFIRM_BUTTON_STATES.default]:
messages[defaultMessage] ? intl.formatMessage(messages[defaultMessage]) : defaultMessage,
[CONFIRM_BUTTON_STATES.pending]:
messages[pendingMessage] ? intl.formatMessage(messages[pendingMessage]) : pendingMessage,
[CONFIRM_BUTTON_STATES.errored]:
messages[erroredMessage] ? intl.formatMessage(messages[erroredMessage]) : erroredMessage,
};

return (
<ModalDialog
title="Confirmation Modal"
variant="default"
isOpen={isOpen}
onClose={onClose}
{...rest}
>
<ModalDialog.Header>
<ModalDialog.Title>
{messages[title] ? intl.formatMessage(messages[title]) : title}
</ModalDialog.Title>
{confirmButtonState === CONFIRM_BUTTON_STATES.errored && (
<Alert
icon={Info}
variant="danger"
>
<Alert.Heading>
<FormattedMessage
id="confirmationModal.error"
defaultMessage="Something went wrong"
description="Error message for the confirmation modal"
/>
</Alert.Heading>
<FormattedMessage
id="confirmationModal.errorDescription"
defaultMessage="Please try again."
description="Error description for the confirmation modal"
/>
</Alert>
)}
</ModalDialog.Header>
<ModalDialog.Body>
{body}
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<Button onClick={onClose} variant="outline-primary">
{messages[cancelText] ? intl.formatMessage(messages[cancelText]) : cancelText}
</Button>
<StatefulButton
labels={translatedConfirmButtonLabels}
state={confirmButtonState}
variant="primary"
disabled={disabled}
onClick={onConfirm}
/>
</ActionRow>
</ModalDialog.Footer>
</ModalDialog>
);
};

ConfirmationModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
Expand All @@ -88,14 +150,14 @@ ConfirmationModal.propTypes = {
ConfirmationModal.defaultProps = {
disabled: false,
confirmButtonLabels: {
[CONFIRM_BUTTON_STATES.default]: 'Confirm',
[CONFIRM_BUTTON_STATES.pending]: 'Loading...',
[CONFIRM_BUTTON_STATES.errored]: 'Try again',
[CONFIRM_BUTTON_STATES.default]: CONFIRM_TEXT,
[CONFIRM_BUTTON_STATES.pending]: LOADING_TEXT,
[CONFIRM_BUTTON_STATES.errored]: TRY_AGAIN_TEXT,
},
confirmButtonState: CONFIRM_BUTTON_STATES.default,
title: DEFAULT_TITLE,
confirmText: 'Confirm',
cancelText: 'Cancel',
confirmText: CONFIRM_TEXT,
cancelText: CANCEL_TEXT,
};

export default ConfirmationModal;
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const CurrentContentHighlightHeader = ({ enterpriseId }) => {
</ActionRow>
<p>
<FormattedMessage
id="highlights.catalog.visibility.tab.catalog.visibility.not.updated.alert.error.header.title.message"
id="highlights.catalogVisibility.tab.catalog.error.title"
defaultMessage="Create up to {maxHighlights} highlights for your learners."
description="Header title for error alert shown to admin when catalog visibility failed to update."
values={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
BROWSE_AND_REQUEST_ALERT_TEXT,
} from './data/constants';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
import { SETTINGS_TABS_VALUES } from '../settings/data/constants';
import { ACCESS_TAB } from '../settings/data/constants';

const mockStore = configureMockStore([thunk]);

Expand All @@ -36,7 +36,7 @@ jest.mock('react-router-dom', () => ({
useNavigate: () => mockNavigate,
}));

const SETTINGS_PAGE_LOCATION = `/${ENTERPRISE_SLUG}/admin/${ROUTE_NAMES.settings}/${SETTINGS_TABS_VALUES.access}`;
const SETTINGS_PAGE_LOCATION = `/${ENTERPRISE_SLUG}/admin/${ROUTE_NAMES.settings}/${ACCESS_TAB}`;

const NewFeatureAlertBrowseAndRequestWrapper = () => (
<Router>
Expand Down
4 changes: 2 additions & 2 deletions src/components/NewFeatureAlertBrowseAndRequest/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
BROWSE_AND_REQUEST_ALERT_COOKIE_PREFIX,
} from '../subscriptions/data/constants';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
import { SETTINGS_TABS_VALUES } from '../settings/data/constants';
import { ACCESS_TAB } from '../settings/data/constants';

/**
* Generates string use to identify cookie
Expand Down Expand Up @@ -37,7 +37,7 @@ const NewFeatureAlertBrowseAndRequest = ({ enterpriseId, enterpriseSlug, intl })
* Redirects user to settings page, access tab
*/
const handleGoToSettings = () => {
navigate(`/${enterpriseSlug}/admin/${ROUTE_NAMES.settings}/${SETTINGS_TABS_VALUES.access}`);
navigate(`/${enterpriseSlug}/admin/${ROUTE_NAMES.settings}/${ACCESS_TAB}`);
};

return (
Expand Down
4 changes: 2 additions & 2 deletions src/components/ProductTours/tests/ProductTours.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
TOUR_TARGETS,
} from '../constants';
import { ROUTE_NAMES } from '../../EnterpriseApp/data/constants';
import { SETTINGS_TABS_VALUES } from '../../settings/data/constants';
import { ACCESS_TAB } from '../../settings/data/constants';
import { SubsidyRequestsContext } from '../../subsidy-requests';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
import { SUPPORTED_SUBSIDY_TYPES } from '../../../data/constants/subsidyRequests';
Expand All @@ -31,7 +31,7 @@ const mockStore = configureMockStore([thunk]);
const ENTERPRISE_SLUG = 'sluggy';

const SUBSCRIPTION_PAGE_LOCATION = `/${ENTERPRISE_SLUG}/admin/${ROUTE_NAMES.subscriptionManagement}`;
const SETTINGS_PAGE_LOCATION = `/${ENTERPRISE_SLUG}/admin/${ROUTE_NAMES.settings}/${SETTINGS_TABS_VALUES.access}`;
const SETTINGS_PAGE_LOCATION = `/${ENTERPRISE_SLUG}/admin/${ROUTE_NAMES.settings}/${ACCESS_TAB}`;
const LEARNER_CREDIT_PAGE_LOCATION = `/${ENTERPRISE_SLUG}/admin/${ROUTE_NAMES.learnerCredit}`;

const ToursWithContext = ({
Expand Down
4 changes: 2 additions & 2 deletions src/components/forms/ValidatedFormCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { ReactNode } from 'react';
import omit from 'lodash/omit';
import isString from 'lodash/isString';

Expand All @@ -9,7 +9,7 @@ import { useFormContext } from './FormContext';

type InheritedParagonCheckboxProps = {
className?: string;
children: string;
children: ReactNode;
};

export type ValidatedFormCheckboxProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ const BudgetActions = ({
</h3>
<p>
<FormattedMessage
id="lcm.budget.detail.page.overview.budget.actions.all.people.choose.learn"
id="lcm.budget.detail.page.overview.budget.actions.all.people.choose.learn.description"
defaultMessage="All people in your organization can choose what to learn
from the catalog and spend from the available balance to enroll."
description="Decription which tells that user can choose from the catalog and spend from the available balance to enroll"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const AssignmentAllocationHelpCollapsibles = ({ enterpriseId, course }) => (
<ul className="x-small pl-4 py-2">
<li>
<FormattedMessage
id="lcm.budget.detail.page.catalog.tab.course.card.total.assignment.cost.will.be.earmarked"
id="lcm.budget.detailsPage.catalog.tab.course.card.total.assignment.cost"
defaultMessage="The total assignment cost will be earmarked as {doubleQoute}assigned{doubleQoute} funds in your
Learner Credit budget so you can{apostrophe}t overspend."
description="A step which explains that the total assignment cost will be earmarked as 'assigned' funds in your Learner Credit budget"
Expand Down
17 changes: 15 additions & 2 deletions src/components/settings/SettingsAccessTab/ActionsTableCell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getConfig } from '@edx/frontend-platform/config';
import { logError } from '@edx/frontend-platform/logging';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';

import { FormattedMessage } from '@edx/frontend-platform/i18n';
import LinkDeactivationAlertModal from './LinkDeactivationAlertModal';
import LinkCopiedToast from './LinkCopiedToast';
import { SETTINGS_ACCESS_EVENTS } from '../../../eventTracking';
Expand Down Expand Up @@ -69,9 +70,21 @@ const ActionsTableCell = ({ row, onDeactivateLink, enterpriseUUID }) => {
<div className="d-flex justify-content-end">
<ActionRow>
{hasClipboard && (
<Button onClick={handleCopyLink} variant="link" size="inline">Copy</Button>
<Button onClick={handleCopyLink} variant="link" size="inline">
<FormattedMessage
id="adminPortal.settings.access.copyLink"
defaultMessage="Copy"
description="Label for the copy link button."
/>
</Button>
)}
<Button onClick={handleDeactivateClick} variant="link" size="inline">Deactivate</Button>
<Button onClick={handleDeactivateClick} variant="link" size="inline">
<FormattedMessage
id="adminPortal.settings.access.deactivateLink"
defaultMessage="Deactivate"
description="Label for the deactivate link button."
/>
</Button>
</ActionRow>
</div>
<LinkDeactivationAlertModal
Expand Down
Loading

0 comments on commit 1bffae3

Please sign in to comment.