feat: Enable capa problem editor for components in libraries (#1290)
* feat: enable the problem editor for library components * fix: don't try to load "advanced settings" when editing problem in library * fix: don't fetch images when editing problem in library * docs: add a note about plans for the editor modal * fix: choosing a problem type then cancelling resulted in an error * chore: remove unused mockApi, clean up problematic 'module' self import * test: update workflow test to test problem editor * feat: show capa content summary on cards in library search results * docs: fix comment typos found in code review * refactor: add 'key-utils' to consolidate opaque key logic
This commit is contained in:
78
src/generic/key-utils.test.ts
Normal file
78
src/generic/key-utils.test.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
getBlockType,
|
||||
getLibraryId,
|
||||
isLibraryKey,
|
||||
isLibraryV1Key,
|
||||
} from './key-utils';
|
||||
|
||||
describe('component utils', () => {
|
||||
describe('getBlockType', () => {
|
||||
for (const [input, expected] of [
|
||||
['lb:org:lib:html:id', 'html'],
|
||||
['lb:OpenCraftX:ALPHA:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd', 'html'],
|
||||
['lb:Axim:beta:problem:571fe018-f3ce-45c9-8f53-5dafcb422fdd', 'problem'],
|
||||
]) {
|
||||
it(`returns '${expected}' for usage key '${input}'`, () => {
|
||||
expect(getBlockType(input)).toStrictEqual(expected);
|
||||
});
|
||||
}
|
||||
|
||||
for (const input of ['', undefined, null, 'not a key', 'lb:foo']) {
|
||||
it(`throws an exception for usage key '${input}'`, () => {
|
||||
expect(() => getBlockType(input as any)).toThrow(`Invalid usageKey: ${input}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('getLibraryId', () => {
|
||||
for (const [input, expected] of [
|
||||
['lb:org:lib:html:id', 'lib:org:lib'],
|
||||
['lb:OpenCraftX:ALPHA:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd', 'lib:OpenCraftX:ALPHA'],
|
||||
['lb:Axim:beta:problem:571fe018-f3ce-45c9-8f53-5dafcb422fdd', 'lib:Axim:beta'],
|
||||
]) {
|
||||
it(`returns '${expected}' for usage key '${input}'`, () => {
|
||||
expect(getLibraryId(input)).toStrictEqual(expected);
|
||||
});
|
||||
}
|
||||
|
||||
for (const input of ['', undefined, null, 'not a key', 'lb:foo']) {
|
||||
it(`throws an exception for usage key '${input}'`, () => {
|
||||
expect(() => getLibraryId(input as any)).toThrow(`Invalid usageKey: ${input}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('isLibraryKey', () => {
|
||||
for (const [input, expected] of [
|
||||
['lib:org:lib', true],
|
||||
['lib:OpenCraftX:ALPHA', true],
|
||||
['lb:org:lib:html:id', false],
|
||||
['lb:OpenCraftX:ALPHA:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd', false],
|
||||
['library-v1:AximX+L1', false],
|
||||
['course-v1:AximX+TS100+23', false],
|
||||
['', false],
|
||||
[undefined, false],
|
||||
] as const) {
|
||||
it(`returns '${expected}' for learning context key '${input}'`, () => {
|
||||
expect(isLibraryKey(input)).toStrictEqual(expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('isLibraryV1Key', () => {
|
||||
for (const [input, expected] of [
|
||||
['library-v1:AximX+L1', true],
|
||||
['lib:org:lib', false],
|
||||
['lib:OpenCraftX:ALPHA', false],
|
||||
['lb:org:lib:html:id', false],
|
||||
['lb:OpenCraftX:ALPHA:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd', false],
|
||||
['course-v1:AximX+TS100+23', false],
|
||||
['', false],
|
||||
[undefined, false],
|
||||
] as const) {
|
||||
it(`returns '${expected}' for learning context key '${input}'`, () => {
|
||||
expect(isLibraryV1Key(input)).toStrictEqual(expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
40
src/generic/key-utils.ts
Normal file
40
src/generic/key-utils.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Given a usage key like `lb:org:lib:html:id`, get the type (e.g. `html`)
|
||||
* @param usageKey e.g. `lb:org:lib:html:id`
|
||||
* @returns The block type as a string
|
||||
*/
|
||||
export function getBlockType(usageKey: string): string {
|
||||
if (usageKey && usageKey.startsWith('lb:')) {
|
||||
const blockType = usageKey.split(':')[3];
|
||||
if (blockType) {
|
||||
return blockType;
|
||||
}
|
||||
}
|
||||
throw new Error(`Invalid usageKey: ${usageKey}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a usage key like `lb:org:lib:html:id`, get the library key
|
||||
* @param usageKey e.g. `lb:org:lib:html:id`
|
||||
* @returns The library key, e.g. `lib:org:lib`
|
||||
*/
|
||||
export function getLibraryId(usageKey: string): string {
|
||||
if (usageKey && usageKey.startsWith('lb:')) {
|
||||
const org = usageKey.split(':')[1];
|
||||
const lib = usageKey.split(':')[2];
|
||||
if (org && lib) {
|
||||
return `lib:${org}:${lib}`;
|
||||
}
|
||||
}
|
||||
throw new Error(`Invalid usageKey: ${usageKey}`);
|
||||
}
|
||||
|
||||
/** Check if this is a V2 library key. */
|
||||
export function isLibraryKey(learningContextKey: string | undefined): learningContextKey is string {
|
||||
return typeof learningContextKey === 'string' && learningContextKey.startsWith('lib:');
|
||||
}
|
||||
|
||||
/** Check if this is a V1 library key. */
|
||||
export function isLibraryV1Key(learningContextKey: string | undefined): learningContextKey is string {
|
||||
return typeof learningContextKey === 'string' && learningContextKey.startsWith('library-v1:');
|
||||
}
|
||||
Reference in New Issue
Block a user