Skip to content

Commit

Permalink
Create, update, delete enhanced filters
Browse files Browse the repository at this point in the history
Test plan:
    - With the enhanced gradebook filters flag enabled
    - Create an ad hoc filter and do not press Save
        - When applied, the filter should take effect
        - Reload the page
        - The adhoc filter should no long appear
    - Create, apply, and save a filter
        - The filter should take effect
        - Reload the page
        - The saved filter should still appear and apply
    - Create, apply, and save a filter
        - Delete the filter
        - Reload the page
        - The adhoc filter should no long appear

To do in subsequent commit:
    - More exact conformity to designs with save and rename UI

flag=enhanced_gradebook_filters

Closes EVAL-2171
Closes EVAL-2172
Closes EVAL-2173
Refs EVAL-1934

Change-Id: Id1c5be631b37e77e7a2f9a3bdd05d366ca44952f
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/283572
Reviewed-by: Eduardo Escobar <[email protected]>
Reviewed-by: Spencer Olson <[email protected]>
QA-Review: Eduardo Escobar <[email protected]>
Product-Review: Jody Sailor
Tested-by: Service Cloud Jenkins <[email protected]>
  • Loading branch information
aaronshaf committed Jan 31, 2022
1 parent 6d8d067 commit dd42090
Show file tree
Hide file tree
Showing 19 changed files with 751 additions and 518 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@
"use-debounce": "^3",
"use-media-set": "^1.1",
"uuid": "^3.2.1",
"yarn-deduplicate": "^3.0.1"
"yarn-deduplicate": "^3.0.1",
"zustand": "^3.6.9"
},
"devDependencies": {
"@apollo/react-common": "~3.0.1",
Expand Down
3 changes: 0 additions & 3 deletions ui/features/gradebook/react/default_gradebook/Gradebook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ type GradebookProps = {
isFiltersLoading: boolean
isModulesLoading: boolean
modules: Module[]
onFiltersChange: (any) => void
performanceControls: PerformanceControls
settingsModalButtonContainer: HTMLElement
viewOptionsMenuNode: HTMLElement
Expand Down Expand Up @@ -4750,8 +4749,6 @@ class Gradebook extends React.Component<GradebookProps, GradebookState> {
this.state.isEssentialDataLoaded && (
<Portal node={this.props.filterNavNode}>
<FilterNav
filters={this.props.filters}
onChange={this.props.onFiltersChange}
gradingPeriods={this.gradingPeriodSet?.gradingPeriods || []}
modules={this.state.modules}
assignmentGroups={this.state.assignmentGroups}
Expand Down
48 changes: 45 additions & 3 deletions ui/features/gradebook/react/default_gradebook/Gradebook.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ import {showConfirmationDialog} from '@canvas/feature-flags/react/ConfirmationDi
import I18n from 'i18n!gradebook'
import _ from 'lodash'
import htmlEscape from 'html-escape'
import type {Assignment, Submission, Filter, Section, SectionMap} from './gradebook.d'
import type {
Assignment,
Filter,
GradebookFilterApiResponse,
GradebookFilterApiRequest,
PartialFilter,
Section,
SectionMap,
Submission
} from './gradebook.d'

export function compareAssignmentDueDates(assignment1, assignment2) {
return assignmentHelper.compareByDueDate(assignment1.object, assignment2.object)
Expand Down Expand Up @@ -173,14 +182,47 @@ export function getAssignmentGroupColumnId(assignmentGroupId: string) {

export function findAllAppliedFilterValuesOfType(type: string, filters: Filter[]) {
return filters
.filter(f => f.isApplied)
.filter(f => f.is_applied)
.flatMap(f => f.conditions.filter(c => c.type === type && c.value))
.map(c => c.value)
}

export function getAllAppliedFilterValues(filters: Filter[]) {
return filters
.filter(f => f.isApplied)
.filter(f => f.is_applied)
.flatMap(f => f.conditions.filter(c => c.value))
.map(c => c.value)
}

// Extra normalization; comes from jsonb payload
export const deserializeFilter = (json: GradebookFilterApiResponse): Filter => {
const filter = json.gradebook_filter
if (!filter.id || typeof filter.id !== 'string') throw new Error('invalid filter id')
if (!Array.isArray(filter.payload.conditions)) throw new Error('invalid filter conditions')
const conditions = filter.payload.conditions.map(c => {
if (!c || typeof c.id !== 'string') throw new Error('invalid condition id')
return {
id: c.id,
type: c.type,
value: c.value,
created_at: String(c.created_at)
}
})
return {
id: filter.id,
name: String(filter.name),
conditions,
is_applied: !!filter.payload.is_applied,
created_at: String(filter.created_at)
}
}

export const serializeFilter = (filter: PartialFilter): GradebookFilterApiRequest => {
return {
name: filter.name,
payload: {
is_applied: filter.is_applied,
conditions: filter.conditions
}
}
}
60 changes: 38 additions & 22 deletions ui/features/gradebook/react/default_gradebook/GradebookData.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, {useRef} from 'react'
import React, {useRef, useEffect} from 'react'
import shallow from 'zustand/shallow'
import {camelize} from 'convert-case'
import Gradebook from './Gradebook'
import PerformanceControls from './PerformanceControls'
import {RequestDispatch} from '@canvas/network'
import useModules from './hooks/useModules'
import useFilters from './hooks/useFilters'
import useStore from './stores/index'

export default function GradebookData(props) {
const performanceControls = useRef(
Expand All @@ -34,36 +34,52 @@ export default function GradebookData(props) {
})
)
const courseId = props.gradebookEnv.context_id
const {
data: modules,
errors: modulesErrors,
loading: isModulesLoading
} = useModules(
dispatch.current,
courseId,
performanceControls.contextModulesPerPage,
props.gradebookEnv.has_modules
)
const flashMessages = useStore(state => state.flashMessages)

const appliedFilters = useStore(state => state.appliedFilters(), shallow)
const isFiltersLoading = useStore(state => state.isFiltersLoading)
const fetchFilters = useStore(state => state.fetchFilters)

const modules = useStore(state => state.modules)
const isModulesLoading = useStore(state => state.isModulesLoading)
const fetchModules = useStore(state => state.fetchModules)

const {
data: filters,
errors: filtersErrors,
loading: isFiltersLoading,
setData: handleFiltersChange
} = useFilters(courseId, props.gradebookEnv.enhanced_gradebook_filters)
// Initial state
// We might be able to do this in gradebook/index.tsx instead
useEffect(() => {
useStore.setState({
courseId,
dispatch: dispatch.current,
performanceControls: performanceControls.current
})
}, [courseId, props.gradebookEnv.enhanced_gradebook_filters])

// Data loading logic goes here
useEffect(() => {
if (props.gradebookEnv.enhanced_gradebook_filters) {
fetchFilters()
}
if (props.gradebookEnv.has_modules) {
fetchModules()
}
}, [
fetchFilters,
fetchModules,
props.gradebookEnv.enhanced_gradebook_filters,
props.gradebookEnv.has_modules
])

return (
<Gradebook
{...props}
flashAlerts={[...modulesErrors, ...filtersErrors]}
filters={filters}
flashAlerts={flashMessages}
filters={appliedFilters}
isFiltersLoading={isFiltersLoading}
isModulesLoading={isModulesLoading}
modules={modules}
// when the rest of DataLoader is moved we can remove these
performanceControls={performanceControls.current}
dispatch={dispatch.current}
onFiltersChange={handleFiltersChange}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,24 +277,26 @@ describe('findAllAppliedFilterValuesOfType', () => {
const filters: Filter[] = [
{
id: '1',
isApplied: true,
name: 'Filter 1',
is_applied: true,
conditions: [
{id: '1', type: 'module', value: '1', createdAt: ''},
{id: '2', type: 'assignment-group', value: '2', createdAt: ''},
{id: '3', type: 'assignment-group', value: '7', createdAt: ''},
{id: '4', type: 'module', value: '3', createdAt: ''}
{id: '1', type: 'module', value: '1', created_at: ''},
{id: '2', type: 'assignment-group', value: '2', created_at: ''},
{id: '3', type: 'assignment-group', value: '7', created_at: ''},
{id: '4', type: 'module', value: '3', created_at: ''}
],
createdAt: ''
created_at: '2019-01-01T00:00:00Z'
},
{
id: '2',
isApplied: false,
name: 'Filter 2',
is_applied: false,
conditions: [
{id: '1', type: 'module', value: '4', createdAt: ''},
{id: '2', type: 'assignment-group', value: '5', createdAt: ''},
{id: '3', type: 'module', value: '6', createdAt: ''}
{id: '1', type: 'module', value: '4', created_at: ''},
{id: '2', type: 'assignment-group', value: '5', created_at: ''},
{id: '3', type: 'module', value: '6', created_at: ''}
],
createdAt: ''
created_at: '2019-01-01T00:00:01Z'
}
]

Expand All @@ -311,24 +313,26 @@ describe('getAllAppliedFilterValues', () => {
const filters: Filter[] = [
{
id: '1',
isApplied: true,
name: 'Filter 1',
is_applied: true,
conditions: [
{id: '1', type: 'module', value: '1', createdAt: ''},
{id: '2', type: 'assignment-group', value: '2', createdAt: ''},
{id: '3', type: 'assignment-group', value: '7', createdAt: ''},
{id: '4', type: 'module', value: '3', createdAt: ''}
{id: '1', type: 'module', value: '1', created_at: ''},
{id: '2', type: 'assignment-group', value: '2', created_at: ''},
{id: '3', type: 'assignment-group', value: '7', created_at: ''},
{id: '4', type: 'module', value: '3', created_at: ''}
],
createdAt: ''
created_at: '2019-01-01T00:00:00Z'
},
{
id: '2',
isApplied: false,
name: 'Filter 2',
is_applied: false,
conditions: [
{id: '1', type: 'module', value: '4', createdAt: ''},
{id: '2', type: 'assignment-group', value: '5', createdAt: ''},
{id: '3', type: 'module', value: '6', createdAt: ''}
{id: '1', type: 'module', value: '4', created_at: ''},
{id: '2', type: 'assignment-group', value: '5', created_at: ''},
{id: '3', type: 'module', value: '6', created_at: ''}
],
createdAt: ''
created_at: '2019-01-01T00:00:01Z'
}
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ describe('GradebookData', () => {
it('renders', () => {
const wrapper = shallow(<GradebookData {...defaultProps} />)
expect(wrapper.find(Gradebook).exists()).toBeTruthy()
expect(wrapper.prop('isModulesLoading')).toStrictEqual(true)
expect(wrapper.prop('isFiltersLoading')).toStrictEqual(false)
expect(wrapper.prop('isModulesLoading')).toStrictEqual(false)
expect(wrapper.prop('filters')).toStrictEqual([])
expect(wrapper.prop('modules')).toStrictEqual([])
expect(wrapper.prop('dispatch')).toBeInstanceOf(RequestDispatch)
expect(wrapper.prop('performanceControls')).toBeInstanceOf(PerformanceControls)
Expand Down
Loading

0 comments on commit dd42090

Please sign in to comment.