@instructure/platform-modules 0.3.0 → 1.0.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.
Files changed (108) hide show
  1. package/README.md +3 -2
  2. package/dist/adapters/canvasGraphql/index.d.ts +4 -40
  3. package/dist/adapters/canvasGraphql/index.d.ts.map +1 -1
  4. package/dist/adapters/canvasGraphql/normalize.d.ts +9 -220
  5. package/dist/adapters/canvasGraphql/normalize.d.ts.map +1 -1
  6. package/dist/adapters/canvasGraphql/queries.d.ts +7 -49
  7. package/dist/adapters/canvasGraphql/queries.d.ts.map +1 -1
  8. package/dist/adapters/inMemory.d.ts +0 -40
  9. package/dist/adapters/inMemory.d.ts.map +1 -1
  10. package/dist/adapters/limits.d.ts +3 -81
  11. package/dist/adapters/limits.d.ts.map +1 -1
  12. package/dist/adapters/types.d.ts +11 -61
  13. package/dist/adapters/types.d.ts.map +1 -1
  14. package/dist/cache/invalidate.d.ts +0 -46
  15. package/dist/cache/invalidate.d.ts.map +1 -1
  16. package/dist/cache/keys.d.ts +3 -55
  17. package/dist/cache/keys.d.ts.map +1 -1
  18. package/dist/components/FilterChips.d.ts +4 -17
  19. package/dist/components/FilterChips.d.ts.map +1 -1
  20. package/dist/components/ModuleCard.d.ts +5 -29
  21. package/dist/components/ModuleCard.d.ts.map +1 -1
  22. package/dist/components/ModuleCard.stories.d.ts +14 -1
  23. package/dist/components/ModuleCard.stories.d.ts.map +1 -1
  24. package/dist/components/ModuleItemRow.d.ts +0 -10
  25. package/dist/components/ModuleItemRow.d.ts.map +1 -1
  26. package/dist/components/ModuleNumberBadge.d.ts +0 -1
  27. package/dist/components/ModuleNumberBadge.d.ts.map +1 -1
  28. package/dist/components/ModulesList.d.ts +2 -54
  29. package/dist/components/ModulesList.d.ts.map +1 -1
  30. package/dist/components/ModulesList.stories.d.ts +11 -128
  31. package/dist/components/ModulesList.stories.d.ts.map +1 -1
  32. package/dist/components/StatusPill.d.ts +4 -6
  33. package/dist/components/StatusPill.d.ts.map +1 -1
  34. package/dist/components/index.d.ts +1 -1
  35. package/dist/components/index.d.ts.map +1 -1
  36. package/dist/components/labels.d.ts +8 -78
  37. package/dist/components/labels.d.ts.map +1 -1
  38. package/dist/components/lightThemeDecorator.d.ts +3 -0
  39. package/dist/components/lightThemeDecorator.d.ts.map +1 -0
  40. package/dist/config/base.d.ts +0 -5
  41. package/dist/config/base.d.ts.map +1 -1
  42. package/dist/config/capabilities.d.ts +8 -0
  43. package/dist/config/capabilities.d.ts.map +1 -0
  44. package/dist/config/context.d.ts +1 -2
  45. package/dist/config/context.d.ts.map +1 -1
  46. package/dist/config/filters.d.ts +15 -0
  47. package/dist/config/filters.d.ts.map +1 -0
  48. package/dist/config/formatters.d.ts +15 -0
  49. package/dist/config/formatters.d.ts.map +1 -0
  50. package/dist/config/index.d.ts +9 -4
  51. package/dist/config/index.d.ts.map +1 -1
  52. package/dist/config/itemMeta.d.ts +5 -0
  53. package/dist/config/itemMeta.d.ts.map +1 -0
  54. package/dist/config/itemTypes.d.ts +4 -0
  55. package/dist/config/itemTypes.d.ts.map +1 -0
  56. package/dist/config/partialModulesConfig.d.ts +0 -1
  57. package/dist/config/partialModulesConfig.d.ts.map +1 -1
  58. package/dist/config/registry.d.ts +2 -30
  59. package/dist/config/registry.d.ts.map +1 -1
  60. package/dist/config/resolve.d.ts +0 -4
  61. package/dist/config/resolve.d.ts.map +1 -1
  62. package/dist/config/schemas.d.ts +37 -7
  63. package/dist/config/schemas.d.ts.map +1 -1
  64. package/dist/config/types.d.ts +8 -14
  65. package/dist/config/types.d.ts.map +1 -1
  66. package/dist/config/useFormatters.d.ts +10 -0
  67. package/dist/config/useFormatters.d.ts.map +1 -0
  68. package/dist/fixtures/generate.d.ts +1 -37
  69. package/dist/fixtures/generate.d.ts.map +1 -1
  70. package/dist/fixtures/index.d.ts +1 -1
  71. package/dist/fixtures/index.d.ts.map +1 -1
  72. package/dist/fixtures/scenarios.d.ts +4 -34
  73. package/dist/fixtures/scenarios.d.ts.map +1 -1
  74. package/dist/hooks/constants.d.ts +6 -40
  75. package/dist/hooks/constants.d.ts.map +1 -1
  76. package/dist/hooks/queryOptions.d.ts +2 -26
  77. package/dist/hooks/queryOptions.d.ts.map +1 -1
  78. package/dist/hooks/useItemsMode.d.ts +3 -84
  79. package/dist/hooks/useItemsMode.d.ts.map +1 -1
  80. package/dist/hooks/useModuleItemQueries.d.ts +1 -45
  81. package/dist/hooks/useModuleItemQueries.d.ts.map +1 -1
  82. package/dist/hooks/useModuleItems.d.ts +0 -16
  83. package/dist/hooks/useModuleItems.d.ts.map +1 -1
  84. package/dist/hooks/useModules.d.ts +3 -24
  85. package/dist/hooks/useModules.d.ts.map +1 -1
  86. package/dist/hooks/useModulesPageData.d.ts +9 -33
  87. package/dist/hooks/useModulesPageData.d.ts.map +1 -1
  88. package/dist/i18n.d.ts +8 -45
  89. package/dist/i18n.d.ts.map +1 -1
  90. package/dist/index.d.ts +5 -12
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +1874 -1344
  93. package/dist/types/domain.d.ts +9 -87
  94. package/dist/types/domain.d.ts.map +1 -1
  95. package/dist/types/enums.d.ts +5 -58
  96. package/dist/types/enums.d.ts.map +1 -1
  97. package/dist/types/extensions.d.ts +0 -43
  98. package/dist/types/extensions.d.ts.map +1 -1
  99. package/dist/types/filterClause.d.ts +18 -0
  100. package/dist/types/filterClause.d.ts.map +1 -0
  101. package/dist/types/index.d.ts +3 -2
  102. package/dist/types/index.d.ts.map +1 -1
  103. package/dist/types/navigation.d.ts +0 -6
  104. package/dist/types/navigation.d.ts.map +1 -1
  105. package/dist/types/schemas.d.ts +2 -28
  106. package/dist/types/schemas.d.ts.map +1 -1
  107. package/locales/en.json +20 -9
  108. package/package.json +35 -31
@@ -1,86 +1,8 @@
1
- /**
2
- * The transport limits both adapters are bound by, defined once.
3
- *
4
- * ## What "inlining" means
5
- *
6
- * A modules page needs two things: the list of modules, and the items inside each
7
- * module. There are two ways to get them.
8
- *
9
- * **Inlined** — ask for the items *in the same request as the module list*, nested
10
- * under each module. One round trip for the whole page. On REST that is
11
- * `?include[]=items`; on GraphQL it is the nested `moduleItemsConnection`. Cheap
12
- * in requests, and the response grows with the total number of items in the
13
- * course.
14
- *
15
- * **On demand** — ask for the module list alone, then fetch a module's items in a
16
- * separate request when the user expands it. The first paint costs one request
17
- * whatever the course size, and each module the user opens costs one more.
18
- *
19
- * Neither is right in general: inlining wins on a small course, on-demand wins on
20
- * a large one, which is why `itemsMode: 'auto'` measures the course and picks.
21
- * "Inlined" always describes *where the items came from*, never whether they were
22
- * fetched at all — a module with `items: undefined` was not inlined, and a module
23
- * with `items: []` was inlined and is genuinely empty.
24
- *
25
- * ## Why the ceiling is 100
26
- *
27
- * Above this many items in a single module, inlining stops being worthwhile — and
28
- * on one transport it stops being possible. The two differ, and conflating them is
29
- * easy:
30
- *
31
- * - **REST refuses outright.** `lib/api/v1/context_module.rb` omits the `items`
32
- * key entirely for any module with more than `Api::MAX_PER_PAGE` (100) items.
33
- * Server-side; a client cannot opt out.
34
- * - **GraphQL has no per-module rule.** `moduleItemsConnection` is an ordinary
35
- * Relay connection, and nothing in `app/graphql/types/module_type.rb` caps it.
36
- *
37
- * It would be wrong to conclude GraphQL will inline anything you ask for, though.
38
- * Canvas runs a query-complexity analyzer, and it charges for the *shape of the
39
- * request* rather than the size of the result: `first: 100, itemsFirst: 100`
40
- * scores ~1.5M against a 375,000 ceiling and is rejected outright, even on a
41
- * course holding thirteen items. So the ceiling is real on both transports — just
42
- * enforced differently, and on different things. See `INLINE_QUERY_NODE_BUDGET`
43
- * for the measurements.
44
- *
45
- * The GraphQL adapter therefore applies this same 100 as a deliberate *policy*,
46
- * so both transports fall back to a per-module fetch at the same point and the
47
- * hooks see one behavior rather than two.
48
- */
1
+ /** Canvas REST omits the `items` key for a module over `Api::MAX_PER_PAGE`. */
49
2
  export declare const INLINE_ITEM_LIMIT = 100;
50
3
  /**
51
- * Ceiling on `pageSize × inlineItemsFirst` for a single inline request.
52
- *
53
- * Canvas runs a GraphQL complexity analyzer and rejects anything over 375,000.
54
- * Complexity is charged on the **shape of the request, not the size of the
55
- * result** — so a course with 13 items is rejected just as readily as one with
56
- * 13,000 if the query asks for `first: 100, itemsFirst: 100`.
57
- *
58
- * Measured against a real Canvas (`/api/graphql`, this document, Aug 2026):
59
- *
60
- * first × itemsFirst complexity verdict
61
- * 100 × 100 1,512,106 rejected
62
- * 100 × 50 757,106 rejected
63
- * 100 × 25 379,606 rejected, barely
64
- * 100 × 20 — accepted
65
- * 25 × 100 378,031 rejected, barely
66
- * 10 × 100 — accepted
67
- *
68
- * That is ~151 per requested item node, so the budget is the largest round
69
- * product comfortably under 375,000/151 ≈ 2,483. Kept as a product rather than
70
- * two separate caps because the analyzer multiplies: 100×20 and 10×100 both fit,
71
- * 25×100 does not.
72
- *
73
- * If a page genuinely needs more than this, it is not an inline page — the
74
- * request is split and the items fetched on demand, which is what
75
- * `resolveItemsMode` is for.
76
- *
77
- * This is a GraphQL fact, and it lives here rather than in the GraphQL adapter
78
- * for two reasons. It is the *default* the hooks assume when an adapter declines
79
- * to declare its own (`ModulesDataAdapter.inlineNodeBudget`), and importing it
80
- * from `adapters/canvasGraphql` was making the transport-agnostic `hooks/` layer
81
- * depend on one specific transport — which also dragged `queries.ts` and its two
82
- * module-scope `gql` parses into any bundle that only wanted the in-memory
83
- * adapter.
4
+ * Canvas GraphQL refuses a request scoring over 375,000 on its complexity analyzer.
5
+ * Measured against `/api/graphql`: ~151 per requested item node, so 2,483 is the true ceiling.
84
6
  */
85
7
  export declare const INLINE_QUERY_NODE_BUDGET = 2000;
86
8
  //# sourceMappingURL=limits.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/adapters/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAA"}
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/adapters/limits.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAEpC;;;GAGG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAA"}
@@ -1,79 +1,29 @@
1
1
  import { ModuleItemsPage, ModulesPage } from '../types/domain';
2
+ import { FilterCapabilities, FilterClause } from '../types/filterClause';
2
3
  /**
3
- * The seam between this package and a host's transport.
4
+ * Five obligations an adapter must meet:
4
5
  *
5
- * An adapter owns exactly three things: how a request is made, what fields it
6
- * asks for, and how the response becomes the domain model. It owns nothing
7
- * about caching, pagination policy, or React — those live in the hooks, so
8
- * that swapping GraphQL for REST changes one file and no behavior.
9
- *
10
- * Contract every adapter must honor:
11
- *
12
- * - **Return a real `pageInfo`.** The GraphQL adapter passes through
13
- * `endCursor`; the REST adapter derives it from the `Link` header. An
14
- * adapter that hardcodes `hasNextPage: false` silently truncates, which is
15
- * the live bug in Canvas Career's modules path today.
16
- * - **Honor `signal`.** Collapsing a module aborts its in-flight request.
17
- * - **Never invent a cursor.** Pass back what the server gave you; do not
18
- * synthesize offsets client-side.
19
- * - **Normalize fully.** Output must satisfy the Zod schemas. No transport
20
- * detail (`_id`, `quiz_lti`, snake_case) may reach a caller.
6
+ * 1. Return the transport's real `pageInfo`. GraphQL: `endCursor`. REST: the parsed `Link` header.
7
+ * 2. Never synthesize a cursor.
8
+ * 3. Abort on the supplied `AbortSignal`.
9
+ * 4. Normalize fully. No transport detail reaches a caller.
10
+ * 5. Honor `ListModulesArgs.filter`, or throw. Serving a clause `capabilities` does not declare
11
+ * issues a cursor against the wrong result set, so every page after it is wrong too.
21
12
  */
22
13
  export interface ModulesDataAdapter {
23
14
  listModules(args: ListModulesArgs): Promise<ModulesPage>;
24
15
  listModuleItems(args: ListModuleItemsArgs): Promise<ModuleItemsPage>;
25
- /**
26
- * The largest `modules × itemsPerModule` product this transport will accept in
27
- * one inline request, or `undefined` for "no transport limit of my own".
28
- *
29
- * The transport declares its own ceiling rather than the hooks assuming one.
30
- * Canvas GraphQL has a complexity analyzer that rejects the *shape* of a large
31
- * inline query (@see INLINE_QUERY_NODE_BUDGET); the in-memory double has no
32
- * such constraint; REST will have a different one again, bounded by
33
- * `Api::MAX_PER_PAGE` and response size rather than by query complexity.
34
- *
35
- * `resolveItemsMode` reads this to decide whether an inline page is deliverable.
36
- * When it is `undefined` the hook falls back to the GraphQL budget, which is
37
- * the conservative choice: guessing too low costs one extra request per opened
38
- * module, guessing too high gets the whole query rejected.
39
- */
40
16
  readonly inlineNodeBudget?: number;
17
+ readonly capabilities?: FilterCapabilities;
41
18
  }
42
19
  export interface ListModulesArgs {
43
20
  courseId: string;
44
- /** `null`/omitted requests the first page. */
45
21
  cursor?: string | null;
46
22
  pageSize: number;
47
- /**
48
- * Ask the server to inline each module's items in the same round trip.
49
- *
50
- * A request, not a guarantee. Canvas REST drops the `items` key for any module
51
- * over `Api::MAX_PER_PAGE` (100) items; the GraphQL path has no such server
52
- * rule but this package applies the same ceiling on purpose, so both transports
53
- * behave alike. Either way an adapter must report an accurate `itemCount` and
54
- * leave `items` **undefined** for a module it did not inline, never `[]`.
55
- *
56
- * @see INLINE_ITEM_LIMIT in `adapters/limits.ts` for why the number is 100 and
57
- * which half of it Canvas actually enforces.
58
- */
23
+ /** Only ever a clause `capabilities` admits. Throw on anything else. */
24
+ filter?: FilterClause;
59
25
  includeItems: boolean;
60
- /**
61
- * How many items to request per module when `includeItems` is true.
62
- *
63
- * Omit to let the adapter use its own ceiling. Supply it when the caller has
64
- * *measured* what the page needs — `useModulesPageData` does, from the `auto`
65
- * probe — because a transport may charge for what the query asks for rather
66
- * than for what exists. @see INLINE_QUERY_NODE_BUDGET
67
- */
68
26
  inlineItemsFirst?: number;
69
- /**
70
- * How many modules to request when inlining.
71
- *
72
- * Omit to use `pageSize`. Supply it when the caller has measured that page 1 is
73
- * the whole list, because the complexity budget is charged on
74
- * `first x inlineItemsFirst` — asking for 100 modules when the course has 2 is
75
- * what forces `inlineItemsFirst` to be clamped below what the page needs.
76
- */
77
27
  inlineFirst?: number;
78
28
  signal?: AbortSignal;
79
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACxD,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IACpE;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;;;;;;;OAWG;IACH,YAAY,EAAE,OAAO,CAAA;IACrB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAE7E;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACxD,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IACpE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAA;CAC3C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB"}
@@ -1,52 +1,6 @@
1
1
  import { QueryClient } from '@tanstack/react-query';
2
2
  import { ModuleItemsKeyOptions, ModulesListKeyOptions } from './keys';
3
- /**
4
- * Invalidation helpers.
5
- *
6
- * ## What is deliberately missing
7
- *
8
- * There is no `invalidateOnExpand` / `invalidateOnCollapse`, and there must
9
- * never be one. **Expanding or collapsing a module invalidates nothing.**
10
- *
11
- * Expansion is UI state. It says which rows are visible; it says nothing about
12
- * whether the server's copy of the data changed. Canvas's modules page ties the
13
- * two together, so every collapse invalidates the module list, every
14
- * invalidation re-walks every page that has been loaded, and a course with
15
- * thirty modules re-fetches the world each time a teacher tidies the screen.
16
- * That is the refetch loop this package exists to not have.
17
- *
18
- * The equivalents here:
19
- * - collapse → cancel that module's in-flight item request (an abort, not an
20
- * invalidation — see `useModulesPageData`), and leave the cached pages alone
21
- * - re-expand → a cache hit, no request
22
- *
23
- * Invalidate when the *data* changed: a mutation, a websocket nudge, an
24
- * explicit user refresh. Not when a disclosure arrow moved.
25
- */
26
- /**
27
- * One module's items. Use after reordering, adding, or deleting items.
28
- *
29
- * Omit `opts` — the default — to hit every variant of that module's items, which
30
- * is what a mutation wants: adding an item changes the module for the student
31
- * view and the teacher view alike, at every page size any consumer rendered
32
- * with. Pass `opts` only to target the exact query one hook instance holds.
33
- */
34
3
  export declare function invalidateModuleItems(queryClient: QueryClient, courseId: string, moduleId: string, opts?: ModuleItemsKeyOptions): Promise<void>;
35
- /**
36
- * The module list for a course.
37
- *
38
- * Omit `opts` to hit every list variant (all page sizes, both views, inline and
39
- * on-demand) — which is almost always what you want, since a mutation that adds
40
- * or removes a module invalidates all of them. Pass `opts` only to target the
41
- * exact query a specific hook instance is rendering.
42
- */
43
4
  export declare function invalidateModuleList(queryClient: QueryClient, courseId: string, opts?: ModulesListKeyOptions): Promise<void>;
44
- /**
45
- * Everything for a course: the list and every module's items.
46
- *
47
- * The blunt instrument. Reasonable after a bulk operation (publish all, import
48
- * a course package); wasteful after a single-item edit, which should use
49
- * `invalidateModuleItems`.
50
- */
51
5
  export declare function invalidateCourse(queryClient: QueryClient, courseId: string): Promise<void>;
52
6
  //# sourceMappingURL=invalidate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"invalidate.d.ts","sourceRoot":"","sources":["../../src/cache/invalidate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC3B,MAAM,QAAQ,CAAA;AAEf;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F"}
1
+ {"version":3,"file":"invalidate.d.ts","sourceRoot":"","sources":["../../src/cache/invalidate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC3B,MAAM,QAAQ,CAAA;AAEf,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F"}
@@ -1,52 +1,12 @@
1
- /**
2
- * Query keys for the modules page.
3
- *
4
- * One factory, one shape, one place to look when you need to know what a given
5
- * invalidation will hit. Every key is a `readonly` tuple so TanStack Query keeps
6
- * the key type narrow instead of widening it to `unknown[]`, and so a typo in a
7
- * literal segment is a compile error rather than a silent cache miss.
8
- *
9
- * The hierarchy is deliberate — each level is a prefix of the next, which is
10
- * what makes `invalidateCourse` able to reach every query for a course without
11
- * enumerating them:
12
- *
13
- * ['platform-modules']
14
- * ['platform-modules', courseId]
15
- * ['platform-modules', courseId, 'list', opts]
16
- * ['platform-modules', courseId, 'module', moduleId, 'items', opts]
17
- */
18
- /**
19
- * Which Canvas audience the page is rendered for.
20
- *
21
- * It is not passed to the adapter — a host configures its adapter for the
22
- * current user already — but it *is* part of every key, because a teacher sees
23
- * unpublished content a student does not. Two views must never share a cache
24
- * entry.
25
- */
1
+ import { FilterClause } from '../types/filterClause';
26
2
  export type ModulesView = 'student' | 'teacher';
27
- /**
28
- * Everything that changes what a list request returns, and therefore has to
29
- * partition the cache. `includeItems` is here because the inline and on-demand
30
- * shapes of the same page are genuinely different payloads.
31
- */
32
3
  export interface ModulesListKeyOptions {
33
4
  view?: ModulesView;
34
5
  pageSize?: number;
35
6
  includeItems?: boolean;
7
+ /** The clause partitions, not the chip. Chips the server never saw share one entry. */
8
+ filter?: FilterClause;
36
9
  }
37
- /**
38
- * The items equivalent, and it exists for the same two reasons the list one does.
39
- *
40
- * `view` because `published` is a per-*item* field, so the teacher/student
41
- * argument above applies to items verbatim: a teacher's page of items includes
42
- * unpublished rows a student's does not.
43
- *
44
- * `pageSize` because these are infinite queries. The cache entry is an
45
- * `InfiniteData` whose `pages` were each fetched at some size; two consumers
46
- * rendering the same module with different `itemPageSize` would otherwise share
47
- * one entry whose pages were fetched at mixed sizes, and neither could say what
48
- * `pages[n]` contains.
49
- */
50
10
  export interface ModuleItemsKeyOptions {
51
11
  view?: ModulesView;
52
12
  pageSize?: number;
@@ -69,18 +29,6 @@ export interface ModulesKeyFactory {
69
29
  items(courseId: string, moduleId: string, opts?: ModuleItemsKeyOptions): ModuleItemsKey;
70
30
  }
71
31
  export declare const modulesKeys: ModulesKeyFactory;
72
- /**
73
- * The prefix shared by every list key for a course, regardless of `opts`.
74
- * Used to invalidate "the module list" without knowing which page size or view
75
- * the caller happened to render with.
76
- */
77
32
  export declare function modulesListPrefix(courseId: string): readonly ['platform-modules', string, 'list'];
78
- /**
79
- * The prefix shared by every items key for one module, regardless of `opts`.
80
- *
81
- * This is the one to reach for when acting on "that module's items" without
82
- * knowing which view or page size rendered them — invalidating after a mutation,
83
- * or canceling in flight requests on collapse.
84
- */
85
33
  export declare function moduleItemsPrefix(courseId: string, moduleId: string): readonly ['platform-modules', string, 'module', string, 'items'];
86
34
  //# sourceMappingURL=keys.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/cache/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/C;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAAA;AACzD,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAA;AACpE,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAA;AACjG,MAAM,MAAM,cAAc,GAAG,SAAS;IACpC,kBAAkB;IAClB,MAAM;IACN,QAAQ;IACR,MAAM;IACN,OAAO;IACP,qBAAqB;CACtB,CAAA;AAID,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAA;IAC3B,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAAA;IAC1C,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,cAAc,CAAA;IACpE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,cAAc,CAAA;CACxF;AAED,eAAO,MAAM,WAAW,EAAE,iBAOzB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,CAEjG;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAElE"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/cache/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/C,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,uFAAuF;IACvF,MAAM,CAAC,EAAE,YAAY,CAAA;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAAA;AACzD,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAA;AACpE,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAA;AACjG,MAAM,MAAM,cAAc,GAAG,SAAS;IACpC,kBAAkB;IAClB,MAAM;IACN,QAAQ;IACR,MAAM;IACN,OAAO;IACP,qBAAqB;CACtB,CAAA;AAID,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAA;IAC3B,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAAA;IAC1C,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,cAAc,CAAA;IACpE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,cAAc,CAAA;CACxF;AAED,eAAO,MAAM,WAAW,EAAE,iBAOzB,CAAA;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,CAEjG;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAElE"}
@@ -1,21 +1,8 @@
1
1
  import { ReactElement } from 'react';
2
- import { ModulesFilterId } from './labels';
3
- /**
4
- * The filter row: a toggle group, not tabs.
5
- *
6
- * R3 says "Filter • tabs", but the design draws five independent secondary
7
- * Buttons under a caption — no tablist, no tabpanel, nothing tying the
8
- * selection to the list. So `role="group"` + `aria-pressed`, one tab stop per
9
- * chip. Checked against the mock in FOUND-355; `Tabs` is the wrong component.
10
- *
11
- * The selected chip is `primary`, not the mock's tinted `secondary`: the mock's
12
- * two fills sit 1.25:1 apart where WCAG 1.4.11 wants 3:1, and its blues come
13
- * from InstUI Button v2, which `ui-buttons@11` does not ship.
14
- */
15
2
  export interface FilterChipsProps {
16
- value: ModulesFilterId;
17
- onChange: (filter: ModulesFilterId) => void;
3
+ value: string;
4
+ onChange: (filter: string) => void;
5
+ describedBy?: string;
18
6
  }
19
- /** @see FilterChipsProps */
20
- export declare function FilterChips({ value, onChange }: FilterChipsProps): ReactElement;
7
+ export declare function FilterChips({ value, onChange, describedBy }: FilterChipsProps): ReactElement;
21
8
  //# sourceMappingURL=FilterChips.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilterChips.d.ts","sourceRoot":"","sources":["../../src/components/FilterChips.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAmC,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAEhF;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,eAAe,CAAA;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;CAC5C;AAED,4BAA4B;AAC5B,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,gBAAgB,GAAG,YAAY,CAmD/E"}
1
+ {"version":3,"file":"FilterChips.d.ts","sourceRoot":"","sources":["../../src/components/FilterChips.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,OAAO,CAAA;AAIzD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,gBAAgB,GAAG,YAAY,CA4C5F"}
@@ -1,44 +1,20 @@
1
- import { NamedExoticComponent } from 'react';
1
+ import { Breakpoints } from '@instructure/platform-with-breakpoints';
2
+ import { ProgressBar } from '@instructure/ui-progress/v11_7';
3
+ import { ComponentProps, NamedExoticComponent } from 'react';
2
4
  import { ModuleItemsState } from '../hooks';
3
5
  import { Module, ModuleItemNavigation } from '../types';
4
- /**
5
- * One module card.
6
- *
7
- * Header: number badge, name, byline, status pill, expand toggle.
8
- * Progress row: **only when `module.progress` exists**.
9
- *
10
- * Expanded body: the item rows, plus the per-module "Load more items" control
11
- * whenever `hasNextPage` is true.
12
- */
13
6
  export interface ModuleCardProps {
14
7
  module: Module;
15
8
  expanded: boolean;
16
9
  onToggle: (moduleId: string) => void;
17
- /** The hook's per-module item state. Only read when `expanded`. */
18
10
  itemsState: ModuleItemsState;
19
11
  today: Date;
20
- /** Show the `inline` vs `onDemand` provenance line. Dev affordance, off by default. */
21
12
  showDiagnostics?: boolean;
22
- /** Stable reference required; see `ModuleItemNavigation`. */
23
13
  navigation?: ModuleItemNavigation;
24
- /**
25
- * Heading level for the module name. Defaults to `'h3'`, one below
26
- * `ModulesList`'s default of `h2`.
27
- */
28
14
  headingLevel?: 'h2' | 'h3' | 'h4';
29
- /**
30
- * Badge text: omitted derives it from `module.position`, `null` renders no
31
- * badge, a string overrides.
32
- */
33
15
  moduleLabel?: string | null;
16
+ breakpoints?: Breakpoints;
34
17
  }
35
- /**
36
- * Memoized, which is only useful because its props are now stable.
37
- *
38
- * `itemsState` comes from `useModulesPageData`'s per-module cache and `onToggle`
39
- * from a ref-backed `useCallback`, so a card re-renders when *its own* module
40
- * changes and not when a sibling's items arrive. Both of those had to be fixed
41
- * first — a `memo` over freshly-built props is pure overhead.
42
- */
18
+ export declare const progressBarTheme: ComponentProps<typeof ProgressBar>['themeOverride'];
43
19
  export declare const ModuleCard: NamedExoticComponent<ModuleCardProps>;
44
20
  //# sourceMappingURL=ModuleCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ModuleCard.d.ts","sourceRoot":"","sources":["../../src/components/ModuleCard.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAQ,KAAK,oBAAoB,EAAwC,MAAM,OAAO,CAAA;AAC7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAO5D;;;;;;;;GAQG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,mEAAmE;IACnE,UAAU,EAAE,gBAAgB,CAAA;IAC5B,KAAK,EAAE,IAAI,CAAA;IACX,uFAAuF;IACvF,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAqUD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,oBAAoB,CAAC,eAAe,CAAwB,CAAA"}
1
+ {"version":3,"file":"ModuleCard.d.ts","sourceRoot":"","sources":["../../src/components/ModuleCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAMzE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAI5D,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,oBAAoB,EAI1B,MAAM,OAAO,CAAA;AACd,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAQ5D,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,UAAU,EAAE,gBAAgB,CAAA;IAC5B,KAAK,EAAE,IAAI,CAAA;IACX,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AA8BD,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC,eAAe,CAQ/E,CAAA;AAsRF,eAAO,MAAM,UAAU,EAAE,oBAAoB,CAAC,eAAe,CAAwB,CAAA"}
@@ -6,7 +6,20 @@ type Story = StoryObj<typeof meta>;
6
6
  export declare const Default: Story;
7
7
  export declare const ThreeDigitNumber: Story;
8
8
  export declare const Locked: Story;
9
- /** A label longer than the square widens the badge instead of spilling out of it. */
10
9
  export declare const LongLabel: Story;
11
10
  export declare const NoBadge: Story;
11
+ export declare const ExpandedWithStatusPills: Story;
12
+ export declare const ExpandedNonGradableItems: Story;
13
+ export declare const ExpandedLockedModule: Story;
14
+ export declare const ExpandedUntouchedModule: Story;
15
+ export declare const ProgressNotStarted: Story;
16
+ export declare const ProgressPartial: Story;
17
+ export declare const ProgressComplete: Story;
18
+ export declare const ProgressNoRequirements: Story;
19
+ export declare const ProgressLocked: Story;
20
+ export declare const ProgressAbsentForTeacher: Story;
21
+ export declare const ProgressStacked: Story;
22
+ export declare const LongModuleName: Story;
23
+ export declare const LetterLabelScheme: Story;
24
+ export declare const NoLabelScheme: Story;
12
25
  //# sourceMappingURL=ModuleCard.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ModuleCard.stories.d.ts","sourceRoot":"","sources":["../../src/components/ModuleCard.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAoBzC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,UAAU,CAWjC,CAAA;AAED,eAAe,IAAI,CAAA;AACnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAA;AAElC,eAAO,MAAM,OAAO,EAAE,KAAU,CAAA;AAEhC,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAA;AAED,eAAO,MAAM,MAAM,EAAE,KAEpB,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAA;AAED,eAAO,MAAM,OAAO,EAAE,KAErB,CAAA"}
1
+ {"version":3,"file":"ModuleCard.stories.d.ts","sourceRoot":"","sources":["../../src/components/ModuleCard.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAKtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AA2BzC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,UAAU,CAYjC,CAAA;AAED,eAAe,IAAI,CAAA;AACnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAA;AAElC,eAAO,MAAM,OAAO,EAAE,KAAU,CAAA;AAEhC,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAA;AAED,eAAO,MAAM,MAAM,EAAE,KAEpB,CAAA;AAED,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAA;AAED,eAAO,MAAM,OAAO,EAAE,KAErB,CAAA;AAED,eAAO,MAAM,uBAAuB,EAAE,KAErC,CAAA;AAED,eAAO,MAAM,wBAAwB,EAAE,KAWtC,CAAA;AAED,eAAO,MAAM,oBAAoB,EAAE,KAElC,CAAA;AAED,eAAO,MAAM,uBAAuB,EAAE,KAErC,CAAA;AAmBD,eAAO,MAAM,kBAAkB,EAAE,KAEhC,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,KAE7B,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,KAEpC,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAA;AAED,eAAO,MAAM,wBAAwB,EAAE,KAEtC,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,KAE7B,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,KAO5B,CAAA;AAQD,eAAO,MAAM,iBAAiB,EAAE,KAU/B,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,KAM3B,CAAA"}
@@ -2,18 +2,8 @@ import { NamedExoticComponent } from 'react';
2
2
  import { ModuleItem, ModuleItemNavigation } from '../types';
3
3
  export interface ModuleItemRowProps {
4
4
  item: ModuleItem;
5
- /** Injected so relative dates ("yesterday") are deterministic. */
6
5
  today: Date;
7
- /** Stable reference required; see `ModuleItemNavigation`. */
8
6
  navigation?: ModuleItemNavigation;
9
7
  }
10
- /**
11
- * Memoized because it is the hottest component in the package.
12
- *
13
- * `item` is a stable reference out of the query cache and `today` is pinned per
14
- * mount, so the default shallow comparison is enough. It is what stops one
15
- * module's observer publishing from re-rendering every other module's rows —
16
- * ~3000 of them on a 30-module course with everything expanded.
17
- */
18
8
  export declare const ModuleItemRow: NamedExoticComponent<ModuleItemRowProps>;
19
9
  //# sourceMappingURL=ModuleItemRow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ModuleItemRow.d.ts","sourceRoot":"","sources":["../../src/components/ModuleItemRow.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAyB,KAAK,oBAAoB,EAAqB,MAAM,OAAO,CAAA;AAC3F,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,oBAAoB,EAE1B,MAAM,UAAU,CAAA;AAuDjB,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAA;IAChB,kEAAkE;IAClE,KAAK,EAAE,IAAI,CAAA;IACX,6DAA6D;IAC7D,UAAU,CAAC,EAAE,oBAAoB,CAAA;CAClC;AAmJD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,oBAAoB,CAAC,kBAAkB,CAA2B,CAAA"}
1
+ {"version":3,"file":"ModuleItemRow.d.ts","sourceRoot":"","sources":["../../src/components/ModuleItemRow.tsx"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,oBAAoB,EAI1B,MAAM,OAAO,CAAA;AASd,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAmChE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,IAAI,CAAA;IACX,UAAU,CAAC,EAAE,oBAAoB,CAAA;CAClC;AAiKD,eAAO,MAAM,aAAa,EAAE,oBAAoB,CAAC,kBAAkB,CAA2B,CAAA"}
@@ -1,6 +1,5 @@
1
1
  import { ReactElement } from 'react';
2
2
  export interface ModuleNumberBadgeProps {
3
- /** `null` renders no badge. */
4
3
  label: string | null;
5
4
  dimmed?: boolean;
6
5
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ModuleNumberBadge.d.ts","sourceRoot":"","sources":["../../src/components/ModuleNumberBadge.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAIzC,MAAM,WAAW,sBAAsB;IACrC,+BAA+B;IAC/B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,MAAc,GACf,EAAE,sBAAsB,GAAG,YAAY,GAAG,IAAI,CAuB9C"}
1
+ {"version":3,"file":"ModuleNumberBadge.d.ts","sourceRoot":"","sources":["../../src/components/ModuleNumberBadge.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAIzC,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,MAAc,GACf,EAAE,sBAAsB,GAAG,YAAY,GAAG,IAAI,CAsB9C"}
@@ -1,72 +1,20 @@
1
- import { ReactElement } from 'react';
1
+ import { ComponentType } from 'react';
2
2
  import { UseModulesPageDataResult } from '../hooks';
3
3
  import { ModuleItemNavigation } from '../types';
4
- import { ModulesFilterId } from './labels';
5
- /**
6
- * The student modules page, approximating the Modules Foundation design.
7
- *
8
- * What it is: course header, a module-name search, an expand/collapse menu, the
9
- * module-level filter row, and a stack of module cards.
10
- *
11
- * What it deliberately is not:
12
- *
13
- * - **No progress strip.** The `01 02 03 …` paginated bar under the filters in
14
- * the mock is out of scope; the user deferred it.
15
- * - **No item-level search.** See `moduleMatchesSearch`: in `onDemand` mode the
16
- * items of collapsed modules are not loaded, so searching them would silently
17
- * under-report. The input filters module *names* and says so on its label.
18
- * Everything renderable comes from `useModulesPageData`; this component owns
19
- * only view state (search text, active filter), never data. Expanding a module
20
- * still invalidates nothing.
21
- */
22
- /** Requests served by the adapter so far, split by operation. */
23
4
  export interface ModulesRequestCounts {
24
5
  listModules: number;
25
6
  listModuleItems: number;
26
7
  }
27
8
  export interface ModulesListProps {
28
- /** The whole result of `useModulesPageData`, passed straight through. */
29
9
  data: UseModulesPageDataResult;
30
- /** Course title in the header. */
31
10
  courseName?: string;
32
- /** The crumb above the title, e.g. "Math". Purely decorative. */
33
11
  courseContext?: string;
34
- /**
35
- * "Now", injected so relative dates and the "Due this week" filter are
36
- * deterministic in stories and tests. The fixtures use a fixed 2025-01-06
37
- * epoch, so a story that wants sensible dates should pin this.
38
- */
39
12
  today?: Date;
40
- /**
41
- * Live counts from `createInMemoryAdapter`'s `onRequest`. When supplied, a
42
- * dev banner renders them. It is the most useful thing on screen for
43
- * reviewing this MVP — "does this scale" as a number rather than a devtools
44
- * exercise — and a host with a real adapter simply omits the prop.
45
- */
46
13
  requestCounts?: ModulesRequestCounts;
47
- /** Also show items-mode and per-module item provenance. Defaults to `requestCounts !== undefined`. */
48
14
  showDiagnostics?: boolean;
49
- /** Which chip starts active. Uncontrolled after mount — the chips own it. */
50
- initialFilter?: ModulesFilterId;
51
- /** Stable reference required; see `ModuleItemNavigation`. */
52
15
  navigation?: ModuleItemNavigation;
53
- /**
54
- * Element for the outer container. Defaults to `'div'`.
55
- *
56
- * Deliberately not `'main'` by default: Canvas's own layout already ships
57
- * `role="main"` (`application.html.erb:267`), so a Canvas host rendering this
58
- * would get two main landmarks. A host that owns its whole page can opt in with
59
- * `as="main"`.
60
- */
61
16
  as?: 'div' | 'main' | 'section';
62
- /**
63
- * Heading level for the course title. Defaults to `'h2'`.
64
- *
65
- * Also not `h1` by default, and for the same reason: a host page usually already
66
- * has one. `ModuleCard` renders one level below this.
67
- */
68
17
  headingLevel?: 'h1' | 'h2' | 'h3';
69
18
  }
70
- /** @see ModulesListProps */
71
- export declare function ModulesList({ data, courseName, courseContext, today, requestCounts, showDiagnostics, initialFilter, navigation, as, headingLevel, }: ModulesListProps): ReactElement;
19
+ export declare const ModulesList: ComponentType<ModulesListProps>;
72
20
  //# sourceMappingURL=ModulesList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ModulesList.d.ts","sourceRoot":"","sources":["../../src/components/ModulesList.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,YAAY,EAA+C,MAAM,OAAO,CAAA;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAG5D,OAAO,EAA4C,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAGzF;;;;;;;;;;;;;;;;GAgBG;AAEH,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,IAAI,EAAE,wBAAwB,CAAA;IAC9B,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ;;;;;OAKG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAA;IACpC,sGAAsG;IACtG,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,eAAe,CAAA;IAC/B,6DAA6D;IAC7D,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC;;;;;;;OAOG;IACH,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAClC;AA0DD,4BAA4B;AAC5B,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,UAAU,EACV,aAAa,EACb,KAAK,EACL,aAAa,EACb,eAA6C,EAC7C,aAAqB,EACrB,UAAU,EACV,EAAU,EACV,YAAmB,GACpB,EAAE,gBAAgB,GAAG,YAAY,CAiRjC"}
1
+ {"version":3,"file":"ModulesList.d.ts","sourceRoot":"","sources":["../../src/components/ModulesList.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,aAAa,EAOnB,MAAM,OAAO,CAAA;AAEd,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAM5D,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,wBAAwB,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,aAAa,CAAC,EAAE,oBAAoB,CAAA;IACpC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;IAC/B,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAClC;AAkVD,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAET,CAAA"}