aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorMathieu Suen <mathieu.suen@sonarsource.com>2023-06-20 16:58:04 +0200
committersonartech <sonartech@sonarsource.com>2023-06-26 20:03:55 +0000
commit4602389376d469331c77fdd9fe443cf6acd87fa7 (patch)
tree320437390372102d796da395336e0b3679c80bb3 /server
parent6a8eba1b0a016b92f5b57d294c33d0c4b18f0367 (diff)
downloadsonarqube-4602389376d469331c77fdd9fe443cf6acd87fa7.tar.gz
sonarqube-4602389376d469331c77fdd9fe443cf6acd87fa7.zip
SONAR-19613 Migrate project notification to new UI
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/design-system/src/components/Checkbox.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/notifications/ProjectNotifications.tsx110
-rw-r--r--server/sonar-web/src/main/js/apps/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx4
3 files changed, 62 insertions, 53 deletions
diff --git a/server/sonar-web/design-system/src/components/Checkbox.tsx b/server/sonar-web/design-system/src/components/Checkbox.tsx
index 6b4a77a3c98..238a52cba6e 100644
--- a/server/sonar-web/design-system/src/components/Checkbox.tsx
+++ b/server/sonar-web/design-system/src/components/Checkbox.tsx
@@ -140,6 +140,7 @@ export const AccessibleCheckbox = styled.input`
padding: 0;
white-space: nowrap;
width: 1px;
+ appearance: none;
&:focus,
&:active {
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/notifications/ProjectNotifications.tsx b/server/sonar-web/src/main/js/apps/projectInformation/notifications/ProjectNotifications.tsx
index d0ddd4fd9fc..760cc32b7d2 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/notifications/ProjectNotifications.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/notifications/ProjectNotifications.tsx
@@ -17,16 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Checkbox, FlagMessage, SubTitle } from 'design-system';
import * as React from 'react';
import {
- withNotifications,
WithNotificationsProps,
+ withNotifications,
} from '../../../components/hoc/withNotifications';
-import { Alert } from '../../../components/ui/Alert';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
-import { translate } from '../../../helpers/l10n';
+import { hasMessage, translate, translateWithParameters } from '../../../helpers/l10n';
+import { NotificationProjectType } from '../../../types/notifications';
import { Component } from '../../../types/types';
-import NotificationsList from '../../account/notifications/NotificationsList';
interface Props {
component: Component;
@@ -34,70 +34,76 @@ interface Props {
export function ProjectNotifications(props: WithNotificationsProps & Props) {
const { channels, component, loading, notifications, perProjectTypes } = props;
- const heading = React.useRef<HTMLHeadingElement>(null);
- React.useEffect(() => {
- if (heading.current) {
- // a11y: provide focus to the heading when the info drawer page is opened.
- heading.current.focus();
+ const handleCheck = (type: NotificationProjectType, channel: string, checked: boolean) => {
+ if (checked) {
+ props.addNotification({ project: component.key, channel, type });
+ } else {
+ props.removeNotification({
+ project: component.key,
+ channel,
+ type,
+ });
}
- }, [heading]);
+ };
- const handleAddNotification = ({ channel, type }: { channel: string; type: string }) => {
- props.addNotification({ project: component.key, channel, type });
+ const getCheckboxId = (type: string, channel: string) => {
+ return `project-notification-${component.key}-${type}-${channel}`;
};
- const handleRemoveNotification = ({ channel, type }: { channel: string; type: string }) => {
- props.removeNotification({
- project: component.key,
- channel,
- type,
- });
+ const getDispatcherLabel = (dispatcher: string) => {
+ const globalMessageKey = ['notification.dispatcher', dispatcher];
+ const projectMessageKey = [...globalMessageKey, 'project'];
+ const shouldUseProjectMessage = hasMessage(...projectMessageKey);
+ return shouldUseProjectMessage
+ ? translate(...projectMessageKey)
+ : translate(...globalMessageKey);
};
- const getCheckboxId = (type: string, channel: string) => {
- return `project-notification-${component.key}-${type}-${channel}`;
+ const isEnabled = (type: string, channel: string) => {
+ return !!notifications.find(
+ (notification) =>
+ notification.type === type &&
+ notification.channel === channel &&
+ notification.project === component.key
+ );
};
- const projectNotifications = notifications.filter(
- (n) => n.project && n.project === component.key
- );
+ const emailChannel = channels[0];
return (
- <div>
- <h3 tabIndex={-1} ref={heading}>
- {translate('project.info.notifications')}
- </h3>
+ <form aria-labelledby="notifications-update-title">
+ <SubTitle>{translate('project.info.notifications')}</SubTitle>
- <Alert className="spacer-top" variant="info">
+ <FlagMessage className="spacer-top" variant="info">
{translate('notification.dispatcher.information')}
- </Alert>
+ </FlagMessage>
<DeferredSpinner loading={loading}>
- <table className="data zebra notifications-table">
- <thead>
- <tr>
- <th aria-label={translate('project')} />
- {channels.map((channel) => (
- <th className="text-center" key={channel}>
- <h4>{translate('notification.channel', channel)}</h4>
- </th>
- ))}
- </tr>
- </thead>
-
- <NotificationsList
- channels={channels}
- checkboxId={getCheckboxId}
- notifications={projectNotifications}
- onAdd={handleAddNotification}
- onRemove={handleRemoveNotification}
- project
- types={perProjectTypes}
- />
- </table>
+ <h3 id="notifications-update-title" className="sw-mt-6">
+ {translate('project_information.project_notifications.title')}
+ </h3>
+ <ul className="sw-list-none sw-mt-4 sw-pl-0">
+ {perProjectTypes.map((type) => (
+ <li className="sw-pl-0 sw-p-2" key={type}>
+ <Checkbox
+ right
+ className="sw-flex sw-justify-between"
+ label={translateWithParameters(
+ 'notification.dispatcher.descrption_x',
+ getDispatcherLabel(type)
+ )}
+ checked={isEnabled(type, emailChannel)}
+ id={getCheckboxId(type, emailChannel)}
+ onCheck={(checked: boolean) => handleCheck(type, emailChannel, checked)}
+ >
+ {getDispatcherLabel(type)}
+ </Checkbox>
+ </li>
+ ))}
+ </ul>
</DeferredSpinner>
- </div>
+ </form>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx b/server/sonar-web/src/main/js/apps/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx
index e7d187c574f..73cd0028e2c 100644
--- a/server/sonar-web/src/main/js/apps/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx
@@ -49,7 +49,9 @@ it('should render correctly', async () => {
const user = userEvent.setup();
renderProjectNotifications();
- expect(await screen.findByText('notification.channel.channel1')).toBeInTheDocument();
+ expect(
+ await screen.findByText('project_information.project_notifications.title')
+ ).toBeInTheDocument();
expect(
screen.getByLabelText(
'notification.dispatcher.descrption_x.notification.dispatcher.NewAlerts.project'