Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/FP-1375: Admin controlled messages #615

Merged
merged 40 commits into from
Jun 2, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8daa5f4
Add CustomMessages and CustomMessageTemplate models.
duckonomy Mar 23, 2022
e59a1d4
Add sagas and reducers for custom messages.
duckonomy Mar 23, 2022
cf0308b
Add frontend components for CustomMessage.
duckonomy Mar 24, 2022
bf2902b
Add tests for custom messages.
duckonomy Mar 24, 2022
e1d574d
Fix tests.
duckonomy Mar 24, 2022
8d1cff4
Fix bug for template state.
duckonomy Mar 24, 2022
990d6a1
Fix style
duckonomy Mar 24, 2022
3355e90
Correct dismissible spelling.
duckonomy Mar 24, 2022
1d8a6d5
Refactor to embed template inside message.
duckonomy Mar 25, 2022
0e86ce7
Fix tests.
duckonomy Mar 25, 2022
703e283
Remove TODO comments.
duckonomy Mar 25, 2022
be93b23
Revert whitespace changes.
duckonomy Mar 25, 2022
a89fdbe
Revert whitespace changes.
duckonomy Mar 25, 2022
31a7cbf
Allow info messages to have dismiss button.
duckonomy Mar 25, 2022
b2db316
Fix linting.
duckonomy Mar 25, 2022
f735a83
Merge branch 'main' into task/FP-1375-admin-controlled-messages
rstijerina Mar 31, 2022
ba29d7d
Merge branch 'main' into task/FP-1375-admin-controlled-messages
duckonomy May 5, 2022
651333e
Remove unused css import.
duckonomy May 5, 2022
dc8b1bb
Change TextField to CharField for sanity.
duckonomy May 5, 2022
927ad40
Use class selector instead of attribute selector.
duckonomy May 5, 2022
9681a8b
Refactor messages.
duckonomy May 6, 2022
6bc1f13
Merge branch 'main' into task/FP-1375-admin-controlled-messages
duckonomy May 6, 2022
545b70c
Fix prettier linting.
duckonomy May 6, 2022
00acb37
Fix server linting.
duckonomy May 6, 2022
23c9535
Fix server testing.
duckonomy May 6, 2022
775ce03
Merge branch 'main' into task/FP-1375-admin-controlled-messages
rstijerina May 11, 2022
9334cfb
Rename message to portal_messages.
duckonomy May 13, 2022
f72a60b
Fix model.
duckonomy May 13, 2022
0d1671e
Refactor put request for custom messages.
duckonomy May 17, 2022
a419a28
Refactor introMessageName to messageName.
duckonomy May 17, 2022
c4b4cf3
Fix prettier linting.
duckonomy May 17, 2022
e28f113
Fix incorrect import.
duckonomy May 17, 2022
0223d2f
Fix test.
duckonomy May 17, 2022
affed79
Simplify custom message payload.
duckonomy Jun 2, 2022
8af744b
Rename messageName to introMessageName.
duckonomy Jun 2, 2022
02b3c12
Fix linting.
duckonomy Jun 2, 2022
bbc22c5
Rename introMessages to introMessageComponents.
duckonomy Jun 2, 2022
313fdf2
Fix linting.
duckonomy Jun 2, 2022
5c5ae6e
Fix backend testing.
duckonomy Jun 2, 2022
1b3da0b
Merge branch 'main' into task/FP-1375-admin-controlled-messages
rstijerina Jun 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { initialState as profile } from '../../../redux/reducers/profile.reducer
import { initialState as workbench } from '../../../redux/reducers/workbench.reducers';
import { initialState as notifications } from '../../../redux/reducers/notifications.reducers';
import { initialTicketCreateState as ticketCreate } from '../../../redux/reducers/tickets.reducers';
import introMessages from '../../../redux/reducers/intro.reducers';
import introMessages from '../../../redux/reducers/message.reducers';
import ManageAccountPage from '../index';

const mockStore = configureStore();
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/Tickets/TicketStandaloneCreate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import renderComponent from 'utils/testing';
import TicketStandaloneCreate from './TicketStandaloneCreate';
import { initialTicketCreateState as ticketCreate } from '../../redux/reducers/tickets.reducers';
import { initialState as workbench } from '../../redux/reducers/workbench.reducers';
import initialIntroMessages from '../../redux/reducers/intro.reducers';
import initialIntroMessages from '../../redux/reducers/message.reducers';
import { initialState as user } from '../../redux/reducers/authenticated_user.reducer';

const mockStore = configureStore();
Expand All @@ -15,7 +15,7 @@ describe('TicketStandaloneCreate', () => {
const store = mockStore({
ticketCreate,
authenticatedUser: user,
introMessages: initialIntroMessages,
introMessages: { ...initialIntroMessages, TICKETS: true },
workbench,
});

Expand Down
1 change: 1 addition & 0 deletions client/src/components/Workbench/AppRouter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function AppRouter() {
useEffect(() => {
if (authenticatedUser) {
dispatch({ type: 'FETCH_INTRO' });
dispatch({ type: 'FETCH_CUSTOM_MESSAGES' });
duckonomy marked this conversation as resolved.
Show resolved Hide resolved
}
}, [authenticatedUser]);
return (
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Workbench/Workbench.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from '../../redux/reducers/tickets.reducers';
import { initialState as authenticatedUser } from '../../redux/reducers/authenticated_user.reducer';
import { initialState as systemMonitor } from '../../redux/reducers/systemMonitor.reducers';
import { initialIntroMessages as introMessages } from '../../redux/reducers/intro.reducers';
import { initialIntroMessages as introMessages } from '../../redux/reducers/message.reducers';
import { initialSystemState as systems } from '../../redux/reducers/datafiles.reducers';

import * as introMessageText from '../../constants/messages';
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Workbench/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('AppRouter', () => {
{ type: 'FETCH_WORKBENCH' },
{ type: 'FETCH_SYSTEMS' },
{ type: 'FETCH_INTRO' },
{ type: 'FETCH_CUSTOM_MESSAGES' },
]);
});
});
66 changes: 66 additions & 0 deletions client/src/components/_common/CustomMessage/CustomMessage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import PropTypes from 'prop-types';
import { SectionMessage } from '_common';
import { useDispatch, useSelector } from 'react-redux';
import styles from './CustomMessage.module.scss';

/**
* A message which, is created by the admin. Like IntroMessage, when dismissed, will not appear again.
*
* _This message is designed for custom messages from the admin.
*
* @example
* // message with identifier
* <CustomMessage
* componentName={identifierForMessageLikeRouteName}
* >
* </CustomMessage>
*/
function CustomMessage({ componentName }) {
const dispatch = useDispatch();
const messages = useSelector((state) => {
return state.customMessages
? state.customMessages.messages.filter((message) => {
return message.unread && message.template.component === componentName;
})
: [];
});

function onDismiss(dismissMessage) {
const newCustomMessages = {
messages: messages.map((message) => {
message.unread =
message.template.id === dismissMessage.template.id
? false
: message.unread;
return message;
}),
};
dispatch({
type: 'SAVE_CUSTOM_MESSAGES',
payload: newCustomMessages,
});
}

return messages.map((message) => {
const template = message.template;
return (
<div key={template.id} className={styles.message}>
<SectionMessage
type={template.message_type}
canDismiss={template.dismissible}
onDismiss={() => onDismiss(message)}
duckonomy marked this conversation as resolved.
Show resolved Hide resolved
>
{template.message}
</SectionMessage>
</div>
);
});
}

CustomMessage.propTypes = {
/** A unique identifier for the message */
componentName: PropTypes.string.isRequired,
};

export default CustomMessage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.message {
margin-top: 12px;
margin-bottom: 12px;
}
41 changes: 41 additions & 0 deletions client/src/components/_common/CustomMessage/CustomMessage.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { render } from '@testing-library/react';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';

import CustomMessage from './CustomMessage';

const mockStore = configureStore();
const store = mockStore({
customMessages: {
messages: [
{
template: {
id: 1,
component: 'TEST',
message_type: 'warning',
dismissible: true,
message: 'Test Message',
},
unread: true,
},
],
},
});

describe('CustomMessage', () => {
describe('elements', () => {
it('renders message text, message type, and dismissability correctly', () => {
const { container, getByText } = render(
<Provider store={store}>
<CustomMessage componentName="TEST"></CustomMessage>
</Provider>
);
expect(container.getElementsByClassName('is-warn').length).toEqual(1);
expect(container.getElementsByClassName('close-button').length).toEqual(
1
);
expect(getByText('Test Message')).not.toEqual(null);
});
});
});
1 change: 1 addition & 0 deletions client/src/components/_common/CustomMessage/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './CustomMessage';
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function isKnownMessage(messageName) {
/**
* A message which, when dismissed, will not appear again unless browser storage is cleared
*
* _This message is designed for user introduction to sections, but can be abstracted further into a `<DismissableMessage>` or abstracted less such that a message need not be passed in._
* _This message is designed for user introduction to sections, but can be abstracted further into a `<DismissibleMessage>` or abstracted less such that a message need not be passed in._
*
* @example
* // message with custom text, class, and identifier
Expand Down
7 changes: 6 additions & 1 deletion client/src/components/_common/Section/SectionMessages.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';

import { IntroMessage, isKnownIntroMessage } from '_common';
import { CustomMessage, IntroMessage, isKnownIntroMessage } from '_common';
import * as MESSAGES from '../../../constants/messages';

import styles from './SectionMessages.module.css';
Expand Down Expand Up @@ -62,6 +62,10 @@ function SectionMessages({
isKnownIntroMessage(introMessageName) || children.length > 0;
const hasMessageClass = 'has-message';

const customMessage = (
<CustomMessage componentName={introMessageName}></CustomMessage>
duckonomy marked this conversation as resolved.
Show resolved Hide resolved
);

useEffect(() => {
if (hasMessage) {
document.body.classList.add(hasMessageClass);
Expand All @@ -73,6 +77,7 @@ function SectionMessages({
return (
<aside className={`${styles['root']} ${className}`}>
{introMessage}
{customMessage}
{children}
</aside>
);
Expand Down
1 change: 1 addition & 0 deletions client/src/components/_common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export {
default as IntroMessage,
isKnownMessage as isKnownIntroMessage,
} from './IntroMessage';
export { default as CustomMessage } from './CustomMessage';
export { default as Pill } from './Pill';
export { default as TextCopyField } from './TextCopyField';
export { default as ShowMore } from './ShowMore';
Expand Down
3 changes: 2 additions & 1 deletion client/src/redux/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import authenticatedUser from './authenticated_user.reducer';
import { pushKeys } from './systems.reducers';
import notifications from './notifications.reducers';
import workbench from './workbench.reducers';
import introMessages from './intro.reducers';
import { introMessages, customMessages } from './message.reducers';
import { onboarding } from './onboarding.reducers';
import projects from './projects.reducers';
import { users } from './users.reducers';
Expand All @@ -42,6 +42,7 @@ export default combineReducers({
notifications,
workbench,
introMessages,
customMessages,
onboarding,
projects,
users,
Expand Down
45 changes: 0 additions & 45 deletions client/src/redux/reducers/intro.reducers.js

This file was deleted.

79 changes: 79 additions & 0 deletions client/src/redux/reducers/message.reducers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
export const initialIntroMessages = {
DASHBOARD: true,
APPLICATIONS: true,
DATA: true,
ALLOCATIONS: true,
HISTORY: true,
ACCOUNT: true,
TICKETS: true,
};

export const initialCustomMessages = {
messages: [],
};

export function introMessages(state = initialIntroMessages, action) {
switch (action.type) {
case 'INTRO_FETCH_STARTED':
return {
...state,
};
case 'INTRO_FETCH_SUCCESS':
return {
...state,
...action.payload,
};
case 'INTRO_FETCH_ERROR':
return {
...state,
};
case 'INTRO_SAVE_STARTED':
return {
...state,
};
case 'INTRO_SAVE_SUCCESS':
return {
...state,
...action.payload,
};
case 'INTRO_SAVE_ERROR':
return {
...action.payload,
};
default:
return state;
}
}

export function customMessages(state = initialCustomMessages, action) {
switch (action.type) {
case 'CUSTOM_MESSAGES_FETCH_STARTED':
return {
...state,
};
case 'CUSTOM_MESSAGES_FETCH_SUCCESS':
return {
...state,
...action.payload,
};
case 'CUSTOM_MESSAGES_FETCH_ERROR':
return {
...state,
};
case 'CUSTOM_MESSAGES_SAVE_STARTED':
return {
...state,
};
case 'CUSTOM_MESSAGES_SAVE_SUCCESS':
return {
...state,
...action.payload,
};
case 'CUSTOM_MESSAGES_SAVE_ERROR':
return {
...action.payload,
};
default:
return state;
}
}
9 changes: 8 additions & 1 deletion client/src/redux/sagas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ import {
import { watchPostRequestAccess } from './requestAccess.sagas';
import { watchAuthenticatedUser } from './authenticated_user.sagas';
import { watchWorkbench } from './workbench.sagas';
import { watchFetchIntroMessages, watchSaveIntroMessages } from './intro.sagas';
import {
watchFetchIntroMessages,
watchSaveIntroMessages,
watchFetchCustomMessages,
watchSaveCustomMessages,
} from './message.sagas';
import {
watchOnboardingAdminList,
watchOnboardingAdminIndividualUser,
Expand Down Expand Up @@ -90,7 +95,9 @@ export default function* rootSaga() {
watchFetchNotifications(),
watchWorkbench(),
watchFetchIntroMessages(),
watchFetchCustomMessages(),
watchSaveIntroMessages(),
watchSaveCustomMessages(),
watchOnboardingAdminList(),
watchOnboardingAdminIndividualUser(),
watchOnboardingAction(),
Expand Down
Loading