From 512faa945206aaac55ee48771ec1834f66d5a53c Mon Sep 17 00:00:00 2001
From: Zashary Maskus-Lavin <zashary.maskus-lavin@oracle.com>
Date: Thu, 14 Sep 2023 16:35:24 -0700
Subject: [PATCH] Modifying html tests to utilize jsx and fixing tests to
 include all pluginPaths

Signed-off-by: Zashary Maskus-Lavin <zashary.maskus-lavin@oracle.com>
---
 DEVELOPER_GUIDE.md                            |  24 -----
 packages/osd-i18n/GUIDELINE.md                |  11 --
 packages/osd-i18n/README.md                   | 101 +-----------------
 packages/osd-i18n/angular/package.json        |   5 -
 src/dev/i18n/README.md                        |  29 +----
 .../test_plugin_1/test_file_4.html            |   8 --
 .../test_plugin_2/test_file.html              |   1 -
 .../test_plugin_2/test_file.jsx               |   6 ++
 .../test_plugin_4/test_file_4.jsx             |  18 ++++
 .../extract_default_translations.test.js.snap |  48 +++++++++
 .../i18n/extract_default_translations.test.js |  17 +--
 11 files changed, 87 insertions(+), 181 deletions(-)
 delete mode 100644 packages/osd-i18n/angular/package.json
 delete mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html
 delete mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html
 create mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx
 create mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4/test_file_4.jsx

diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md
index b084b32bd1f1..422784751761 100644
--- a/DEVELOPER_GUIDE.md
+++ b/DEVELOPER_GUIDE.md
@@ -925,30 +925,6 @@ license.
 
 The following developer guide rules are specific for working with the React framework.
 
-#### Prefer reactDirective over react-component
-
-When using `ngReact` to embed your react components inside Angular HTML, prefer the
-`reactDirective` service over the `react-component` directive.
-You can read more about these two ngReact methods [here](https://github.com/ngReact/ngReact#features).
-
-Using `react-component` means adding a bunch of components into angular, while `reactDirective` keeps them isolated, and is also a more succinct syntax.
-
-**Good:**
-
-```html
-<hello-component
-  fname="person.fname"
-  lname="person.lname"
-  watch-depth="reference"
-></hello-component>
-```
-
-**Bad:**
-
-```html
-<react-component name="HelloComponent" props="person" watch-depth="reference" />
-```
-
 #### Name action functions and prop functions appropriately
 
 Name action functions in the form of a strong verb and passed properties in the form of on<Subject><Change>. E.g:
diff --git a/packages/osd-i18n/GUIDELINE.md b/packages/osd-i18n/GUIDELINE.md
index ae5b2b5ca298..ac2fb0047c9b 100644
--- a/packages/osd-i18n/GUIDELINE.md
+++ b/packages/osd-i18n/GUIDELINE.md
@@ -92,17 +92,6 @@ The long term plan is to rely on using `FormattedMessage` and `i18n.translate()`
 Currently, we support the following ReactJS `i18n` tools, but they will be removed in future releases:
 - Usage of `props.intl.formatmessage()` (where `intl` is  passed to `props` by `injectI18n` HOC).
 
-#### In AngularJS
-
-The long term plan is to rely on using `i18n.translate()` by statically importing `i18n` from the `@osd/i18n` package. **Avoid using the `i18n` filter and the `i18n` service injected in controllers, directives, services.**
-
-- Call JS function `i18n.translate()` from the `@osd/i18n` package.
-- Use `i18nId` directive in template.
-
-Currently, we support the following AngluarJS `i18n` tools, but they will be removed in future releases:
-- Usage of `i18n` service in controllers, directives, services by injecting it.
-- Usage of `i18n` filter in template for attribute translation. Note: Use one-time binding ("{{:: ... }}") in filters wherever it's possible to prevent unnecessary expression re-evaluation.
-
 #### In JavaScript
 
 - Use `i18n.translate()` in NodeJS or any other framework agnostic code, where `i18n` is the I18n engine from `@osd/i18n` package.
diff --git a/packages/osd-i18n/README.md b/packages/osd-i18n/README.md
index 16e740e925d5..a58a15ab4fd9 100644
--- a/packages/osd-i18n/README.md
+++ b/packages/osd-i18n/README.md
@@ -1,14 +1,13 @@
 # I18n
 
-OpenSearch Dashboards relies on several UI frameworks (ReactJS and AngularJS) and
+OpenSearch Dashboards relies on UI frameworks (ReactJS) and
 requires localization in different environments (browser and NodeJS).
 Internationalization engine is framework agnostic and consumable in
-all parts of OpenSearch Dashboards (ReactJS, AngularJS and NodeJS). In order to simplify
+all parts of OpenSearch Dashboards (ReactJS and NodeJS). In order to simplify
 internationalization in UI frameworks, the additional abstractions are
-built around the I18n engine: `react-intl` for React and custom
-components for AngularJS. [React-intl](https://github.com/yahoo/react-intl)
+built around the I18n engine: `react-intl` for React. [React-intl](https://github.com/yahoo/react-intl)
 is built around [intl-messageformat](https://github.com/yahoo/intl-messageformat),
-so both React and AngularJS frameworks use the same engine and the same
+so the React framework uses the same engine and the same
 message syntax.
 
 ## Localization files
@@ -343,98 +342,6 @@ export const MyComponent = injectI18n(
 );
 ```
 
-## AngularJS
-
-The long term plan is to rely on using `i18n.translate()` by statically importing `i18n` from the `@osd/i18n` package. **Avoid using the `i18n` filter and the `i18n` service injected in controllers, directives, services.**
-
-AngularJS wrapper has 4 entities: translation `provider`, `service`, `directive`
-and `filter`. Both the directive and the filter use the translation `service`
-with i18n engine under the hood.
-
-The translation `provider` is used for `service` configuration and
-has the following methods:
-- `addMessages(messages: Map<string, string>, [locale: string])` - provides a way to register
-translations with the library
-- `setLocale(locale: string)` - tells the library which language to use by given
-language key
-- `getLocale()` - returns the current locale
-- `setDefaultLocale(locale: string)` - tells the library which language to fallback
-when missing translations
-- `getDefaultLocale()` - returns the default locale
-- `setFormats(formats: object)` - supplies a set of options to the underlying formatter
-- `getFormats()` - returns current formats
-- `getRegisteredLocales()` - returns array of locales having translations
-- `init(messages: Map<string, string>)` - initializes the engine
-
-The translation `service` provides only one method:
-- `i18n(id: string, { values: object, defaultMessage: string, description: string })` –
-translate message by id
-
-The translation `filter` is used for attributes translation and has
-the following syntax:
-```
-{{ ::'translationId' | i18n: { values: object, defaultMessage: string, description: string } }}
-```
-
-Where:
-- `translationId` - translation id to be translated
-- `values` - values to pass into translation
-- `defaultMessage` - will be used unless translation was successful (the final
-  fallback in english, will be used for generating `en.json`)
-- `description` - optional context comment that will be extracted by i18n tools
-and added as a comment next to translation message at `defaultMessages.json`
-
-The translation `directive` has the following syntax:
-```html
-<ANY
-  i18n-id="{string}"
-  i18n-default-message="{string}"
-  [i18n-values="{object}"]
-  [i18n-description="{string}"]
-></ANY>
-```
-
-Where:
-- `i18n-id` - translation id to be translated
-- `i18n-default-message` - will be used unless translation was successful
-- `i18n-values` - values to pass into translation
-- `i18n-description` - optional context comment that will be extracted by i18n tools
-and added as a comment next to translation message at `defaultMessages.json`
-
-If HTML rendering in `i18n-values` is required then value key in `i18n-values` object
-should have `html_` prefix. Otherwise the value will be inserted to the message without
-HTML rendering.\
-Example:
-```html
-<p
-  i18n-id="namespace.id"
-  i18n-default-message="Text with an emphasized {text}."
-  i18n-values="{
-    html_text: '<em>text</em>',
-  }"
-></p>
-```
-
-Angular `I18n` module is placed into `autoload` module, so it will be
-loaded automatically. After that we can use i18n directive in Angular templates:
-```html
-<span
-  i18n-id="welcome"
-  i18n-default-message="Hello!"
-></span>
-```
-
-In order to translate attributes in AngularJS we should use `i18nFilter`:
-```html
-<input
-  type="text"
-  placeholder="{{ ::'osd.management.objects.searchAriaLabel' | i18n: {
-    defaultMessage: 'Search { title } Object',
-    values: { title }
-  } }}"
->
-```
-
 ## I18n tools
 
 In order to simplify localization process, some additional tools were implemented:
diff --git a/packages/osd-i18n/angular/package.json b/packages/osd-i18n/angular/package.json
deleted file mode 100644
index 1979e988fa7c..000000000000
--- a/packages/osd-i18n/angular/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "browser": "../target/web/angular",
-  "main": "../target/node/angular",
-  "types": "../target/types/angular/index.d.ts"
-}
diff --git a/src/dev/i18n/README.md b/src/dev/i18n/README.md
index 4a9e3f45eff0..7cfb938d38ec 100644
--- a/src/dev/i18n/README.md
+++ b/src/dev/i18n/README.md
@@ -4,7 +4,7 @@
 
 ### Description
 
-The tool is used to extract default messages from all `*.{js, ts, jsx, tsx, html }` files in provided plugins directories to a JSON file.
+The tool is used to extract default messages from all `*.{js, ts, jsx, tsx }` files in provided plugins directories to a JSON file.
 
 It uses Babel to parse code and build an AST for each file or a single JS expression if whole file parsing is impossible. The tool is able to validate, extract and match IDs, default messages and descriptions only if they are defined statically and together, otherwise it will fail with detailed explanation. That means one can't define ID in one place and default message in another, or use function call to dynamically create default message etc.
 
@@ -18,33 +18,6 @@ The `defaultMessage` must contain ICU references to all keys in the `values` and
 
 The `description` is optional, `values` is optional too unless `defaultMessage` references to it.
 
-* **Angular (.html)**
-
-  * **Filter**
-
-    ```
-    {{ ::'pluginNamespace.messageId' | i18n: {
-      defaultMessage: 'Default message string literal, {key}',
-      values: { key: 'value' },
-      description: 'Message context or description'
-    } }}
-    ```
-
-    * Don't break `| i18n: {` with line breaks, and don't skip whitespaces around `i18n:`.
-    * `::` operator is optional. Omit it if you need data binding for the `values`.
-
-  * **Directive**
-
-    ```html
-    <p
-      i18n-id="pluginNamespace.messageId"
-      i18n-default-message="Default message string literal, {key}. {emphasizedText}"
-      i18n-values="{ key: value, html_emphasizedText: htmlString }"
-      i18n-description="Message context or description"
-    ></p>
-    ```
-
-    * `html_` prefixes will be removed from `i18n-values` keys before validation.
 
 * **React (.jsx, .tsx)**
 
diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html
deleted file mode 100644
index f725fa288405..000000000000
--- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<osd-top-nav name="dashboard" config="topNavMenu">
-  <div data-transclude-slots>
-    <div
-      i18n-id="plugin_1.id_7"
-      i18n-default-message="Message 7"
-    ></div>
-  </div>
-</osd-top-nav>
diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html
deleted file mode 100644
index c12843602b13..000000000000
--- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html
+++ /dev/null
@@ -1 +0,0 @@
-<p>{{ ::'plugin_2.message-id' | i18n: { defaultMessage: 'Message text' } }}</p>
diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx
new file mode 100644
index 000000000000..b3f0c8d2b9c1
--- /dev/null
+++ b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+i18n('plugin_2.message-id', { defaultMessage: 'Message text' });
diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4/test_file_4.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4/test_file_4.jsx
new file mode 100644
index 000000000000..5ce7b916bcd4
--- /dev/null
+++ b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4/test_file_4.jsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable */
+class Component extends PureComponent {
+  render() {
+    return (
+      <div data-transclude-slots>
+          <FormattedMessage
+          id="plugin_4.id_1"
+          defaultMessage="Message 4"
+          />
+        </div>
+    );
+  }
+}
\ No newline at end of file
diff --git a/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap b/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap
index 68ed0433f1ae..cf91d6252d05 100644
--- a/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap
+++ b/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap
@@ -33,6 +33,54 @@ Array [
 ]
 `;
 
+exports[`dev/i18n/extract_default_translations extracts messages from path to map 2`] = `
+Array [
+  Array [
+    "plugin_2.message-id",
+    Object {
+      "description": undefined,
+      "message": "Message text",
+    },
+  ],
+]
+`;
+
+exports[`dev/i18n/extract_default_translations extracts messages from path to map 3`] = `
+Array [
+  Array [
+    "plugin_3.duplicate_id",
+    Object {
+      "description": undefined,
+      "message": "Message 1",
+    },
+  ],
+]
+`;
+
+exports[`dev/i18n/extract_default_translations extracts messages from path to map 4`] = `
+Array [
+  Array [
+    "plugin_3.duplicate_id",
+    Object {
+      "description": undefined,
+      "message": "Message 1",
+    },
+  ],
+]
+`;
+
+exports[`dev/i18n/extract_default_translations extracts messages from path to map 5`] = `
+Array [
+  Array [
+    "plugin_4.id_1",
+    Object {
+      "description": undefined,
+      "message": "Message 4",
+    },
+  ],
+]
+`;
+
 exports[`dev/i18n/extract_default_translations throws on id collision 1`] = `
 Array [
   " I18N ERROR  Error in src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx
diff --git a/src/dev/i18n/extract_default_translations.test.js b/src/dev/i18n/extract_default_translations.test.js
index ea4754f3645e..c995ec562735 100644
--- a/src/dev/i18n/extract_default_translations.test.js
+++ b/src/dev/i18n/extract_default_translations.test.js
@@ -42,6 +42,7 @@ const pluginsPaths = [
   path.join(fixturesPath, 'test_plugin_2'),
   path.join(fixturesPath, 'test_plugin_3'),
   path.join(fixturesPath, 'test_plugin_3_additional_path'),
+  path.join(fixturesPath, 'test_plugin_4'),
 ];
 
 const config = {
@@ -52,17 +53,19 @@ const config = {
       'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3',
       'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path',
     ],
+    plugin_4: ['src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4'],
   },
   exclude: [],
 };
 
 describe('dev/i18n/extract_default_translations', () => {
   test('extracts messages from path to map', async () => {
-    const [pluginPath] = pluginsPaths;
-    const resultMap = new Map();
+    for (const pluginPath of pluginsPaths) {
+      const resultMap = new Map();
 
-    await extractMessagesFromPathToMap(pluginPath, resultMap, config, new ErrorReporter());
-    expect([...resultMap].sort()).toMatchSnapshot();
+      await extractMessagesFromPathToMap(pluginPath, resultMap, config, new ErrorReporter());
+      expect([...resultMap].sort()).toMatchSnapshot();
+    }
   });
 
   test('throws on id collision', async () => {
@@ -88,11 +91,11 @@ describe('dev/i18n/extract_default_translations', () => {
     const id = 'plugin_3.message-id';
     const filePath1 = path.resolve(
       __dirname,
-      '__fixtures__/extract_default_translations/test_plugin_3/test_file.html'
+      '__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx'
     );
     const filePath2 = path.resolve(
       __dirname,
-      '__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.html'
+      '__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.jsx'
     );
     expect(() => validateMessageNamespace(id, filePath1, config.paths)).not.toThrow();
     expect(() => validateMessageNamespace(id, filePath2, config.paths)).not.toThrow();
@@ -103,7 +106,7 @@ describe('dev/i18n/extract_default_translations', () => {
     const id = 'wrong_plugin_namespace.message-id';
     const filePath = path.resolve(
       __dirname,
-      '__fixtures__/extract_default_translations/test_plugin_2/test_file.html'
+      '__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx'
     );
 
     expect(() => validateMessageNamespace(id, filePath, config.paths, { report })).not.toThrow();