@instructure/platform-modules 0.1.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 (81) hide show
  1. package/README.md +75 -0
  2. package/dist/adapters/canvasGraphql/index.d.ts +54 -0
  3. package/dist/adapters/canvasGraphql/index.d.ts.map +1 -0
  4. package/dist/adapters/canvasGraphql/normalize.d.ts +345 -0
  5. package/dist/adapters/canvasGraphql/normalize.d.ts.map +1 -0
  6. package/dist/adapters/canvasGraphql/queries.d.ts +69 -0
  7. package/dist/adapters/canvasGraphql/queries.d.ts.map +1 -0
  8. package/dist/adapters/inMemory.d.ts +63 -0
  9. package/dist/adapters/inMemory.d.ts.map +1 -0
  10. package/dist/adapters/index.d.ts +3 -0
  11. package/dist/adapters/index.d.ts.map +1 -0
  12. package/dist/adapters/limits.d.ts +86 -0
  13. package/dist/adapters/limits.d.ts.map +1 -0
  14. package/dist/adapters/types.d.ts +87 -0
  15. package/dist/adapters/types.d.ts.map +1 -0
  16. package/dist/cache/index.d.ts +3 -0
  17. package/dist/cache/index.d.ts.map +1 -0
  18. package/dist/cache/invalidate.d.ts +52 -0
  19. package/dist/cache/invalidate.d.ts.map +1 -0
  20. package/dist/cache/keys.d.ts +86 -0
  21. package/dist/cache/keys.d.ts.map +1 -0
  22. package/dist/components/FilterChips.d.ts +19 -0
  23. package/dist/components/FilterChips.d.ts.map +1 -0
  24. package/dist/components/ModuleCard.d.ts +32 -0
  25. package/dist/components/ModuleCard.d.ts.map +1 -0
  26. package/dist/components/ModuleItemRow.d.ts +17 -0
  27. package/dist/components/ModuleItemRow.d.ts.map +1 -0
  28. package/dist/components/ModulesList.d.ts +69 -0
  29. package/dist/components/ModulesList.d.ts.map +1 -0
  30. package/dist/components/ModulesList.stories.d.ts +138 -0
  31. package/dist/components/ModulesList.stories.d.ts.map +1 -0
  32. package/dist/components/StatusPill.d.ts +18 -0
  33. package/dist/components/StatusPill.d.ts.map +1 -0
  34. package/dist/components/index.d.ts +7 -0
  35. package/dist/components/index.d.ts.map +1 -0
  36. package/dist/components/labels.d.ts +89 -0
  37. package/dist/components/labels.d.ts.map +1 -0
  38. package/dist/config/index.d.ts +3 -0
  39. package/dist/config/index.d.ts.map +1 -0
  40. package/dist/config/registry.d.ts +38 -0
  41. package/dist/config/registry.d.ts.map +1 -0
  42. package/dist/config/schemas.d.ts +16 -0
  43. package/dist/config/schemas.d.ts.map +1 -0
  44. package/dist/fixtures/generate.d.ts +48 -0
  45. package/dist/fixtures/generate.d.ts.map +1 -0
  46. package/dist/fixtures/index.d.ts +3 -0
  47. package/dist/fixtures/index.d.ts.map +1 -0
  48. package/dist/fixtures/scenarios.d.ts +42 -0
  49. package/dist/fixtures/scenarios.d.ts.map +1 -0
  50. package/dist/hooks/constants.d.ts +49 -0
  51. package/dist/hooks/constants.d.ts.map +1 -0
  52. package/dist/hooks/index.d.ts +7 -0
  53. package/dist/hooks/index.d.ts.map +1 -0
  54. package/dist/hooks/queryOptions.d.ts +58 -0
  55. package/dist/hooks/queryOptions.d.ts.map +1 -0
  56. package/dist/hooks/useItemsMode.d.ts +114 -0
  57. package/dist/hooks/useItemsMode.d.ts.map +1 -0
  58. package/dist/hooks/useModuleItemQueries.d.ts +56 -0
  59. package/dist/hooks/useModuleItemQueries.d.ts.map +1 -0
  60. package/dist/hooks/useModuleItems.d.ts +41 -0
  61. package/dist/hooks/useModuleItems.d.ts.map +1 -0
  62. package/dist/hooks/useModules.d.ts +50 -0
  63. package/dist/hooks/useModules.d.ts.map +1 -0
  64. package/dist/hooks/useModulesPageData.d.ts +78 -0
  65. package/dist/hooks/useModulesPageData.d.ts.map +1 -0
  66. package/dist/i18n.d.ts +92 -0
  67. package/dist/i18n.d.ts.map +1 -0
  68. package/dist/index.d.ts +19 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +1966 -0
  71. package/dist/types/domain.d.ts +147 -0
  72. package/dist/types/domain.d.ts.map +1 -0
  73. package/dist/types/enums.d.ts +93 -0
  74. package/dist/types/enums.d.ts.map +1 -0
  75. package/dist/types/extensions.d.ts +48 -0
  76. package/dist/types/extensions.d.ts.map +1 -0
  77. package/dist/types/index.d.ts +4 -0
  78. package/dist/types/index.d.ts.map +1 -0
  79. package/dist/types/schemas.d.ts +38 -0
  80. package/dist/types/schemas.d.ts.map +1 -0
  81. package/package.json +80 -0
@@ -0,0 +1,86 @@
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 analyser, 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 behaviour rather than two.
48
+ */
49
+ export declare const INLINE_ITEM_LIMIT = 100;
50
+ /**
51
+ * Ceiling on `pageSize × inlineItemsFirst` for a single inline request.
52
+ *
53
+ * Canvas runs a GraphQL complexity analyser 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 analyser 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.
84
+ */
85
+ export declare const INLINE_QUERY_NODE_BUDGET = 2000;
86
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,87 @@
1
+ import { ModuleItemsPage, ModulesPage } from '../types/domain';
2
+ /**
3
+ * The seam between this package and a host's transport.
4
+ *
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 behaviour.
9
+ *
10
+ * Contract every adapter must honour:
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
+ * - **Honour `signal`.** Collapsing a module aborts its in-flight request.
17
+ * - **Never invent a cursor.** Pass back what the server gave you; do not
18
+ * synthesise offsets client-side.
19
+ * - **Normalise fully.** Output must satisfy the Zod schemas. No transport
20
+ * detail (`_id`, `quiz_lti`, snake_case) may reach a caller.
21
+ */
22
+ export interface ModulesDataAdapter {
23
+ listModules(args: ListModulesArgs): Promise<ModulesPage>;
24
+ 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 analyser 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
+ readonly inlineNodeBudget?: number;
41
+ }
42
+ export interface ListModulesArgs {
43
+ courseId: string;
44
+ /** `null`/omitted requests the first page. */
45
+ cursor?: string | null;
46
+ 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
+ */
59
+ 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
+ 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
+ inlineFirst?: number;
78
+ signal?: AbortSignal;
79
+ }
80
+ export interface ListModuleItemsArgs {
81
+ courseId: string;
82
+ moduleId: string;
83
+ cursor?: string | null;
84
+ pageSize: number;
85
+ signal?: AbortSignal;
86
+ }
87
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ export { moduleItemsPrefix, modulesKeys, modulesListPrefix, type ModuleItemsKey, type ModuleItemsKeyOptions, type ModulesAllKey, type ModulesCourseKey, type ModulesKeyFactory, type ModulesListKey, type ModulesListKeyOptions, type ModulesView, } from './keys';
2
+ export { invalidateCourse, invalidateModuleItems, invalidateModuleList } from './invalidate';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,GACjB,MAAM,QAAQ,CAAA;AACf,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,52 @@
1
+ import { QueryClient } from '@tanstack/react-query';
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
+ 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
+ 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
+ export declare function invalidateCourse(queryClient: QueryClient, courseId: string): Promise<void>;
52
+ //# sourceMappingURL=invalidate.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,86 @@
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
+ */
26
+ 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
+ export interface ModulesListKeyOptions {
33
+ view?: ModulesView;
34
+ pageSize?: number;
35
+ includeItems?: boolean;
36
+ }
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
+ export interface ModuleItemsKeyOptions {
51
+ view?: ModulesView;
52
+ pageSize?: number;
53
+ }
54
+ export type ModulesAllKey = readonly ['platform-modules'];
55
+ export type ModulesCourseKey = readonly ['platform-modules', string];
56
+ export type ModulesListKey = readonly ['platform-modules', string, 'list', ModulesListKeyOptions];
57
+ export type ModuleItemsKey = readonly [
58
+ 'platform-modules',
59
+ string,
60
+ 'module',
61
+ string,
62
+ 'items',
63
+ ModuleItemsKeyOptions
64
+ ];
65
+ export interface ModulesKeyFactory {
66
+ readonly all: ModulesAllKey;
67
+ course(courseId: string): ModulesCourseKey;
68
+ list(courseId: string, opts?: ModulesListKeyOptions): ModulesListKey;
69
+ items(courseId: string, moduleId: string, opts?: ModuleItemsKeyOptions): ModuleItemsKey;
70
+ }
71
+ 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
+ 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 cancelling in flight requests on collapse.
84
+ */
85
+ export declare function moduleItemsPrefix(courseId: string, moduleId: string): readonly ['platform-modules', string, 'module', string, 'items'];
86
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,19 @@
1
+ import { ReactElement } from 'react';
2
+ import { ModulesFilterId } from './labels';
3
+ /**
4
+ * The filter row.
5
+ *
6
+ * InstUI has no chip/tag component available in this repo, so these are small
7
+ * pill-shaped Buttons carrying `aria-pressed` — a toggle group in behaviour,
8
+ * radio-ish in appearance, which is what the mock draws. Selection is single,
9
+ * not multi: the mock shows exactly one chip active.
10
+ */
11
+ export interface FilterChipsProps {
12
+ value: ModulesFilterId;
13
+ onChange: (filter: ModulesFilterId) => void;
14
+ /** Per-chip match counts, rendered when supplied. Cheap: module-level only. */
15
+ counts?: Partial<Record<ModulesFilterId, number>>;
16
+ }
17
+ /** @see FilterChipsProps */
18
+ export declare function FilterChips({ value, onChange, counts }: FilterChipsProps): ReactElement;
19
+ //# sourceMappingURL=FilterChips.d.ts.map
@@ -0,0 +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,EAAsB,MAAM,UAAU,CAAA;AAEpG;;;;;;;GAOG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,eAAe,CAAA;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAA;CAClD;AAED,4BAA4B;AAC5B,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,GAAG,YAAY,CAoDvF"}
@@ -0,0 +1,32 @@
1
+ import { NamedExoticComponent } from 'react';
2
+ import { ModuleItemsState } from '../hooks';
3
+ import { Module } from '../types';
4
+ export interface ModuleCardProps {
5
+ module: Module;
6
+ expanded: boolean;
7
+ onToggle: (moduleId: string) => void;
8
+ /** The hook's per-module item state. Only read when `expanded`. */
9
+ itemsState: ModuleItemsState;
10
+ today: Date;
11
+ /** Show the `inline` vs `onDemand` provenance line. Dev affordance, off by default. */
12
+ showDiagnostics?: boolean;
13
+ /**
14
+ * Heading level for the module name. Defaults to `'h3'`, one below
15
+ * `ModulesList`'s default of `h2`.
16
+ *
17
+ * The name was bold `Text`, which meant a 120-module course offered a screen
18
+ * reader no heading list to navigate by — the only heading in the tree was the
19
+ * page title.
20
+ */
21
+ headingLevel?: 'h2' | 'h3' | 'h4';
22
+ }
23
+ /**
24
+ * Memoised, which is only useful because its props are now stable.
25
+ *
26
+ * `itemsState` comes from `useModulesPageData`'s per-module cache and `onToggle`
27
+ * from a ref-backed `useCallback`, so a card re-renders when *its own* module
28
+ * changes and not when a sibling's items arrive. Both of those had to be fixed
29
+ * first — a `memo` over freshly-built props is pure overhead.
30
+ */
31
+ export declare const ModuleCard: NamedExoticComponent<ModuleCardProps>;
32
+ //# sourceMappingURL=ModuleCard.d.ts.map
@@ -0,0 +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,MAAM,UAAU,CAAA;AA+BtC,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;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAClC;AAgVD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,oBAAoB,CAAC,eAAe,CAAwB,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { NamedExoticComponent } from 'react';
2
+ import { ModuleItem } from '../types';
3
+ export interface ModuleItemRowProps {
4
+ item: ModuleItem;
5
+ /** Injected so relative dates ("yesterday") are deterministic. */
6
+ today: Date;
7
+ }
8
+ /**
9
+ * Memoised because it is the hottest component in the package.
10
+ *
11
+ * `item` is a stable reference out of the query cache and `today` is pinned per
12
+ * mount, so the default shallow comparison is enough. It is what stops one
13
+ * module's observer publishing from re-rendering every other module's rows —
14
+ * ~3000 of them on a 30-module course with everything expanded.
15
+ */
16
+ export declare const ModuleItemRow: NamedExoticComponent<ModuleItemRowProps>;
17
+ //# sourceMappingURL=ModuleItemRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModuleItemRow.d.ts","sourceRoot":"","sources":["../../src/components/ModuleItemRow.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAQ,KAAK,oBAAoB,EAAqB,MAAM,OAAO,CAAA;AAC1E,OAAO,EAA+B,KAAK,UAAU,EAAuB,MAAM,UAAU,CAAA;AA6D5F,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAA;IAChB,kEAAkE;IAClE,KAAK,EAAE,IAAI,CAAA;CACZ;AA0GD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,oBAAoB,CAAC,kBAAkB,CAA2B,CAAA"}
@@ -0,0 +1,69 @@
1
+ import { ReactElement } from 'react';
2
+ import { UseModulesPageDataResult } from '../hooks';
3
+ import { ModulesFilterId } from './labels';
4
+ /**
5
+ * The student modules page, approximating the Modules Foundation design.
6
+ *
7
+ * What it is: course header, a module-name search, an expand/collapse menu, the
8
+ * module-level filter row, and a stack of module cards.
9
+ *
10
+ * What it deliberately is not:
11
+ *
12
+ * - **No progress strip.** The `01 02 03 …` paginated bar under the filters in
13
+ * the mock is out of scope; the user deferred it.
14
+ * - **No item-level search.** See `moduleMatchesSearch`: in `onDemand` mode the
15
+ * items of collapsed modules are not loaded, so searching them would silently
16
+ * under-report. The input filters module *names* and says so on its label.
17
+ * Everything renderable comes from `useModulesPageData`; this component owns
18
+ * only view state (search text, active filter), never data. Expanding a module
19
+ * still invalidates nothing.
20
+ */
21
+ /** Requests served by the adapter so far, split by operation. */
22
+ export interface ModulesRequestCounts {
23
+ listModules: number;
24
+ listModuleItems: number;
25
+ }
26
+ export interface ModulesListProps {
27
+ /** The whole result of `useModulesPageData`, passed straight through. */
28
+ data: UseModulesPageDataResult;
29
+ /** Course title in the header. */
30
+ courseName?: string;
31
+ /** The crumb above the title, e.g. "Math". Purely decorative. */
32
+ courseContext?: string;
33
+ /**
34
+ * "Now", injected so relative dates and the "Due this week" filter are
35
+ * deterministic in stories and tests. The fixtures use a fixed 2025-01-06
36
+ * epoch, so a story that wants sensible dates should pin this.
37
+ */
38
+ today?: Date;
39
+ /**
40
+ * Live counts from `createInMemoryAdapter`'s `onRequest`. When supplied, a
41
+ * dev banner renders them. It is the most useful thing on screen for
42
+ * reviewing this MVP — "does this scale" as a number rather than a devtools
43
+ * exercise — and a host with a real adapter simply omits the prop.
44
+ */
45
+ requestCounts?: ModulesRequestCounts;
46
+ /** Also show items-mode and per-module item provenance. Defaults to `requestCounts !== undefined`. */
47
+ showDiagnostics?: boolean;
48
+ /** Which chip starts active. Uncontrolled after mount — the chips own it. */
49
+ initialFilter?: ModulesFilterId;
50
+ /**
51
+ * Element for the outer container. Defaults to `'div'`.
52
+ *
53
+ * Deliberately not `'main'` by default: Canvas's own layout already ships
54
+ * `role="main"` (`application.html.erb:267`), so a Canvas host rendering this
55
+ * would get two main landmarks. A host that owns its whole page can opt in with
56
+ * `as="main"`.
57
+ */
58
+ as?: 'div' | 'main' | 'section';
59
+ /**
60
+ * Heading level for the course title. Defaults to `'h2'`.
61
+ *
62
+ * Also not `h1` by default, and for the same reason: a host page usually already
63
+ * has one. `ModuleCard` renders one level below this.
64
+ */
65
+ headingLevel?: 'h1' | 'h2' | 'h3';
66
+ }
67
+ /** @see ModulesListProps */
68
+ export declare function ModulesList({ data, courseName, courseContext, today, requestCounts, showDiagnostics, initialFilter, as, headingLevel, }: ModulesListProps): ReactElement;
69
+ //# sourceMappingURL=ModulesList.d.ts.map
@@ -0,0 +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;AAIxD,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,UAAU,CAAA;AAGjB;;;;;;;;;;;;;;;;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;;;;;;;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,EAAU,EACV,YAAmB,GACpB,EAAE,gBAAgB,GAAG,YAAY,CAqSjC"}
@@ -0,0 +1,138 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { ReactElement } from 'react';
3
+ import { InMemoryFixture, ItemsMode } from '../index';
4
+ import { ModulesFilterId } from './labels';
5
+ /**
6
+ * The Modules Foundation design over the real data layer.
7
+ *
8
+ * Harness: `createInMemoryAdapter` over a fixture, a
9
+ * `QueryClient` whose identity survives a re-render, and the adapter's
10
+ * `onRequest` feeding a live counter. Nothing is mocked — the adapter is a real
11
+ * implementation of the contract, including declining to inline a module above
12
+ * `INLINE_ITEM_LIMIT` as Canvas REST does — so the request count in the dev
13
+ * banner is the genuine cost of what is on screen.
14
+ *
15
+ * The counter is the most useful thing here. Switch `itemsMode` to `onDemand`,
16
+ * hit "Expand all" on the large course, and watch what the design costs.
17
+ */
18
+ declare const FIXTURES: Record<string, () => InMemoryFixture>;
19
+ /**
20
+ * The four scales, as either audience.
21
+ *
22
+ * The teacher shape comes from `buildFixture`'s own `view` option rather than
23
+ * being derived here by stripping fields. That matters for what the story shows:
24
+ * a teacher sees unpublished modules and items — which is the whole reason the
25
+ * teacher view exists — and no progress, because Canvas resolves `progression`
26
+ * to null for a viewer with no progression record. Deriving it by deletion used
27
+ * to produce the one combination Canvas cannot: an unpublished module with a
28
+ * progress bar beside it.
29
+ */
30
+ type ProgressMode = 'student' | 'teacher';
31
+ interface ModulesListStoryProps {
32
+ fixture: keyof typeof FIXTURES;
33
+ itemsMode: ItemsMode;
34
+ progressMode: ProgressMode;
35
+ initialFilter: ModulesFilterId;
36
+ /** Modules per `listModules` request. Canvas caps this at 100. */
37
+ pageSize: number;
38
+ /** Items per `listModuleItems` request. Canvas caps this at 100. */
39
+ itemPageSize: number;
40
+ itemConcurrency: number;
41
+ /** Expand these module ids on mount, so a story shows item rows without a click. */
42
+ initialExpandedModuleIds: string[];
43
+ /**
44
+ * Which i18n bridge to render through.
45
+ *
46
+ * `none` is a host that has not wired i18n: the package interpolates the
47
+ * English source keys itself. `pseudo` is the standard pre-translation audit —
48
+ * every string is bracketed and padded, so an untranslated literal is visible
49
+ * on sight and a layout that cannot survive a longer language shows up before
50
+ * a translator is ever involved.
51
+ */
52
+ i18n: 'none' | 'pseudo';
53
+ }
54
+ /**
55
+ * Its own `QueryClient`, deliberately — the global preview decorator builds one
56
+ * in its render body, and these stories are about cache behaviour.
57
+ */
58
+ declare function ModulesListHarness(props: ModulesListStoryProps): ReactElement;
59
+ declare const meta: Meta<typeof ModulesListHarness>;
60
+ export default meta;
61
+ type Story = StoryObj<typeof meta>;
62
+ /**
63
+ * The design, near enough: 5 modules × 8 items with module 1 open.
64
+ *
65
+ * `auto` resolves to `inline`, so opening any other module costs zero extra
66
+ * requests — the counter in the dev banner stays put as you click around.
67
+ */
68
+ export declare const Default: Story;
69
+ /**
70
+ * 120 modules × 25 items. `auto` resolves to `onDemand` and first paint is a
71
+ * single request no matter how big the course is.
72
+ *
73
+ * The point of this story is the button at the bottom: `hasNextPage` is true,
74
+ * it says so, and the remaining 20 modules are one click away rather than gone.
75
+ * Canvas Career shows 100 and says nothing.
76
+ */
77
+ export declare const LargeCourse: Story;
78
+ /**
79
+ * 3 modules, the middle one 250 items, pinned to `inline` — the Canvas fallback
80
+ * in miniature. Modules 1 and 3 come back with items attached; module 2 is over
81
+ * `INLINE_ITEM_LIMIT` so the server omits its items and that *one module* falls
82
+ * back to a per-module fetch, then pages 100 at a time. "Load more items" stays
83
+ * on the card until all 250 are loaded.
84
+ */
85
+ export declare const FatModuleInlineFallback: Story;
86
+ /** No modules. The empty state. */
87
+ export declare const EmptyCourse: Story;
88
+ /**
89
+ * Teacher / anonymous: `progress` is undefined on every module and `status` on
90
+ * every item.
91
+ *
92
+ * The card renders no progress bar and no status pill, and nowhere does it say
93
+ * "0%" — "we did not fetch progress" and "the student has done none of it" are
94
+ * different facts. Every filter chip except "All" correctly matches nothing,
95
+ * because a module-level filter has nothing to read.
96
+ */
97
+ export declare const TeacherViewNoProgress: Story;
98
+ /** `progress.status === 'in_progress'`. */
99
+ export declare const FilterInProgress: Story;
100
+ /** `progress.status` is `all_submitted` or `all_graded`. */
101
+ export declare const FilterComplete: Story;
102
+ /**
103
+ * `progress.latestDueAt` within 7 days of the injected "today" (2025-01-08).
104
+ *
105
+ * On the small fixture rather than the large one, deliberately: `latestDueAt` is
106
+ * the *latest* due date in a module, and a 25-item module's latest date is
107
+ * always months out, so the large course legitimately has nothing in a 7-day
108
+ * window. A story that only ever showed the empty state would prove nothing
109
+ * about the chip.
110
+ */
111
+ export declare const FilterDueThisWeek: Story;
112
+ /** `progress.missingCount > 0`. */
113
+ export declare const FilterMissing: Story;
114
+ /**
115
+ * Every module status and every item status on one screen.
116
+ *
117
+ * `courseSmall` is sized to `MODULE_STATUS_CYCLE.length` precisely so it can
118
+ * show all six `ModuleStatus` values; expanding every module then shows every
119
+ * `ModuleItemStatus` too, so a reviewer can check the whole pill mapping in one
120
+ * screenshot. Note the mock's single green "All items complete" pill is split
121
+ * here into "All items complete" (`all_submitted`) and "All items graded"
122
+ * (`all_graded`), because the domain distinguishes them and the view should not
123
+ * quietly collapse the two.
124
+ */
125
+ export declare const StatusCoverage: Story;
126
+ /**
127
+ * Every user-visible string, bracketed and padded.
128
+ *
129
+ * This is the story that makes the i18n seam reviewable rather than asserted: a
130
+ * string rendering *without* `⟦ ⟧` never went through `t`. Padding to ~135%
131
+ * approximates German, so it also shows whether the card header, the chip row
132
+ * and the "Load more" button survive a longer language.
133
+ *
134
+ * The one deliberate exception is the dev diagnostics banner, which is developer
135
+ * text and is documented as untranslated in `ModuleCard`.
136
+ */
137
+ export declare const PseudoLocalised: Story;
138
+ //# sourceMappingURL=ModulesList.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModulesList.stories.d.ts","sourceRoot":"","sources":["../../src/components/ModulesList.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEtD,OAAO,EAAE,KAAK,YAAY,EAAqB,MAAM,OAAO,CAAA;AAC5D,OAAO,EAQL,KAAK,eAAe,EACpB,KAAK,SAAS,EAGf,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAG/C;;;;;;;;;;;;GAYG;AAEH,QAAA,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,eAAe,CAKnD,CAAA;AAmBD;;;;;;;;;;GAUG;AAIH,KAAK,YAAY,GAAG,SAAS,GAAG,SAAS,CAAA;AAEzC,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,OAAO,QAAQ,CAAA;IAC9B,SAAS,EAAE,SAAS,CAAA;IACpB,YAAY,EAAE,YAAY,CAAA;IAC1B,aAAa,EAAE,eAAe,CAAA;IAC9B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,oFAAoF;IACpF,wBAAwB,EAAE,MAAM,EAAE,CAAA;IAClC;;;;;;;;OAQG;IACH,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAA;CACxB;AAqED;;;GAGG;AACH,iBAAS,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,GAAG,YAAY,CAetE;AAgBD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,kBAAkB,CA6BzC,CAAA;AAED,eAAe,IAAI,CAAA;AACnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAA;AAElC;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,KAErB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAE,KAErC,CAAA;AAED,mCAAmC;AACnC,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAEnC,CAAA;AASD,2CAA2C;AAC3C,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAA;AAED,4DAA4D;AAC5D,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAA;AAED,mCAAmC;AACnC,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,EAAE,KAM5B,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,KAO7B,CAAA"}