Skip to content

Commit 7079b61

Browse files
committed
Add config for mention notification subscription handling
1 parent 92f2aa3 commit 7079b61

File tree

9 files changed

+255
-185
lines changed

9 files changed

+255
-185
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { Button, Checkbox } from '@hypothesis/frontend-shared';
2+
import classnames from 'classnames';
3+
import { useCallback } from 'preact/hooks';
4+
5+
import type { SelectedDays, WeekDay } from '../config';
6+
7+
const dayNames: [WeekDay, string][] = [
8+
['sun', 'Sunday'],
9+
['mon', 'Monday'],
10+
['tue', 'Tuesday'],
11+
['wed', 'Wednesday'],
12+
['thu', 'Thursday'],
13+
['fri', 'Friday'],
14+
['sat', 'Saturday'],
15+
];
16+
17+
export type EmailDigestPreferencesProps = {
18+
/** Currently selected days */
19+
selectedDays: SelectedDays;
20+
/** Callback to fully or partially update currently selected days, without saving */
21+
onSelectedDaysChange: (newSelectedDays: Partial<SelectedDays>) => void;
22+
};
23+
24+
export default function EmailDigestPreferences({
25+
selectedDays,
26+
onSelectedDaysChange,
27+
}: EmailDigestPreferencesProps) {
28+
const setAllTo = useCallback(
29+
(enabled: boolean) =>
30+
onSelectedDaysChange({
31+
sun: enabled,
32+
mon: enabled,
33+
tue: enabled,
34+
wed: enabled,
35+
thu: enabled,
36+
fri: enabled,
37+
sat: enabled,
38+
}),
39+
[onSelectedDaysChange],
40+
);
41+
const selectAll = useCallback(() => setAllTo(true), [setAllTo]);
42+
const selectNone = useCallback(() => setAllTo(false), [setAllTo]);
43+
44+
return (
45+
<div className="flex flex-col gap-y-3">
46+
<h2 className="text-lg/6 text-brand">Student activity digest</h2>
47+
<p>Receive email notifications when your students annotate.</p>
48+
<p>Select the days you{"'"}d like your emails:</p>
49+
50+
<div className="flex justify-between pl-4">
51+
<div className="flex flex-col gap-1">
52+
{dayNames.map(([day, name]) => (
53+
<span
54+
key={day}
55+
className={classnames(
56+
// The checked icon sets fill from the text color
57+
'text-grey-6',
58+
)}
59+
>
60+
<Checkbox
61+
name={day}
62+
checked={selectedDays[day]}
63+
onChange={() =>
64+
onSelectedDaysChange({ [day]: !selectedDays[day] })
65+
}
66+
data-testid={`${day}-checkbox`}
67+
>
68+
<span
69+
className={classnames(
70+
// Override the color set for the checkbox fill
71+
'text-grey-9',
72+
)}
73+
>
74+
{name}
75+
</span>
76+
</Checkbox>
77+
</span>
78+
))}
79+
</div>
80+
<div className="flex items-start gap-2">
81+
<Button
82+
variant="secondary"
83+
type="button"
84+
onClick={selectAll}
85+
data-testid="select-all-button"
86+
>
87+
Select all
88+
</Button>
89+
<Button
90+
variant="secondary"
91+
type="button"
92+
onClick={selectNone}
93+
data-testid="select-none-button"
94+
>
95+
Select none
96+
</Button>
97+
</div>
98+
</div>
99+
</div>
100+
);
101+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Checkbox } from '@hypothesis/frontend-shared';
2+
import classnames from 'classnames';
3+
4+
export type EmailMentionsPreferencesProps = {
5+
subscribed: boolean;
6+
};
7+
8+
export default function EmailMentionsPreferences({
9+
subscribed,
10+
}: EmailMentionsPreferencesProps) {
11+
return (
12+
<div className="flex flex-col gap-y-3">
13+
<h2 className="text-lg/6 text-brand">Mentions</h2>
14+
<p>
15+
Receive email notifications when you are mentioned in an annotation.
16+
</p>
17+
<div className="px-4">
18+
<span
19+
className={classnames(
20+
// The checked icon sets fill from the text color
21+
'text-grey-6',
22+
)}
23+
>
24+
<Checkbox
25+
name="mention_email_subscribed"
26+
defaultChecked={subscribed}
27+
data-testid="mentions-checkbox"
28+
>
29+
<span
30+
className={classnames(
31+
// Override the color set for the checkbox fill
32+
'text-grey-9',
33+
)}
34+
>
35+
Receive mention notifications
36+
</span>
37+
</Checkbox>
38+
</span>
39+
</div>
40+
</div>
41+
);
42+
}

lms/static/scripts/frontend_apps/components/EmailPreferences.tsx

Lines changed: 0 additions & 132 deletions
This file was deleted.

lms/static/scripts/frontend_apps/components/EmailPreferencesApp.tsx

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
2+
Button,
23
InfoIcon,
34
Link,
5+
Panel,
46
ToastMessages,
57
useToastMessages,
68
} from '@hypothesis/frontend-shared';
@@ -9,7 +11,8 @@ import { useCallback, useState } from 'preact/hooks';
911

1012
import type { SelectedDays, WeekDay } from '../config';
1113
import { useConfig } from '../config';
12-
import EmailPreferences from './EmailPreferences';
14+
import EmailDigestPreferences from './EmailDigestPreferences';
15+
import EmailMentionsPreferences from './EmailMentionsPreferences';
1316

1417
export default function EmailPreferencesApp() {
1518
const { emailPreferences } = useConfig(['emailPreferences']);
@@ -71,15 +74,41 @@ export default function EmailPreferencesApp() {
7174
/>
7275
</div>
7376
</div>
74-
<div className="flex flex-col gap-y-4 py-8">
75-
<div className="max-w-[450px] mx-auto">
76-
<EmailPreferences
77-
selectedDays={selectedDays}
78-
updateSelectedDays={updateSelectedDays}
79-
onSave={onSave}
80-
saving={saving}
81-
/>
82-
</div>
77+
<div className="flex flex-col gap-y-4 pb-8 pt-4 md:pt-8 px-4">
78+
<form
79+
method="post"
80+
onSubmit={onSave}
81+
className="flex flex-col gap-3 mx-auto max-w-[450px]"
82+
>
83+
<Panel
84+
title="Email notifications"
85+
fullWidthHeader
86+
buttons={
87+
<Button
88+
variant="primary"
89+
type="submit"
90+
disabled={saving}
91+
data-testid="save-button"
92+
>
93+
Save
94+
</Button>
95+
}
96+
>
97+
<div className="flex flex-col gap-y-8">
98+
{emailPreferences.is_instructor && (
99+
<EmailDigestPreferences
100+
selectedDays={selectedDays}
101+
onSelectedDaysChange={updateSelectedDays}
102+
/>
103+
)}
104+
{emailPreferences.mention_email_feature_enabled && (
105+
<EmailMentionsPreferences
106+
subscribed={emailPreferences.mention_email_subscribed}
107+
/>
108+
)}
109+
</div>
110+
</Panel>
111+
</form>
83112
<p className="text-center text-grey-8">
84113
<InfoIcon className="inline" /> Do you need help? Visit our{' '}
85114
<Link

lms/static/scripts/frontend_apps/components/test/EmailPreferences-test.js renamed to lms/static/scripts/frontend_apps/components/test/EmailDigestPreferences-test.js

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { mount } from '@hypothesis/frontend-testing';
22

3-
import EmailPreferences from '../EmailPreferences';
3+
import EmailDigestPreferences from '../EmailDigestPreferences';
44

5-
describe('EmailPreferences', () => {
5+
describe('EmailDigestPreferences', () => {
66
let fakeUpdateSelectedDays;
77
const initialSelectedDays = {
88
sun: true,
@@ -20,9 +20,9 @@ describe('EmailPreferences', () => {
2020

2121
function createComponent(props = {}) {
2222
return mount(
23-
<EmailPreferences
23+
<EmailDigestPreferences
2424
selectedDays={initialSelectedDays}
25-
updateSelectedDays={fakeUpdateSelectedDays}
25+
onSelectedDaysChange={fakeUpdateSelectedDays}
2626
{...props}
2727
/>,
2828
);
@@ -87,22 +87,4 @@ describe('EmailPreferences', () => {
8787
});
8888
});
8989
});
90-
91-
[true, false].forEach(saving => {
92-
it('disables save button while saving', () => {
93-
const wrapper = createComponent({ saving });
94-
const button = wrapper.find('Button[data-testid="save-button"]');
95-
96-
assert.equal(button.prop('disabled'), saving);
97-
});
98-
});
99-
100-
it('saves preferences', () => {
101-
const onSave = sinon.stub();
102-
const wrapper = createComponent({ onSave });
103-
104-
wrapper.find('form').simulate('submit');
105-
106-
assert.called(onSave);
107-
});
10890
});

0 commit comments

Comments
 (0)