Skip to content

Commit ef68562

Browse files
Merge pull request #4544 from dpalou/MOBILE-4417
MOBILE-4417 format-text: Support alternative content using data attrs
2 parents 6e39053 + 1012870 commit ef68562

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-0
lines changed

src/core/directives/format-text.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,9 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
495495
* @returns Promise resolved when done.
496496
*/
497497
protected treatHTMLElements(div: HTMLElement, site?: CoreSite): ElementController[] {
498+
// Treat alternative content elements first, that way the search of elements won't treat the elements that are removed.
499+
this.treatAlternativeContentElements(div);
500+
498501
const images = Array.from(div.querySelectorAll('img'));
499502
const anchors = Array.from(div.querySelectorAll('a'));
500503
const audios = Array.from(div.querySelectorAll('audio'));
@@ -650,6 +653,40 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
650653
];
651654
}
652655

656+
/**
657+
* Treat elements with data attributes to display an alternative content in the app.
658+
*
659+
* @param div Container where to search the elements.
660+
*/
661+
protected treatAlternativeContentElements(div: HTMLElement): void {
662+
const appAltElements = Array.from(div.querySelectorAll<HTMLElement>(
663+
'*[data-app-alt-url],*[data-app-alt-msg]',
664+
));
665+
666+
appAltElements.forEach((element) => {
667+
const url = element.dataset.appAltUrl;
668+
const message = element.dataset.appAltMsg;
669+
if (!message && !url) {
670+
return;
671+
}
672+
673+
let newContent = message ? `<p>${message}</p>` : '';
674+
if (url) {
675+
// Create a link using the appUrl format to reuse all the logic of appUrl data attributes.
676+
let dataAttributes = `data-app-url="${url}"`;
677+
for (const attr in element.dataset) {
678+
if (!['appUrl', 'appAltUrl', 'appAltMsg'].includes(attr)) {
679+
dataAttributes += ` data-${CoreText.camelCaseToKebabCase(attr)}="${element.dataset[attr]}"`;
680+
}
681+
}
682+
683+
newContent += `<p><a href="${url}" ${dataAttributes}>${url}</a></p>`;
684+
}
685+
686+
element.innerHTML = newContent;
687+
});
688+
}
689+
653690
/**
654691
* Treat elements with an app-url data attribute.
655692
*
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
@app @core @javascript
2+
Feature: Test functionality added by the format-text directive
3+
4+
Background:
5+
Given the following "users" exist:
6+
| username | firstname | lastname | email |
7+
| student1 | Student1 | student1 | student1@example.com |
8+
And the following "courses" exist:
9+
| fullname | shortname |
10+
| Course 1 | C1 |
11+
And the following "course enrolments" exist:
12+
| user | course | role |
13+
| student1 | C1 | student |
14+
15+
Scenario: Displays alternative content in the app
16+
Given the following "activities" exist:
17+
| activity | course | name | intro |
18+
| label | C1 | Label title | <div data-app-alt-url="#wwwroot#/my/courses.php">Content for browser</div> |
19+
| label | C1 | Label 2 title | <div data-app-alt-url="#wwwroot#/?redirect=0" data-app-alt-msg="Open this link" data-open-in="inapp" data-app-url-confirm="Custom confirm.">Content for browser</div> |
20+
Given I entered the course "Course 1" as "student1" in the app
21+
Then I should find "Open this link" in the app
22+
And I should find "$WWWROOT/my/courses.php" in the app
23+
And I should find "$WWWROOT/?redirect=0" in the app
24+
But I should not find "Content for browser" in the app
25+
26+
When I press "$WWWROOT/my/courses.php" in the app
27+
Then I should not find "Custom confirm" in the app
28+
29+
When I press "OK" in the app
30+
Then the app should have opened a browser tab with url "$WWWROOTPATTERN"
31+
32+
When I close the browser tab opened by the app
33+
And I press "$WWWROOT/?redirect=0" in the app
34+
Then I should find "Custom confirm" in the app
35+
36+
When I press "OK" in the app
37+
Then the app should have opened a browser tab with url "$WWWROOTPATTERN"

src/core/singletons/tests/text.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,13 @@ describe('CoreText singleton', () => {
117117
expect(CoreText.countWords('<span>a</span><span>b</span>')).toEqual(1);
118118
});
119119

120+
it('converts camelCase to kebab-case', () => {
121+
expect(CoreText.camelCaseToKebabCase('camelCase')).toEqual('camel-case');
122+
expect(CoreText.camelCaseToKebabCase('camelCaseWithNumbers123')).toEqual('camel-case-with-numbers123');
123+
expect(CoreText.camelCaseToKebabCase('')).toEqual('');
124+
expect(CoreText.camelCaseToKebabCase('snake_case')).toEqual('snake_case');
125+
expect(CoreText.camelCaseToKebabCase('already-kebab-case')).toEqual('already-kebab-case');
126+
expect(CoreText.camelCaseToKebabCase('mix_of-differentTextCases')).toEqual('mix_of-different-text-cases');
127+
});
128+
120129
});

src/core/singletons/text.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ export class CoreText {
110110
return Translate.instant('core.humanreadablesize', { size: bytes, unit: units[keys[pos]] });
111111
}
112112

113+
/**
114+
* Convert a camel case text to kebab case.
115+
* Example: "myVariableName" -> "my-variable-name".
116+
*
117+
* @param text The text to convert.
118+
* @returns The converted text.
119+
*/
120+
static camelCaseToKebabCase(text: string): string {
121+
return text.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase());
122+
}
123+
113124
/**
114125
* Copies a text to clipboard and shows a toast message.
115126
*

0 commit comments

Comments
 (0)