Skip to content

Commit

Permalink
DOP-4282: Implement container for lab drawers (#992)
Browse files Browse the repository at this point in the history
  • Loading branch information
rayangler authored Jan 29, 2024
1 parent 1c23441 commit 5624334
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 17 deletions.
44 changes: 44 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"react-loadable": "^5.5.0",
"react-loading-skeleton": "^3.1.0",
"react-player": "^2.9.0",
"react-resizable": "^3.0.5",
"react-transition-group": "^4.4.1",
"readable-stream": "^4.3.0",
"realm-web": "^2.0.0",
Expand Down
28 changes: 28 additions & 0 deletions src/components/Instruqt/InstruqtFrame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { forwardRef } from 'react';
import { css, cx } from '@leafygreen-ui/emotion';

const iframeStyle = css`
border: none;
`;

const InstruqtFrame = forwardRef(({ title, height, embedValue }, ref) => {
const labTitle = title || 'MongoDB Interactive Lab';
const frameTitle = `Instruqt - ${labTitle}`;
const frameHeight = height || '640';
const frameSrc = `https://play.instruqt.com/embed${embedValue}`;

return (
<iframe
ref={ref}
className={cx(iframeStyle)}
allowFullScreen
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-modals"
title={frameTitle}
height={frameHeight}
width="100%"
src={frameSrc}
/>
);
});

export default InstruqtFrame;
121 changes: 121 additions & 0 deletions src/components/Instruqt/LabDrawer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { Resizable } from 'react-resizable';
import { cx, css } from '@leafygreen-ui/emotion';
import { palette } from '@leafygreen-ui/palette';
import useViewport from '../../hooks/useViewport';
import { theme } from '../../theme/docsTheme';
import InstruqtFrame from './InstruqtFrame';

const labContainerStyle = css`
background-color: ${palette.gray.dark3};
z-index: 9999;
position: fixed !important;
bottom: 0;
padding-top: 21px;
color: ${palette.white};
`;

const handleContainerStyle = css`
width: 100%;
display: flex;
justify-content: center;
`;

const handleStyle = css`
position: absolute;
border-radius: 4px;
cursor: ns-resize;
top: 10px;
background-color: ${palette.white};
width: 50px;
height: 4px;
@media ${theme.screenSize.upToMedium} {
display: none;
}
`;

const topContainerStyle = css`
margin-bottom: 11px;
padding-left: 17px;
height: 28px;
display: flex;
justify-content: center;
@media ${theme.screenSize.upToSmall} {
display: block;
}
`;

const titleStyle = css`
line-height: 28px;
width: 50vw;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
@media ${theme.screenSize.upToSmall} {
text-align: left;
}
`;

const CustomResizeHandle = React.forwardRef((props, ref) => {
const { handleAxis, ...restProps } = props;
return (
<div className={cx(handleContainerStyle)}>
<div className={cx(handleStyle)} ref={ref} {...restProps} />
</div>
);
});

const LabDrawer = ({ title, embedValue }) => {
const viewportSize = useViewport();
const labTitle = title || 'MongoDB Interactive Lab';

const defaultMeasurement = 200;
const defaultWidth = viewportSize.width ?? defaultMeasurement;
const defaultHeight = (viewportSize.height * 2) / 3 ?? defaultMeasurement;

const minHeight = 60;
const maxHeight = viewportSize.height ?? defaultMeasurement;
const [height, setHeight] = useState(defaultHeight);

const frameHeight = height - minHeight;

// Shrink height of the drawer if new max height is less than the current height
useEffect(() => {
if (maxHeight < height) {
setHeight(maxHeight);
}
}, [height, maxHeight]);

const handleResize = (_e, { size }) => {
setHeight(size.height);
};

return createPortal(
<Resizable
className={cx(labContainerStyle)}
height={height}
maxConstraints={[defaultWidth, maxHeight]}
minConstraints={[defaultWidth, minHeight]}
width={defaultWidth}
resizeHandles={['n']}
handle={<CustomResizeHandle />}
onResize={handleResize}
>
{/* Need this div with style as a wrapper to help with resizing */}
<div style={{ width: defaultWidth + 'px', height: height + 'px' }}>
<div className={cx(topContainerStyle)}>
<div className={cx(titleStyle)}>{labTitle}</div>
</div>
<InstruqtFrame title={title} embedValue={embedValue} height={frameHeight} />
</div>
</Resizable>,
document.body
);
};

export default LabDrawer;
37 changes: 20 additions & 17 deletions src/components/Instruqt/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { css } from '@emotion/react';
import IconButton from '@leafygreen-ui/icon-button';
import Icon from '@leafygreen-ui/icon';
import { theme } from '../../theme/docsTheme';
import LabDrawer from './LabDrawer';
import InstruqtFrame from './InstruqtFrame';
import { InstruqtContext } from './instruqt-context';

const controlsStyle = css`
Expand All @@ -17,10 +19,11 @@ const controlsStyle = css`
margin-bottom: ${theme.size.default};
`;

const Instruqt = ({ nodeData: { argument }, nodeData }) => {
const embedValue = argument[0].value;
const Instruqt = ({ nodeData }) => {
const embedValue = nodeData?.argument[0]?.value;
const title = nodeData?.options?.title;
const iframeRef = useRef(null);
const { setIsOpen } = useContext(InstruqtContext);
const { setIsOpen, hasLab } = useContext(InstruqtContext);

const onFullScreen = useCallback(() => {
if (iframeRef) {
Expand All @@ -44,20 +47,20 @@ const Instruqt = ({ nodeData: { argument }, nodeData }) => {

return (
<>
<iframe
ref={iframeRef}
allowFullScreen
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-modals"
title={`Instruqt ${embedValue}`}
height="640"
width="100%"
src={`https://play.instruqt.com/embed${embedValue}`}
/>
<div css={controlsStyle}>
<IconButton aria-label="Full Screen" onClick={onFullScreen}>
<Icon glyph="FullScreenEnter" />
</IconButton>
</div>
{process.env.GATSBY_FEATURE_LAB_DRAWER === 'true' && hasLab ? (
<>
<LabDrawer embedValue={embedValue} title={title} />
</>
) : (
<>
<InstruqtFrame title={title} embedValue={embedValue} ref={iframeRef} />
<div css={controlsStyle}>
<IconButton aria-label="Full Screen" onClick={onFullScreen}>
<Icon glyph="FullScreenEnter" />
</IconButton>
</div>
</>
)}
</>
);
};
Expand Down
40 changes: 40 additions & 0 deletions tests/unit/Instruqt.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { render } from '@testing-library/react';
import Instruqt from '../../src/components/Instruqt';
import { InstruqtProvider } from '../../src/components/Instruqt/instruqt-context';
import mockData from './data/Instruqt.test.json';

const renderComponent = (nodeData, hasLabDrawer = false) => {
return render(
<InstruqtProvider hasInstruqtLab={hasLabDrawer}>
<Instruqt nodeData={nodeData} />
</InstruqtProvider>
);
};

describe('Instruqt', () => {
beforeEach(() => {
process.env.GATSBY_FEATURE_LAB_DRAWER = false;
});

it('renders null when directive argument does not exist', () => {
const wrapper = renderComponent(mockData.noArgument);
expect(wrapper.queryByTitle('Instruqt', { exact: false })).toBeFalsy();
});

it('renders as embedded content', () => {
const wrapper = renderComponent(mockData.example);
expect(wrapper.queryByTitle('Instruqt', { exact: false })).toBeTruthy();
});

describe('lab drawer', () => {
it('renders in a drawer', () => {
// This may be removed as the feature work is complete
process.env.GATSBY_FEATURE_LAB_DRAWER = true;
const hasLabDrawer = true;
const wrapper = renderComponent(mockData.example, hasLabDrawer);
expect(wrapper.queryByTitle('Instruqt', { exact: false })).toBeTruthy();
expect(wrapper.queryByText('MongoDB Interactive Lab')).toBeTruthy();
});
});
});
21 changes: 21 additions & 0 deletions tests/unit/data/Instruqt.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"noArgument": {
"type": "directive",
"children": [],
"domain": "mongodb",
"name": "instruqt",
"argument": []
},
"example": {
"type": "directive",
"children": [],
"domain": "mongodb",
"name": "instruqt",
"argument": [
{
"type": "text",
"value": "/mongodb-docs/tracks/getting-started-with-mongodb?token=em_j_d_wUT93QTFvgsZ"
}
]
}
}

0 comments on commit 5624334

Please sign in to comment.