From da65f41bd418c6c4a208fde1decc840d7a38b4fe Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:39:40 -0500 Subject: [PATCH 01/35] Update source file api.md --- docs/extend/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extend/api.md b/docs/extend/api.md index cb03f7f0c..0f1ad5322 100644 --- a/docs/extend/api.md +++ b/docs/extend/api.md @@ -336,7 +336,7 @@ return [ (new Extend\ApiSerializer(UserSerializer::class)) // One attribute at a time ->attribute('firstName', function ($serializer, $user, $attributes) { - return $user->first_name + return $user->first_name }) // Multiple modifications at once, more complex logic ->mutate(function($serializer, $user, $attributes) { From 8171e4672600df26e0730ee8fc7857c67a33d307 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:39:45 -0500 Subject: [PATCH 02/35] Update source file github-actions.md --- docs/extend/github-actions.md | 59 +++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/docs/extend/github-actions.md b/docs/extend/github-actions.md index 3179a4ec7..1051a2b13 100644 --- a/docs/extend/github-actions.md +++ b/docs/extend/github-actions.md @@ -4,8 +4,6 @@ In public repos, [GitHub Actions](https://github.com/features/actions) allow you In this guide, you will learn how to add pre-defined workflows to your extension. -## Setup - :::tip [Flarum CLI](https://github.com/flarum/cli) You can use the CLI to automatically add and update workflows to your code: @@ -15,6 +13,9 @@ $ flarum-cli infra githubActions ::: +## Backend + + All you need to do is create a `.github/workflows/backend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_backend.yml). You need to specify the configuration as follows: ```yaml @@ -34,9 +35,7 @@ jobs: backend_directory: . ``` -## Backend - -Flarum provides a pre-defined workflow for running certain jobs for the backend of your extension. These are the currently available jobs: +These are the currently available jobs: | Name | Key | Description | |-------------------------------------------------|--------------------------|----------------------------------------| @@ -69,4 +68,52 @@ For more details on parameters, [checkout the full predefined reusable workflow ## Frontend -Soon.. +All you need to do is create a `.github/workflows/frontend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). You need to specify the configuration as follows: + +```yaml +name: Frontend + +on: [workflow_dispatch, push, pull_request] + +jobs: + run: + uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main + with: + enable_bundlewatch: false + enable_prettier: true + enable_typescript: false + + frontend_directory: ./js + backend_directory: . + js_package_manager: yarn + main_git_branch: main + + secrets: + bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} +``` + +Unlike the backend workflow, the frontend workflow runs everything in a single job. Here are the available parameters: + +| Name | Key | Description | Format | +|-----------------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|--------| +| Build Script | `build_script` | Script to run for production build. Empty value to disable. | string | +| Build Typings Script | `build_typings_script` | Script to run for typings build. Empty value to disable. | string | +| Format Script | `format_script` | Script to run for code formatting. Empty value to disable. | string | +| Check Typings Script | `check_typings_script` | Script to run for tyiping check. Empty value to disable. | string | +| Type Coverage Script | `type_coverage_script` | Script to run for type coverage. Empty value to disable. | string | +| Test Script | `test_script` | Script to run for tests. Empty value to disable. | string | +| Enable Bundlewatch | `enable_bundlewatch` | Enable Bundlewatch? | string | +| Enable Prettier | `enable_prettier` | Enable Prettier? | string | +| Enable Typescript | `enable_typescript` | Enable TypeScript? | string | +| Enable Tests | `enable_tests` | Enable Tests? | string | +| Backend Directory | `backend_directory` | The directory of the project where backend code is located. This should contain a `composer.json` file, and is generally the root directory of the repo. | string | +| Frontend Directory | `frontend_directory` | The directory of the project where frontend code is located. This should contain a `package.json` file. | string | +| Main Git Branch | `main_git_branch` | The main git branch to use for the workflow. | string | +| Node Version | `node_version` | The node version to use for the workflow. | string | +| JS Package Manager | `js_package_manager` | The package manager to use (ex. yarn) | string | +| Cache Dependency Path | `cache_dependency_path` | The path to the cache dependency file. | string | +:::tip + +For more details on parameters, [checkout the full predefined reusable workflow file](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). + +::: From ef162f55ba19fc52fd7a7eeb9b1a4bf58b517aa6 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:39:47 -0500 Subject: [PATCH 03/35] Update source file models.md --- docs/extend/models.md | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/docs/extend/models.md b/docs/extend/models.md index 007f0e693..31cd4bee1 100644 --- a/docs/extend/models.md +++ b/docs/extend/models.md @@ -251,38 +251,37 @@ export default class Tag extends Model { } ``` -You must then register your new model with the store: +You must then register your new model with the store using the frontend `Store` extender in a new `extend.js` module: ```js -app.store.models.tags = Tag; -``` - - +``` -### Extending Models +:::info -To add attributes and relationships to existing models, modify the model class prototype: +Remember to export the `extend` module from your entry `index.js` file: ```js -Discussion.prototype.user = Model.hasOne('user'); -Discussion.prototype.posts = Model.hasMany('posts'); -Discussion.prototype.slug = Model.attribute('slug'); +export { default as extend } from './extend'; ``` - +### Extending Models + +To add attributes and relationships to existing models, use the `Model` extender: + +```ts + new Extend.Model(Discussion) + .attribute('slug') + .hasOne('user') + .hasMany('posts') +``` ### Saving Resources From cc94f857dbc97715740914efdc7dc2fc7b17d839 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:39:50 -0500 Subject: [PATCH 04/35] Update source file routes.md --- docs/extend/routes.md | 51 +++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/docs/extend/routes.md b/docs/extend/routes.md index 12997bbae..1e83b1f32 100644 --- a/docs/extend/routes.md +++ b/docs/extend/routes.md @@ -160,20 +160,28 @@ On the backend, instead of adding your frontend route via the `Routes` extender, Now when `yourforum.com/users` is visited, the forum frontend will be displayed. However, since the frontend doesn't yet know about the `users` route, the discussion list will still be rendered. -Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). To register a new route, add an object for it to `app.routes`: +Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). -```js -app.routes['acme.users'] = { path: '/users', component: UsersPage }; -``` - - +``` + +:::info + +Remember to export the `extend` module from your entry `index.js` file: + +```js +export { default as extend } from './extend'; +``` + +::: Now when `yourforum.com/users` is visited, the forum frontend will be loaded and the `UsersPage` component will be rendered in the content area. For more information on frontend pages, please see [that documentation section](frontend-pages.md). @@ -181,16 +189,12 @@ Advanced use cases might also be interested in using [route resolvers](frontend- ### Route Parameters -Frontend routes also allow you to capture segments of the URI, but the [Mithril route syntax](https://mithril.js.org/route.html) is slightly different: +Frontend routes also allow you to capture segments of the URI: ```jsx -app.routes['acme.user'] = { path: '/user/:id', component: UserPage }; -``` - - + .add('acme.user', '/user/:id', ) +``` Route parameters will be passed into the `attrs` of the route's component. They will also be available through [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) @@ -203,6 +207,21 @@ const url = app.route('acme.user', { id: 123, foo: 'bar' }); // http://yourforum.com/users/123?foo=bar ``` +The extender also allows you to define a route helper method: + +```js + new Extend.Routes() + .add('acme.user', '/user/:id', ) + .helper('acmeUser', (user) => app.route('acme.user', { id: user.id() })) +``` + +This allows you to generate URLs to the route using the `acmeUser` helper method: + +```js +const url = app.route.acmeUser(user); +// http://yourforum.com/users/123 +``` + ### Linking to Other Pages A forum wouldn't be very useful if it only had one page. From 62dab0741f9ce778e2f691a6f1eea5a96da7d545 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:39:52 -0500 Subject: [PATCH 05/35] Update source file testing.md --- docs/extend/testing.md | 164 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/docs/extend/testing.md b/docs/extend/testing.md index f38b90fbf..6a9b32402 100644 --- a/docs/extend/testing.md +++ b/docs/extend/testing.md @@ -20,8 +20,6 @@ $ flarum-cli infra backendTesting ::: -Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: - ```bash composer require --dev flarum/testing:^1.0 ``` @@ -405,7 +403,167 @@ NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that f ## Frontend Tests -Coming Soon! +### Setup + +:::tip [Flarum CLI](https://github.com/flarum/cli) + +You can use the CLI to automatically add and update frontend testing infrastructure to your code: + +```bash +$ flarum-cli infra frontendTesting +``` + +::: + +First, you need to install the Jest config dev dependency: + +```bash +$ yarn add --dev @flarum/jest-config +``` + +Then, add the following to your `package.json`: + +```json +{ + "type": "module", + "scripts": { + ..., + "test": "yarn node --experimental-vm-modules $(yarn bin jest)" + } +} +``` + +Rename `webpack.config.js` to `webpack.config.cjs`. This is necessary because Jest doesn't support ESM yet. + +Create a `jest.config.cjs` file in the root of your extension: + +```js +module.exports = require('@flarum/jest-config')(); +``` + +If you are using TypeScript, create tsconfig.test.json with the following content: + +```json +{ + "extends": "./tsconfig.json", + "include": ["tests/**/*"], + "files": ["../../../node_modules/@flarum/jest-config/shims.d.ts"] +} +``` + +Then, you will need to set up a file structure for tests: + +``` +js +├── dist +├── src +├── tests +│ ├── unit +│ │ └── functionTest.test.js +│ ├── integration +│ │ └── componentTest.test.js +├── package.json +├── tsconfig.json +├── tsconfig.test.json +├── jest.config.cjs +└── webpack.config.cjs +``` + +#### GitHub Testing Workflow + +To run tests on every commit and pull request, check out the [GitHub Actions](github-actions.md) page. + +### Using Unit Tests + +Like any other JS project, you can use Jest to write unit tests for your frontend code. Checkout the [Jest docs](https://jestjs.io/docs/using-matchers) for more information on how to write tests. + +Here's a simple example of a unit test fo core's `abbreviateNumber` function: + +```ts +import abbreviateNumber from '../../../../src/common/utils/abbreviateNumber'; + +test('does not change small numbers', () => { + expect(abbreviateNumber(1)).toBe('1'); +}); + +test('abbreviates large numbers', () => { + expect(abbreviateNumber(1000000)).toBe('1M'); + expect(abbreviateNumber(100500)).toBe('100.5K'); +}); + +test('abbreviates large numbers with decimal places', () => { + expect(abbreviateNumber(100500)).toBe('100.5K'); + expect(abbreviateNumber(13234)).toBe('13.2K'); +}); +``` + +### Using Integration Tests + +Integration tests are used to test the components of your frontend code and the interaction between different components. For example, you might test that a page component renders the correct content based on certain parameters. + +Here's a simple example of an integration test for core's `Alert` component: + +```ts +import Alert from '../../../../src/common/components/Alert'; +import m from 'mithril'; +import mq from 'mithril-query'; +import { jest } from '@jest/globals'; + +describe('Alert displays as expected', () => { + it('should display alert messages with an icon', () => { + const alert = mq(m(Alert, { type: 'error' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toHaveElement('i.icon'); + }); + + it('should display alert messages with a custom icon when using a title', () => { + const alert = mq(Alert, { type: 'error', icon: 'fas fa-users', title: 'Woops..' }); + expect(alert).toContainRaw('Woops..'); + expect(alert).toHaveElement('i.fas.fa-users'); + }); + + it('should display alert messages with a title', () => { + const alert = mq(m(Alert, { type: 'error', title: 'Error Title' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toContainRaw('Error Title'); + expect(alert).toHaveElement('.Alert-title'); + }); + + it('should display alert messages with custom controls', () => { + const alert = mq(Alert, { type: 'error', controls: [m('button', { className: 'Button--test' }, 'Click me!')] }); + expect(alert).toHaveElement('button.Button--test'); + }); +}); + +describe('Alert is dismissible', () => { + it('should show dismiss button', function () { + const alert = mq(m(Alert, { dismissible: true }, 'Shoot!')); + expect(alert).toHaveElement('button.Alert-dismiss'); + }); + + it('should call ondismiss when dismiss button is clicked', function () { + const ondismiss = jest.fn(); + const alert = mq(Alert, { dismissible: true, ondismiss }); + alert.click('.Alert-dismiss'); + expect(ondismiss).toHaveBeenCalled(); + }); + + it('should not be dismissible if not chosen', function () { + const alert = mq(Alert, { type: 'error', dismissible: false }); + expect(alert).not.toHaveElement('button.Alert-dismiss'); + }); +}); +``` + +#### Methods + +These are the custom methods that are available for mithril component tests: +* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector. +* **`toContainRaw(content)`** - Checks if the component HTML contains the given content. + +To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests. + + ## E2E Tests From 969eea1ab812e042a2501b4b9699b7c5f2f8d23f Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:14 -0500 Subject: [PATCH 06/35] New translations testing.md (Spanish) --- .../current/extend/testing.md | 164 +++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/extend/testing.md b/i18n/es/docusaurus-plugin-content-docs/current/extend/testing.md index e84669186..838725732 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/extend/testing.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/extend/testing.md @@ -18,8 +18,6 @@ $ flarum-cli infra backendTesting ::: -Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: - ```bash composer require --dev flarum/testing:^1.0 ``` @@ -400,7 +398,167 @@ NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that f ## Frontend Tests -Coming Soon! +### Configuración + +:::tip [Flarum CLI](https://github.com/flarum/cli) + +You can use the CLI to automatically add and update frontend testing infrastructure to your code: + +```bash +$ flarum-cli infra frontendTesting +``` + +::: + +First, you need to install the Jest config dev dependency: + +```bash +$ yarn add --dev @flarum/jest-config +``` + +Then, add the following to your `package.json`: + +```json +{ + "type": "module", + "scripts": { + ..., + "test": "yarn node --experimental-vm-modules $(yarn bin jest)" + } +} +``` + +Rename `webpack.config.js` to `webpack.config.cjs`. This is necessary because Jest doesn't support ESM yet. + +Create a `jest.config.cjs` file in the root of your extension: + +```js +module.exports = require('@flarum/jest-config')(); +``` + +If you are using TypeScript, create tsconfig.test.json with the following content: + +```json +{ + "extends": "./tsconfig.json", + "include": ["tests/**/*"], + "files": ["../../../node_modules/@flarum/jest-config/shims.d.ts"] +} +``` + +Then, you will need to set up a file structure for tests: + +``` +js +├── dist +├── src +├── tests +│ ├── unit +│ │ └── functionTest.test.js +│ ├── integration +│ │ └── componentTest.test.js +├── package.json +├── tsconfig.json +├── tsconfig.test.json +├── jest.config.cjs +└── webpack.config.cjs +``` + +#### Flujo de trabajo de pruebas en Github + +To run tests on every commit and pull request, check out the [GitHub Actions](github-actions.md) page. + +### Using Unit Tests + +Like any other JS project, you can use Jest to write unit tests for your frontend code. Checkout the [Jest docs](https://jestjs.io/docs/using-matchers) for more information on how to write tests. + +Here's a simple example of a unit test fo core's `abbreviateNumber` function: + +```ts +import abbreviateNumber from '../../../../src/common/utils/abbreviateNumber'; + +test('does not change small numbers', () => { + expect(abbreviateNumber(1)).toBe('1'); +}); + +test('abbreviates large numbers', () => { + expect(abbreviateNumber(1000000)).toBe('1M'); + expect(abbreviateNumber(100500)).toBe('100.5K'); +}); + +test('abbreviates large numbers with decimal places', () => { + expect(abbreviateNumber(100500)).toBe('100.5K'); + expect(abbreviateNumber(13234)).toBe('13.2K'); +}); +``` + +### Uso de las pruebas de integración + +Integration tests are used to test the components of your frontend code and the interaction between different components. For example, you might test that a page component renders the correct content based on certain parameters. + +Here's a simple example of an integration test for core's `Alert` component: + +```ts +import Alert from '../../../../src/common/components/Alert'; +import m from 'mithril'; +import mq from 'mithril-query'; +import { jest } from '@jest/globals'; + +describe('Alert displays as expected', () => { + it('should display alert messages with an icon', () => { + const alert = mq(m(Alert, { type: 'error' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toHaveElement('i.icon'); + }); + + it('should display alert messages with a custom icon when using a title', () => { + const alert = mq(Alert, { type: 'error', icon: 'fas fa-users', title: 'Woops..' }); + expect(alert).toContainRaw('Woops..'); + expect(alert).toHaveElement('i.fas.fa-users'); + }); + + it('should display alert messages with a title', () => { + const alert = mq(m(Alert, { type: 'error', title: 'Error Title' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toContainRaw('Error Title'); + expect(alert).toHaveElement('.Alert-title'); + }); + + it('should display alert messages with custom controls', () => { + const alert = mq(Alert, { type: 'error', controls: [m('button', { className: 'Button--test' }, 'Click me!')] }); + expect(alert).toHaveElement('button.Button--test'); + }); +}); + +describe('Alert is dismissible', () => { + it('should show dismiss button', function () { + const alert = mq(m(Alert, { dismissible: true }, 'Shoot!')); + expect(alert).toHaveElement('button.Alert-dismiss'); + }); + + it('should call ondismiss when dismiss button is clicked', function () { + const ondismiss = jest.fn(); + const alert = mq(Alert, { dismissible: true, ondismiss }); + alert.click('.Alert-dismiss'); + expect(ondismiss).toHaveBeenCalled(); + }); + + it('should not be dismissible if not chosen', function () { + const alert = mq(Alert, { type: 'error', dismissible: false }); + expect(alert).not.toHaveElement('button.Alert-dismiss'); + }); +}); +``` + +#### Methods + +These are the custom methods that are available for mithril component tests: +* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector. +* **`toContainRaw(content)`** - Checks if the component HTML contains the given content. + +To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests. + + ## E2E Tests From 4f1b3203655590b352b7dff4986f0c995f649bd5 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:16 -0500 Subject: [PATCH 07/35] New translations models.md (Spanish) --- .../current/extend/models.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/extend/models.md b/i18n/es/docusaurus-plugin-content-docs/current/extend/models.md index 5d09f2b12..e2ede462c 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/extend/models.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/extend/models.md @@ -252,39 +252,40 @@ export default class Tag extends Model { } ``` -You must then register your new model with the store: +You must then register your new model with the store using the frontend `Store` extender in a new `extend.js` module: ```js -app.store.models.tags = Tag; +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Store() + .add('tags', Tag), +]; ``` +:::info - -### Extending Models -To add attributes and relationships to existing models, modify the model class prototype: - -```js -Discussion.prototype.user = Model.hasOne('user'); -Discussion.prototype.posts = Model.hasMany('posts'); -Discussion.prototype.slug = Model.attribute('slug'); +export { default as extend } from './extend'; ``` +::: - ### Saving Resources + To send data back through the API, call the `save` method on a model instance. This method returns a Promise which resolves with the same model instance: ```js From 48321d8a33efd2bccb288d009af714ebdd293cb5 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:17 -0500 Subject: [PATCH 08/35] New translations github-actions.md (Spanish) --- .../current/extend/github-actions.md | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/extend/github-actions.md b/i18n/es/docusaurus-plugin-content-docs/current/extend/github-actions.md index 2303a07d3..0ed7f6dd9 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/extend/github-actions.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/extend/github-actions.md @@ -4,8 +4,6 @@ In public repos, [GitHub Actions](https://github.com/features/actions) allow you In this guide, you will learn how to add pre-defined workflows to your extension. -## Configuración - :::tip [Flarum CLI](https://github.com/flarum/cli) You can use the CLI to automatically add and update workflows to your code: @@ -15,6 +13,9 @@ $ flarum-cli infra githubActions ::: +## Backend + + All you need to do is create a `.github/workflows/backend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_backend.yml). You need to specify the configuration as follows: ```yaml @@ -34,9 +35,7 @@ jobs: backend_directory: . ``` -## Backend - -Flarum provides a pre-defined workflow for running certain jobs for the backend of your extension. These are the currently available jobs: +These are the currently available jobs: | Name | Key | Descripción | | ----------------------------------------------- | ------------------------ | -------------------------------------- | @@ -69,4 +68,52 @@ For more details on parameters, [checkout the full predefined reusable workflow ## Frontend -Soon.. +All you need to do is create a `.github/workflows/frontend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). You need to specify the configuration as follows: + +```yaml +name: Frontend + +on: [workflow_dispatch, push, pull_request] + +jobs: + run: + uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main + with: + enable_bundlewatch: false + enable_prettier: true + enable_typescript: false + + frontend_directory: ./js + backend_directory: . + js_package_manager: yarn + main_git_branch: main + + secrets: + bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} +``` + +Unlike the backend workflow, the frontend workflow runs everything in a single job. Here are the available parameters: + +| Name | Key | Descripción | Format | +| --------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| Build Script | `build_script` | Script to run for production build. Empty value to disable. | string | +| Build Typings Script | `build_typings_script` | Script to run for typings build. Empty value to disable. | string | +| Format Script | `format_script` | Script to run for code formatting. Empty value to disable. | string | +| Check Typings Script | `check_typings_script` | Script to run for tyiping check. Empty value to disable. | string | +| Type Coverage Script | `type_coverage_script` | Script to run for type coverage. Empty value to disable. | string | +| Test Script | `test_script` | Script to run for tests. Empty value to disable. | string | +| Enable Bundlewatch | `enable_bundlewatch` | Enable Bundlewatch? | string | +| Enable Prettier | `enable_prettier` | Enable Prettier? | string | +| Enable Typescript | `enable_typescript` | Enable TypeScript? | string | +| Enable Tests | `enable_tests` | Enable Tests? | string | +| Backend Directory | `backend_directory` | The directory of the project where backend code is located. This should contain a `composer.json` file, and is generally the root directory of the repo. | string | +| Frontend Directory | `frontend_directory` | The directory of the project where frontend code is located. This should contain a `package.json` file. | string | +| Main Git Branch | `main_git_branch` | The main git branch to use for the workflow. | string | +| Node Version | `node_version` | The node version to use for the workflow. | string | +| JS Package Manager | `js_package_manager` | The package manager to use (ex. yarn) | string | +| Cache Dependency Path | `cache_dependency_path` | The path to the cache dependency file. | string | + :::tip + +For more details on parameters, [checkout the full predefined reusable workflow file](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). + +::: From b113c1d54012fc28a4584b0ecdb0128a4355f1e3 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:20 -0500 Subject: [PATCH 09/35] New translations testing.md (German) --- .../current/extend/testing.md | 164 +++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/i18n/de/docusaurus-plugin-content-docs/current/extend/testing.md b/i18n/de/docusaurus-plugin-content-docs/current/extend/testing.md index b09037be8..2607b03da 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/extend/testing.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/extend/testing.md @@ -18,8 +18,6 @@ $ flarum-cli infra backendTesting ::: -Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: - ```bash composer require --dev flarum/testing:^1.0 ``` @@ -400,7 +398,167 @@ NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that f ## Frontend Tests -Coming Soon! +### Setup + +:::tip [Flarum CLI](https://github.com/flarum/cli) + +You can use the CLI to automatically add and update frontend testing infrastructure to your code: + +```bash +$ flarum-cli infra frontendTesting +``` + +::: + +First, you need to install the Jest config dev dependency: + +```bash +$ yarn add --dev @flarum/jest-config +``` + +Then, add the following to your `package.json`: + +```json +{ + "type": "module", + "scripts": { + ..., + "test": "yarn node --experimental-vm-modules $(yarn bin jest)" + } +} +``` + +Rename `webpack.config.js` to `webpack.config.cjs`. This is necessary because Jest doesn't support ESM yet. + +Create a `jest.config.cjs` file in the root of your extension: + +```js +module.exports = require('@flarum/jest-config')(); +``` + +If you are using TypeScript, create tsconfig.test.json with the following content: + +```json +{ + "extends": "./tsconfig.json", + "include": ["tests/**/*"], + "files": ["../../../node_modules/@flarum/jest-config/shims.d.ts"] +} +``` + +Then, you will need to set up a file structure for tests: + +``` +js +├── dist +├── src +├── tests +│ ├── unit +│ │ └── functionTest.test.js +│ ├── integration +│ │ └── componentTest.test.js +├── package.json +├── tsconfig.json +├── tsconfig.test.json +├── jest.config.cjs +└── webpack.config.cjs +``` + +#### GitHub Testing Workflow + +To run tests on every commit and pull request, check out the [GitHub Actions](github-actions.md) page. + +### Using Unit Tests + +Like any other JS project, you can use Jest to write unit tests for your frontend code. Checkout the [Jest docs](https://jestjs.io/docs/using-matchers) for more information on how to write tests. + +Here's a simple example of a unit test fo core's `abbreviateNumber` function: + +```ts +import abbreviateNumber from '../../../../src/common/utils/abbreviateNumber'; + +test('does not change small numbers', () => { + expect(abbreviateNumber(1)).toBe('1'); +}); + +test('abbreviates large numbers', () => { + expect(abbreviateNumber(1000000)).toBe('1M'); + expect(abbreviateNumber(100500)).toBe('100.5K'); +}); + +test('abbreviates large numbers with decimal places', () => { + expect(abbreviateNumber(100500)).toBe('100.5K'); + expect(abbreviateNumber(13234)).toBe('13.2K'); +}); +``` + +### Using Integration Tests + +Integration tests are used to test the components of your frontend code and the interaction between different components. For example, you might test that a page component renders the correct content based on certain parameters. + +Here's a simple example of an integration test for core's `Alert` component: + +```ts +import Alert from '../../../../src/common/components/Alert'; +import m from 'mithril'; +import mq from 'mithril-query'; +import { jest } from '@jest/globals'; + +describe('Alert displays as expected', () => { + it('should display alert messages with an icon', () => { + const alert = mq(m(Alert, { type: 'error' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toHaveElement('i.icon'); + }); + + it('should display alert messages with a custom icon when using a title', () => { + const alert = mq(Alert, { type: 'error', icon: 'fas fa-users', title: 'Woops..' }); + expect(alert).toContainRaw('Woops..'); + expect(alert).toHaveElement('i.fas.fa-users'); + }); + + it('should display alert messages with a title', () => { + const alert = mq(m(Alert, { type: 'error', title: 'Error Title' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toContainRaw('Error Title'); + expect(alert).toHaveElement('.Alert-title'); + }); + + it('should display alert messages with custom controls', () => { + const alert = mq(Alert, { type: 'error', controls: [m('button', { className: 'Button--test' }, 'Click me!')] }); + expect(alert).toHaveElement('button.Button--test'); + }); +}); + +describe('Alert is dismissible', () => { + it('should show dismiss button', function () { + const alert = mq(m(Alert, { dismissible: true }, 'Shoot!')); + expect(alert).toHaveElement('button.Alert-dismiss'); + }); + + it('should call ondismiss when dismiss button is clicked', function () { + const ondismiss = jest.fn(); + const alert = mq(Alert, { dismissible: true, ondismiss }); + alert.click('.Alert-dismiss'); + expect(ondismiss).toHaveBeenCalled(); + }); + + it('should not be dismissible if not chosen', function () { + const alert = mq(Alert, { type: 'error', dismissible: false }); + expect(alert).not.toHaveElement('button.Alert-dismiss'); + }); +}); +``` + +#### Methods + +These are the custom methods that are available for mithril component tests: +* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector. +* **`toContainRaw(content)`** - Checks if the component HTML contains the given content. + +To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests. + + ## E2E Tests From f14a26ffe347c0c1c22d352be86c4069588f28dc Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:22 -0500 Subject: [PATCH 10/35] New translations models.md (German) --- .../current/extend/models.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/i18n/de/docusaurus-plugin-content-docs/current/extend/models.md b/i18n/de/docusaurus-plugin-content-docs/current/extend/models.md index 822f354f3..5fecd6926 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/extend/models.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/extend/models.md @@ -252,39 +252,40 @@ export default class Tag extends Model { } ``` -You must then register your new model with the store: +You must then register your new model with the store using the frontend `Store` extender in a new `extend.js` module: ```js -app.store.models.tags = Tag; +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Store() + .add('tags', Tag), +]; ``` +:::info - -### Extending Models -To add attributes and relationships to existing models, modify the model class prototype: - -```js -Discussion.prototype.user = Model.hasOne('user'); -Discussion.prototype.posts = Model.hasMany('posts'); -Discussion.prototype.slug = Model.attribute('slug'); +export { default as extend } from './extend'; ``` +::: - ### Saving Resources + To send data back through the API, call the `save` method on a model instance. This method returns a Promise which resolves with the same model instance: ```js From 4bad93c6cf21bf1040325eec0f57d894f2fb6e21 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:24 -0500 Subject: [PATCH 11/35] New translations github-actions.md (German) --- .../current/extend/github-actions.md | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/i18n/de/docusaurus-plugin-content-docs/current/extend/github-actions.md b/i18n/de/docusaurus-plugin-content-docs/current/extend/github-actions.md index d810720cd..904bdf995 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/extend/github-actions.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/extend/github-actions.md @@ -4,8 +4,6 @@ In public repos, [GitHub Actions](https://github.com/features/actions) allow you In this guide, you will learn how to add pre-defined workflows to your extension. -## Setup - :::tip [Flarum CLI](https://github.com/flarum/cli) You can use the CLI to automatically add and update workflows to your code: @@ -15,6 +13,9 @@ $ flarum-cli infra githubActions ::: +## Backend + + All you need to do is create a `.github/workflows/backend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_backend.yml). You need to specify the configuration as follows: ```yaml @@ -34,9 +35,7 @@ jobs: backend_directory: . ``` -## Backend - -Flarum provides a pre-defined workflow for running certain jobs for the backend of your extension. These are the currently available jobs: +These are the currently available jobs: | Name | Key | Description | | ----------------------------------------------- | ------------------------ | -------------------------------------- | @@ -69,4 +68,52 @@ For more details on parameters, [checkout the full predefined reusable workflow ## Frontend -Soon.. +All you need to do is create a `.github/workflows/frontend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). You need to specify the configuration as follows: + +```yaml +name: Frontend + +on: [workflow_dispatch, push, pull_request] + +jobs: + run: + uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main + with: + enable_bundlewatch: false + enable_prettier: true + enable_typescript: false + + frontend_directory: ./js + backend_directory: . + js_package_manager: yarn + main_git_branch: main + + secrets: + bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} +``` + +Unlike the backend workflow, the frontend workflow runs everything in a single job. Here are the available parameters: + +| Name | Key | Description | Format | +| --------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| Build Script | `build_script` | Script to run for production build. Empty value to disable. | string | +| Build Typings Script | `build_typings_script` | Script to run for typings build. Empty value to disable. | string | +| Format Script | `format_script` | Script to run for code formatting. Empty value to disable. | string | +| Check Typings Script | `check_typings_script` | Script to run for tyiping check. Empty value to disable. | string | +| Type Coverage Script | `type_coverage_script` | Script to run for type coverage. Empty value to disable. | string | +| Test Script | `test_script` | Script to run for tests. Empty value to disable. | string | +| Enable Bundlewatch | `enable_bundlewatch` | Enable Bundlewatch? | string | +| Enable Prettier | `enable_prettier` | Enable Prettier? | string | +| Enable Typescript | `enable_typescript` | Enable TypeScript? | string | +| Enable Tests | `enable_tests` | Enable Tests? | string | +| Backend Directory | `backend_directory` | The directory of the project where backend code is located. This should contain a `composer.json` file, and is generally the root directory of the repo. | string | +| Frontend Directory | `frontend_directory` | The directory of the project where frontend code is located. This should contain a `package.json` file. | string | +| Main Git Branch | `main_git_branch` | The main git branch to use for the workflow. | string | +| Node Version | `node_version` | The node version to use for the workflow. | string | +| JS Package Manager | `js_package_manager` | The package manager to use (ex. yarn) | string | +| Cache Dependency Path | `cache_dependency_path` | The path to the cache dependency file. | string | + :::tip + +For more details on parameters, [checkout the full predefined reusable workflow file](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). + +::: From 419858ce8d22489cdbd47cc4876a6b9db0b0ef61 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:27 -0500 Subject: [PATCH 12/35] New translations testing.md (Italian) --- .../current/extend/testing.md | 164 +++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/i18n/it/docusaurus-plugin-content-docs/current/extend/testing.md b/i18n/it/docusaurus-plugin-content-docs/current/extend/testing.md index f516478f0..7b45a1f70 100644 --- a/i18n/it/docusaurus-plugin-content-docs/current/extend/testing.md +++ b/i18n/it/docusaurus-plugin-content-docs/current/extend/testing.md @@ -18,8 +18,6 @@ $ flarum-cli infra backendTesting ::: -Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: - ```bash composer require --dev flarum/testing:^1.0 ``` @@ -400,7 +398,167 @@ NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that f ## Frontend Tests -Coming Soon! +### Setup + +:::tip [Flarum CLI](https://github.com/flarum/cli) + +You can use the CLI to automatically add and update frontend testing infrastructure to your code: + +```bash +$ flarum-cli infra frontendTesting +``` + +::: + +First, you need to install the Jest config dev dependency: + +```bash +$ yarn add --dev @flarum/jest-config +``` + +Then, add the following to your `package.json`: + +```json +{ + "type": "module", + "scripts": { + ..., + "test": "yarn node --experimental-vm-modules $(yarn bin jest)" + } +} +``` + +Rename `webpack.config.js` to `webpack.config.cjs`. This is necessary because Jest doesn't support ESM yet. + +Create a `jest.config.cjs` file in the root of your extension: + +```js +module.exports = require('@flarum/jest-config')(); +``` + +If you are using TypeScript, create tsconfig.test.json with the following content: + +```json +{ + "extends": "./tsconfig.json", + "include": ["tests/**/*"], + "files": ["../../../node_modules/@flarum/jest-config/shims.d.ts"] +} +``` + +Then, you will need to set up a file structure for tests: + +``` +js +├── dist +├── src +├── tests +│ ├── unit +│ │ └── functionTest.test.js +│ ├── integration +│ │ └── componentTest.test.js +├── package.json +├── tsconfig.json +├── tsconfig.test.json +├── jest.config.cjs +└── webpack.config.cjs +``` + +#### GitHub Testing Workflow + +To run tests on every commit and pull request, check out the [GitHub Actions](github-actions.md) page. + +### Using Unit Tests + +Like any other JS project, you can use Jest to write unit tests for your frontend code. Checkout the [Jest docs](https://jestjs.io/docs/using-matchers) for more information on how to write tests. + +Here's a simple example of a unit test fo core's `abbreviateNumber` function: + +```ts +import abbreviateNumber from '../../../../src/common/utils/abbreviateNumber'; + +test('does not change small numbers', () => { + expect(abbreviateNumber(1)).toBe('1'); +}); + +test('abbreviates large numbers', () => { + expect(abbreviateNumber(1000000)).toBe('1M'); + expect(abbreviateNumber(100500)).toBe('100.5K'); +}); + +test('abbreviates large numbers with decimal places', () => { + expect(abbreviateNumber(100500)).toBe('100.5K'); + expect(abbreviateNumber(13234)).toBe('13.2K'); +}); +``` + +### Using Integration Tests + +Integration tests are used to test the components of your frontend code and the interaction between different components. For example, you might test that a page component renders the correct content based on certain parameters. + +Here's a simple example of an integration test for core's `Alert` component: + +```ts +import Alert from '../../../../src/common/components/Alert'; +import m from 'mithril'; +import mq from 'mithril-query'; +import { jest } from '@jest/globals'; + +describe('Alert displays as expected', () => { + it('should display alert messages with an icon', () => { + const alert = mq(m(Alert, { type: 'error' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toHaveElement('i.icon'); + }); + + it('should display alert messages with a custom icon when using a title', () => { + const alert = mq(Alert, { type: 'error', icon: 'fas fa-users', title: 'Woops..' }); + expect(alert).toContainRaw('Woops..'); + expect(alert).toHaveElement('i.fas.fa-users'); + }); + + it('should display alert messages with a title', () => { + const alert = mq(m(Alert, { type: 'error', title: 'Error Title' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toContainRaw('Error Title'); + expect(alert).toHaveElement('.Alert-title'); + }); + + it('should display alert messages with custom controls', () => { + const alert = mq(Alert, { type: 'error', controls: [m('button', { className: 'Button--test' }, 'Click me!')] }); + expect(alert).toHaveElement('button.Button--test'); + }); +}); + +describe('Alert is dismissible', () => { + it('should show dismiss button', function () { + const alert = mq(m(Alert, { dismissible: true }, 'Shoot!')); + expect(alert).toHaveElement('button.Alert-dismiss'); + }); + + it('should call ondismiss when dismiss button is clicked', function () { + const ondismiss = jest.fn(); + const alert = mq(Alert, { dismissible: true, ondismiss }); + alert.click('.Alert-dismiss'); + expect(ondismiss).toHaveBeenCalled(); + }); + + it('should not be dismissible if not chosen', function () { + const alert = mq(Alert, { type: 'error', dismissible: false }); + expect(alert).not.toHaveElement('button.Alert-dismiss'); + }); +}); +``` + +#### Methods + +These are the custom methods that are available for mithril component tests: +* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector. +* **`toContainRaw(content)`** - Checks if the component HTML contains the given content. + +To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests. + + ## E2E Tests From af2b830067abccbd07e5b4af814bd175d4ea1a5e Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:29 -0500 Subject: [PATCH 13/35] New translations models.md (Italian) --- .../current/extend/models.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/i18n/it/docusaurus-plugin-content-docs/current/extend/models.md b/i18n/it/docusaurus-plugin-content-docs/current/extend/models.md index 9dbc19698..ee01a5899 100644 --- a/i18n/it/docusaurus-plugin-content-docs/current/extend/models.md +++ b/i18n/it/docusaurus-plugin-content-docs/current/extend/models.md @@ -252,39 +252,40 @@ export default class Tag extends Model { } ``` -You must then register your new model with the store: +You must then register your new model with the store using the frontend `Store` extender in a new `extend.js` module: ```js -app.store.models.tags = Tag; +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Store() + .add('tags', Tag), +]; ``` +:::info - -### Extending Models -To add attributes and relationships to existing models, modify the model class prototype: - -```js -Discussion.prototype.user = Model.hasOne('user'); -Discussion.prototype.posts = Model.hasMany('posts'); -Discussion.prototype.slug = Model.attribute('slug'); +export { default as extend } from './extend'; ``` +::: - ### Saving Resources + To send data back through the API, call the `save` method on a model instance. This method returns a Promise which resolves with the same model instance: ```js From 09a31a8f3e45885f73640366d53ded1a9afdd34a Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:30 -0500 Subject: [PATCH 14/35] New translations github-actions.md (Italian) --- .../current/extend/github-actions.md | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/i18n/it/docusaurus-plugin-content-docs/current/extend/github-actions.md b/i18n/it/docusaurus-plugin-content-docs/current/extend/github-actions.md index 7f1c222b7..c8514c6cc 100644 --- a/i18n/it/docusaurus-plugin-content-docs/current/extend/github-actions.md +++ b/i18n/it/docusaurus-plugin-content-docs/current/extend/github-actions.md @@ -4,8 +4,6 @@ In public repos, [GitHub Actions](https://github.com/features/actions) allow you In this guide, you will learn how to add pre-defined workflows to your extension. -## Setup - :::tip [Flarum CLI](https://github.com/flarum/cli) You can use the CLI to automatically add and update workflows to your code: @@ -15,6 +13,9 @@ $ flarum-cli infra githubActions ::: +## Backend + + All you need to do is create a `.github/workflows/backend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_backend.yml). You need to specify the configuration as follows: ```yaml @@ -34,9 +35,7 @@ jobs: backend_directory: . ``` -## Backend - -Flarum provides a pre-defined workflow for running certain jobs for the backend of your extension. These are the currently available jobs: +These are the currently available jobs: | Name | Key | Descrizione | | ----------------------------------------------- | ------------------------ | -------------------------------------- | @@ -69,4 +68,52 @@ For more details on parameters, [checkout the full predefined reusable workflow ## Frontend -Soon.. +All you need to do is create a `.github/workflows/frontend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). You need to specify the configuration as follows: + +```yaml +name: Frontend + +on: [workflow_dispatch, push, pull_request] + +jobs: + run: + uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main + with: + enable_bundlewatch: false + enable_prettier: true + enable_typescript: false + + frontend_directory: ./js + backend_directory: . + js_package_manager: yarn + main_git_branch: main + + secrets: + bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} +``` + +Unlike the backend workflow, the frontend workflow runs everything in a single job. Here are the available parameters: + +| Name | Key | Descrizione | Format | +| --------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| Build Script | `build_script` | Script to run for production build. Empty value to disable. | string | +| Build Typings Script | `build_typings_script` | Script to run for typings build. Empty value to disable. | string | +| Format Script | `format_script` | Script to run for code formatting. Empty value to disable. | string | +| Check Typings Script | `check_typings_script` | Script to run for tyiping check. Empty value to disable. | string | +| Type Coverage Script | `type_coverage_script` | Script to run for type coverage. Empty value to disable. | string | +| Test Script | `test_script` | Script to run for tests. Empty value to disable. | string | +| Enable Bundlewatch | `enable_bundlewatch` | Enable Bundlewatch? | string | +| Enable Prettier | `enable_prettier` | Enable Prettier? | string | +| Enable Typescript | `enable_typescript` | Enable TypeScript? | string | +| Enable Tests | `enable_tests` | Enable Tests? | string | +| Backend Directory | `backend_directory` | The directory of the project where backend code is located. This should contain a `composer.json` file, and is generally the root directory of the repo. | string | +| Frontend Directory | `frontend_directory` | The directory of the project where frontend code is located. This should contain a `package.json` file. | string | +| Main Git Branch | `main_git_branch` | The main git branch to use for the workflow. | string | +| Node Version | `node_version` | The node version to use for the workflow. | string | +| JS Package Manager | `js_package_manager` | The package manager to use (ex. yarn) | string | +| Cache Dependency Path | `cache_dependency_path` | The path to the cache dependency file. | string | + :::tip + +For more details on parameters, [checkout the full predefined reusable workflow file](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). + +::: From 7e8b93d038440d9c44eaa7506890b437f95c7781 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:33 -0500 Subject: [PATCH 15/35] New translations testing.md (Turkish) --- .../current/extend/testing.md | 164 +++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/i18n/tr/docusaurus-plugin-content-docs/current/extend/testing.md b/i18n/tr/docusaurus-plugin-content-docs/current/extend/testing.md index 7b09839ce..56124ff0e 100644 --- a/i18n/tr/docusaurus-plugin-content-docs/current/extend/testing.md +++ b/i18n/tr/docusaurus-plugin-content-docs/current/extend/testing.md @@ -18,8 +18,6 @@ $ flarum-cli infra backendTesting ::: -Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: - ```bash composer require --dev flarum/testing:^1.0 ``` @@ -400,7 +398,167 @@ NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that f ## Frontend Tests -Coming Soon! +### Setup + +:::tip [Flarum CLI](https://github.com/flarum/cli) + +You can use the CLI to automatically add and update frontend testing infrastructure to your code: + +```bash +$ flarum-cli infra frontendTesting +``` + +::: + +First, you need to install the Jest config dev dependency: + +```bash +$ yarn add --dev @flarum/jest-config +``` + +Then, add the following to your `package.json`: + +```json +{ + "type": "module", + "scripts": { + ..., + "test": "yarn node --experimental-vm-modules $(yarn bin jest)" + } +} +``` + +Rename `webpack.config.js` to `webpack.config.cjs`. This is necessary because Jest doesn't support ESM yet. + +Create a `jest.config.cjs` file in the root of your extension: + +```js +module.exports = require('@flarum/jest-config')(); +``` + +If you are using TypeScript, create tsconfig.test.json with the following content: + +```json +{ + "extends": "./tsconfig.json", + "include": ["tests/**/*"], + "files": ["../../../node_modules/@flarum/jest-config/shims.d.ts"] +} +``` + +Then, you will need to set up a file structure for tests: + +``` +js +├── dist +├── src +├── tests +│ ├── unit +│ │ └── functionTest.test.js +│ ├── integration +│ │ └── componentTest.test.js +├── package.json +├── tsconfig.json +├── tsconfig.test.json +├── jest.config.cjs +└── webpack.config.cjs +``` + +#### GitHub Testing Workflow + +To run tests on every commit and pull request, check out the [GitHub Actions](github-actions.md) page. + +### Using Unit Tests + +Like any other JS project, you can use Jest to write unit tests for your frontend code. Checkout the [Jest docs](https://jestjs.io/docs/using-matchers) for more information on how to write tests. + +Here's a simple example of a unit test fo core's `abbreviateNumber` function: + +```ts +import abbreviateNumber from '../../../../src/common/utils/abbreviateNumber'; + +test('does not change small numbers', () => { + expect(abbreviateNumber(1)).toBe('1'); +}); + +test('abbreviates large numbers', () => { + expect(abbreviateNumber(1000000)).toBe('1M'); + expect(abbreviateNumber(100500)).toBe('100.5K'); +}); + +test('abbreviates large numbers with decimal places', () => { + expect(abbreviateNumber(100500)).toBe('100.5K'); + expect(abbreviateNumber(13234)).toBe('13.2K'); +}); +``` + +### Using Integration Tests + +Integration tests are used to test the components of your frontend code and the interaction between different components. For example, you might test that a page component renders the correct content based on certain parameters. + +Here's a simple example of an integration test for core's `Alert` component: + +```ts +import Alert from '../../../../src/common/components/Alert'; +import m from 'mithril'; +import mq from 'mithril-query'; +import { jest } from '@jest/globals'; + +describe('Alert displays as expected', () => { + it('should display alert messages with an icon', () => { + const alert = mq(m(Alert, { type: 'error' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toHaveElement('i.icon'); + }); + + it('should display alert messages with a custom icon when using a title', () => { + const alert = mq(Alert, { type: 'error', icon: 'fas fa-users', title: 'Woops..' }); + expect(alert).toContainRaw('Woops..'); + expect(alert).toHaveElement('i.fas.fa-users'); + }); + + it('should display alert messages with a title', () => { + const alert = mq(m(Alert, { type: 'error', title: 'Error Title' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toContainRaw('Error Title'); + expect(alert).toHaveElement('.Alert-title'); + }); + + it('should display alert messages with custom controls', () => { + const alert = mq(Alert, { type: 'error', controls: [m('button', { className: 'Button--test' }, 'Click me!')] }); + expect(alert).toHaveElement('button.Button--test'); + }); +}); + +describe('Alert is dismissible', () => { + it('should show dismiss button', function () { + const alert = mq(m(Alert, { dismissible: true }, 'Shoot!')); + expect(alert).toHaveElement('button.Alert-dismiss'); + }); + + it('should call ondismiss when dismiss button is clicked', function () { + const ondismiss = jest.fn(); + const alert = mq(Alert, { dismissible: true, ondismiss }); + alert.click('.Alert-dismiss'); + expect(ondismiss).toHaveBeenCalled(); + }); + + it('should not be dismissible if not chosen', function () { + const alert = mq(Alert, { type: 'error', dismissible: false }); + expect(alert).not.toHaveElement('button.Alert-dismiss'); + }); +}); +``` + +#### Methods + +These are the custom methods that are available for mithril component tests: +* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector. +* **`toContainRaw(content)`** - Checks if the component HTML contains the given content. + +To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests. + + ## E2E Tests From a924e566e294b66bf11d7caed8d02ebf9ed610c1 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:35 -0500 Subject: [PATCH 16/35] New translations models.md (Turkish) --- .../current/extend/models.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/i18n/tr/docusaurus-plugin-content-docs/current/extend/models.md b/i18n/tr/docusaurus-plugin-content-docs/current/extend/models.md index 822f354f3..5fecd6926 100644 --- a/i18n/tr/docusaurus-plugin-content-docs/current/extend/models.md +++ b/i18n/tr/docusaurus-plugin-content-docs/current/extend/models.md @@ -252,39 +252,40 @@ export default class Tag extends Model { } ``` -You must then register your new model with the store: +You must then register your new model with the store using the frontend `Store` extender in a new `extend.js` module: ```js -app.store.models.tags = Tag; +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Store() + .add('tags', Tag), +]; ``` +:::info - -### Extending Models -To add attributes and relationships to existing models, modify the model class prototype: - -```js -Discussion.prototype.user = Model.hasOne('user'); -Discussion.prototype.posts = Model.hasMany('posts'); -Discussion.prototype.slug = Model.attribute('slug'); +export { default as extend } from './extend'; ``` +::: - ### Saving Resources + To send data back through the API, call the `save` method on a model instance. This method returns a Promise which resolves with the same model instance: ```js From ccf6cf516e3d2b7c0ed1e9052cf2f661de13ea0c Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:36 -0500 Subject: [PATCH 17/35] New translations github-actions.md (Turkish) --- .../current/extend/github-actions.md | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/i18n/tr/docusaurus-plugin-content-docs/current/extend/github-actions.md b/i18n/tr/docusaurus-plugin-content-docs/current/extend/github-actions.md index d810720cd..904bdf995 100644 --- a/i18n/tr/docusaurus-plugin-content-docs/current/extend/github-actions.md +++ b/i18n/tr/docusaurus-plugin-content-docs/current/extend/github-actions.md @@ -4,8 +4,6 @@ In public repos, [GitHub Actions](https://github.com/features/actions) allow you In this guide, you will learn how to add pre-defined workflows to your extension. -## Setup - :::tip [Flarum CLI](https://github.com/flarum/cli) You can use the CLI to automatically add and update workflows to your code: @@ -15,6 +13,9 @@ $ flarum-cli infra githubActions ::: +## Backend + + All you need to do is create a `.github/workflows/backend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_backend.yml). You need to specify the configuration as follows: ```yaml @@ -34,9 +35,7 @@ jobs: backend_directory: . ``` -## Backend - -Flarum provides a pre-defined workflow for running certain jobs for the backend of your extension. These are the currently available jobs: +These are the currently available jobs: | Name | Key | Description | | ----------------------------------------------- | ------------------------ | -------------------------------------- | @@ -69,4 +68,52 @@ For more details on parameters, [checkout the full predefined reusable workflow ## Frontend -Soon.. +All you need to do is create a `.github/workflows/frontend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). You need to specify the configuration as follows: + +```yaml +name: Frontend + +on: [workflow_dispatch, push, pull_request] + +jobs: + run: + uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main + with: + enable_bundlewatch: false + enable_prettier: true + enable_typescript: false + + frontend_directory: ./js + backend_directory: . + js_package_manager: yarn + main_git_branch: main + + secrets: + bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} +``` + +Unlike the backend workflow, the frontend workflow runs everything in a single job. Here are the available parameters: + +| Name | Key | Description | Format | +| --------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| Build Script | `build_script` | Script to run for production build. Empty value to disable. | string | +| Build Typings Script | `build_typings_script` | Script to run for typings build. Empty value to disable. | string | +| Format Script | `format_script` | Script to run for code formatting. Empty value to disable. | string | +| Check Typings Script | `check_typings_script` | Script to run for tyiping check. Empty value to disable. | string | +| Type Coverage Script | `type_coverage_script` | Script to run for type coverage. Empty value to disable. | string | +| Test Script | `test_script` | Script to run for tests. Empty value to disable. | string | +| Enable Bundlewatch | `enable_bundlewatch` | Enable Bundlewatch? | string | +| Enable Prettier | `enable_prettier` | Enable Prettier? | string | +| Enable Typescript | `enable_typescript` | Enable TypeScript? | string | +| Enable Tests | `enable_tests` | Enable Tests? | string | +| Backend Directory | `backend_directory` | The directory of the project where backend code is located. This should contain a `composer.json` file, and is generally the root directory of the repo. | string | +| Frontend Directory | `frontend_directory` | The directory of the project where frontend code is located. This should contain a `package.json` file. | string | +| Main Git Branch | `main_git_branch` | The main git branch to use for the workflow. | string | +| Node Version | `node_version` | The node version to use for the workflow. | string | +| JS Package Manager | `js_package_manager` | The package manager to use (ex. yarn) | string | +| Cache Dependency Path | `cache_dependency_path` | The path to the cache dependency file. | string | + :::tip + +For more details on parameters, [checkout the full predefined reusable workflow file](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). + +::: From bf08cbc8c0ab7ed9b5e45d6897b2fd8282941c6c Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:38 -0500 Subject: [PATCH 18/35] New translations testing.md (Chinese Simplified) --- .../current/extend/testing.md | 164 +++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/extend/testing.md b/i18n/zh/docusaurus-plugin-content-docs/current/extend/testing.md index 3b427e82b..b1342ede0 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/extend/testing.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/extend/testing.md @@ -18,8 +18,6 @@ $ flarum-cli infra backendTesting ::: -Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: - ```bash composer require --dev flarum/testing:^1.0 ``` @@ -400,7 +398,167 @@ NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that f ## Frontend Tests -Coming Soon! +### Setup + +:::tip [Flarum CLI](https://github.com/flarum/cli) + +You can use the CLI to automatically add and update frontend testing infrastructure to your code: + +```bash +$ flarum-cli infra frontendTesting +``` + +::: + +First, you need to install the Jest config dev dependency: + +```bash +$ yarn add --dev @flarum/jest-config +``` + +Then, add the following to your `package.json`: + +```json +{ + "type": "module", + "scripts": { + ..., + "test": "yarn node --experimental-vm-modules $(yarn bin jest)" + } +} +``` + +Rename `webpack.config.js` to `webpack.config.cjs`. This is necessary because Jest doesn't support ESM yet. + +Create a `jest.config.cjs` file in the root of your extension: + +```js +module.exports = require('@flarum/jest-config')(); +``` + +If you are using TypeScript, create tsconfig.test.json with the following content: + +```json +{ + "extends": "./tsconfig.json", + "include": ["tests/**/*"], + "files": ["../../../node_modules/@flarum/jest-config/shims.d.ts"] +} +``` + +Then, you will need to set up a file structure for tests: + +``` +js +├── dist +├── src +├── tests +│ ├── unit +│ │ └── functionTest.test.js +│ ├── integration +│ │ └── componentTest.test.js +├── package.json +├── tsconfig.json +├── tsconfig.test.json +├── jest.config.cjs +└── webpack.config.cjs +``` + +#### GitHub Testing Workflow + +To run tests on every commit and pull request, check out the [GitHub Actions](github-actions.md) page. + +### Using Unit Tests + +Like any other JS project, you can use Jest to write unit tests for your frontend code. Checkout the [Jest docs](https://jestjs.io/docs/using-matchers) for more information on how to write tests. + +Here's a simple example of a unit test fo core's `abbreviateNumber` function: + +```ts +import abbreviateNumber from '../../../../src/common/utils/abbreviateNumber'; + +test('does not change small numbers', () => { + expect(abbreviateNumber(1)).toBe('1'); +}); + +test('abbreviates large numbers', () => { + expect(abbreviateNumber(1000000)).toBe('1M'); + expect(abbreviateNumber(100500)).toBe('100.5K'); +}); + +test('abbreviates large numbers with decimal places', () => { + expect(abbreviateNumber(100500)).toBe('100.5K'); + expect(abbreviateNumber(13234)).toBe('13.2K'); +}); +``` + +### Using Integration Tests + +Integration tests are used to test the components of your frontend code and the interaction between different components. For example, you might test that a page component renders the correct content based on certain parameters. + +Here's a simple example of an integration test for core's `Alert` component: + +```ts +import Alert from '../../../../src/common/components/Alert'; +import m from 'mithril'; +import mq from 'mithril-query'; +import { jest } from '@jest/globals'; + +describe('Alert displays as expected', () => { + it('should display alert messages with an icon', () => { + const alert = mq(m(Alert, { type: 'error' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toHaveElement('i.icon'); + }); + + it('should display alert messages with a custom icon when using a title', () => { + const alert = mq(Alert, { type: 'error', icon: 'fas fa-users', title: 'Woops..' }); + expect(alert).toContainRaw('Woops..'); + expect(alert).toHaveElement('i.fas.fa-users'); + }); + + it('should display alert messages with a title', () => { + const alert = mq(m(Alert, { type: 'error', title: 'Error Title' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toContainRaw('Error Title'); + expect(alert).toHaveElement('.Alert-title'); + }); + + it('should display alert messages with custom controls', () => { + const alert = mq(Alert, { type: 'error', controls: [m('button', { className: 'Button--test' }, 'Click me!')] }); + expect(alert).toHaveElement('button.Button--test'); + }); +}); + +describe('Alert is dismissible', () => { + it('should show dismiss button', function () { + const alert = mq(m(Alert, { dismissible: true }, 'Shoot!')); + expect(alert).toHaveElement('button.Alert-dismiss'); + }); + + it('should call ondismiss when dismiss button is clicked', function () { + const ondismiss = jest.fn(); + const alert = mq(Alert, { dismissible: true, ondismiss }); + alert.click('.Alert-dismiss'); + expect(ondismiss).toHaveBeenCalled(); + }); + + it('should not be dismissible if not chosen', function () { + const alert = mq(Alert, { type: 'error', dismissible: false }); + expect(alert).not.toHaveElement('button.Alert-dismiss'); + }); +}); +``` + +#### Methods + +These are the custom methods that are available for mithril component tests: +* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector. +* **`toContainRaw(content)`** - Checks if the component HTML contains the given content. + +To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests. + + ## E2E Tests From 3eb28013d32d60dca6d44633611fb3bcb510ab36 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:40 -0500 Subject: [PATCH 19/35] New translations models.md (Chinese Simplified) --- .../current/extend/models.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/extend/models.md b/i18n/zh/docusaurus-plugin-content-docs/current/extend/models.md index 822f354f3..5fecd6926 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/extend/models.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/extend/models.md @@ -252,39 +252,40 @@ export default class Tag extends Model { } ``` -You must then register your new model with the store: +You must then register your new model with the store using the frontend `Store` extender in a new `extend.js` module: ```js -app.store.models.tags = Tag; +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Store() + .add('tags', Tag), +]; ``` +:::info - -### Extending Models -To add attributes and relationships to existing models, modify the model class prototype: - -```js -Discussion.prototype.user = Model.hasOne('user'); -Discussion.prototype.posts = Model.hasMany('posts'); -Discussion.prototype.slug = Model.attribute('slug'); +export { default as extend } from './extend'; ``` +::: - ### Saving Resources + To send data back through the API, call the `save` method on a model instance. This method returns a Promise which resolves with the same model instance: ```js From 7f6ce1d2269ccc36915bc3c0feff91a39ac77dbb Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:41 -0500 Subject: [PATCH 20/35] New translations github-actions.md (Chinese Simplified) --- .../current/extend/github-actions.md | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/extend/github-actions.md b/i18n/zh/docusaurus-plugin-content-docs/current/extend/github-actions.md index d810720cd..904bdf995 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/extend/github-actions.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/extend/github-actions.md @@ -4,8 +4,6 @@ In public repos, [GitHub Actions](https://github.com/features/actions) allow you In this guide, you will learn how to add pre-defined workflows to your extension. -## Setup - :::tip [Flarum CLI](https://github.com/flarum/cli) You can use the CLI to automatically add and update workflows to your code: @@ -15,6 +13,9 @@ $ flarum-cli infra githubActions ::: +## Backend + + All you need to do is create a `.github/workflows/backend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_backend.yml). You need to specify the configuration as follows: ```yaml @@ -34,9 +35,7 @@ jobs: backend_directory: . ``` -## Backend - -Flarum provides a pre-defined workflow for running certain jobs for the backend of your extension. These are the currently available jobs: +These are the currently available jobs: | Name | Key | Description | | ----------------------------------------------- | ------------------------ | -------------------------------------- | @@ -69,4 +68,52 @@ For more details on parameters, [checkout the full predefined reusable workflow ## Frontend -Soon.. +All you need to do is create a `.github/workflows/frontend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). You need to specify the configuration as follows: + +```yaml +name: Frontend + +on: [workflow_dispatch, push, pull_request] + +jobs: + run: + uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main + with: + enable_bundlewatch: false + enable_prettier: true + enable_typescript: false + + frontend_directory: ./js + backend_directory: . + js_package_manager: yarn + main_git_branch: main + + secrets: + bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} +``` + +Unlike the backend workflow, the frontend workflow runs everything in a single job. Here are the available parameters: + +| Name | Key | Description | Format | +| --------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| Build Script | `build_script` | Script to run for production build. Empty value to disable. | string | +| Build Typings Script | `build_typings_script` | Script to run for typings build. Empty value to disable. | string | +| Format Script | `format_script` | Script to run for code formatting. Empty value to disable. | string | +| Check Typings Script | `check_typings_script` | Script to run for tyiping check. Empty value to disable. | string | +| Type Coverage Script | `type_coverage_script` | Script to run for type coverage. Empty value to disable. | string | +| Test Script | `test_script` | Script to run for tests. Empty value to disable. | string | +| Enable Bundlewatch | `enable_bundlewatch` | Enable Bundlewatch? | string | +| Enable Prettier | `enable_prettier` | Enable Prettier? | string | +| Enable Typescript | `enable_typescript` | Enable TypeScript? | string | +| Enable Tests | `enable_tests` | Enable Tests? | string | +| Backend Directory | `backend_directory` | The directory of the project where backend code is located. This should contain a `composer.json` file, and is generally the root directory of the repo. | string | +| Frontend Directory | `frontend_directory` | The directory of the project where frontend code is located. This should contain a `package.json` file. | string | +| Main Git Branch | `main_git_branch` | The main git branch to use for the workflow. | string | +| Node Version | `node_version` | The node version to use for the workflow. | string | +| JS Package Manager | `js_package_manager` | The package manager to use (ex. yarn) | string | +| Cache Dependency Path | `cache_dependency_path` | The path to the cache dependency file. | string | + :::tip + +For more details on parameters, [checkout the full predefined reusable workflow file](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). + +::: From 00b4108cd7cddb5e12458790bc32ab6888d9ebd3 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:44 -0500 Subject: [PATCH 21/35] New translations testing.md (Vietnamese) --- .../current/extend/testing.md | 164 +++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/i18n/vi/docusaurus-plugin-content-docs/current/extend/testing.md b/i18n/vi/docusaurus-plugin-content-docs/current/extend/testing.md index bf033dacf..f9d866094 100644 --- a/i18n/vi/docusaurus-plugin-content-docs/current/extend/testing.md +++ b/i18n/vi/docusaurus-plugin-content-docs/current/extend/testing.md @@ -18,8 +18,6 @@ $ flarum-cli infra backendTesting ::: -Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: - ```bash composer require --dev flarum/testing:^1.0 ``` @@ -400,7 +398,167 @@ NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that f ## Frontend Tests -Coming Soon! +### Setup + +:::tip [Flarum CLI](https://github.com/flarum/cli) + +You can use the CLI to automatically add and update frontend testing infrastructure to your code: + +```bash +$ flarum-cli infra frontendTesting +``` + +::: + +First, you need to install the Jest config dev dependency: + +```bash +$ yarn add --dev @flarum/jest-config +``` + +Then, add the following to your `package.json`: + +```json +{ + "type": "module", + "scripts": { + ..., + "test": "yarn node --experimental-vm-modules $(yarn bin jest)" + } +} +``` + +Rename `webpack.config.js` to `webpack.config.cjs`. This is necessary because Jest doesn't support ESM yet. + +Create a `jest.config.cjs` file in the root of your extension: + +```js +module.exports = require('@flarum/jest-config')(); +``` + +If you are using TypeScript, create tsconfig.test.json with the following content: + +```json +{ + "extends": "./tsconfig.json", + "include": ["tests/**/*"], + "files": ["../../../node_modules/@flarum/jest-config/shims.d.ts"] +} +``` + +Then, you will need to set up a file structure for tests: + +``` +js +├── dist +├── src +├── tests +│ ├── unit +│ │ └── functionTest.test.js +│ ├── integration +│ │ └── componentTest.test.js +├── package.json +├── tsconfig.json +├── tsconfig.test.json +├── jest.config.cjs +└── webpack.config.cjs +``` + +#### Github Testing Workflow + +To run tests on every commit and pull request, check out the [GitHub Actions](github-actions.md) page. + +### Using Unit Tests + +Like any other JS project, you can use Jest to write unit tests for your frontend code. Checkout the [Jest docs](https://jestjs.io/docs/using-matchers) for more information on how to write tests. + +Here's a simple example of a unit test fo core's `abbreviateNumber` function: + +```ts +import abbreviateNumber from '../../../../src/common/utils/abbreviateNumber'; + +test('does not change small numbers', () => { + expect(abbreviateNumber(1)).toBe('1'); +}); + +test('abbreviates large numbers', () => { + expect(abbreviateNumber(1000000)).toBe('1M'); + expect(abbreviateNumber(100500)).toBe('100.5K'); +}); + +test('abbreviates large numbers with decimal places', () => { + expect(abbreviateNumber(100500)).toBe('100.5K'); + expect(abbreviateNumber(13234)).toBe('13.2K'); +}); +``` + +### Using Integration Tests + +Integration tests are used to test the components of your frontend code and the interaction between different components. For example, you might test that a page component renders the correct content based on certain parameters. + +Here's a simple example of an integration test for core's `Alert` component: + +```ts +import Alert from '../../../../src/common/components/Alert'; +import m from 'mithril'; +import mq from 'mithril-query'; +import { jest } from '@jest/globals'; + +describe('Alert displays as expected', () => { + it('should display alert messages with an icon', () => { + const alert = mq(m(Alert, { type: 'error' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toHaveElement('i.icon'); + }); + + it('should display alert messages with a custom icon when using a title', () => { + const alert = mq(Alert, { type: 'error', icon: 'fas fa-users', title: 'Woops..' }); + expect(alert).toContainRaw('Woops..'); + expect(alert).toHaveElement('i.fas.fa-users'); + }); + + it('should display alert messages with a title', () => { + const alert = mq(m(Alert, { type: 'error', title: 'Error Title' }, 'Shoot!')); + expect(alert).toContainRaw('Shoot!'); + expect(alert).toContainRaw('Error Title'); + expect(alert).toHaveElement('.Alert-title'); + }); + + it('should display alert messages with custom controls', () => { + const alert = mq(Alert, { type: 'error', controls: [m('button', { className: 'Button--test' }, 'Click me!')] }); + expect(alert).toHaveElement('button.Button--test'); + }); +}); + +describe('Alert is dismissible', () => { + it('should show dismiss button', function () { + const alert = mq(m(Alert, { dismissible: true }, 'Shoot!')); + expect(alert).toHaveElement('button.Alert-dismiss'); + }); + + it('should call ondismiss when dismiss button is clicked', function () { + const ondismiss = jest.fn(); + const alert = mq(Alert, { dismissible: true, ondismiss }); + alert.click('.Alert-dismiss'); + expect(ondismiss).toHaveBeenCalled(); + }); + + it('should not be dismissible if not chosen', function () { + const alert = mq(Alert, { type: 'error', dismissible: false }); + expect(alert).not.toHaveElement('button.Alert-dismiss'); + }); +}); +``` + +#### Methods + +These are the custom methods that are available for mithril component tests: +* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector. +* **`toContainRaw(content)`** - Checks if the component HTML contains the given content. + +To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests. + + ## E2E Tests From 3948fffcae2e1055085e21f1a3a1f0a0fecce7ca Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:46 -0500 Subject: [PATCH 22/35] New translations models.md (Vietnamese) --- .../current/extend/models.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/i18n/vi/docusaurus-plugin-content-docs/current/extend/models.md b/i18n/vi/docusaurus-plugin-content-docs/current/extend/models.md index 2c2b0a5a1..52e6c7a41 100644 --- a/i18n/vi/docusaurus-plugin-content-docs/current/extend/models.md +++ b/i18n/vi/docusaurus-plugin-content-docs/current/extend/models.md @@ -252,39 +252,40 @@ export default class Tag extends Model { } ``` -You must then register your new model with the store: +You must then register your new model with the store using the frontend `Store` extender in a new `extend.js` module: ```js -app.store.models.tags = Tag; +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Store() + .add('tags', Tag), +]; ``` +:::info - -### Extending Models -To add attributes and relationships to existing models, modify the model class prototype: - -```js -Discussion.prototype.user = Model.hasOne('user'); -Discussion.prototype.posts = Model.hasMany('posts'); -Discussion.prototype.slug = Model.attribute('slug'); +export { default as extend } from './extend'; ``` +::: - ### Saving Resources + To send data back through the API, call the `save` method on a model instance. This method returns a Promise which resolves with the same model instance: ```js From 47ca5bd016693f8b2dc6ddd14a229b87949a4bb1 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:48 -0500 Subject: [PATCH 23/35] New translations github-actions.md (Vietnamese) --- .../current/extend/github-actions.md | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/i18n/vi/docusaurus-plugin-content-docs/current/extend/github-actions.md b/i18n/vi/docusaurus-plugin-content-docs/current/extend/github-actions.md index 89a9422a7..07a44462e 100644 --- a/i18n/vi/docusaurus-plugin-content-docs/current/extend/github-actions.md +++ b/i18n/vi/docusaurus-plugin-content-docs/current/extend/github-actions.md @@ -4,8 +4,6 @@ In public repos, [GitHub Actions](https://github.com/features/actions) allow you In this guide, you will learn how to add pre-defined workflows to your extension. -## Setup - :::tip [Flarum CLI](https://github.com/flarum/cli) You can use the CLI to automatically add and update workflows to your code: @@ -15,6 +13,9 @@ $ flarum-cli infra githubActions ::: +## Backend + + All you need to do is create a `.github/workflows/backend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_backend.yml). You need to specify the configuration as follows: ```yaml @@ -34,9 +35,7 @@ jobs: backend_directory: . ``` -## Backend - -Flarum provides a pre-defined workflow for running certain jobs for the backend of your extension. These are the currently available jobs: +These are the currently available jobs: | Name | Key | Mô tả | | ----------------------------------------------- | ------------------------ | -------------------------------------- | @@ -69,4 +68,52 @@ For more details on parameters, [checkout the full predefined reusable workflow ## Frontend -Soon.. +All you need to do is create a `.github/workflows/frontend.yml` file in your extension, it will reuse a predefined workflow by the core development team which can be found [here](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). You need to specify the configuration as follows: + +```yaml +name: Frontend + +on: [workflow_dispatch, push, pull_request] + +jobs: + run: + uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main + with: + enable_bundlewatch: false + enable_prettier: true + enable_typescript: false + + frontend_directory: ./js + backend_directory: . + js_package_manager: yarn + main_git_branch: main + + secrets: + bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} +``` + +Unlike the backend workflow, the frontend workflow runs everything in a single job. Here are the available parameters: + +| Name | Key | Mô tả | Format | +| --------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| Build Script | `build_script` | Script to run for production build. Empty value to disable. | string | +| Build Typings Script | `build_typings_script` | Script to run for typings build. Empty value to disable. | string | +| Format Script | `format_script` | Script to run for code formatting. Empty value to disable. | string | +| Check Typings Script | `check_typings_script` | Script to run for tyiping check. Empty value to disable. | string | +| Type Coverage Script | `type_coverage_script` | Script to run for type coverage. Empty value to disable. | string | +| Test Script | `test_script` | Script to run for tests. Empty value to disable. | string | +| Enable Bundlewatch | `enable_bundlewatch` | Enable Bundlewatch? | string | +| Enable Prettier | `enable_prettier` | Enable Prettier? | string | +| Enable Typescript | `enable_typescript` | Enable TypeScript? | string | +| Enable Tests | `enable_tests` | Enable Tests? | string | +| Backend Directory | `backend_directory` | The directory of the project where backend code is located. This should contain a `composer.json` file, and is generally the root directory of the repo. | string | +| Frontend Directory | `frontend_directory` | The directory of the project where frontend code is located. This should contain a `package.json` file. | string | +| Main Git Branch | `main_git_branch` | The main git branch to use for the workflow. | string | +| Node Version | `node_version` | The node version to use for the workflow. | string | +| JS Package Manager | `js_package_manager` | The package manager to use (ex. yarn) | string | +| Cache Dependency Path | `cache_dependency_path` | The path to the cache dependency file. | string | + :::tip + +For more details on parameters, [checkout the full predefined reusable workflow file](https://github.com/flarum/framework/blob/main/.github/workflows/REUSABLE_frontend.yml). + +::: From 0be1e3d2aafa8b87715aa0eab03a5dd9c6de689f Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:49 -0500 Subject: [PATCH 24/35] New translations routes.md (Spanish) --- .../current/extend/routes.md | 83 +++++++++++-------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/extend/routes.md b/i18n/es/docusaurus-plugin-content-docs/current/extend/routes.md index ee0508ce0..d14c1a651 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/extend/routes.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/extend/routes.md @@ -145,7 +145,7 @@ class HelloWorldController implements RequestHandlerInterface ### Controladores API -El espacio de nombres `Flarum\Api\Controller` contiene una serie de clases abstractas de controladores que puedes extender para implementar fácilmente nuevos recursos JSON-API. Consulte [Working with Data](data.md) para obtener más información. +El espacio de nombres `Flarum\Api\Controller` contiene una serie de clases abstractas de controladores que puedes extender para implementar fácilmente nuevos recursos JSON-API. Consulte [Working with Data](/extend/data.md) para obtener más información. ## Rutas en el frontend @@ -160,62 +160,73 @@ En el backend, en lugar de añadir tu ruta del frontend a través del extensor ` Ahora, cuando se visite `suforo.com/usuarios`, se mostrará el frontend del foro. Sin embargo, dado que el frontend no conoce todavía la ruta `users`, la lista de discusión se seguirá mostrando. -Flarum se basa en el [sistema de rutas de Mithril](https://mithril.js.org/index.html#routing), añadiendo nombres de rutas y una clase abstracta para páginas (`common/components/Page`). Para registrar una nueva ruta, añade un objeto para ella a `app.routes`: +Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). + +To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: + +```jsx +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Routes() + .add('acme.users', '/users', ), +]; +``` + +:::info + +Remember to export the `extend` module from your entry `index.js` file: ```js -app.routes['acme.users'] = { path: '/users', component: UsersPage }; +export { default as extend } from './extend'; ``` +::: - -Ahora, cuando se visite `suforo.com/usuarios`, se cargará el frontend del foro y se mostrará el componente `UsersPage` en el área de contenido. Para más información sobre las páginas del frontend, por favor vea [esa sección de documentación](frontend-pages.md). -Los casos de uso avanzados también pueden estar interesados en utilizar [route resolvers](frontend-pages.md#route-resolvers-advanced). ### Parámetros de ruta -Las rutas frontales también permiten capturar segmentos del URI, pero la [sintaxis de la ruta Mithril](https://mithril.js.org/route.html) es ligeramente diferente: + +Frontend routes also allow you to capture segments of the URI: ```jsx -app.routes['acme.user'] = { path: '/user/:id', component: UserPage }; + new Extend.Routes() + .add('acme.user', '/user/:id', ) ``` +Route parameters will be passed into the `attrs` of the route's component. They will also be available through [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) - -Los parámetros de la ruta se pasarán a los `attrs` del componente de la ruta. También estarán disponibles a través de [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) ### Generación de URLs -Para generar una URL a una ruta en el frontend, utilice el método `app.route`. Este método acepta dos argumentos: el nombre de la ruta y un hash de parámetros. Los parámetros rellenarán los segmentos de URI que coincidan, de lo contrario se añadirán como parámetros de consulta. + +To generate a URL to a route on the frontend, use the `app.route` method. This accepts two arguments: the route name, and a hash of parameters. Los parámetros rellenarán los segmentos de URI que coincidan, de lo contrario se añadirán como parámetros de consulta. ```js -import Link from 'flarum/components/Link'; +const url = app.route('acme.user', { id: 123, foo: 'bar' }); +// http://yourforum.com/users/123?foo=bar +``` -// Link can be used just like any other component: -Hello World! +The extender also allows you to define a route helper method: -// You'll frequently use Link with generated routes: -Hello World! +```js + new Extend.Routes() + .add('acme.user', '/user/:id', ) + .helper('acmeUser', (user) => app.route('acme.user', { id: user.id() })) +``` -// Link can even generate external links with the external attr: -Hello World! +This allows you to generate URLs to the route using the `acmeUser` helper method: -// The above example with external = true is equivalent to: -Hello World! -// but is provided for flexibility: sometimes you might have links -// that are conditionally internal or external. +```js +const url = app.route.acmeUser(user); +// http://yourforum.com/users/123 ``` ### Enlaces a otras páginas -Un foro no sería muy útil si sólo tuviera una página. Mientras que usted podría, por supuesto, implementar enlaces a otras partes de su foro con etiquetas de anclaje HTML y enlaces codificados, esto puede ser difícil de mantener, y derrota el propósito de que Flarum sea una [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) en primer lugar. +A forum wouldn't be very useful if it only had one page. While you could, of course, implement links to other parts of your forum with HTML anchor tags and hardcoded links, this can be difficult to maintain, and defeats the purpose of Flarum being a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) in the first place. -Flarum utiliza la API de enrutamiento de Mithril para proporcionar un componente `Link` que envuelve limpiamente los enlaces a otras páginas internas. Su uso es bastante simple: +Flarum uses Mithril's routing API to provide a `Link` component that neatly wraps links to other internal pages. Its use is fairly simple: ```jsx import Link from 'flarum/common/components/Link'; @@ -237,14 +248,14 @@ import Link from 'flarum/common/components/Link'; ## Contenido -Cada vez que visitas una ruta del frontend, el backend construye un documento HTML con el andamiaje necesario para arrancar la aplicación JavaScript del frontend. Puedes modificar fácilmente este documento para realizar tareas como: +Whenever you visit a frontend route, the backend constructs a HTML document with the scaffolding necessary to boot up the frontend JavaScript application. You can easily modify this document to perform tasks like: * Cambiar el `` de la página * Añadir recursos externos de JavaScript y CSS * Añadir contenido SEO y etiquetas `<meta>`. * Añadir datos a la carga útil de JavaScript (por ejemplo, para precargar los recursos que se van a renderizar en la página inmediatamente, evitando así una petición innecesaria a la API) -Puedes hacer cambios en el frontend usando el método `content` del extensor `Frontend`. Este método acepta un cierre que recibe dos parámetros: un objeto `Flarum\Frontend\Document` que representa el documento HTML que se mostrará, y el objeto `Request`. +You can make blanket changes to the frontend using the `Frontend` extender's `content` method. This accepts a closure which receives two parameters: a `Flarum\Frontend\Document` object which represents the HTML document that will be displayed, and the `Request` object. ```php use Flarum\Frontend\Document; @@ -258,7 +269,7 @@ return [ ]; ``` -También puede añadir contenido en sus registros de ruta de frontend: +You can also add content onto your frontend route registrations: ```php return [ From 09116d44586b0008b2ac78053e339d90f0ff52b7 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:50 -0500 Subject: [PATCH 25/35] New translations api.md (Spanish) --- i18n/es/docusaurus-plugin-content-docs/current/extend/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/extend/api.md b/i18n/es/docusaurus-plugin-content-docs/current/extend/api.md index 9e51fe470..06d35f1cc 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/extend/api.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/extend/api.md @@ -331,7 +331,7 @@ return [ (new Extend\ApiSerializer(UserSerializer::class)) // One attribute at a time ->attribute('firstName', function ($serializer, $user, $attributes) { - return $user->first_name + return $user->first_name }) // Multiple modifications at once, more complex logic ->mutate(function($serializer, $user, $attributes) { From 251f331549a82396fdf1c5f5de7aab451ba54b0e Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:51 -0500 Subject: [PATCH 26/35] New translations routes.md (German) --- .../current/extend/routes.md | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/i18n/de/docusaurus-plugin-content-docs/current/extend/routes.md b/i18n/de/docusaurus-plugin-content-docs/current/extend/routes.md index 2644c7bd2..b4f2102e4 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/extend/routes.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/extend/routes.md @@ -160,37 +160,46 @@ On the backend, instead of adding your frontend route via the `Routes` extender, Now when `yourforum.com/users` is visited, the forum frontend will be displayed. However, since the frontend doesn't yet know about the `users` route, the discussion list will still be rendered. -Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). To register a new route, add an object for it to `app.routes`: +Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). -```js -app.routes['acme.users'] = { path: '/users', component: UsersPage }; -``` - - -<!-- To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: +To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: ```jsx -export const extend = [ +import Extend from 'flarum/common/extenders'; + +export default [ new Extend.Routes() - .add('/users', 'acme.users', <UsersPage />) + .add('acme.users', '/users', <UsersPage />), ]; -``` --> +``` + +:::info + +Remember to export the `extend` module from your entry `index.js` file: + +```js +export { default as extend } from './extend'; +``` + +::: + Now when `yourforum.com/users` is visited, the forum frontend will be loaded and the `UsersPage` component will be rendered in the content area. For more information on frontend pages, please see [that documentation section](frontend-pages.md). + Advanced use cases might also be interested in using [route resolvers](frontend-pages.md#route-resolvers-advanced). + ### Route Parameters -Frontend routes also allow you to capture segments of the URI, but the [Mithril route syntax](https://mithril.js.org/route.html) is slightly different: + +Frontend routes also allow you to capture segments of the URI: ```jsx -app.routes['acme.user'] = { path: '/user/:id', component: UserPage }; + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) ``` - -<!-- ```jsx - new Extend.Routes() - .add('/user/:id', 'acme.user', <UsersPage />) -``` --> Route parameters will be passed into the `attrs` of the route's component. They will also be available through [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) + ### Generating URLs + To generate a URL to a route on the frontend, use the `app.route` method. This accepts two arguments: the route name, and a hash of parameters. Parameters will fill in matching URI segments, otherwise they will be appended as query params. ```js @@ -198,6 +207,21 @@ const url = app.route('acme.user', { id: 123, foo: 'bar' }); // http://yourforum.com/users/123?foo=bar ``` +The extender also allows you to define a route helper method: + +```js + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) + .helper('acmeUser', (user) => app.route('acme.user', { id: user.id() })) +``` + +This allows you to generate URLs to the route using the `acmeUser` helper method: + +```js +const url = app.route.acmeUser(user); +// http://yourforum.com/users/123 +``` + ### Linking to Other Pages A forum wouldn't be very useful if it only had one page. While you could, of course, implement links to other parts of your forum with HTML anchor tags and hardcoded links, this can be difficult to maintain, and defeats the purpose of Flarum being a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) in the first place. From 5279f5bd08786d38eedc940cbe8c53184f857dac Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:52 -0500 Subject: [PATCH 27/35] New translations api.md (German) --- i18n/de/docusaurus-plugin-content-docs/current/extend/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/de/docusaurus-plugin-content-docs/current/extend/api.md b/i18n/de/docusaurus-plugin-content-docs/current/extend/api.md index 0ad440f0e..073d858e1 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/extend/api.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/extend/api.md @@ -331,7 +331,7 @@ return [ (new Extend\ApiSerializer(UserSerializer::class)) // One attribute at a time ->attribute('firstName', function ($serializer, $user, $attributes) { - return $user->first_name + return $user->first_name }) // Multiple modifications at once, more complex logic ->mutate(function($serializer, $user, $attributes) { From 577459549cfa02d61aebd5014099d57087fc392f Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:53 -0500 Subject: [PATCH 28/35] New translations routes.md (Italian) --- .../current/extend/routes.md | 101 ++++++++++-------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/i18n/it/docusaurus-plugin-content-docs/current/extend/routes.md b/i18n/it/docusaurus-plugin-content-docs/current/extend/routes.md index 5003697d2..84a3c1855 100644 --- a/i18n/it/docusaurus-plugin-content-docs/current/extend/routes.md +++ b/i18n/it/docusaurus-plugin-content-docs/current/extend/routes.md @@ -160,91 +160,102 @@ Sul backend, invece di aggiungere il tuo percorso frontend tramite `Routes`, pot Ora quando `tuoforum.com/utente` viene visitato, verrà visualizzato il frontend del forum. Tuttavia, poiché il frontend non conosce ancora la rotta `users`, verrà visualizzato l'elenco di discussioni. -Flarum si basa sul [sistema di instradamento di Mithril](https://mithril.js.org/index.html#routing), che aggiunge nomi di percorsi e una classe astratta per le pagine (`common/components/Page`). Per registrare un nuovo percorso, aggiungi un oggetto `app.routes`: +Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). + +To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: + +```jsx +import Extend from 'flarum/common/extenders'; + +export default [ + new Extend.Routes() + .add('acme.users', '/users', <UsersPage />), +]; +``` + +:::info + +Remember to export the `extend` module from your entry `index.js` file: ```js -app.routes['acme.users'] = { path: '/users', component: UsersPage }; +export { default as extend } from './extend'; ``` +::: -<!-- To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: +Now when `yourforum.com/users` is visited, the forum frontend will be loaded and the `UsersPage` component will be rendered in the content area. For more information on frontend pages, please see [that documentation section](frontend-pages.md). + +Advanced use cases might also be interested in using [route resolvers](frontend-pages.md#route-resolvers-advanced). -```jsx -export const extend = [ - new Extend.Routes() - .add('/users', 'acme.users', <UsersPage />) -]; -``` --> -Ora quanto `tuoforum.com/utente` verrà caricato il frontend del forum ed anche il componente `UsersPage` verrà renderizzato. Per ulteriori informazioni sulle pagine di frontend, vedere [questa sezione della documentazione](frontend-pages.md). -I casi di utilizzo avanzato potrebbero anche essere interessati a guardare i [risolutori di percorsi](frontend-pages.md#route-resolvers-advanced). ### Parametri percorsi -I percorsi delfrontend consentono anche di acquisire segmenti dell'URI, ma la [sintassi di Mithril](https://mithril.js.org/route.html) è leggermente diversa: + +Frontend routes also allow you to capture segments of the URI: ```jsx -app.routes['acme.user'] = { path: '/user/:id', component: UserPage }; + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) ``` +Route parameters will be passed into the `attrs` of the route's component. They will also be available through [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) -<!-- ```jsx - new Extend.Routes() - .add('/user/:id', 'acme.user', <UsersPage />) -``` --> -I parametri del percorso verranno passati in `attrs` del componente. Saranno disponibili anche tramite [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) ### Generare URL -Per generare un URL ad un percorso sul frontend, utilizzare il metodo `app.route`. Accetta due argomenti: il nome della rotta e un hash di parametri. I parametri riempiranno i segmenti URI corrispondenti, altrimenti verranno aggiunti come parametri della query. + +To generate a URL to a route on the frontend, use the `app.route` method. This accepts two arguments: the route name, and a hash of parameters. I parametri riempiranno i segmenti URI corrispondenti, altrimenti verranno aggiunti come parametri della query. ```js -import Link from 'flarum/components/Link'; +const url = app.route('acme.user', { id: 123, foo: 'bar' }); +// http://yourforum.com/users/123?foo=bar +``` -// Link can be used just like any other component: -<Link href="/route/known/to/mithril">Hello World!</Link> +The extender also allows you to define a route helper method: -// You'll frequently use Link with generated routes: -<Link href={app.route('settings')}>Hello World!</Link> +```js + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) + .helper('acmeUser', (user) => app.route('acme.user', { id: user.id() })) +``` -// Link can even generate external links with the external attr: -<Link external={true} href="https://google.com">Hello World!</Link> +This allows you to generate URLs to the route using the `acmeUser` helper method: -// The above example with external = true is equivalent to: -<a href="https://google.com">Hello World!</a> -// but is provided for flexibility: sometimes you might have links -// that are conditionally internal or external. +```js +const url = app.route.acmeUser(user); +// http://yourforum.com/users/123 ``` ### Collegamenti ad altre pagine -Un forum non sarebbe molto utile se avesse solo una pagina. Sebbene tu possa, ovviamente, implementare link ad altre parti del tuo forum con tag di ancoraggio HTML e link , questi possono essere difficile da mantenere e vanificano lo scopo di Flarum di essere una [Applicazione a pagina singola](https://en.wikipedia.org/wiki/Single-page_application). +A forum wouldn't be very useful if it only had one page. While you could, of course, implement links to other parts of your forum with HTML anchor tags and hardcoded links, this can be difficult to maintain, and defeats the purpose of Flarum being a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) in the first place. -Flarum utilizza l'API di routing di Mithril per fornire un componente `Link` che racchiude in modo ordinato i collegamenti ad altre pagine interne. Il suo utilizzo è abbastanza semplice: +Flarum uses Mithril's routing API to provide a `Link` component that neatly wraps links to other internal pages. Its use is fairly simple: ```jsx import Link from 'flarum/common/components/Link'; -// Link può essere usato come qualsiasi altro componente: -<Link href="/route/known/to/mithril">Ciao Mondo!</Link> +// Link can be used just like any other component: +<Link href="/route/known/to/mithril">Hello World!</Link> -// Utilizzerai frequentemente Link con itinerari generati: -<Link href={app.route('settings')}>Ciao Mondo!</Link> +// You'll frequently use Link with generated routes: +<Link href={app.route('settings')}>Hello World!</Link> -// Il link può anche generare collegamenti esterni con attributi esterni: -<Link external={true} href="https://google.com">Ciao Mondo!</Link> +// Link can even generate external links with the external attr: +<Link external={true} href="https://google.com">Hello World!</Link> -// L'esempio precedente con esterno = true equivale a: -<a href="https://google.com">Ciao Mondo!</a> -// ma è previsto per flessibilità: a volte si possono avere link -// che sono condizionalmente interni o esterni. +// The above example with external = true is equivalent to: +<a href="https://google.com">Hello World!</a> +// but is provided for flexibility: sometimes you might have links +// that are conditionally internal or external. ``` ## Contenuto -Ogni volta che visiti percorso sul frontend, il backend costruisce un documento HTML con lo "scheletro" necessario per avviare l'applicazione JavaScript frontend. Puoi facilmente modificare questo documento per eseguire attività come: +Whenever you visit a frontend route, the backend constructs a HTML document with the scaffolding necessary to boot up the frontend JavaScript application. You can easily modify this document to perform tasks like: * Modificare il `<title>` della pagina * Aggiunta di risorse JavaScript e CSS esterne * Aggiunta di contenuti SEO e tag `<meta>` * Aggiunta di dati al payload JavaScript (ad es. Per precaricare le risorse che verranno visualizzate immediatamente sulla pagina, evitando così una richiesta non necessaria all'API) -Puoi apportare modifiche generali al frontend utilizzando l'extender `Frontend` e metodo `content`. Accetta una chiusura che riceve due parametri: un oggetto `Flarum\Frontend\Document` che rappresenta il documento HTML che verrà visualizzato e un oggetto `Request`. +You can make blanket changes to the frontend using the `Frontend` extender's `content` method. This accepts a closure which receives two parameters: a `Flarum\Frontend\Document` object which represents the HTML document that will be displayed, and the `Request` object. ```php use Flarum\Frontend\Document; @@ -258,7 +269,7 @@ return [ ]; ``` -Puoi anche aggiungere contenuti tuo frontend: +You can also add content onto your frontend route registrations: ```php return [ From cd592b06e553fa18636f8886e79045a59f250394 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:54 -0500 Subject: [PATCH 29/35] New translations api.md (Italian) --- i18n/it/docusaurus-plugin-content-docs/current/extend/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/it/docusaurus-plugin-content-docs/current/extend/api.md b/i18n/it/docusaurus-plugin-content-docs/current/extend/api.md index 350468f32..4121180ad 100644 --- a/i18n/it/docusaurus-plugin-content-docs/current/extend/api.md +++ b/i18n/it/docusaurus-plugin-content-docs/current/extend/api.md @@ -331,7 +331,7 @@ return [ (new Extend\ApiSerializer(UserSerializer::class)) // One attribute at a time ->attribute('firstName', function ($serializer, $user, $attributes) { - return $user->first_name + return $user->first_name }) // Multiple modifications at once, more complex logic ->mutate(function($serializer, $user, $attributes) { From 27618afb09c366481f79f7df33543c3f8a7ba3a8 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:55 -0500 Subject: [PATCH 30/35] New translations routes.md (Turkish) --- .../current/extend/routes.md | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/i18n/tr/docusaurus-plugin-content-docs/current/extend/routes.md b/i18n/tr/docusaurus-plugin-content-docs/current/extend/routes.md index 4bbc19dde..3a67b0aaf 100644 --- a/i18n/tr/docusaurus-plugin-content-docs/current/extend/routes.md +++ b/i18n/tr/docusaurus-plugin-content-docs/current/extend/routes.md @@ -160,37 +160,46 @@ On the backend, instead of adding your frontend route via the `Routes` extender, Now when `yourforum.com/users` is visited, the forum frontend will be displayed. However, since the frontend doesn't yet know about the `users` route, the discussion list will still be rendered. -Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). To register a new route, add an object for it to `app.routes`: +Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). -```js -app.routes['acme.users'] = { path: '/users', component: UsersPage }; -``` - - -<!-- To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: +To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: ```jsx -export const extend = [ +import Extend from 'flarum/common/extenders'; + +export default [ new Extend.Routes() - .add('/users', 'acme.users', <UsersPage />) + .add('acme.users', '/users', <UsersPage />), ]; -``` --> +``` + +:::info + +Remember to export the `extend` module from your entry `index.js` file: + +```js +export { default as extend } from './extend'; +``` + +::: + Now when `yourforum.com/users` is visited, the forum frontend will be loaded and the `UsersPage` component will be rendered in the content area. For more information on frontend pages, please see [that documentation section](frontend-pages.md). + Advanced use cases might also be interested in using [route resolvers](frontend-pages.md#route-resolvers-advanced). + ### Route Parameters -Frontend routes also allow you to capture segments of the URI, but the [Mithril route syntax](https://mithril.js.org/route.html) is slightly different: + +Frontend routes also allow you to capture segments of the URI: ```jsx -app.routes['acme.user'] = { path: '/user/:id', component: UserPage }; + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) ``` - -<!-- ```jsx - new Extend.Routes() - .add('/user/:id', 'acme.user', <UsersPage />) -``` --> Route parameters will be passed into the `attrs` of the route's component. They will also be available through [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) + ### Generating URLs + To generate a URL to a route on the frontend, use the `app.route` method. This accepts two arguments: the route name, and a hash of parameters. Parameters will fill in matching URI segments, otherwise they will be appended as query params. ```js @@ -198,6 +207,21 @@ const url = app.route('acme.user', { id: 123, foo: 'bar' }); // http://yourforum.com/users/123?foo=bar ``` +The extender also allows you to define a route helper method: + +```js + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) + .helper('acmeUser', (user) => app.route('acme.user', { id: user.id() })) +``` + +This allows you to generate URLs to the route using the `acmeUser` helper method: + +```js +const url = app.route.acmeUser(user); +// http://yourforum.com/users/123 +``` + ### Linking to Other Pages A forum wouldn't be very useful if it only had one page. While you could, of course, implement links to other parts of your forum with HTML anchor tags and hardcoded links, this can be difficult to maintain, and defeats the purpose of Flarum being a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) in the first place. From 444f4e22b79c480e1331a80001fd84f4c473aed1 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:57 -0500 Subject: [PATCH 31/35] New translations api.md (Turkish) --- i18n/tr/docusaurus-plugin-content-docs/current/extend/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/tr/docusaurus-plugin-content-docs/current/extend/api.md b/i18n/tr/docusaurus-plugin-content-docs/current/extend/api.md index f5ca9b1f5..ee9a01f50 100644 --- a/i18n/tr/docusaurus-plugin-content-docs/current/extend/api.md +++ b/i18n/tr/docusaurus-plugin-content-docs/current/extend/api.md @@ -331,7 +331,7 @@ return [ (new Extend\ApiSerializer(UserSerializer::class)) // One attribute at a time ->attribute('firstName', function ($serializer, $user, $attributes) { - return $user->first_name + return $user->first_name }) // Multiple modifications at once, more complex logic ->mutate(function($serializer, $user, $attributes) { From 129d6c14403ddb8c47dd7adebf520a05a7156278 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:58 -0500 Subject: [PATCH 32/35] New translations routes.md (Chinese Simplified) --- .../current/extend/routes.md | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/extend/routes.md b/i18n/zh/docusaurus-plugin-content-docs/current/extend/routes.md index 8a61803c6..a35bc38bf 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/extend/routes.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/extend/routes.md @@ -160,37 +160,46 @@ On the backend, instead of adding your frontend route via the `Routes` extender, Now when `yourforum.com/users` is visited, the forum frontend will be displayed. However, since the frontend doesn't yet know about the `users` route, the discussion list will still be rendered. -Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). To register a new route, add an object for it to `app.routes`: +Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). -```js -app.routes['acme.users'] = { path: '/users', component: UsersPage }; -``` - - -<!-- To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: +To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: ```jsx -export const extend = [ +import Extend from 'flarum/common/extenders'; + +export default [ new Extend.Routes() - .add('/users', 'acme.users', <UsersPage />) + .add('acme.users', '/users', <UsersPage />), ]; -``` --> +``` + +:::info + +Remember to export the `extend` module from your entry `index.js` file: + +```js +export { default as extend } from './extend'; +``` + +::: + Now when `yourforum.com/users` is visited, the forum frontend will be loaded and the `UsersPage` component will be rendered in the content area. For more information on frontend pages, please see [that documentation section](frontend-pages.md). + Advanced use cases might also be interested in using [route resolvers](frontend-pages.md#route-resolvers-advanced). + ### Route Parameters -Frontend routes also allow you to capture segments of the URI, but the [Mithril route syntax](https://mithril.js.org/route.html) is slightly different: + +Frontend routes also allow you to capture segments of the URI: ```jsx -app.routes['acme.user'] = { path: '/user/:id', component: UserPage }; + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) ``` - -<!-- ```jsx - new Extend.Routes() - .add('/user/:id', 'acme.user', <UsersPage />) -``` --> Route parameters will be passed into the `attrs` of the route's component. They will also be available through [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) + ### Generating URLs + To generate a URL to a route on the frontend, use the `app.route` method. This accepts two arguments: the route name, and a hash of parameters. Parameters will fill in matching URI segments, otherwise they will be appended as query params. ```js @@ -198,6 +207,21 @@ const url = app.route('acme.user', { id: 123, foo: 'bar' }); // http://yourforum.com/users/123?foo=bar ``` +The extender also allows you to define a route helper method: + +```js + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) + .helper('acmeUser', (user) => app.route('acme.user', { id: user.id() })) +``` + +This allows you to generate URLs to the route using the `acmeUser` helper method: + +```js +const url = app.route.acmeUser(user); +// http://yourforum.com/users/123 +``` + ### Linking to Other Pages A forum wouldn't be very useful if it only had one page. While you could, of course, implement links to other parts of your forum with HTML anchor tags and hardcoded links, this can be difficult to maintain, and defeats the purpose of Flarum being a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) in the first place. From a2ee7e090d6a47223cd9ed96b5c6fd8b9e091ef5 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:40:59 -0500 Subject: [PATCH 33/35] New translations api.md (Chinese Simplified) --- i18n/zh/docusaurus-plugin-content-docs/current/extend/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/extend/api.md b/i18n/zh/docusaurus-plugin-content-docs/current/extend/api.md index 367568967..63350767a 100644 --- a/i18n/zh/docusaurus-plugin-content-docs/current/extend/api.md +++ b/i18n/zh/docusaurus-plugin-content-docs/current/extend/api.md @@ -331,7 +331,7 @@ return [ (new Extend\ApiSerializer(UserSerializer::class)) // One attribute at a time ->attribute('firstName', function ($serializer, $user, $attributes) { - return $user->first_name + return $user->first_name }) // Multiple modifications at once, more complex logic ->mutate(function($serializer, $user, $attributes) { From b1cb2eb6f3e22fc5586b2128dc91a40a8035826b Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:41:00 -0500 Subject: [PATCH 34/35] New translations routes.md (Vietnamese) --- .../current/extend/routes.md | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/i18n/vi/docusaurus-plugin-content-docs/current/extend/routes.md b/i18n/vi/docusaurus-plugin-content-docs/current/extend/routes.md index 0fa870656..7ae636129 100644 --- a/i18n/vi/docusaurus-plugin-content-docs/current/extend/routes.md +++ b/i18n/vi/docusaurus-plugin-content-docs/current/extend/routes.md @@ -160,37 +160,46 @@ On the backend, instead of adding your frontend route via the `Routes` extender, Now when `yourforum.com/users` is visited, the forum frontend will be displayed. However, since the frontend doesn't yet know about the `users` route, the discussion list will still be rendered. -Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). To register a new route, add an object for it to `app.routes`: +Flarum builds on [Mithril's routing system](https://mithril.js.org/index.html#routing), adding route names and an abstract class for pages (`common/components/Page`). -```js -app.routes['acme.users'] = { path: '/users', component: UsersPage }; -``` - - -<!-- To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: +To register the route on the frontend, there is a `Routes` extender which works much like the backend one. Instead of a controller, however, you pass a component instance as the third argument: ```jsx -export const extend = [ +import Extend from 'flarum/common/extenders'; + +export default [ new Extend.Routes() - .add('/users', 'acme.users', <UsersPage />) + .add('acme.users', '/users', <UsersPage />), ]; -``` --> +``` + +:::info + +Remember to export the `extend` module from your entry `index.js` file: + +```js +export { default as extend } from './extend'; +``` + +::: + Now when `yourforum.com/users` is visited, the forum frontend will be loaded and the `UsersPage` component will be rendered in the content area. For more information on frontend pages, please see [that documentation section](frontend-pages.md). + Advanced use cases might also be interested in using [route resolvers](frontend-pages.md#route-resolvers-advanced). + ### Các tham số Route -Frontend routes also allow you to capture segments of the URI, but the [Mithril route syntax](https://mithril.js.org/route.html) is slightly different: + +Frontend routes also allow you to capture segments of the URI: ```jsx -app.routes['acme.user'] = { path: '/user/:id', component: UserPage }; + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) ``` - -<!-- ```jsx - new Extend.Routes() - .add('/user/:id', 'acme.user', <UsersPage />) -``` --> Route parameters will be passed into the `attrs` of the route's component. They will also be available through [`m.route.param`](https://mithril.js.org/route.html#mrouteparam) + ### Tạo URLs + To generate a URL to a route on the frontend, use the `app.route` method. This accepts two arguments: the route name, and a hash of parameters. Parameters will fill in matching URI segments, otherwise they will be appended as query params. ```js @@ -198,6 +207,21 @@ const url = app.route('acme.user', { id: 123, foo: 'bar' }); // http://yourforum.com/users/123?foo=bar ``` +The extender also allows you to define a route helper method: + +```js + new Extend.Routes() + .add('acme.user', '/user/:id', <UsersPage />) + .helper('acmeUser', (user) => app.route('acme.user', { id: user.id() })) +``` + +This allows you to generate URLs to the route using the `acmeUser` helper method: + +```js +const url = app.route.acmeUser(user); +// http://yourforum.com/users/123 +``` + ### Liên kết đến trang khác A forum wouldn't be very useful if it only had one page. While you could, of course, implement links to other parts of your forum with HTML anchor tags and hardcoded links, this can be difficult to maintain, and defeats the purpose of Flarum being a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) in the first place. From 30a2d19acd66c283e32fc385ff2c46f2c78de931 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 16 Feb 2023 04:41:01 -0500 Subject: [PATCH 35/35] New translations api.md (Vietnamese) --- i18n/vi/docusaurus-plugin-content-docs/current/extend/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/vi/docusaurus-plugin-content-docs/current/extend/api.md b/i18n/vi/docusaurus-plugin-content-docs/current/extend/api.md index 542a31400..c2810f98a 100644 --- a/i18n/vi/docusaurus-plugin-content-docs/current/extend/api.md +++ b/i18n/vi/docusaurus-plugin-content-docs/current/extend/api.md @@ -331,7 +331,7 @@ return [ (new Extend\ApiSerializer(UserSerializer::class)) // One attribute at a time ->attribute('firstName', function ($serializer, $user, $attributes) { - return $user->first_name + return $user->first_name }) // Multiple modifications at once, more complex logic ->mutate(function($serializer, $user, $attributes) {