From f21f7f9708b9570bd13d2836d86ff59dc40c5c11 Mon Sep 17 00:00:00 2001 From: Ben Silverman Date: Tue, 29 Dec 2020 13:21:17 -0500 Subject: [PATCH 1/4] #17 Tests for SemanticField, MediaEmbed components --- .../SemanticField/SemanticField.test.js | 20 +++++++++++++++++++ .../SlateMediaEmbedElement.js | 1 + .../SlateMediaEmbedElement.test.js | 19 ++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/components/SemanticField/SemanticField.test.js create mode 100644 src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.test.js diff --git a/src/components/SemanticField/SemanticField.test.js b/src/components/SemanticField/SemanticField.test.js new file mode 100644 index 00000000..e1cfefbc --- /dev/null +++ b/src/components/SemanticField/SemanticField.test.js @@ -0,0 +1,20 @@ +/** + * @jest-environment jsdom + */ + +import { render } from '@testing-library/react'; +import { Formik } from 'formik'; +import SemanticField from './SemanticField'; + +test('renders semantic field', async () => { + const { getByRole } = render( + + + , + ); + const field = getByRole('textbox'); + expect(field).toBeInTheDocument(); +}); diff --git a/src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.js b/src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.js index 032eb576..c3c962a7 100644 --- a/src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.js +++ b/src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.js @@ -15,6 +15,7 @@ const SlateMediaEmbedElement = ({ title="embed" src={`${url}`} frameBorder="0" + data-testid="slate-iframe" /> diff --git a/src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.test.js b/src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.test.js new file mode 100644 index 00000000..f68b7e54 --- /dev/null +++ b/src/components/SlateMediaEmbedElement/SlateMediaEmbedElement.test.js @@ -0,0 +1,19 @@ +/** + * @jest-environment jsdom + */ + +import { render } from '@testing-library/react'; +import { Formik } from 'formik'; +import SlateMediaEmbedElement from './SlateMediaEmbedElement'; + +test('renders slate media embed element', async () => { + const { getByTestId } = render( + + + , + ); + const elem = getByTestId('slate-iframe'); + expect(elem).toBeInTheDocument(); +}); From 56372f3f6c271a6021a2a5c9dfe40cba1e4ba586 Mon Sep 17 00:00:00 2001 From: Ben Silverman Date: Tue, 29 Dec 2020 16:52:00 -0500 Subject: [PATCH 2/4] #17 Lint HeatMap.js --- src/components/HeatMap/HeatMap.js | 59 +++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/components/HeatMap/HeatMap.js b/src/components/HeatMap/HeatMap.js index a2c1976a..e30a2a0c 100644 --- a/src/components/HeatMap/HeatMap.js +++ b/src/components/HeatMap/HeatMap.js @@ -7,29 +7,42 @@ import $ from 'jquery'; import { DocumentFiltersContext, DocumentAnnotationsContext } from '../../contexts/DocumentContext'; -function HeatMap({ pdf }) { +const HeatMap = ({ pdf }) => { const lineHeight = 18; const documentHeight = $('#document-container').height() || 0; - let scaleFactor = $('#document-container').get(0) === undefined ? 1 : $('#document-container').height() / $('#document-container').get(0).scrollHeight; + const scaleFactor = $('#document-container').get(0) === undefined + ? 1 + : $('#document-container').height() / $('#document-container').get(0).scrollHeight; const minStrokeHeight = 1; - const offsetTop = $('#document-container').offset() === undefined ? 0 : $('#document-container').offset().top; - const grandularity = lineHeight * scaleFactor >= minStrokeHeight ? lineHeight : Math.ceil(minStrokeHeight / scaleFactor); + const offsetTop = $('#document-container').offset() === undefined + ? 0 + : $('#document-container').offset().top; + const grandularity = lineHeight * scaleFactor >= minStrokeHeight + ? lineHeight + : Math.ceil(minStrokeHeight / scaleFactor); const [channelAnnotations] = useContext(DocumentAnnotationsContext); const [documentFilters] = useContext(DocumentFiltersContext); const n = (Math.ceil($('#document-card-container').height() / grandularity)); - const map = new Array(isNaN(n) ? 0 : n); + const map = new Array(Number.isNaN(n) ? 0 : n); for (const side in channelAnnotations) { if (channelAnnotations[side] !== null) { for (const anno of channelAnnotations[side]) { - if (documentFilters.annotationIds[side] !== null && documentFilters.annotationIds[side].includes(anno._id)) { + if (documentFilters.annotationIds[side] !== null + && documentFilters.annotationIds[side].includes(anno._id)) { if (anno.position.height !== undefined) { let h = anno.position.height; - // if for some reason the height of this annotation is a negative number we are going to recalculate the value in the loop + // if for some reason the height of this annotation is a negative number, + // recalculate the value in the loop if (h < 0) { - const annotationBeginning = $(`#document-content-container span[annotation-id='${anno._id}'] .annotation-beginning-marker`); - const annotationEnding = $(`#document-content-container span[annotation-id='${anno._id}'] .annotation-ending-marker`); - if (annotationBeginning.get(0) !== undefined && annotationEnding.get(0) !== undefined) { + const annotationBeginning = $( + `#document-content-container span[annotation-id='${anno._id}'] .annotation-beginning-marker`, + ); + const annotationEnding = $( + `#document-content-container span[annotation-id='${anno._id}'] .annotation-ending-marker`, + ); + if (annotationBeginning.get(0) !== undefined + && annotationEnding.get(0) !== undefined) { const annotationBeginningPosition = annotationBeginning.offset(); const annotationEndingPosition = annotationEnding.offset(); h = (annotationEndingPosition.top - annotationBeginningPosition.top) + lineHeight; @@ -37,8 +50,11 @@ function HeatMap({ pdf }) { h = 0; } } - // now we have to convert the annotations position and height into starting and ending indexs for the map - let startIndex = Math.floor((anno.position.top - offsetTop) / grandularity); + // convert the annotation position and height + // into starting and ending indexs for the map + let startIndex = Math.floor( + (anno.position.top - offsetTop) / grandularity, + ); startIndex = startIndex < 0 ? 0 : startIndex; const endIndex = Math.floor((anno.position.top + h - offsetTop) / grandularity); for (let i = startIndex; i <= endIndex; i += 1) { @@ -57,8 +73,21 @@ function HeatMap({ pdf }) { return ( <> -
- {map.map((v, i) =>
)} +
+ {map.map((v, i) => ( +
+ ))}
); -} +}; export default HeatMap; From f778e4968f17dab104d3476ed31be626b0fdac6a Mon Sep 17 00:00:00 2001 From: Ben Silverman Date: Tue, 29 Dec 2020 16:58:36 -0500 Subject: [PATCH 3/4] #17 Test for HeatMap component --- src/components/HeatMap/HeatMap.test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/components/HeatMap/HeatMap.test.js diff --git a/src/components/HeatMap/HeatMap.test.js b/src/components/HeatMap/HeatMap.test.js new file mode 100644 index 00000000..a4b8059a --- /dev/null +++ b/src/components/HeatMap/HeatMap.test.js @@ -0,0 +1,24 @@ +/** + * @jest-environment jsdom + */ + +import { render } from '@testing-library/react'; +import HeatMap from './HeatMap'; +import { DocumentFiltersContext, DocumentAnnotationsContext } from '../../contexts/DocumentContext'; + +test('renders heatmap', async () => { + const { getByTestId } = render( + + + + + , + ); + const heatMap = getByTestId('heat-map'); + expect(heatMap).toBeInTheDocument(); +}); + From 7c1af2e68a07fd76efcf8938b13b4d7149a651d8 Mon Sep 17 00:00:00 2001 From: Ben Silverman Date: Tue, 29 Dec 2020 17:11:22 -0500 Subject: [PATCH 4/4] #17 Lint pages/documents/[slug]/index.jsx --- src/pages/documents/[slug]/index.jsx | 43 +++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/pages/documents/[slug]/index.jsx b/src/pages/documents/[slug]/index.jsx index ae6a282f..20adae30 100644 --- a/src/pages/documents/[slug]/index.jsx +++ b/src/pages/documents/[slug]/index.jsx @@ -3,7 +3,7 @@ /* eslint-disable no-underscore-dangle */ /* eslint-disable no-restricted-syntax */ import { useState, useEffect } from 'react'; -import { isMobile } from "react-device-detect" +import { isMobile } from 'react-device-detect'; import { useSession } from 'next-auth/client'; import $ from 'jquery'; import { @@ -109,7 +109,8 @@ const DocumentPage = (props) => { const [annotationChannel1Loaded, setAnnotationChannel1Loaded] = useState(false); const [annotationChannel2Loaded, setAnnotationChannel2Loaded] = useState(false); const minDisplayWidth = 1150; - // if true annotations will displayed in channels otherwise they will be displayed as popovers that show on hover or on click + // popovers for mobile + // eslint-disable-next-line no-unused-vars const [displayAnnotationsInChannels, setDisplayAnnotationsInChannels] = useState(!isMobile); const [scrollToAnnotation, setScrollToAnnotation] = useState(validQuery); @@ -117,9 +118,9 @@ const DocumentPage = (props) => { const [showMoreInfoShareModal, setShowMoreInfoShareModal] = useState(); const [session, loading] = useSession(); - // the interesection between the groups that this user is in and the groups the document is shared too + // interesection between user's groups and groups the document is shared to const [groupIntersection, setGroupIntersection] = useState(); - // the people the user can share their annotations with which is generated from the groupIntersection + // other users this user can share annotations with, generated from groupIntersection const [membersIntersection, setMembersIntersection] = useState([]); @@ -381,25 +382,32 @@ const DocumentPage = (props) => { }); useEffect(() => { + // eslint-disable-next-line no-undef window.addEventListener('resize', () => { + // eslint-disable-next-line no-undef setDisplayAnnotationsInChannels(window.innerWidth > minDisplayWidth && !isMobile); }); }); async function getIntersectionOfGroupsAndUsers() { - // when the session gets loaded in we are going to get the intersection of groups and the users that applies to - if (session !== undefined && groupIntersection === undefined) { // this means we haven't set it yet + // when session loaded, get intersection of groups and the users that applies to + if (session && !groupIntersection) { const userGroupIds = session.user.groups.map((g) => g.id); const intersection = userGroupIds.filter((id) => document.groups.includes(id)); const intersectionGroups = await Promise.all(intersection.map((id) => getGroupById(id))); let intersectionMembers = []; for (let i = 0; i < intersectionGroups.length; i += 1) { - // filtering out members for this specific group that we have already included in the intersectionMembers array - const members = intersectionGroups[i].members.filter((m) => !intersectionMembers.some((im) => im.id === m.id)); + // filtering out members for this specific group + // that we have already included in the intersectionMembers array + const members = intersectionGroups[i].members + // eslint-disable-next-line no-loop-func + .filter((m) => !intersectionMembers.some((im) => im.id === m.id)); intersectionMembers = intersectionMembers.concat(members); } - // before we set the intersection of members we need to remove the id of the current user session - setMembersIntersection(intersectionMembers.filter((m) => m.id !== session.user.id).map((m) => ({ ...m, name: FirstNameLastInitial(m.name) }))); + // remove id of current user session before setting intersection of members + setMembersIntersection(intersectionMembers + .filter((m) => m.id !== session.user.id) + .map((m) => ({ ...m, name: FirstNameLastInitial(m.name) }))); setGroupIntersection(intersectionGroups); } } @@ -418,7 +426,9 @@ const DocumentPage = (props) => { return ( - + {!session && loading && ( @@ -525,11 +535,16 @@ const DocumentPage = (props) => {

Only you can see the annotation

-

Share with group(s)

+

+ Share with group(s) +

- Share this annotation with all members of your group(s) who have access to this document. + Share this annotation with all members of + your group(s) who have access to this document. +

+

+ Share with user(s)

-

Share with user(s)

Share this annotation with a specific user or users only.