@instructure/platform-modules 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/adapters/canvasGraphql/index.d.ts +7 -33
- package/dist/adapters/canvasGraphql/index.d.ts.map +1 -1
- package/dist/adapters/canvasGraphql/normalize.d.ts +45 -187
- package/dist/adapters/canvasGraphql/normalize.d.ts.map +1 -1
- package/dist/adapters/canvasGraphql/queries.d.ts +8 -36
- package/dist/adapters/canvasGraphql/queries.d.ts.map +1 -1
- package/dist/adapters/inMemory.d.ts +4 -28
- package/dist/adapters/inMemory.d.ts.map +1 -1
- package/dist/adapters/limits.d.ts +17 -74
- package/dist/adapters/limits.d.ts.map +1 -1
- package/dist/adapters/types.d.ts +10 -40
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/cache/invalidate.d.ts +0 -23
- package/dist/cache/invalidate.d.ts.map +1 -1
- package/dist/cache/keys.d.ts +3 -44
- package/dist/cache/keys.d.ts.map +1 -1
- package/dist/components/FilterChips.d.ts +5 -9
- package/dist/components/FilterChips.d.ts.map +1 -1
- package/dist/components/ModuleCard.d.ts +10 -25
- package/dist/components/ModuleCard.d.ts.map +1 -1
- package/dist/components/ModuleCard.stories.d.ts +15 -0
- package/dist/components/ModuleCard.stories.d.ts.map +1 -1
- package/dist/components/ModuleItemRow.d.ts +1 -1
- package/dist/components/ModuleItemRow.d.ts.map +1 -1
- package/dist/components/ModuleNumberBadge.d.ts.map +1 -1
- package/dist/components/ModulesList.d.ts +2 -23
- package/dist/components/ModulesList.d.ts.map +1 -1
- package/dist/components/ModulesList.stories.d.ts +28 -87
- package/dist/components/ModulesList.stories.d.ts.map +1 -1
- package/dist/components/StatusPill.d.ts +0 -3
- package/dist/components/StatusPill.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/labels.d.ts +28 -45
- package/dist/components/labels.d.ts.map +1 -1
- package/dist/config/context.d.ts +0 -1
- package/dist/config/context.d.ts.map +1 -1
- package/dist/config/registry.d.ts +5 -13
- package/dist/config/registry.d.ts.map +1 -1
- package/dist/config/types.d.ts +0 -3
- package/dist/config/types.d.ts.map +1 -1
- package/dist/fixtures/generate.d.ts +6 -17
- package/dist/fixtures/generate.d.ts.map +1 -1
- package/dist/fixtures/index.d.ts +1 -1
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/scenarios.d.ts +7 -32
- package/dist/fixtures/scenarios.d.ts.map +1 -1
- package/dist/hooks/constants.d.ts +5 -22
- package/dist/hooks/constants.d.ts.map +1 -1
- package/dist/hooks/queryOptions.d.ts +3 -24
- package/dist/hooks/queryOptions.d.ts.map +1 -1
- package/dist/hooks/useItemsMode.d.ts +3 -52
- package/dist/hooks/useItemsMode.d.ts.map +1 -1
- package/dist/hooks/useModuleItemQueries.d.ts +1 -39
- package/dist/hooks/useModuleItemQueries.d.ts.map +1 -1
- package/dist/hooks/useModuleItems.d.ts +1 -1
- package/dist/hooks/useModules.d.ts +1 -1
- package/dist/hooks/useModulesPageData.d.ts +3 -29
- package/dist/hooks/useModulesPageData.d.ts.map +1 -1
- package/dist/i18n.d.ts +14 -21
- package/dist/i18n.d.ts.map +1 -1
- package/dist/index.d.ts +3 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1208 -1071
- package/dist/types/domain.d.ts +25 -80
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/types/enums.d.ts +10 -41
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/extensions.d.ts +0 -21
- package/dist/types/extensions.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/navigation.d.ts +1 -1
- package/dist/types/schemas.d.ts +5 -26
- package/dist/types/schemas.d.ts.map +1 -1
- package/locales/en.json +7 -8
- package/package.json +22 -19
package/README.md
CHANGED
|
@@ -58,15 +58,15 @@ every key has a default. A host supplies as much or as little as it wants:
|
|
|
58
58
|
const translations = useMemo(() => ({ pageTitle: 'Learning Path' }), [])
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
The
|
|
61
|
+
The catalog is separately importable when you want to extract from it or build a
|
|
62
62
|
full one — `import en from '@instructure/platform-modules/locales/en.json'`.
|
|
63
63
|
|
|
64
|
-
Resolution walks the host
|
|
64
|
+
Resolution walks the host catalog, then `en.json`, then the key itself, taking the
|
|
65
65
|
first entry that is actually a string. Three consequences worth knowing:
|
|
66
66
|
|
|
67
67
|
- **Rebranding and translating are the same edit.** `{ pageTitle: 'Learning Path' }` is the whole
|
|
68
68
|
of the page-title ask, which is why there is no separate `strings` config key.
|
|
69
|
-
- **A key the
|
|
69
|
+
- **A key the catalog does not carry renders as its own text**, so a host-registered label passes
|
|
70
70
|
through rather than showing a bare key.
|
|
71
71
|
- **Only a string overrides.** `''` blanks a string deliberately; a `null` or a number from an
|
|
72
72
|
untyped host falls back to English rather than rendering `null` or throwing.
|
|
@@ -100,8 +100,8 @@ const translations = useMemo(
|
|
|
100
100
|
|
|
101
101
|
**Always supply `other`, whatever your language.** It is the floor the package falls back to,
|
|
102
102
|
and in most locales it is also a real category — Russian uses it for any count with a decimal
|
|
103
|
-
part, so a
|
|
104
|
-
form needs `other` alone, or just a plain string. Nothing validates a host
|
|
103
|
+
part, so a catalog of only `one`/`few`/`many` renders English at `2.5`. A language with one
|
|
104
|
+
form needs `other` alone, or just a plain string. Nothing validates a host catalog, so this
|
|
105
105
|
is a contract rather than a checked rule.
|
|
106
106
|
|
|
107
107
|
Selection reads the `count` option, which is why it arrives as a number rather than a
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { ModulesDataAdapter } from '../types';
|
|
2
2
|
import { UnsupportedModuleItem } from './normalize';
|
|
3
|
-
/**
|
|
4
|
-
* The Canvas GraphQL implementation of `ModulesDataAdapter`.
|
|
5
|
-
*
|
|
6
|
-
* `executeQuery` is injected rather than pulled from `usePlatformUi()`. Two
|
|
7
|
-
* reasons, both load-bearing: the adapter stays a plain function that a test
|
|
8
|
-
* can drive with `vi.fn()` and no React, and it stays host-agnostic — Canvas
|
|
9
|
-
* Career supplies a different `executeQuery` (different endpoint, different
|
|
10
|
-
* auth) for the identical documents.
|
|
11
|
-
*/
|
|
12
3
|
export type ExecuteQuery = <TResult, TVariables>(query: unknown, variables: TVariables) => Promise<TResult>;
|
|
13
4
|
export { INLINE_ITEM_LIMIT, INLINE_QUERY_NODE_BUDGET } from '../limits';
|
|
14
5
|
export interface CanvasGraphqlAdapterOptions {
|
|
@@ -17,35 +8,18 @@ export interface CanvasGraphqlAdapterOptions {
|
|
|
17
8
|
inlineItemLimit?: number;
|
|
18
9
|
/** @see INLINE_QUERY_NODE_BUDGET */
|
|
19
10
|
nodeBudget?: number;
|
|
20
|
-
/**
|
|
21
|
-
* Notified when an item is dropped for having a content type outside the
|
|
22
|
-
* closed domain enum. Unset by default: dropping is the deliberate policy,
|
|
23
|
-
* this is the seam for making it visible.
|
|
24
|
-
*/
|
|
11
|
+
/** Unset by default: dropping is the policy, this is the seam for seeing it. */
|
|
25
12
|
onUnsupportedItem?: (item: UnsupportedModuleItem) => void;
|
|
26
13
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* then not requested at all, so `ModuleItem.status` comes back undefined —
|
|
32
|
-
* already the contract's "not fetched" — rather than filled with somebody
|
|
33
|
-
* else's data.
|
|
14
|
+
* Omit for any viewer without a single subject. Omitting is safe by construction: the
|
|
15
|
+
* submission field set is then not requested, so no status can be filled with somebody
|
|
16
|
+
* else's data. `progression` is still fetched, so a student viewer without an id gets a
|
|
17
|
+
* status derived from requirements alone — which is why `complete` is withheld there.
|
|
34
18
|
*
|
|
35
|
-
*
|
|
36
|
-
* viewer's own id. Canvas's `submissionsConnection` returns *every* visible
|
|
37
|
-
* student's submissions to a viewer holding `manage_grades` or
|
|
38
|
-
* `view_all_grades`, ordered by `user_id`. @see the `SUBMISSION_FIELDS`
|
|
39
|
-
* docstring in `./queries`.
|
|
19
|
+
* When supplied it must be the viewer's own id. @see `SUBMISSION_FIELDS` in `./queries`
|
|
40
20
|
*/
|
|
41
21
|
studentId?: string | null;
|
|
42
|
-
/**
|
|
43
|
-
* Validate normalised output against the Zod schemas before returning.
|
|
44
|
-
*
|
|
45
|
-
* On by default. This is the check that keeps the GraphQL and REST adapters
|
|
46
|
-
* from drifting, and it is the only place the contract is enforced at
|
|
47
|
-
* runtime — turn it off only if a profile says it costs something real.
|
|
48
|
-
*/
|
|
22
|
+
/** On by default: the only place the contract is enforced at runtime. */
|
|
49
23
|
validate?: boolean;
|
|
50
24
|
}
|
|
51
25
|
export declare function createCanvasGraphqlAdapter({ executeQuery, inlineItemLimit, nodeBudget, onUnsupportedItem, studentId, validate, }: CanvasGraphqlAdapterOptions): ModulesDataAdapter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,UAAU,CAAA;AACxF,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,UAAU,CAAA;AACxF,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAA;AAWpB,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,UAAU,EAC7C,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,UAAU,KAClB,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AAEvE,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,YAAY,CAAA;IAC1B,6BAA6B;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACzD;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAyBD,wBAAgB,0BAA0B,CAAC,EACzC,YAAY,EACZ,eAAmC,EACnC,UAAqC,EACrC,iBAAiB,EACjB,SAAS,EACT,QAAe,GAChB,EAAE,2BAA2B,GAAG,kBAAkB,CAuElD;AAED,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,0BAA0B,EAC1B,KAAK,sBAAsB,EAC3B,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAA"}
|
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import { CompletionRequirement, CompletionRequirementType, Module, ModuleItem, ModuleItemsPage, ModuleItemStatus, ModuleItemType, ModuleProgress, ModulesPage, ModuleStatus, PageInfo } from '../../types/domain';
|
|
2
|
-
/**
|
|
3
|
-
* GraphQL node → domain model.
|
|
4
|
-
*
|
|
5
|
-
* Everything transport-shaped stops here. The three normalisation rules from
|
|
6
|
-
* `types/domain.ts` are enforced in this file and nowhere else:
|
|
7
|
-
*
|
|
8
|
-
* 1. **Ids are strings.** Canvas hands back `ID`, which the JSON transport
|
|
9
|
-
* may render as a number; every id goes through `String()`.
|
|
10
|
-
* 2. **`published` is always populated.** On this transport Canvas's resolver
|
|
11
|
-
* cannot return null for any viewer, so a null is a partial response and
|
|
12
|
-
* throws — see `resolvePublished`, which explains why the REST rule about
|
|
13
|
-
* absence meaning "published" does not apply here.
|
|
14
|
-
* 3. **One closed item enum.** `__typename` is the discriminator, with
|
|
15
|
-
* `Assignment { isNewQuiz: true }` collapsing to `'quiz'`.
|
|
16
|
-
*/
|
|
17
2
|
export interface GraphqlPageInfo {
|
|
18
3
|
hasNextPage: boolean;
|
|
19
4
|
endCursor: string | null;
|
|
@@ -24,7 +9,7 @@ export interface GraphqlCompletionRequirement {
|
|
|
24
9
|
minScore?: number | null;
|
|
25
10
|
minPercentage?: number | null;
|
|
26
11
|
}
|
|
27
|
-
/**
|
|
12
|
+
/** Present only for a student viewer. */
|
|
28
13
|
export interface GraphqlSubmission {
|
|
29
14
|
_id?: string | number | null;
|
|
30
15
|
/** `SubmissionState`: unsubmitted | submitted | pending_review | graded | ungraded | deleted. */
|
|
@@ -34,7 +19,7 @@ export interface GraphqlSubmission {
|
|
|
34
19
|
submittedAt?: string | null;
|
|
35
20
|
attempt?: number | null;
|
|
36
21
|
missing?: boolean | null;
|
|
37
|
-
/** Per-student, override-applied due date,
|
|
22
|
+
/** Per-student, override-applied due date, denormalized onto the submission. */
|
|
38
23
|
cachedDueDate?: string | null;
|
|
39
24
|
}
|
|
40
25
|
export interface GraphqlSubmissionConnection {
|
|
@@ -48,7 +33,6 @@ export interface GraphqlModuleItemContent {
|
|
|
48
33
|
pointsPossible?: number | null;
|
|
49
34
|
submissionsConnection?: GraphqlSubmissionConnection | null;
|
|
50
35
|
}
|
|
51
|
-
/** A `Requirement` as it appears in `ModuleProgression.requirementsMet`. */
|
|
52
36
|
export interface GraphqlRequirement {
|
|
53
37
|
id: string | number;
|
|
54
38
|
type: string;
|
|
@@ -63,7 +47,6 @@ export interface GraphqlModuleProgression {
|
|
|
63
47
|
started?: boolean | null;
|
|
64
48
|
requirementsMet?: (GraphqlRequirement | null)[] | null;
|
|
65
49
|
}
|
|
66
|
-
/** `Module.submissionStatistics`, i.e. Canvas's `ModuleStatistics`. */
|
|
67
50
|
export interface GraphqlModuleStatistics {
|
|
68
51
|
latestDueAt?: string | null;
|
|
69
52
|
missingAssignmentCount?: number | null;
|
|
@@ -100,6 +83,7 @@ export interface GraphqlModuleNode {
|
|
|
100
83
|
requireSequentialProgress?: boolean | null;
|
|
101
84
|
prerequisites?: ({
|
|
102
85
|
id: string | number;
|
|
86
|
+
name?: string | null;
|
|
103
87
|
} | null)[] | null;
|
|
104
88
|
completionRequirements?: (GraphqlCompletionRequirement | null)[] | null;
|
|
105
89
|
/** Absent for a teacher/anonymous viewer, and `null` when Canvas has none. */
|
|
@@ -123,221 +107,95 @@ export interface GetModuleItemsResult {
|
|
|
123
107
|
moduleItemsConnection?: GraphqlModuleItemConnection | null;
|
|
124
108
|
} | null;
|
|
125
109
|
}
|
|
126
|
-
/** What a `PartialCanvasResponseError` is about. */
|
|
127
110
|
export type PartialResponseSubject = 'module' | 'moduleItem';
|
|
128
111
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* - `moduleItemsTotalCount` is `Int!`. Coercing a missing value to `0` makes
|
|
135
|
-
* `resolveItemsMode` see an empty course and answer `inline`, so the page
|
|
136
|
-
* asks Canvas to inline every item it has — the most expensive transport
|
|
137
|
-
* setting, chosen when the response is least trustworthy.
|
|
138
|
-
* - `published` is null when the viewer cannot manage the object, which for
|
|
139
|
-
* such a viewer means "published". For a viewer who *can* manage, Canvas
|
|
140
|
-
* always sends a boolean, so a null contradicts what the host told us and
|
|
141
|
-
* the wrong guess renders an unpublished thing as published — to the one
|
|
142
|
-
* audience that acts on that badge.
|
|
143
|
-
*
|
|
144
|
-
* Failing is recoverable in a way silence is not: the hook surfaces the error,
|
|
145
|
-
* the host can retry, and `useItemsMode` already falls through to `onDemand` on
|
|
146
|
-
* a failed probe rather than deadlocking the page.
|
|
112
|
+
* Thrown rather than defaulted, because the sensible-looking default is the dangerous one:
|
|
113
|
+
* a missing `moduleItemsTotalCount` coerced to `0` makes `resolveItemsMode` answer `inline`
|
|
114
|
+
* exactly when the response is least trustworthy, and a null `published` guessed `true`
|
|
115
|
+
* renders unpublished content as published to the audience that acts on that badge.
|
|
147
116
|
*/
|
|
148
117
|
export declare class PartialCanvasResponseError extends Error {
|
|
149
118
|
readonly subject: PartialResponseSubject;
|
|
150
|
-
/** The module or item id, so a host can log which row came back incomplete. */
|
|
151
119
|
readonly id: string;
|
|
152
|
-
/** The field that was missing. */
|
|
153
120
|
readonly field: string;
|
|
154
121
|
constructor(subject: PartialResponseSubject, id: string, field: string);
|
|
155
122
|
}
|
|
156
|
-
/** Reported when a `__typename` has no place in the closed domain enum. */
|
|
157
123
|
export interface UnsupportedModuleItem {
|
|
158
|
-
/** The module item id, so a host can look the row up in Canvas. */
|
|
159
124
|
id: string;
|
|
160
125
|
/** The `content.__typename` we could not map, or `null` when content was absent. */
|
|
161
126
|
typename: string | null;
|
|
162
127
|
}
|
|
163
128
|
export interface NormalizeOptions {
|
|
164
129
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* was deleted and came back `null`).
|
|
168
|
-
*
|
|
169
|
-
* The alternatives were worse: widening the enum makes the shared contract
|
|
170
|
-
* chase Canvas's release cadence, and throwing takes the whole page down for
|
|
171
|
-
* one unknown row.
|
|
172
|
-
*
|
|
173
|
-
* Dropping is silent data loss, so it must not *also* be unobservable. This
|
|
174
|
-
* defaults to a `console.warn` rather than to nothing — matching
|
|
175
|
-
* `platform-datetime` and ~30 other files in the repo — because "a host can log
|
|
176
|
-
* it" is only true if the host knows to opt in. Pass a no-op to silence it.
|
|
130
|
+
* Dropping an unmappable item is silent data loss, so this defaults to `console.warn`
|
|
131
|
+
* rather than to nothing. Pass a no-op to silence it.
|
|
177
132
|
*/
|
|
178
133
|
onUnsupportedItem?: (item: UnsupportedModuleItem) => void;
|
|
134
|
+
/**
|
|
135
|
+
* Whether the query selected the submission field set. Absent means it did.
|
|
136
|
+
*
|
|
137
|
+
* With no `studentId` the adapter omits submissions but still fetches
|
|
138
|
+
* `progression`, so a met requirement is the only per-item signal left. Without
|
|
139
|
+
* this, `complete` would be claimed on work whose submission was never read.
|
|
140
|
+
*/
|
|
141
|
+
submissionsIncluded?: boolean;
|
|
179
142
|
}
|
|
180
|
-
/**
|
|
181
|
-
* `__typename` (+ `isNewQuiz`) → domain type, or `null` when unmappable.
|
|
182
|
-
*
|
|
183
|
-
* A New Quiz is an `Assignment` with an LTI submission type in Canvas's data
|
|
184
|
-
* model; every UI that shows it calls it a quiz, so rule 3 collapses it here
|
|
185
|
-
* rather than leaving each caller to re-derive it.
|
|
186
|
-
*/
|
|
187
143
|
export declare function normalizeItemType(content: GraphqlModuleItemContent | null | undefined): ModuleItemType | null;
|
|
188
|
-
/**
|
|
189
|
-
* ISO 8601 duration → whole minutes.
|
|
190
|
-
*
|
|
191
|
-
* Canvas exposes `estimatedDuration` as an `ISO8601Duration` scalar (`PT45M`)
|
|
192
|
-
* while the domain model carries minutes, because that is the only unit either
|
|
193
|
-
* host's UI renders. Year and month designators are rejected rather than
|
|
194
|
-
* guessed at — they are not calendar-independent, and Canvas never emits them
|
|
195
|
-
* for this field.
|
|
196
|
-
*/
|
|
197
144
|
export declare function parseDurationMinutes(value: string | null | undefined): number | null;
|
|
198
|
-
/**
|
|
199
|
-
* Wire string → closed enum, with anything unrecognised becoming `'unknown'`.
|
|
200
|
-
*
|
|
201
|
-
* Unlike an unmappable item `__typename`, an unknown requirement type is not
|
|
202
|
-
* dropped: the requirement still exists, the student still has to satisfy it,
|
|
203
|
-
* and hiding the row would be a worse lie than labelling it vaguely.
|
|
204
|
-
*/
|
|
205
145
|
export declare function normalizeCompletionRequirementType(type: string): CompletionRequirementType;
|
|
206
146
|
export declare function normalizePageInfo(pageInfo: GraphqlPageInfo | null | undefined): PageInfo;
|
|
207
|
-
/**
|
|
208
|
-
* The module-level student state every item in that module is read against.
|
|
209
|
-
*
|
|
210
|
-
* Built once per module and threaded into the item normaliser so the inline and
|
|
211
|
-
* on-demand paths derive the identical status for the identical item — which is
|
|
212
|
-
* why `GET_MODULE_ITEMS` re-reads `progression` alongside its items.
|
|
213
|
-
*/
|
|
214
147
|
export interface ModuleStudentContext {
|
|
215
|
-
/** Canvas reports the module locked for this student. */
|
|
216
148
|
locked: boolean;
|
|
217
149
|
/** Raw `ModuleProgression.workflowState`: locked | unlocked | started | completed. */
|
|
218
150
|
workflowState: string | null;
|
|
219
151
|
completed: boolean;
|
|
220
152
|
started: boolean;
|
|
221
|
-
/**
|
|
222
|
-
* Module *item* ids whose completion requirement the student has satisfied.
|
|
223
|
-
*
|
|
224
|
-
* `ModuleProgression.requirementsMet` is a list of `Requirement`, and
|
|
225
|
-
* `Requirement.id` is the module item id — the same join `completionRequirements`
|
|
226
|
-
* uses. The domain model wants a count, so the list collapses here.
|
|
227
|
-
*/
|
|
153
|
+
/** `Requirement.id` is the module *item* id — the same join `completionRequirements` uses. */
|
|
228
154
|
requirementsMetItemIds: ReadonlySet<string>;
|
|
229
155
|
}
|
|
230
|
-
/**
|
|
231
|
-
* The student's submission for an item, or `null`.
|
|
232
|
-
*
|
|
233
|
-
* Canvas returns a connection because the field is shared with the teacher view
|
|
234
|
-
* (where it is every student's submission); for a student it holds at most one
|
|
235
|
-
* row, so the first non-null node is the answer.
|
|
236
|
-
*/
|
|
237
156
|
export declare function firstSubmission(content: GraphqlModuleItemContent | null | undefined): GraphqlSubmission | null;
|
|
238
157
|
/**
|
|
239
|
-
* `
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
* defines, matching Canvas's own `filterRequirementsMet`: a progression can
|
|
244
|
-
* outlive the requirement that produced it, and counting a stale entry would
|
|
245
|
-
* show a student 4 of 3. Canvas matches on id, type, `minScore` *and*
|
|
246
|
-
* `minPercentage`; we match on id and type only, because the score bounds are
|
|
247
|
-
* the part a teacher edits in place and an edited bound should not silently
|
|
248
|
-
* un-meet a requirement the student genuinely satisfied.
|
|
158
|
+
* `requirementsMet` is filtered against the requirements the module currently defines, as
|
|
159
|
+
* Canvas's own `filterRequirementsMet` does — a progression outliving its requirement would
|
|
160
|
+
* show a student 4 of 3. We match on id and type only, not Canvas's score bounds too: those
|
|
161
|
+
* are what a teacher edits in place, and an edit should not silently un-meet a requirement.
|
|
249
162
|
*/
|
|
250
163
|
export declare function buildStudentContext(progression: GraphqlModuleProgression | null | undefined, completionRequirements: readonly CompletionRequirement[]): ModuleStudentContext | undefined;
|
|
251
164
|
/**
|
|
252
|
-
*
|
|
165
|
+
* `excused` precedes the other submission rules as it does in Canvas — `Submission#missing?`
|
|
166
|
+
* and `#late?` both return false for an excused submission.
|
|
253
167
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* and a locked module is locked for every item in it.
|
|
258
|
-
* 2. `missing` — `submission.missing`. Ahead of `graded` on purpose: this
|
|
259
|
-
* is the precedence Canvas's own student pill uses, and a
|
|
260
|
-
* teacher entering a zero on a missing submission should
|
|
261
|
-
* not make the "Missing" chip stop finding it.
|
|
262
|
-
* 3. `graded` — `state === 'graded'`, or `gradingStatus` is `graded`/
|
|
263
|
-
* `excused`. Excused counts as graded because there is
|
|
264
|
-
* nothing left for the student to do.
|
|
265
|
-
* 4. `submitted` — handed in and awaiting a grade: `submittedAt` is set, or
|
|
266
|
-
* the state is `submitted`/`pending_review`.
|
|
267
|
-
* 5. `in_progress` — the student has engaged but produced nothing gradable:
|
|
268
|
-
* either an attempt is open (`attempt > 0` with no
|
|
269
|
-
* `submittedAt`), or the item's completion requirement is
|
|
270
|
-
* already met with no submission at all — a viewed page, a
|
|
271
|
-
* followed link, a contribution to an ungraded discussion.
|
|
272
|
-
* 6. `not_started` — everything else.
|
|
168
|
+
* `missing` ahead of `complete` is a **deliberate divergence**: Canvas's
|
|
169
|
+
* `ModuleItemStatusIcon` falls through to Complete on a met requirement, so a `must_view` met
|
|
170
|
+
* on a past-due assignment hides the missing work. That pill is flag-gated `context_modules_v2`.
|
|
273
171
|
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* comment already flags the same gap at module level ("All items complete" is
|
|
277
|
-
* not in the designer's list). If the designer adds one, this is the rule to
|
|
278
|
-
* change and the only one.
|
|
172
|
+
* `complete` does not test gradability: Canvas stores `requirements_met` per ContentTag with
|
|
173
|
+
* no reference to content type.
|
|
279
174
|
*/
|
|
280
|
-
export declare function deriveModuleItemStatus(content: GraphqlModuleItemContent | null | undefined, itemId: string, student: ModuleStudentContext): ModuleItemStatus;
|
|
175
|
+
export declare function deriveModuleItemStatus(content: GraphqlModuleItemContent | null | undefined, itemId: string, student: ModuleStudentContext, submissionsIncluded?: boolean): ModuleItemStatus;
|
|
281
176
|
/**
|
|
282
|
-
* Per-module rollup.
|
|
177
|
+
* Per-module rollup.
|
|
283
178
|
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* the module is not completed. That is the design's
|
|
287
|
-
* "1+ item past due", read from the module-level
|
|
288
|
-
* statistic rather than from items, so the filter row
|
|
289
|
-
* works with nothing expanded.
|
|
290
|
-
* 3. `all_graded` — completed, *and* items were fetched, *and* every
|
|
291
|
-
* gradable one is `graded`.
|
|
292
|
-
* 4. `all_submitted` — completed. The honest floor: `progression.completed`
|
|
293
|
-
* means every requirement is met, which is submission,
|
|
294
|
-
* not grading. See the caveat below.
|
|
295
|
-
* 5. `in_progress` — started, or at least one requirement met.
|
|
296
|
-
* 6. `not_started` — everything else.
|
|
179
|
+
* `missing` is read from `submissionStatistics`, not from items, so the filter row
|
|
180
|
+
* works with nothing expanded.
|
|
297
181
|
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* with the module in the same response. Upgrading on the evidence we have beats
|
|
302
|
-
* claiming a grade we cannot see.
|
|
182
|
+
* Excused items leave the `all_graded` population rather than counting as graded —
|
|
183
|
+
* an exempted item was never assessed, the same reading the item pill takes. A
|
|
184
|
+
* module whose every gradable item is excused therefore reports `all_submitted`.
|
|
303
185
|
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
* never revisits the module. No upgrade to `all_graded` ever happens.
|
|
310
|
-
*
|
|
311
|
-
* Closing it means either recomputing the module's status in the UI from the items
|
|
312
|
-
* it holds — which reintroduces the inline/onDemand asymmetry the module-level
|
|
313
|
-
* design exists to avoid — or Canvas exposing the signal. Left open deliberately
|
|
314
|
-
* rather than papered over.
|
|
186
|
+
* `all_graded` is reachable **only in inline mode**: Canvas has no module-level
|
|
187
|
+
* "everything is graded" signal, so it takes the items riding along in the same
|
|
188
|
+
* response. In `onDemand` mode a completed module reports `all_submitted` and stays
|
|
189
|
+
* there even once its items arrive all graded, because `progress` is derived once
|
|
190
|
+
* from the list response. Known limitation, left open rather than papered over.
|
|
315
191
|
*/
|
|
316
192
|
export declare function deriveModuleStatus(student: ModuleStudentContext, missingCount: number, requirementsMet: number, items: ModuleItem[] | undefined): ModuleStatus;
|
|
317
|
-
/**
|
|
318
|
-
* The module's `ModuleProgress`, or `undefined` when there is no student.
|
|
319
|
-
*
|
|
320
|
-
* `requirementCount` is the module's own when Canvas set one and the number of
|
|
321
|
-
* completion requirements otherwise — Canvas uses `requirement_count` for
|
|
322
|
-
* "complete any N of these", so it is the denominator whenever it is present,
|
|
323
|
-
* and the full list is the denominator when it is not. `requirementsMet` is
|
|
324
|
-
* clamped to it so the pair can never read "3 of 1", and `percentComplete` is
|
|
325
|
-
* derived from that same clamped pair rather than computed independently, so a
|
|
326
|
-
* host can never render a bar that disagrees with the fraction beside it.
|
|
327
|
-
*/
|
|
328
193
|
export declare function deriveModuleProgress(node: GraphqlModuleNode, completionRequirements: readonly CompletionRequirement[], student: ModuleStudentContext | undefined, items: ModuleItem[] | undefined): ModuleProgress | undefined;
|
|
329
194
|
/**
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
* `content.dueAt`. That ordering is what closes the gap a live-Canvas probe
|
|
335
|
-
* found: `dueAt` exists on `Assignment` only, `Quiz` has none, and a graded
|
|
336
|
-
* `Discussion` keeps its date on the backing assignment, but all three carry a
|
|
337
|
-
* submission. The fallback still matters, because a teacher viewer gets an
|
|
338
|
-
* assignment's `dueAt` and no submission at all.
|
|
339
|
-
*
|
|
340
|
-
* `status` is present only when `student` is — see the student-state block.
|
|
195
|
+
* `dueAt` prefers `submission.cachedDueDate`, the per-student override-applied date: `dueAt`
|
|
196
|
+
* exists on `Assignment` only, a `Quiz` has none, and a graded `Discussion` keeps its date on
|
|
197
|
+
* the backing assignment — but all three carry a submission. The `content.dueAt` fallback is
|
|
198
|
+
* for a teacher viewer, who gets the assignment's date and no submission.
|
|
341
199
|
*/
|
|
342
200
|
export declare function normalizeModuleItem(node: GraphqlModuleItemNode, moduleId: string, completionRequirementsByItemId: ReadonlyMap<string, CompletionRequirement>, options?: NormalizeOptions, student?: ModuleStudentContext): ModuleItem | null;
|
|
343
201
|
export declare function normalizeModule(node: GraphqlModuleNode, options?: NormalizeOptions): Module;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,yBAAyB,EACzB,MAAM,EACN,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EACZ,QAAQ,EACT,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,45 +1,17 @@
|
|
|
1
1
|
import { DocumentNode } from 'graphql';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* `includeItems` is an `@include` directive rather than a second document, so there is one
|
|
4
|
+
* operation name and one persisted-query hash.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
* 1. One document means one operation name, which means one persisted-query
|
|
9
|
-
* hash to register with Canvas. Two documents would double that
|
|
10
|
-
* registration surface for what is a single conceptual read.
|
|
11
|
-
* 2. The directive is evaluated server-side, so `includeItems: false` skips
|
|
12
|
-
* resolving every per-item field — the title, content type, points and
|
|
13
|
-
* submissions for each row.
|
|
14
|
-
*
|
|
15
|
-
* What it does *not* skip is the
|
|
16
|
-
* `ModuleItemsVisibleLoader`. `moduleItemsTotalCount` and
|
|
17
|
-
* `completionRequirements` are selected unconditionally and
|
|
18
|
-
* `module_type.rb:137-141,166-169` resolves both through that same loader,
|
|
19
|
-
* so it runs whatever `includeItems` is. The saving is per-item field
|
|
20
|
-
* resolution, not the visibility query.
|
|
21
|
-
* 3. The response type is a single shape with an optional key, which is
|
|
22
|
-
* exactly how the domain model already treats it (`items?: ModuleItem[]`,
|
|
23
|
-
* where `undefined` means "not fetched"). A second document would force
|
|
24
|
-
* the normaliser to branch on which document produced the payload.
|
|
25
|
-
*
|
|
26
|
-
* `itemsFirst` bounds the inline item fetch. When a module has more items than
|
|
27
|
-
* that, the normaliser drops the partial list rather than reporting a
|
|
28
|
-
* truncated one — see `normalize.ts`.
|
|
6
|
+
* It skips per-item field resolution but **not** the `ModuleItemsVisibleLoader`:
|
|
7
|
+
* `moduleItemsTotalCount` and `completionRequirements` are selected unconditionally and
|
|
8
|
+
* `module_type.rb:137-141,166-169` resolves both through it regardless.
|
|
29
9
|
*/
|
|
30
10
|
export declare const GET_MODULES: DocumentNode;
|
|
31
11
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* requirement is stored on the module, keyed by item id — without it, items
|
|
36
|
-
* fetched on demand would lose the requirement that items fetched inline keep,
|
|
37
|
-
* and the two paths would produce different domain objects for the same item.
|
|
38
|
-
*
|
|
39
|
-
* `progression` is re-read for the same reason: `ModuleItem.status` depends on
|
|
40
|
-
* module-level state (is the module locked for me, which of its requirements
|
|
41
|
-
* have I met), so an item fetched on demand would otherwise carry a different
|
|
42
|
-
* status from the identical item fetched inline.
|
|
12
|
+
* `completionRequirements` and `progression` are re-read at module level because an item's
|
|
13
|
+
* requirement and status both depend on them — without that, an item fetched on demand would
|
|
14
|
+
* differ from the identical item fetched inline.
|
|
43
15
|
*/
|
|
44
16
|
export declare const GET_MODULE_ITEMS: DocumentNode;
|
|
45
17
|
export interface GetModulesVariables {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAgH3C;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,YA2DzB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAiC9B,CAAA;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,kBAAkB,EAAE,OAAO,CAAA;IAC3B,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB"}
|
|
@@ -1,36 +1,12 @@
|
|
|
1
1
|
import { Module, ModuleItem } from '../types/domain';
|
|
2
2
|
import { ModulesDataAdapter } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* An adapter backed by a plain object instead of a network.
|
|
5
|
-
*
|
|
6
|
-
* It exists so the hooks can be built and tested without waiting on a real
|
|
7
|
-
* adapter, but it is not scaffolding — it stays as the test double for hook
|
|
8
|
-
* tests and as the fastest way to render a modules UI in Storybook with no
|
|
9
|
-
* server at all.
|
|
10
|
-
*
|
|
11
|
-
* It is a real implementation of the contract, deliberately: it paginates with
|
|
12
|
-
* opaque cursors, honours `AbortSignal`, and declines to inline items for modules
|
|
13
|
-
* over `INLINE_ITEM_LIMIT` — so hooks written against it meet the same edges
|
|
14
|
-
* Canvas will hand them, including the one Canvas Career gets wrong.
|
|
15
|
-
*/
|
|
16
3
|
export { INLINE_ITEM_LIMIT } from './limits';
|
|
17
4
|
export interface InMemoryFixture {
|
|
18
|
-
/**
|
|
19
|
-
* The course these modules belong to.
|
|
20
|
-
*
|
|
21
|
-
* Present so the adapter can *check* it. Without it, `courseId` was
|
|
22
|
-
* destructured and dropped, which meant one adapter instance served identical
|
|
23
|
-
* modules for every course id in existence — and since `cache/keys.ts` keys the
|
|
24
|
-
* query cache by `courseId`, the entire class of bug where it is mis-threaded,
|
|
25
|
-
* defaulted or collided across courses was unfalsifiable against the package's
|
|
26
|
-
* primary test double. A hook that hardcoded `courseId: '1'` internally would
|
|
27
|
-
* have passed every test and every story.
|
|
28
|
-
*/
|
|
5
|
+
/** Checked, not just stored: without it one instance would serve every course id. */
|
|
29
6
|
courseId: string;
|
|
30
7
|
modules: Module[];
|
|
31
8
|
itemsByModuleId: Record<string, ModuleItem[]>;
|
|
32
9
|
}
|
|
33
|
-
/** What a served request was for. Handed to `onRequest`. */
|
|
34
10
|
export interface InMemoryRequest {
|
|
35
11
|
operation: 'listModules' | 'listModuleItems';
|
|
36
12
|
courseId: string;
|
|
@@ -40,7 +16,7 @@ export interface InMemoryRequest {
|
|
|
40
16
|
export interface InMemoryAdapterOptions {
|
|
41
17
|
/**
|
|
42
18
|
* Invoked once per served request. Test hook for asserting exact request
|
|
43
|
-
* counts, which is how the scaling
|
|
19
|
+
* counts, which is how the scaling behavior is locked in.
|
|
44
20
|
*
|
|
45
21
|
* Receives the operation name for the common case, and the full request for
|
|
46
22
|
* tests that care *which* course or module was asked for.
|
|
@@ -51,8 +27,8 @@ export interface InMemoryAdapterOptions {
|
|
|
51
27
|
* Thrown when a caller asks this adapter for a course it was not built for.
|
|
52
28
|
*
|
|
53
29
|
* A real transport would return that other course's data, or 404. Silently
|
|
54
|
-
* serving the fixture regardless is the one
|
|
55
|
-
* production, so it is the one
|
|
30
|
+
* serving the fixture regardless is the one behavior that could not happen in
|
|
31
|
+
* production, so it is the one behavior the test double must not have.
|
|
56
32
|
*/
|
|
57
33
|
export declare class InMemoryCourseMismatchError extends Error {
|
|
58
34
|
readonly expected: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/adapters/inMemory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAgC,MAAM,iBAAiB,CAAA;AAEvF,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEvF
|
|
1
|
+
{"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/adapters/inMemory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAgC,MAAM,iBAAiB,CAAA;AAEvF,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE5C,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,aAAa,GAAG,iBAAiB,CAAA;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,GAAG,iBAAiB,EAAE,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;CAC7F;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;gBAEb,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAS/C;AA2BD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,sBAA2B,GACnC,kBAAkB,CA0DpB"}
|