diff --git a/app/assets/javascripts/discourse/app/components/user-status-picker.gjs b/app/assets/javascripts/discourse/app/components/user-status-picker.gjs
new file mode 100644
index 00000000000..589d2cfd2b0
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/user-status-picker.gjs
@@ -0,0 +1,99 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { on } from "@ember/modifier";
+import { action } from "@ember/object";
+import { scheduleOnce } from "@ember/runloop";
+import { htmlSafe } from "@ember/template";
+import DButton from "discourse/components/d-button";
+import EmojiPicker from "discourse/components/emoji-picker";
+import concatClass from "discourse/helpers/concat-class";
+import { emojiUnescape } from "discourse/lib/text";
+import { escapeExpression } from "discourse/lib/utilities";
+import autoFocus from "discourse/modifiers/auto-focus";
+import i18n from "discourse-common/helpers/i18n";
+
+export default class UserStatusPicker extends Component {
+ @tracked isFocused = false;
+ @tracked emojiPickerIsActive = false;
+
+ get emojiHtml() {
+ return emojiUnescape(escapeExpression(`:${this.args.status.emoji}:`));
+ }
+
+ focusEmojiButton() {
+ document.querySelector(".user-status-picker .btn-emoji")?.focus();
+ }
+
+ @action
+ blur() {
+ this.isFocused = false;
+ }
+
+ @action
+ emojiSelected(emoji) {
+ this.args.status.emoji = emoji;
+ this.emojiPickerIsActive = false;
+
+ scheduleOnce("afterRender", this, this.focusEmojiButton);
+ }
+
+ @action
+ focus() {
+ this.isFocused = true;
+ }
+
+ @action
+ onEmojiPickerOutsideClick() {
+ this.emojiPickerIsActive = false;
+ }
+
+ @action
+ updateDescription(event) {
+ this.args.status.description = event.target.value;
+ this.args.status.emoji ||= "speech_balloon";
+ }
+
+ @action
+ toggleEmojiPicker() {
+ this.emojiPickerIsActive = !this.emojiPickerIsActive;
+ }
+
+
+
+
+
+
+}
diff --git a/app/assets/javascripts/discourse/app/components/user-status-picker.hbs b/app/assets/javascripts/discourse/app/components/user-status-picker.hbs
deleted file mode 100644
index 0a526547e08..00000000000
--- a/app/assets/javascripts/discourse/app/components/user-status-picker.hbs
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/components/user-status-picker.js b/app/assets/javascripts/discourse/app/components/user-status-picker.js
deleted file mode 100644
index d51411a62ac..00000000000
--- a/app/assets/javascripts/discourse/app/components/user-status-picker.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import Component from "@ember/component";
-import { action, computed } from "@ember/object";
-import { scheduleOnce } from "@ember/runloop";
-import { emojiUnescape } from "discourse/lib/text";
-import { escapeExpression } from "discourse/lib/utilities";
-
-export default class UserStatusPicker extends Component {
- tagName = "";
- isFocused = false;
- emojiPickerIsActive = false;
-
- didInsertElement() {
- super.didInsertElement(...arguments);
-
- if (!this.status) {
- this.set("status", {});
- }
-
- document.querySelector(".user-status-description")?.focus();
- }
-
- @computed("status.emoji")
- get emojiHtml() {
- const emoji = escapeExpression(`:${this.status.emoji}:`);
- return emojiUnescape(emoji);
- }
-
- focusEmojiButton() {
- document.querySelector(".btn-emoji")?.focus();
- }
-
- @action
- blur() {
- this.set("isFocused", false);
- }
-
- @action
- emojiSelected(emoji) {
- this.set("status.emoji", emoji);
- this.set("emojiPickerIsActive", false);
-
- scheduleOnce("afterRender", this, this.focusEmojiButton);
- }
-
- @action
- focus() {
- this.set("isFocused", true);
- }
-
- @action
- onEmojiPickerOutsideClick() {
- this.set("emojiPickerIsActive", false);
- }
-
- @action
- setDefaultEmoji() {
- if (!this.status.emoji) {
- this.set("status.emoji", "speech_balloon");
- }
- }
-
- @action
- toggleEmojiPicker(event) {
- event.stopPropagation();
- this.set("emojiPickerIsActive", !this.emojiPickerIsActive);
- }
-}
diff --git a/app/assets/javascripts/discourse/tests/integration/components/user-status-picker-test.gjs b/app/assets/javascripts/discourse/tests/integration/components/user-status-picker-test.gjs
new file mode 100644
index 00000000000..1f929617182
--- /dev/null
+++ b/app/assets/javascripts/discourse/tests/integration/components/user-status-picker-test.gjs
@@ -0,0 +1,58 @@
+import { click, fillIn, render } from "@ember/test-helpers";
+import { TrackedObject } from "@ember-compat/tracked-built-ins";
+import { module, test } from "qunit";
+import UserStatusPicker from "discourse/components/user-status-picker";
+import { setupRenderingTest } from "discourse/tests/helpers/component-test";
+
+module("Integration | Component | user-status-picker", function (hooks) {
+ setupRenderingTest(hooks);
+
+ test("it renders current status", async function (assert) {
+ const status = new TrackedObject({
+ emoji: "tooth",
+ description: "off to dentist",
+ });
+
+ await render();
+
+ assert
+ .dom(".emoji")
+ .hasAttribute("alt", status.emoji, "the status emoji is shown");
+ assert
+ .dom(".user-status-description")
+ .hasValue(status.description, "the status description is shown");
+ });
+
+ test("it focuses the input on insert", async function (assert) {
+ const status = new TrackedObject({});
+ await render();
+
+ assert.dom(".user-status-description").isFocused();
+ });
+
+ test("it picks emoji", async function (assert) {
+ const status = new TrackedObject({
+ emoji: "tooth",
+ description: "off to dentist",
+ });
+
+ await render();
+
+ await click(".btn-emoji");
+ await fillIn(".emoji-picker-content .filter", "mega");
+ await click(".results .emoji");
+
+ assert.dom(".emoji").hasAttribute("alt", "mega");
+ assert.strictEqual(status.emoji, "mega");
+ });
+
+ test("it sets default emoji when user starts typing a description", async function (assert) {
+ const status = new TrackedObject({});
+
+ await render();
+
+ await fillIn(".user-status-description", "s");
+ assert.dom(".emoji").hasAttribute("alt", "speech_balloon");
+ assert.strictEqual(status.emoji, "speech_balloon");
+ });
+});
diff --git a/app/assets/javascripts/discourse/tests/integration/components/user-status-picker-test.js b/app/assets/javascripts/discourse/tests/integration/components/user-status-picker-test.js
deleted file mode 100644
index 7d046d92497..00000000000
--- a/app/assets/javascripts/discourse/tests/integration/components/user-status-picker-test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { click, fillIn, render } from "@ember/test-helpers";
-import { hbs } from "ember-cli-htmlbars";
-import { module, test } from "qunit";
-import { setupRenderingTest } from "discourse/tests/helpers/component-test";
-import { query } from "discourse/tests/helpers/qunit-helpers";
-
-module("Integration | Component | user-status-picker", function (hooks) {
- setupRenderingTest(hooks);
-
- test("it renders current status", async function (assert) {
- const status = {
- emoji: "tooth",
- description: "off to dentist",
- };
- this.set("status", status);
- await render(hbs``);
- assert.equal(
- query(".emoji").alt,
- status.emoji,
- "the status emoji is shown"
- );
- assert.equal(
- query(".user-status-description").value,
- status.description,
- "the status description is shown"
- );
- });
-
- test("it focuses the input on insert", async function (assert) {
- await render(hbs``);
-
- assert.dom(".user-status-description").isFocused();
- });
-
- test("it picks emoji", async function (assert) {
- const status = {
- emoji: "tooth",
- description: "off to dentist",
- };
- this.set("status", status);
- await render(hbs``);
-
- const newEmoji = "mega";
- await click(".btn-emoji");
- await fillIn(".emoji-picker-content .filter", newEmoji);
- await click(".results .emoji");
-
- assert.equal(query(".emoji").alt, newEmoji);
- });
-
- test("it sets default emoji when user starts typing a description", async function (assert) {
- const defaultEmoji = "speech_balloon";
-
- this.set("status", null);
- await render(hbs``);
- await fillIn(".user-status-description", "s");
-
- assert.equal(query(".emoji").alt, defaultEmoji);
- });
-});