Skip to content

Commit

Permalink
feat: Optimize collections listing [PT-185588012]
Browse files Browse the repository at this point in the history
This change is to optimize the materials collection rendering when used on large collection pages like the NGSS Assessment Portal.

Here are the changes that were made:

- Styled Landing page content html textarea on the projects admin page to be full width, and taller and to use a monospace font.  Previously it was a standard unstyled textarea.  This just makes viewing and editing it easier.
- Added optional opacity animation setting to existing cleanUpAssignViewAdjustment function in langing_page.html.haml.  This was a request to ensure the content was visible when the each NGSS content area was revealed.
- Added a public loadMaterialsCollections() method which caches the results for later use in renderMaterialsCollection().  The api endpoint already supported getting multiple collections.
- Fixes a race condition between the MaterialsCollection onDataLoad callback prop and the initial mount of the component.  The existing code in the project landing pages assumes the component is mounted.
  • Loading branch information
dougmartin committed Jul 24, 2023
1 parent 841a09c commit d88984e
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 49 deletions.
2 changes: 1 addition & 1 deletion rails/app/assets/javascripts/react-components.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion rails/app/views/admin/projects/_form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
= f.text_field :landing_page_slug
%li
Landing page content:
= f.text_area :landing_page_content, class: 'mceNoEditor'
= f.text_area :landing_page_content, class: 'mceNoEditor', style: 'width: 100%; height: 300px; font-family: monospace;'
%li
Project card image url:
= f.text_field :project_card_image_url
Expand Down
12 changes: 8 additions & 4 deletions rails/app/views/admin/projects/landing_page.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,20 @@
}
}

// This function should be called after a materials collection is fully loaded on a
// landing page via PortalPages.renderMaterialsCollection(). In the case that the
// collection page is embedded in a class's assign lightbox via an iframe, it ensures
// This function should be called after a materials collection is fully loaded on a
// landing page via PortalPages.renderMaterialsCollection(). In the case that the
// collection page is embedded in a class's assign lightbox via an iframe, it ensures
// that the iframe is resized to show the collection's entire contents.
function cleanUpAssignViewAdjustment() {
function cleanUpAssignViewAdjustment(options) {
if (isAssignWrapped) {
setParentIframeHeight();
jQuery('#collectionIframe', window.parent.document).css({'visibility': 'visible'});
jQuery('#collectionIframeLoading', window.parent.document).css({'display': 'none'});
}
if (options && options.skipAnimation) {
jQuery(".resourceItem").css('transition', 'none').css('opacity', 1);
return;
}
jQuery(".resourceItem").animate({'opacity': 1}, 500);
}

Expand Down
63 changes: 35 additions & 28 deletions react-components/src/library/components/materials-collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import Component from '../helpers/component'
import shuffleArray from '../helpers/shuffle-array'
import stemFinderResult from './stem-finder-result'
import portalObjectHelpers from '../helpers/portal-object-helpers'
import { loadMaterialsCollection } from '../helpers/materials-collection-cache'

const MaterialsCollection = Component({
getInitialState: function () {
return {
materials: []
materials: [],
loadedData: false
}
},

Expand All @@ -18,34 +20,39 @@ const MaterialsCollection = Component({
},

UNSAFE_componentWillMount: function () {
jQuery.ajax({
url: Portal.API_V1.MATERIALS_BIN_COLLECTIONS,
data: { id: this.props.collection,
skip_lightbox_reloads: true
},
dataType: 'json',
success: function (data) {
let materials = data[0].materials
if (this.props.randomize) {
materials = shuffleArray(materials)
loadMaterialsCollection(this.props.collection, function (data) {
let materials = data.materials
if (this.props.randomize) {
materials = shuffleArray(materials)
}
if (this.props.featured) {
// props.featured is the ID of the material we
// wish to insert at the start of the list
let featuredID = this.props.featured
let sortFeatured = function (a, b) {
if (a.id === featuredID) return -1
if (b.id === featuredID) return 1
return 0
}
if (this.props.featured) {
// props.featured is the ID of the material we
// wish to insert at the start of the list
let featuredID = this.props.featured
let sortFeatured = function (a, b) {
if (a.id === featuredID) return -1
if (b.id === featuredID) return 1
return 0
}
materials.sort(sortFeatured)
}
this.setState({ materials: materials })
if (this.props.onDataLoad) {
this.props.onDataLoad(materials)
}
}.bind(this)
})
materials.sort(sortFeatured)
}
this.setState({ materials: materials, loadedData: true })
}.bind(this))
},

componentDidMount: function () {
const checkForDataLoaded = () => {
if (!this.props.onDataLoad) {
return
}
if (this.state.loadedData) {
this.props.onDataLoad(this.state.materials)
} else {
setTimeout(checkForDataLoaded, 10)
}
}

checkForDataLoaded()
},

render: function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import React from 'react'

import SMaterialsList from '../search/materials-list'
import { loadMaterialsCollection } from '../../helpers/materials-collection-cache'

const shuffle = function (a) {
let idx = a.length
Expand All @@ -32,21 +33,15 @@ export default class MaterialsCollection extends React.Component {
componentDidMount () {
this.mounted = true
const { randomize, onDataLoad } = this.props
jQuery.ajax({
url: Portal.API_V1.MATERIALS_BIN_COLLECTIONS,
data: { id: this.props.collection },
dataType: 'json',
success: data => {
let { materials } = data[0]
if (randomize) {
materials = shuffle(materials)
}
if (onDataLoad) {
onDataLoad(materials)
}
if (this.mounted) {
this.setState({ materials })
}
loadMaterialsCollection(this.props.collection, ({ materials }) => {
if (randomize) {
materials = shuffle(materials)
}
if (onDataLoad) {
onDataLoad(materials)
}
if (this.mounted) {
this.setState({ materials })
}
})
}
Expand Down
38 changes: 38 additions & 0 deletions react-components/src/library/helpers/materials-collection-cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const cache = {}

export const loadMaterialsCollections = (ids, callback) => {
const onComplete = () => {
const data = ids.map(id => cache[id] || { name: 'MISSING-COLLECTION-' + id, materials: [] })
callback(data)
}

// ensure we only request each id once
const missingIds = ids.filter(id => !cache[id])
if (missingIds.length === 0) {
onComplete()
return
}

jQuery.ajax({
url: Portal.API_V1.MATERIALS_BIN_COLLECTIONS,
data: {
id: missingIds,
skip_lightbox_reloads: true
},
dataType: 'json',
success: (missingData) => {
missingIds.forEach((id, index) => {
cache[id] = missingData[index]
})
},
complete: () => {
onComplete()
}
})
}

export const loadMaterialsCollection = (id, callback) => {
loadMaterialsCollections([id], (data) => {
callback(data[0])
})
}
8 changes: 8 additions & 0 deletions react-components/src/library/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import ResourceRequirements from './components/browse-page/resource-requirements
import ResourceLicense from './components/browse-page/resource-license'
import ResourceProjects from './components/browse-page/resource-projects'
import showTab from './helpers/tabs'
import { loadMaterialsCollections } from './helpers/materials-collection-cache'

// previously React and ReactDOM were set by the react-rails gem
window.React = React
Expand Down Expand Up @@ -200,6 +201,13 @@ window.PortalComponents = {
renderSignupForm: signupFunctions.renderSignupForm,

MaterialsCollection: MaterialsCollection,

// this loads a set of materials collections in a single AJAX call and caches them for use
// in a later call to renderMaterialsCollection
loadMaterialsCollections: function (ids, callback) {
loadMaterialsCollections(ids, callback)
},

// this is a different format to match to existing project pages which had 2 formats itself
renderMaterialsCollection: function (collectionId, selectorOrElement, limitOrOptions) {
let options = limitOrOptions || {}
Expand Down

0 comments on commit d88984e

Please sign in to comment.