Skip to content

Commit 7c88764

Browse files
feat: updating help section and new slots created (#530)
1 parent ba2e46b commit 7c88764

File tree

19 files changed

+601
-64
lines changed

19 files changed

+601
-64
lines changed

src/components/Footer.test.jsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AppContext } from '@edx/frontend-platform/react';
88

99
import Footer from './Footer';
1010
import FooterSlot from '../plugin-slots/FooterSlot';
11+
import StudioFooterHelpSectionSlot from '../plugin-slots/StudioFooterHelpSectionSlot';
1112

1213
const FooterWithContext = ({ locale = 'es' }) => {
1314
const contextValue = useMemo(() => ({
@@ -90,3 +91,32 @@ describe('<Footer />', () => {
9091
});
9192
});
9293
});
94+
95+
describe('<StudioFooterHelpSectionSlot />', () => {
96+
const SectionWithContext = ({ locale = 'es' }) => {
97+
const contextValue = useMemo(() => ({
98+
authenticatedUser: null,
99+
config: {
100+
LOGO_TRADEMARK_URL: process.env.LOGO_TRADEMARK_URL,
101+
LMS_BASE_URL: process.env.LMS_BASE_URL,
102+
},
103+
}), []);
104+
105+
return (
106+
<IntlProvider locale={locale}>
107+
<AppContext.Provider
108+
value={contextValue}
109+
>
110+
<StudioFooterHelpSectionSlot />
111+
</AppContext.Provider>
112+
</IntlProvider>
113+
);
114+
};
115+
116+
it('renders correctly', () => {
117+
const tree = renderer
118+
.create(<SectionWithContext />)
119+
.toJSON();
120+
expect(tree).toMatchSnapshot();
121+
});
122+
});

src/components/__snapshots__/Footer.test.jsx.snap

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,68 @@ exports[`<Footer /> renders correctly renders without a language selector in es
128128
</div>
129129
</footer>
130130
`;
131+
132+
exports[`<StudioFooterHelpSectionSlot /> renders correctly 1`] = `
133+
[
134+
<div
135+
className="m-0 mt-6 row align-items-center justify-content-center"
136+
>
137+
<div
138+
className="col border-top mr-2"
139+
/>
140+
<button
141+
className="btn btn-outline-primary btn-sm"
142+
data-testid="helpToggleButton"
143+
disabled={false}
144+
onClick={[Function]}
145+
type="button"
146+
>
147+
<span
148+
className="pgn__icon pgn__icon__sm btn-icon-before"
149+
>
150+
<svg
151+
aria-hidden={true}
152+
fill="none"
153+
focusable={false}
154+
height={24}
155+
role="img"
156+
viewBox="0 0 24 24"
157+
width={24}
158+
xmlns="http://www.w3.org/2000/svg"
159+
>
160+
<path
161+
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm1 17h-2v-2h2v2Zm2.07-7.75-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25Z"
162+
fill="currentColor"
163+
/>
164+
</svg>
165+
</span>
166+
Looking for help with Studio?
167+
<span
168+
className="pgn__icon pgn__icon__sm btn-icon-after"
169+
>
170+
<svg
171+
aria-hidden={true}
172+
fill="none"
173+
focusable={false}
174+
height={24}
175+
role="img"
176+
viewBox="0 0 24 24"
177+
width={24}
178+
xmlns="http://www.w3.org/2000/svg"
179+
>
180+
<path
181+
d="M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41Z"
182+
fill="currentColor"
183+
/>
184+
</svg>
185+
</span>
186+
</button>
187+
<div
188+
className="col border-top ml-2"
189+
/>
190+
</div>,
191+
<div
192+
className="px-4 container-mw-xl container-fluid"
193+
/>,
194+
]
195+
`;

src/components/studio-footer/StudioFooter.jsx

Lines changed: 3 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
import React, { useContext, useState } from 'react';
1+
import React, { useContext } from 'react';
22
import isEmpty from 'lodash/isEmpty';
33
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
44
import { ensureConfig } from '@edx/frontend-platform';
55
import { AppContext } from '@edx/frontend-platform/react';
66
import {
77
ActionRow,
8-
Button,
98
Container,
109
Hyperlink,
11-
TransitionReplace,
1210
} from '@openedx/paragon';
13-
import { ExpandLess, ExpandMore, Help } from '@openedx/paragon/icons';
1411
import classNames from 'classnames';
1512
import PropTypes from 'prop-types';
1613

1714
import messages from './messages';
1815
import StudioFooterLogoSlot from '../../plugin-slots/StudioFooterLogoSlot';
16+
import StudioFooterHelpSectionSlot from '../../plugin-slots/StudioFooterHelpSectionSlot';
1917

2018
ensureConfig([
2119
'LMS_BASE_URL',
@@ -32,76 +30,18 @@ const StudioFooter = ({
3230
containerProps,
3331
}) => {
3432
const intl = useIntl();
35-
const [isOpen, setIsOpen] = useState(false);
3633
const { config } = useContext(AppContext);
3734

3835
const { containerClassName, ...restContainerProps } = containerProps || {};
3936

4037
return (
4138
<>
42-
<div className="m-0 mt-6 row align-items-center justify-content-center">
43-
<div className="col border-top mr-2" />
44-
<Button
45-
data-testid="helpToggleButton"
46-
variant="outline-primary"
47-
onClick={() => setIsOpen(!isOpen)}
48-
iconBefore={Help}
49-
iconAfter={isOpen ? ExpandLess : ExpandMore}
50-
size="sm"
51-
>
52-
{isOpen ? intl.formatMessage(messages.closeHelpButtonLabel)
53-
: intl.formatMessage(messages.openHelpButtonLabel)}
54-
</Button>
55-
<div className="col border-top ml-2" />
56-
</div>
39+
<StudioFooterHelpSectionSlot containerProps={containerProps} />
5740
<Container
5841
size="xl"
5942
className={classNames('px-4', containerClassName)}
6043
{...restContainerProps}
6144
>
62-
<TransitionReplace>
63-
{isOpen ? (
64-
<ActionRow key="help-link-button-row" className="py-4" data-testid="helpButtonRow">
65-
<ActionRow.Spacer />
66-
<Button as="a" href="https://docs.openedx.org/" size="sm">
67-
<FormattedMessage {...messages.edxDocumentationButtonLabel} />
68-
</Button>
69-
<Button
70-
as="a"
71-
href="https://openedx.org/"
72-
size="sm"
73-
data-testid="openEdXPortalButton"
74-
>
75-
<FormattedMessage {...messages.openEdxPortalButtonLabel} />
76-
</Button>
77-
<Button
78-
as="a"
79-
href="https://www.edx.org/course/edx101-overview-of-creating-an-edx-course#.VO4eaLPF-n1"
80-
size="sm"
81-
>
82-
<FormattedMessage {...messages.edx101ButtonLabel} />
83-
</Button>
84-
<Button
85-
as="a"
86-
href="https://www.edx.org/course/studiox-creating-a-course-with-edx-studio"
87-
size="sm"
88-
>
89-
<FormattedMessage {...messages.studioXButtonLabel} />
90-
</Button>
91-
{!isEmpty(config.SUPPORT_EMAIL) && (
92-
<Button
93-
as="a"
94-
href={`mailto:${config.SUPPORT_EMAIL}`}
95-
size="sm"
96-
data-testid="contactUsButton"
97-
>
98-
<FormattedMessage {...messages.contactUsButtonLabel} />
99-
</Button>
100-
)}
101-
<ActionRow.Spacer />
102-
</ActionRow>
103-
) : null}
104-
</TransitionReplace>
10545
<ActionRow className="pt-3 m-0 x-small">
10646
© {new Date().getFullYear()} <Hyperlink destination={config.MARKETING_SITE_BASE_URL} target="_blank" className="ml-2">{config.SITE_NAME}</Hyperlink>
10747
<ActionRow.Spacer />

src/components/studio-footer/StudioFooter.test.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { useMemo } from 'react';
33
import { render, screen } from '@testing-library/react';
44
import userEvent from '@testing-library/user-event';
55
import '@testing-library/jest-dom/extend-expect';
6+
import '@testing-library/jest-dom';
67
import { IntlProvider } from '@edx/frontend-platform/i18n';
78
import { AppContext } from '@edx/frontend-platform/react';
89
import StudioFooterSlot from '../../plugin-slots/StudioFooterSlot';
@@ -77,7 +78,7 @@ describe('Footer', () => {
7778
render(<Component />);
7879
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
7980
await user.click(helpToggleButton);
80-
expect(screen.getByTestId('openEdXPortalButton')).toBeVisible();
81+
expect(screen.getByTestId('openEdXDemoCourseButton')).toBeVisible();
8182
});
8283
it('should not show contact us button', async () => {
8384
const user = userEvent.setup();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import { useIntl } from '@edx/frontend-platform/i18n';
3+
import {
4+
Button,
5+
} from '@openedx/paragon';
6+
import { ExpandLess, ExpandMore, Help } from '@openedx/paragon/icons';
7+
import PropTypes from 'prop-types';
8+
import messages from '../messages';
9+
10+
const HelpButton = ({ isOpen, setIsOpen }) => {
11+
const intl = useIntl();
12+
return (
13+
<Button
14+
data-testid="helpToggleButton"
15+
variant="outline-primary"
16+
onClick={() => setIsOpen(!isOpen)}
17+
iconBefore={Help}
18+
iconAfter={isOpen ? ExpandLess : ExpandMore}
19+
size="sm"
20+
>
21+
{isOpen ? intl.formatMessage(messages.closeHelpButtonLabel)
22+
: intl.formatMessage(messages.openHelpButtonLabel)}
23+
</Button>
24+
);
25+
};
26+
27+
HelpButton.propTypes = {
28+
setIsOpen: PropTypes.func.isRequired,
29+
isOpen: PropTypes.bool.isRequired,
30+
};
31+
32+
export default HelpButton;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React, { useMemo } from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import userEvent from '@testing-library/user-event';
4+
import { IntlProvider } from '@edx/frontend-platform/i18n';
5+
import { AppContext } from '@edx/frontend-platform/react';
6+
import HelpButton from './HelpButton';
7+
import '@testing-library/jest-dom';
8+
9+
// eslint-disable-next-line react/prop-types
10+
const ButtonWithContext = ({ locale = 'en', isOpen, setIsOpen }) => {
11+
const contextValue = useMemo(() => ({
12+
authenticatedUser: null,
13+
config: { },
14+
}), []);
15+
16+
return (
17+
<IntlProvider locale={locale}>
18+
<AppContext.Provider
19+
value={contextValue}
20+
>
21+
<HelpButton isOpen={isOpen} setIsOpen={setIsOpen} />
22+
</AppContext.Provider>
23+
</IntlProvider>
24+
);
25+
};
26+
27+
describe('HelpButton', () => {
28+
const mockSetIsOpen = jest.fn();
29+
30+
beforeEach(() => {
31+
mockSetIsOpen.mockClear();
32+
});
33+
34+
it('renders with "open" label when isOpen is false', () => {
35+
render(<ButtonWithContext isOpen={false} setIsOpen={mockSetIsOpen} />);
36+
expect(screen.getByTestId('helpToggleButton')).toBeInTheDocument();
37+
expect(screen.getByRole('button')).toHaveTextContent(/help|open/i);
38+
});
39+
40+
it('renders with "close" label when isOpen is true', () => {
41+
render(<ButtonWithContext isOpen setIsOpen={mockSetIsOpen} />);
42+
expect(screen.getByTestId('helpToggleButton')).toBeInTheDocument();
43+
expect(screen.getByRole('button')).toHaveTextContent(/close|help/i);
44+
});
45+
46+
it('calls setIsOpen with the toggled value when clicked', async () => {
47+
const user = userEvent.setup();
48+
const { rerender } = render(<ButtonWithContext isOpen={false} setIsOpen={mockSetIsOpen} />);
49+
await user.click(screen.getByTestId('helpToggleButton'));
50+
expect(mockSetIsOpen).toHaveBeenCalledWith(true);
51+
rerender(<ButtonWithContext isOpen setIsOpen={mockSetIsOpen} />);
52+
await user.click(screen.getByTestId('helpToggleButton'));
53+
expect(mockSetIsOpen).toHaveBeenCalledWith(false);
54+
});
55+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React, { useContext } from 'react';
2+
import { AppContext } from '@edx/frontend-platform/react';
3+
import isEmpty from 'lodash/isEmpty';
4+
import { ensureConfig } from '@edx/frontend-platform';
5+
import { FormattedMessage } from '@edx/frontend-platform/i18n';
6+
import {
7+
ActionRow,
8+
Button,
9+
} from '@openedx/paragon';
10+
import messages from '../messages';
11+
12+
ensureConfig([
13+
'SUPPORT_EMAIL',
14+
], 'Studio Footer Help Content component');
15+
16+
const BUTTONS = [
17+
{
18+
as: 'a',
19+
href: 'https://docs.openedx.org/',
20+
size: 'sm',
21+
message: messages.edxDocumentationButtonLabel,
22+
dataTestid: null,
23+
},
24+
{
25+
as: 'a',
26+
href: 'https://openedx.org/',
27+
size: 'sm',
28+
message: messages.openEdxPortalButtonLabel,
29+
dataTestid: 'openEdXPortalButton',
30+
},
31+
{
32+
as: 'a',
33+
href: 'https://www.edx.org/course/edx101-overview-of-creating-an-edx-course#.VO4eaLPF-n1',
34+
size: 'sm',
35+
message: messages.edx101ButtonLabel,
36+
dataTestid: 'openEdXDemoCourseButton',
37+
},
38+
{
39+
as: 'a',
40+
href: 'https://www.edx.org/course/studiox-creating-a-course-with-edx-studio',
41+
size: 'sm',
42+
message: messages.studioXButtonLabel,
43+
dataTestid: null,
44+
},
45+
];
46+
47+
const HelpContent = () => {
48+
const { config } = useContext(AppContext);
49+
return (
50+
<ActionRow key="help-link-button-row" className="py-4" data-testid="helpButtonRow">
51+
<ActionRow.Spacer />
52+
53+
{BUTTONS.map(({
54+
as, href, size, message, dataTestid,
55+
}) => (
56+
<Button as={as} href={href} size={size} key={message.id} data-testid={dataTestid}>
57+
<FormattedMessage {...message} />
58+
</Button>
59+
))}
60+
61+
{!isEmpty(config.SUPPORT_EMAIL) && (
62+
<Button
63+
as="a"
64+
href={`mailto:${config.SUPPORT_EMAIL}`}
65+
size="sm"
66+
data-testid="contactUsButton"
67+
>
68+
<FormattedMessage {...messages.contactUsButtonLabel} />
69+
</Button>
70+
)}
71+
<ActionRow.Spacer />
72+
</ActionRow>
73+
);
74+
};
75+
76+
export default HelpContent;

0 commit comments

Comments
 (0)