Skip to content

Commit 5a61aa2

Browse files
chore: ADRs
1 parent d3600d4 commit 5a61aa2

7 files changed

+253
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
1. Run Environment Decisions
2+
-----------------------------
3+
4+
Status
5+
------
6+
Accepted
7+
8+
Context
9+
--------
10+
We would like a historical record of decisions about the run environment for the app as it evolves over time.
11+
12+
Decision
13+
---------
14+
This app is designed to be run within the context of the Learning MFE (@edx/frontend-app-learning) as an embedded view for Open Response Assessment (ORA) xblocks. It consists of a base (`xblock`) view rendered in an iframe **within the Learning MFE’s unit display iframe**
15+
16+
This view (the **xblock** view) provides high-level configuration and progress information about the ORA, and allows the user to jump to the current active step (or certain previous steps that may be re-visit-able) in a “distraction-free” modal experience.
17+
18+
The modal is launched **by the Learning MFE**, triggered by a message sent from the **xblock** view (using JS `postMessage` interface to communicate across iframe boundaries).
19+
20+
The modal view allows the learner to work their way through the assessment process for a submission and review their grades when available.
21+
22+
Consequences
23+
------------
24+
Environments that run this MFE **outside of the learning MFE** will not be able to approprately render this navigation scheme without creating some kind of listener for the `plugin.modal` message sent over `postMessage` to open the "modal" views from the **xblock** view.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2. App-Specific Terms
2+
---------------------
3+
4+
Status
5+
------
6+
Accepted
7+
8+
Context
9+
--------
10+
We would like to keep a record of app-specific terms that may otherwise be overloaded in technical contexts.
11+
12+
Decision
13+
--------
14+
In the context of this app, we will use the following terms in the associated ways:
15+
16+
**Assessment** refers to the interactive form for filling out an assessment. These can be either editable or read-only.
17+
* Assessment views will load the top-level `Assessment` component from `src/components/Assessment` and it will determine the appropriate
18+
* The `Graded` view specifically renders a set of read-only assessments directly from `src/components/Assessment/ReadonlyAssessment`.
19+
20+
**Response** refers to a learner/team’s response to the ORA prompt (that which is graded in assessments).
21+
* In Assessment views, the loaded `response` is the one being graded.
22+
23+
**Rubric** refers to the definition data for assessments. this includes all of the metadata for criteria and options.
24+
* the `Rubric` component from `src/components/Rubric` is used for the Submission view, where we want to display the assessment metadata without providing interactive fields for filling out an actual assessment.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
3. Tech Stack Decisions
2+
------------------------
3+
4+
Status
5+
------
6+
Accepted
7+
8+
Context
9+
-------
10+
We would like to keep a historical record of decisions about the tech stack for this app as it evolves over time.
11+
12+
Decision
13+
--------
14+
We will use the following technologies in this app:
15+
**redux**
16+
We use redux in this app to track top/app-level data. This includes submitted assessments, submission status, validation status, and active form fields. Our redux implementation has a single `app` reducer, with a small set of actions and selectors, which are loaded through hooks before access in components. These hooks are then forwarded through app-level hooks under `src/hooks` in a hook file based on the the areas they touch.
17+
18+
**@reduxjs-toolkit**
19+
We use a simple utility library wrapped around the basic redux functionality to provide cleaner action definition and memoized selector creation, following with edX frontend engineering practices.
20+
21+
**@tanstack/react-query**
22+
We use Tanstack’s react-query library in this app to manage and track upstream api data. This allows us to manage static upstream data in a distinct data environment from the app-level data, and provides built-in memoization and tracking of network event status.
23+
24+
**jest-when**
25+
We use jest-when in this app to manage mock behavior in tests, especially in Typescript tests, where inspecting mock method usage is diffucult otherwise.
26+
27+
**@edx/react-unit-test-utils**
28+
We use a small testing utils library to provide:
29+
* mocks and mock utils for messaging and various hooks/libraries
30+
* `StrictDict` utility for keystores that complain when called with invalid keys (reduces/simplifies bugs)
31+
* Snapshot/validation library for components, similar to enzyme, but based on still-supported technology.
32+
33+
**@edx/frontend-platform/i18n**
34+
We use the edX `frontend-platform`'s internationalization (i18n) library to manage message translation. All user-facing strings in the app should be wrapped in `formatMessage` calls, provided by the `useIntl` method in the above library (forwarded from `react-intl`).
35+
36+
**@edx/frontend-platform/auth**
37+
We use the edX `frontend-platform`'s authentication library to manage authenticated communication with edX backend services.
38+
39+
**@edx/frontend-platform/react**
40+
We use the edX `frontend-platform`'s `React` library to provide app-level setup code, wrapping the app in top-level componentry that allows the app to respond to the state of its connection the the devstack.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
4. Architecture Decisions
2+
-------------------------
3+
4+
Status
5+
------
6+
Accepted
7+
8+
Context
9+
-------
10+
We would like to keep a historical record on the architectural decisions we make with this app as
11+
it evolves over time.
12+
13+
Decision
14+
--------
15+
# `constants/` directory
16+
* `src/constants`
17+
* `stepNames` is a keystore of names for discrete steps within the ORA lifecycle
18+
* `stepRoutes` and `routeSteps` are objects that map `stepName`s to `route` strings and back
19+
* these route strings are the base of the uri for the different views
20+
* `stepStates` is a keystore of available states for a step to be in.
21+
* This includes 2 ui-only states: `submitted` and `trainingValidation` which are used locally on consumption to override the value passed from upstream API based on local state.
22+
* `MutationStatus` is a keystore of available values for tanstack mutation statuses
23+
* `feedbackRequirement` is a keystore of available values for feedback requirement fields.
24+
* `closedReasons` is keystore of available values for `closed_reason` data for an ORA step
25+
* `queryKeys` is a keyStore that provides root query Keys for tanstack fetch queries.
26+
* src/constants/mockData
27+
28+
# `views/` and `components/` directories
29+
* The `views` folder specifically holds top-level views for the app. The goal of this folder is to provide a location where engineers can look for the top-level access-points of the app (minus the actual arch/data setup from the `App.jsx` component.
30+
* Components that are ONLY used within the confines of a single view may be defined as children of that view’s code directory
31+
* The components directory holds all re-useable components that are accessed/used across views.
32+
* These components may have children
33+
* (`Assessment/ReadonlyAssessment/AssessmentCriterion.jsx`, etc)
34+
* They may also be interdependent
35+
* `Rubric` and `Assessment` both use/leverage the `CriterionContainer` component for different uses.
36+
37+
# `data/` directory
38+
This directory houses the various data models for the app.
39+
40+
`data/redux` houses the redux app reducer and selectors, used to track assessment, form, and submissions tate.
41+
42+
`data/services/lms` houses the LMS service code, which has its own section in this doc.
43+
44+
# `hooks/` directory
45+
Provides a top-level set of hooks for non-component-specific behaviors and data access.
46+
47+
## `assessment` hooks
48+
This is a set of hooks specifically targeted towards data and behavior necessary for the running and displaying of assessment steps in the UI.
49+
50+
It provides hooks that communicate between the redux data storing assessment form data and just-submitted assessments and the lms service shooks to manage tasks like:
51+
* Providing arguments for form field components
52+
* Providing `isInvalid` check for the assessment
53+
* Submitting assessments
54+
55+
## `actions` hooks
56+
Provides the props for paragon `Button` components for “simple” (instant) actions and props for `StatefulButton` components for asynchronous tasks.
57+
58+
Provides local message configuration and internationalization.
59+
60+
These hooks are used in various components and are written to try and provide their relevant functionality dependent on the current app state.
61+
62+
Actions (buttons) include: Finish Later, Exit, Start next step, Load next response in current step.
63+
64+
## `app` hooks
65+
This module forwards app-level selectors and action hooks from lms service and redux for app-level data.
66+
67+
This is the core place for components that can be run from anywhere in the app to be able to draw app state.
68+
69+
## `modal` hooks
70+
This module provides a pair of hooks to open the modal (from xblock view) to a given view, or to close said modal (from within the modal view).
71+
72+
## `routing` hooks
73+
This module provides hooks to fetch the active view and associated step

docs/decisions/0005-data-conceits.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
5. Data Conceits
2+
------------------
3+
4+
Status
5+
-------
6+
Accepted
7+
8+
Context
9+
-------
10+
We would like to keep a historical record of data conceits for the app as it evolves over time.
11+
12+
Decision
13+
--------
14+
# Active Step
15+
The Active Step is the current workflow step of the ORA, independent of the UI.
16+
* Values from `stepNames` in `src/constants`
17+
* access from hooks with:
18+
* `useActiveStepName()` from `src/hooks/app` if you only need the name
19+
* `useGlobalState().activeStepName` from `src/hooks/app` if you need other global state info as well
20+
21+
# Active View Step
22+
The Active View Step is the step that is being currently viewed in the UI (or `xblock` for the base XBlock view)
23+
* values from `stepNames` in `src/constants`
24+
* Access from hooks with `useViewStep()` from `src/hooks/routing`
25+
26+
# “Has submitted”
27+
`hasSubmitted` (in redux app state) refers explicitly to whether the associate form/action for a given step has been submitted.
28+
* For `submission` step, this means that the response has just been submitted.
29+
* For assessment steps, this means that a given assessment has been submitted
30+
* This does not necessarily mean that the full assessment step is done, in the case of peer and self assessment steps.
31+
* Access from hook with `useHasSubmitted()` from `src/hooks/app` or `src/hooks/assessment`

docs/decisions/0006-lms-service.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
6. LMS Service decisions
2+
------------------------
3+
4+
Status
5+
------
6+
Accepted
7+
8+
Context
9+
-------
10+
We would like to keep a historical record of our LMS service code decisions for the app as it
11+
evolves over time.
12+
13+
Decision
14+
---------
15+
The data for this app is coming from the edX Learning Management System (LMS). We have defined a fine-tuned Backend for the app that provides a small set of API endpoints to load the configuration information for an ORA, load the learner data/progress, and all of the actions a learner would need to take upon their response or assessments.
16+
17+
File structure:
18+
* `api.js` for isolated api definitions
19+
* `urls.js` for url generators, based on existing url
20+
* `hooks/data.js` for loading data models (`oraConfig` and `pageData`)
21+
* `pageData` is queried optionally with a page argument for modal views not in the `hasSubmitted` state
22+
* `hooks/selectors` is a collection of selector hooks that access the data from the data queries. Some notable top-level examples are:
23+
* `useIsPageDataLoaded` and `useIsOraConfigLoaded` from `src/hooks/app` allow the app to determine if these values are loaded before attempting to render components that require them.
24+
* `useStepState({ step })` draws on the ora config and progress state to return a string state value (from `stepStates`)
25+
* `useGlobalState({ step })` draws together a number of other selectors to provide one top-level state (with optional view-step passed).
26+
* `activeStepName` - current workflow step name (from `stepNames`)
27+
* `activeStepState` - state of current workflow step. (from `stepStates`)
28+
* `cancellationInfo`
29+
* `effectiveGrade` - the assessment(s) of the type specified as the `effective_assessment_type` in the api.
30+
* `null` if has not received final grade
31+
* includes one or more assessment objects for the learner’s response.
32+
* includes a `stepScore` object (points earned / total)
33+
* `hasReceivedFinalGrade` - boolean value for if the learner has received a final grade
34+
* `lastStep` - returns the stepName for the final step in the step order (or submission if no assessment types are provided)
35+
* `stepState` - returns the `stepState` of the **passed** step, which may or may not be equal to the activeStepName
36+
* `hooks/actions` is a collection of action hooks that send commands to the upstream api endpoints, including submitting responses or assessments, saving drafts, and refreshing page data.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
7. App-State Components
2+
------------------------
3+
4+
Status
5+
------
6+
Accepted
7+
8+
Context
9+
--------
10+
We would like to keep a historical record of decisions for top-level componentry patterns for the
11+
app as it evolves over time
12+
13+
Decision
14+
---------
15+
Because of the complexity of trying to share functionality/behaviors across views in ways that did not require an excessive amount of configuration per-use, we opted for this app to write a set of top-level app-state-aware components (`ModalActions`, `StatusAlerts`, and `StepProgressIndicator`) in the `components/` directory. These components interrogate the global state (mostly through use of the `useGlobalState` hook), and provide the appropriate experience based on the current state.
16+
17+
**`ModalActions`** component provides the `ActionRow` for the bottom of the Modal view.
18+
**`StatusAlerts`** component provides the top-level `StatusAlert`s for the top of both the XBlock and Modal views
19+
**`StepProgressIndicator`** component provides the component that lives in the header for Modal components, providing in-step progress as well as next-step actions when available.
20+
21+
This provides the benefit of being able to map/configure **all** of the variants/behaviors of these components in a single location, and provides a clean place to make modifications to that workflow based on global app state, rather than just based on which view is hosting them.
22+
23+
Consequences
24+
-------------
25+
Because of the consolidated nature of this behavior, there will not be nearly as many per-view variants of app-state components, grouped with the views. Thus for people wishing to inspect the behavior of a view, they will need to know the states in which that view can be displayed, and then inspect the top-level componentry areas they want to examine in those states.

0 commit comments

Comments
 (0)