Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Coach accessibility improvements #10212

Merged
merged 14 commits into from
Mar 15, 2023
Merged
2 changes: 1 addition & 1 deletion kolibri/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"js-cookie": "^3.0.1",
"knuth-shuffle-seeded": "^1.0.6",
"kolibri-constants": "0.1.42",
"kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#4b8f272d37446ff0e65c4048c0339e279976103b",
"kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#a671f6f898eef07a3167dc74648ef329f8e3af44",
"lockr": "0.8.5",
"lodash": "^4.17.21",
"loglevel": "^1.8.1",
Expand Down
83 changes: 83 additions & 0 deletions kolibri/plugins/coach/assets/src/composables/useCoachTabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Provides a place to store last tabs interaction
* and helper methods to save and retrieve it.
* This information is typically used to find out
* if a tab was clicked recently so that we can decide
* on whether we should programatically re-focus it after
* navigating to a page.
*
* Motivation: Our routing architecture where parts of a page
* containing tabs get reloaded on route change would result
* in losing focus from the active tab. Therefore, we need
* to programatically re-focus after components are mounted again.
* However, for that we need to be able to estimate when navigation
* occured as a result of user interaction with tabs because in other
* cases focus shouldn't be manipulated (e.g. when visiting a page for
* the first time before clicking on tabs) .
*
* Usage: When tabs are clicked, save that interaction by calling `saveTabsClick`.
* Then when you need find out if tabs were clicked recently,
* call `wereTabsClickedRecently`.
*/
import { reactive } from 'kolibri.lib.vueCompositionApi';

// tabs interaction is considered to be recent
// when it's not older than ...
const RECENT_INTERACTION_LIMIT_IN_MS = 3000;
const TabsEvents = {
CLICK: 'click',
};

/**
* Needs to be placed outside of the `useCoachTabs`
* function so that it behaves like global state.
* Why: Due to our routing structure, tab components are re-mounted
* after a tab is clicked. Without placing this state outside, it would
* be lost as components that use this composable are initialized again.
*/
const lastTabsInteraction = reactive({
tabsInterfaceId: '',
event: '',
timestamp: '',
});

export function useCoachTabs() {
/**
* Stores an interaction with tabs
*
* @param {String} tabsInterfaceId ID of a tabbed interface interacted with
* @param {String} event An event kind. Available kinds: 'click'
*/
function saveTabsInteraction(tabsInterfaceId, event) {
lastTabsInteraction.tabsInterfaceId = tabsInterfaceId;
lastTabsInteraction.event = event;
lastTabsInteraction.timestamp = Date.now();
}

/**
* Stores a click interaction with tabs
*
* @param {String} tabsInterfaceId ID of a tabbed interface interacted with
*/
function saveTabsClick(tabsInterfaceId) {
saveTabsInteraction(tabsInterfaceId, TabsEvents.CLICK);
}

/**
* @param {String} tabsInterfaceId ID of a tabbed interface
* @returns {Boolean} `true` when tabs with the provided ID were
* clicked recently
*/
function wereTabsClickedRecently(tabsInterfaceId) {
if (lastTabsInteraction.tabsInterfaceId !== tabsInterfaceId) {
return false;
}
const diff = Math.abs(Date.now() - lastTabsInteraction.timestamp);
return diff < RECENT_INTERACTION_LIMIT_IN_MS;
}

return {
saveTabsClick,
wereTabsClickedRecently,
};
}
21 changes: 21 additions & 0 deletions kolibri/plugins/coach/assets/src/constants/tabsConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const REPORTS_TABS_ID = 'coachReports';
export const ReportsTabs = {
LESSONS: 'tabLessons',
QUIZZES: 'tabQuizzes',
GROUPS: 'tabGroups',
LEARNERS: 'tabLearners',
};

export const PLAN_TABS_ID = 'coachPlan';
export const PlanTabs = {
LESSONS: 'tabLessons',
QUIZZES: 'tabQuizzes',
GROUPS: 'tabGroups',
};

export const REPORTS_GROUP_TABS_ID = 'coachPlanGroup';
export const ReportsGroupTabs = {
REPORTS: 'tabReports',
MEMBERS: 'tabMembers',
ACTIVITY: 'tabActivity',
};
Loading