Commit Graph

211 Commits

Author SHA1 Message Date
Jan Cernik
043fc0a117
UX: Small topic map improvements and fixes (#28215) 2024-08-12 15:37:05 -03:00
Krzysztof Kotlarek
300ef67481
UX: move admin flag form to form-kit (#28187)
Rewrite the admin flag form to use FormKit. This is a draft because waiting for Checkbox improvements.
2024-08-05 11:01:25 +10:00
Discourse Translator Bot
f5fc49f5db
Update translations (#28115)
* Update translations

* DEV: Spec failed because of translation update

---------

Co-authored-by: Gerhard Schlager <gerhard.schlager@discourse.org>
2024-07-29 15:16:40 +02:00
Joffrey JAFFEUX
0fbce0aa85
DEV: adds a way to set a title/description to a radio (#28049)
Usage:

```
<Form as |form|>
  <form.Field @name="foo" @title="Foo" as |field|>
    <field.RadioGroup as |RadioGroup|>
      <RadioGroup.Radio @value="one" as |radio|>
        <radio.Title>One title</radio.Title>
        <radio.Description>One description</radio.Description>
      </RadioGroup.Radio>
    </field.RadioGroup>
  </form.Field>
</Form>
```
2024-07-24 14:25:34 +02:00
Discourse Translator Bot
5b5d5b4b4a
Update translations (#28041) 2024-07-23 15:23:42 +02:00
Joffrey JAFFEUX
1aa24f83bb
DEV: form-kit improvements (#27966)
- correctly support @title on fields
- correctly support @subtitle on fields
- improves error message when a field name is incorrect in assertions
2024-07-18 10:30:18 +02:00
Discourse Translator Bot
6dd09b0868
Update translations (#27936)
* Update translations

* DEV: Spec failed after recent translation changes

---------

Co-authored-by: Gerhard Schlager <gerhard.schlager@discourse.org>
2024-07-17 15:49:33 +02:00
chapoi
2ca06ba236
DEV: form-kit
This PR introduces FormKit, a component-based form library designed to simplify form creation and management. This library provides a single `Form` component, various field components, controls, validation mechanisms, and customization options. Additionally, it includes helpers to facilitate testing and writing specifications for forms.

1. **Form Component**:
   - The main component that encapsulates form logic and structure.
   - Yields various utilities like `Field`, `Submit`, `Alert`, etc.

   **Example Usage**:
   ```gjs
   import Form from "discourse/form";

   <template>
     <Form as |form|>
       <form.Field
         @name="username"
         @title="Username"
         @validation="required"
         as |field|
       >
         <field.Input />
       </form.Field>

       <form.Field @name="age" @title="Age" as |field|>
         <field.Input @type="number" />
       </form.Field>

       <form.Submit />
     </Form>
   </template>
   ```

2. **Validation**:
   - Built-in validation rules such as `required`, `number`, `length`, and `url`.
   - Custom validation callbacks for more complex validation logic.

   **Example Usage**:
   ```javascript
   validateUsername(name, value, data, { addError }) {
     if (data.bar / 2 === value) {
       addError(name, "That's not how maths work.");
     }
   }
   ```

   ```hbs
   <form.Field @name="username" @validate={{this.validateUsername}} />
   ```

3. **Customization**:
   - Plugin outlets for extending form functionality.
   - Styling capabilities through propagated attributes.
   - Custom controls with properties provided by `form` and `field`.

   **Example Usage**:
   ```hbs
   <Form class="my-form" as |form|>
     <form.Field class="my-field" as |field|>
       <MyCustomControl id={{field.id}} @onChange={{field.set}} />
     </form.Field>
   </Form>
   ```

4. **Helpers for Testing**:
   - Test assertions for form and field validation.

   **Example usage**:
   ```javascript
   assert.form().hasErrors("the form shows errors");
   assert.form().field("foo").hasValue("bar", "user has set the value");
   ```

   - Helper for interacting with he form

   **Example usage**:
   ```javascript
   await formKit().field("foo").fillIn("bar");
   ```

5. **Page Object for System Specs**:
   - Page objects for interacting with forms in system specs.
   - Methods for submitting forms, checking alerts, and interacting with fields.

   **Example Usage**:
   ```ruby
   form = PageObjects::Components::FormKit.new(".my-form")
   form.submit
   expect(form).to have_an_alert("message")
   ```

   **Field Interactions**:
   ```ruby
   field = form.field("foo")
   expect(field).to have_value("bar")
   field.fill_in("bar")
   ```


6. **Collections handling**:
   - A specific component to handle array of objects

   **Example Usage**:
   ```gjs
    <Form @data={{hash foo=(array (hash bar=1) (hash bar=2))}} as |form|>
      <form.Collection @name="foo" as |collection|>
        <collection.Field @name="bar" @title="Bar" as |field|>
          <field.Input />
        </collection.Field>
      </form.Collection>
    </Form>
   ```
2024-07-17 11:59:35 +02:00
Discourse Translator Bot
9d8044a2ee
FEATURE: Add Uyghur language (#27183)
Co-authored-by: Gerhard Schlager <gerhard.schlager@discourse.org>
2024-05-27 09:58:18 +02:00
Jarek Radosz
87769a83c4
DEV: Implement glimmer topic-list (#26743)
(experimental)

The initial implementation of glimmer topic-list and related components. Does not include new APIs and isn't compatible with existing customization. That's gonna come in future PRs.

Enabled by adding groups to `experimental_glimmer_topic_list_groups` setting.
2024-05-21 14:36:15 +02:00
Jarek Radosz
e579cfc08f
DEV: Avoid using the old action helper (#26935) 2024-05-08 20:26:48 +02:00
David Battersby
dad6912566
DEV: add toast progress bar to styleguide (#26767) 2024-04-26 16:49:58 +08:00
Joffrey JAFFEUX
1060e4573a
DEV: allows fabricators to use faker (#26555)
The complexity of the situation is that we don't want to load faker into production by default but fabricators and styleguide are available on production.

This is made possible through app/assets/javascripts/discourse/app/lib/load-faker.js which contains a function to ensure faker is loaded asynchronously (loadFaker) and another function to access the loaded faker (getLoadedFaker).

Note 1: this commit also refactors fabricators to have access to context and use faker where possible
Note 2: this commit moves automation to admin bundle

---------

Co-authored-by: David Taylor <david@taylorhq.com>
2024-04-08 21:00:09 +02:00
David Taylor
1df97e86c1
DEV: Introduce faker.js for use in tests & styleguide (#26533)
Available as a normal synchronous module in tests
Available as an async import in core, or via the `loadFaker` helper in themes/plugins (which cannot use async import directly)
2024-04-05 16:57:17 +01:00
Discourse Translator Bot
ec3d29a1fa
Update translations (#26136) 2024-03-12 15:40:11 +01:00
Jarek Radosz
5d90332cfc
DEV: Use the "new" service import (#26059) 2024-03-06 18:05:11 +01:00
Jarek Radosz
11067c73d0
DEV: Use fn+mut instead of action+mut (#26057)
One step closer to removing all `action` helper usage
2024-03-06 18:05:03 +01:00
Discourse Translator Bot
6791eb1a94
Update translations (#26030) 2024-03-05 16:47:46 +01:00
Loïc Guitaut
f7d7092a7a DEV: Update rubocop-discourse to latest version
The lastest version of rubocop-discourse enables rules regarding
plugins.
2024-03-04 15:08:35 +01:00
Jarek Radosz
36a9b5d0fa
DEV: Introduce a helper for handling events (#25433)
Instead of

```hbs
{{on "input" (action this.foo value="target.value")}}
{{on "input" (action (mut this.bar) value="target.value")}}
```

you can use:

```hbs
{{on "input" (with-event-value this.foo)}}
{{on "input" (with-event-value (fn (mut this.bar)))}}
```

or in gjs:

```gjs
import { fn } from "@ember/helper";
import { on } from "@ember/modifier";
import withEventValue from "discourse/helpers/with-event-value";
…
{{on "input" (withEventValue (fn (mut this.bar)))}}
```
2024-02-28 14:00:53 +01:00
Jarek Radosz
f44bee1333
DEV: Use autocomplete="new-password" (#25913)
And normalize `<PasswordField />` arguments

(we were getting `[DOM] Input elements should have autocomplete attributes (suggested: "current-password")` in smoke test logs, this may or may not fix that 😛)
2024-02-27 13:29:12 +01:00
Discourse Translator Bot
8eab06cb2f
Update translations (#25659) 2024-02-13 16:11:30 +01:00
Discourse Translator Bot
c8c20585a7
Update translations (#25579) 2024-02-06 22:35:44 +01:00
Discourse Translator Bot
c3b8216869
Update translations (#25476) 2024-01-30 17:05:37 +01:00
Penar Musaraj
b6f64a70f0
UX: Add "filter for more" to icon picker (#25263)
Repurposes the existing "filter for more" row from the tag drop component.
2024-01-23 21:53:13 +01:00
Discourse Translator Bot
da2c0cd5c0
Update translations (#25386) 2024-01-23 18:29:34 +01:00
Martin Brennan
a03f87bdbd
DEV: Move core plugin TL -> group settings (#25355)
* DEV: Change poll_minimum_trust_level_to_create to group based setting

New setting is poll_create_allowed_groups

c.f. https://meta.discourse.org/t/changes-coming-to-settings-for-giving-access-to-features-from-trust-levels-to-groups/283408

* DEV: Move styleguide_admin_only to group based setting

Not exactly a TL -> group change, but still part of the
overall effort here:

https://meta.discourse.org/t/changes-coming-to-settings-for-giving-access-to-features-from-trust-levels-to-groups/283408

New setting is styleguide_allowed_groups
2024-01-23 11:35:14 +10:00
Discourse Translator Bot
50f8a51923
Update translations (#25278) 2024-01-16 14:54:32 +01:00
Discourse Translator Bot
2e0ec679c5
Update translations (#25185) 2024-01-09 15:09:36 +01:00
Discourse Translator Bot
cd818ab63f
Update translations (#25041) 2023-12-28 10:12:53 +01:00
Discourse Translator Bot
7d0562f10e
Update translations (#24848) 2023-12-12 14:23:51 +01:00
Jarek Radosz
41942357ed
DEV: Use class attribute instead of @class arg (#24804) 2023-12-12 13:09:05 +01:00
Discourse Translator Bot
cae2b83af8
Update translations (#24716) 2023-12-05 14:39:08 +01:00
Jarek Radosz
7cac167928
DEV: Fix "ember/no-empty-glimmer-component-classes" lint (#24629) 2023-11-29 15:14:30 +01:00
Discourse Translator Bot
c90772fd3a
Update translations (#24599) 2023-11-29 10:41:27 +01:00
Jarek Radosz
916e1371b3
DEV: Fix "ember/no-global-jquery" lint (#24586) 2023-11-28 10:53:38 +01:00
Jordan Vidrine
fef14c004c
UX: Add category & section for syntax & BEM (#24516) 2023-11-22 17:44:04 -06:00
chapoi
c766125fe8
UX: add BEM documentation to styleguide (#24512)
* UX: add BEM documentation to styleguide

* grammar fix

Co-authored-by: Jarek Radosz <jradosz@gmail.com>

* typo fix

Co-authored-by: Jarek Radosz <jradosz@gmail.com>

* add another hierarchy layer

---------

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
2023-11-22 16:04:19 +01:00
Martin Brennan
e37fb3042d
FEATURE: Admin plugin list redesign (#24363)
* Remove checkmark for official plugins
* Add author for plugin, which is By Discourse for all discourse
  and discourse-org github plugins
* Link to meta topic instead of github repo
* Add experimental flag for plugin metadata and show this as a
  badge on the plugin list if present

---------

Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
2023-11-21 09:37:11 +10:00
Jordan Vidrine
9d1e8265da
FEATURE: Add ability to hide modal header (#24290) 2023-11-08 12:15:35 -06:00
David Taylor
c3c557c2df
FIX: Correct import path in styleguide (#24092)
Followup to 605ec0ad9f
2023-10-25 12:13:38 +01:00
David Taylor
605ec0ad9f
DEV: Update styleguide to use real i18n helper implementation (#24080) 2023-10-25 10:15:19 +01:00
Godfrey Chan
c34f8b65cb
DEV: Rename I18n imports to discourse-i18n (#23915)
As of #23867 this is now a real package, so updating the imports to
use the real package name, rather than relying on the alias. The
name change in the package name is because `I18n` is not a valid
name as NPM packages must be all lowercase.

This commit also introduces an eslint rule to prevent importing from
the old I18n path.

For themes/plugins, the old 'i18n' name remains functional.
2023-10-18 11:07:09 +01:00
Discourse Translator Bot
88f851432b
Update translations (#23873) 2023-10-11 11:18:02 +02:00
David Taylor
48193767bf DEV: Sort imports
Automatically generated by `eslint --fix` to satisfy the updated configuration
2023-10-10 21:46:54 +01:00
Andrei Prigorshnev
fcc9d99ba2
DEV: Start renaming cookAsync() function to cook() (#23462)
Some time ago, we introduced the `cookAsync` instead of the existing 
`cook` function, and planned to migrate everything to it. Then after 
migrating, we wanted to raname the function to simply `cook`.

I've checked Core and plugins, and currently we call `cookAsync` everywhere, 
there are no calls to the `cook` function anymore. So we're good 
to proceed with this refactoring.

This PR makes the first step by making current cookAsync and cook functions 
do the same thing. Effectively now the `cook` function becomes an alias 
for the `cookAsync` function.
2023-10-09 20:22:46 +04:00
Discourse Translator Bot
24feb20abc
Update translations (#23757) 2023-10-04 09:54:20 +02:00
Discourse Translator Bot
287d0ec842
Update translations (#23627) 2023-09-27 11:03:00 +02:00
Joffrey JAFFEUX
2a10ea0e3f
DEV: FloatKit (#23650)
This PR introduces three new concepts to Discourse codebase through an addon called "FloatKit":

- menu
- tooltip
- toast


## Tooltips
### Component

Simple cases can be express with an API similar to DButton:

```hbs
<DTooltip 
  @Label={{i18n "foo.bar"}}
  @ICON="check"
  @content="Something"
/>
```

More complex cases can use blocks:

```hbs
<DTooltip>
  <:trigger>
   {{d-icon "check"}}
   <span>{{i18n "foo.bar"}}</span>
  </:trigger>
  <:content>
   Something
  </:content>
</DTooltip>
```

### Service

You can manually show a tooltip using the `tooltip` service:

```javascript
const tooltipInstance = await this.tooltip.show(
  document.querySelector(".my-span"),
  options
)

// and later manual close or destroy it
tooltipInstance.close();
tooltipInstance.destroy();

// you can also just close any open tooltip through the service
this.tooltip.close();
```

The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:

```javascript
const tooltipInstance = this.tooltip.register(
  document.querySelector(".my-span"),
  options
)

// when done you can destroy the instance to remove the listeners
tooltipInstance.destroy();
```

Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:

```javascript
const tooltipInstance = await this.tooltip.show(
  document.querySelector(".my-span"),
  { 
    component: MyComponent,
    data: { foo: 1 }
  }
)
```

## Menus

Menus are very similar to tooltips and provide the same kind of APIs:

### Component

```hbs
<DMenu @ICON="plus" @Label={{i18n "foo.bar"}}>
  <ul>
    <li>Foo</li>
    <li>Bat</li>
    <li>Baz</li>
  </ul>
</DMenu>
```

They also support blocks:

```hbs
<DMenu>
  <:trigger>
    {{d-icon "plus"}}
    <span>{{i18n "foo.bar"}}</span>
  </:trigger>
  <:content>
    <ul>
      <li>Foo</li>
      <li>Bat</li>
      <li>Baz</li>
    </ul>
  </:content>
</DMenu>
```

### Service

You can manually show a menu using the `menu` service:

```javascript
const menuInstance = await this.menu.show(
  document.querySelector(".my-span"),
  options
)

// and later manual close or destroy it
menuInstance.close();
menuInstance.destroy();

// you can also just close any open tooltip through the service
this.menu.close();
```

The service also allows you to register event listeners on a trigger, it removes the need for you to manage open/close of a tooltip started through the service:

```javascript
const menuInstance = this.menu.register(
   document.querySelector(".my-span"),
   options
)

// when done you can destroy the instance to remove the listeners
menuInstance.destroy();
```

Note that the service also allows you to use a custom component as content which will receive `@data` and `@close` as args:

```javascript
const menuInstance = await this.menu.show(
  document.querySelector(".my-span"),
  { 
    component: MyComponent,
    data: { foo: 1 }
  }
)
```


## Toasts

Interacting with toasts is made only through the `toasts` service.

A default component is provided (DDefaultToast) and can be used through dedicated service methods:

- this.toasts.success({ ... });
- this.toasts.warning({ ... });
- this.toasts.info({ ... });
- this.toasts.error({ ... });
- this.toasts.default({ ... });

```javascript
this.toasts.success({
  data: {
    title: "Foo",
    message: "Bar",
    actions: [
      {
        label: "Ok",
        class: "btn-primary",
        action: (componentArgs) => {
          // eslint-disable-next-line no-alert
          alert("Closing toast:" + componentArgs.data.title);
          componentArgs.close();
        },
      }
    ]
  },
});
```

You can also provide your own component:

```javascript
this.toasts.show(MyComponent, {
  autoClose: false,
  class: "foo",
  data: { baz: 1 },
})
```

Co-authored-by: Martin Brennan <mjrbrennan@gmail.com>
Co-authored-by: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com>
Co-authored-by: David Taylor <david@taylorhq.com>
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
2023-09-26 13:39:52 +02:00
Jarek Radosz
c75b379d6f
DEV: Future-proof htmlSafe interactions (#23596)
See https://github.com/discourse/discourse-encrypt/pull/282

> `cooked` was an Ember SafeString. The internal storage of the string changed from `.string` to `.__string` at some point between Ember 3.28 and Ember 5. Instead, we can use `toString()` which is guaranteed to work in all situations
2023-09-14 23:04:57 +02:00