Skip to content

Conversation

@Prashant-thakur77
Copy link
Contributor

@Prashant-thakur77 Prashant-thakur77 commented Oct 21, 2025

Fixes #5425

Summary

This PR completes the removal of Vuetify from the Send Email dialog as part of the larger Vuetify migration effort (#5060). The dialog now uses Kolibri Design System components exclusively while maintaining all existing functionality.

Changes Made:

✅ Replaced VDialog and ConfirmationDialog with KModal

✅ Replaced VFlex and VLayout with custom CSS flex styles

✅ Replaced VForm with native form element

✅ Replaced VTextField and VTextarea with KTextbox

✅ Replaced VTooltip with KTooltip

✅ Implemented form validation using generateFormMixin

✅ Created new StudioChip component to replace VChip
Screenshot From 2025-10-21 11-23-43
image
Screenshot From 2025-10-21 11-30-37

References

Sub-issue of #5060.

Reviewer guidance

Login as [email protected] with password a
Go to Administration > Users
Select few users in the table
Click Email

Visual Changes:

Minor styling differences due to KDS vs Vuetify
Consistent with Kolibri Design System patterns
Maintains all existing functionality

@Prashant-thakur77 Prashant-thakur77 changed the title Remove vuetify email dialog [Remove Vuetify from Studio] Send e-mail dialog Oct 21, 2025
Copy link
Member

@LianaHarris360 LianaHarris360 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for updating the EmailUsersDialog component. This is a good start so far. There’s still a couple of Vuetify components that can be removed. The VBtn and VCardText can be replaced with KButton and a div. There’s also a few user-facing strings that still need to be translated. The KModal title, the From and To labels within the form, “Subject line”, “Email body”, and the phrase “Add placeholder to message”, and all of the placeholder labels.

@Prashant-thakur77
Copy link
Contributor Author

@LianaHarris360

There’s also a few user-facing strings that still need to be translated. The KModal title, the From and To labels within the form, “Subject line”, “Email body”, and the phrase “Add placeholder to message”, and all of the placeholder labels.

I have a doubt related #5426 (comment) this.Here i was told that wrapped strings are not to be used in administraion.So please do tell what i need to do here in this context

Copy link
Member

@MisRob MisRob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chiming in with some additional notes.

Overall congratulations on your first more complex issue. Overall nice work. All main features seem to be preserved Thanks for examining the current experience carefully.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this file, I would recommend to take the same approach that I suggested in your other PR. For the same reasons.

Copy link
Contributor Author

@Prashant-thakur77 Prashant-thakur77 Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the test file

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this is nicely written, clear and helpful test suite.

@Prashant-thakur77
Copy link
Contributor Author

Prashant-thakur77 commented Nov 1, 2025

Hey @MisRob, just a quick note — when triggering the email dialog from the UserActionDropdown for a specific user, their name doesn’t show up as a chip in the “To” field.
Wanted to check if that’s intentional or an oversight. 🙂
Checked it in unstable.
Screencast From 2025-11-01 22-35-19.webm
I just looked into it a little and its happening because of passing wrong prop insite EmailDialog in UserActionDropdown.

EmailUsersDialog
v-model="emailDialog"
:query="{ ids: [userId] }"

which actually should be

EmailUsersDialog
v-model="emailDialog"
:initialRecipients="[userId]"

@MisRob
Copy link
Member

MisRob commented Nov 3, 2025

@Prashant-thakur77

Wanted to check if that’s intentional or an oversight

Oversight, I would say.

@Prashant-thakur77
Copy link
Contributor Author

Should i update it?

@MisRob
Copy link
Member

MisRob commented Nov 3, 2025

@Prashant-thakur77 That'd be nice, thank you.

@MisRob
Copy link
Member

MisRob commented Nov 7, 2025

Hi @Prashant-thakur77, just checking in on what's status or if you need re-review from @LianaHarris360.

From brief skim, it seems you've resolved most of the feedback. I only don't see changes related to this comment - is it still work in progress, or some clarification is needed?

@Prashant-thakur77
Copy link
Contributor Author

Prashant-thakur77 commented Nov 7, 2025

I only don't see changes related to this comment - is it still work in progress, or some clarification is needed?

YES @MisRob do i need to make whole area clickable chips for this as in StudioChip component because i have already implemented it using button as you mentioned and this has been updated(the button component is in EmailUsersdialog not in studiochip) and the chips with x button have been implemented with help of kicon button in StudioChip component.And those chips without clear button such as used in (in front of front text )or when just one recepient is present are also in Studiochip.
I just have doubt that do we need to use the Studiochip for the placeholder button too or is it fine the way i have currently implemented it with button in EmailUsersDialog?
(Just a note that the Vchip wasnot used in placeholder button before too).

The chips are working as expected.And you also mentioned to remove the extra computed effects such as(darken on clicking) and will further notify during review :)

@Prashant-thakur77
Copy link
Contributor Author

Prashant-thakur77 commented Nov 7, 2025

r if you need re-review from @LianaHarris360.

apart from it everything else if fine.
Yes , changes are ready for the review.

@MisRob
Copy link
Member

MisRob commented Nov 7, 2025

Ah I see @Prashant-thakur77. It's good consideration. I think that having <button> related logic in EmailUsersDialog is fine - I am not yet sure if it will be needed at another place or not, so there's no need to abstract those parts into StudioChip yet. I was primarily interested whether the comment is resolved and if we should re-review. I will let @LianaHarris360 to start the next review round :) Thanks both.

Copy link
Member

@LianaHarris360 LianaHarris360 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating your PR and resolving the feedback, it's almost ready to be merged! There were some visual elements that are slightly different from the initial modal that I commented on.

Copy link
Member

@LianaHarris360 LianaHarris360 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requested changes have been made and everything looks good to me, thank you @Prashant-thakur77!

Copy link
Member

@MisRob MisRob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Prashant-thakur77 and @LianaHarris360, overall it's in a good shape. I'm leaving few cleanup notes for code. As for user experience, I'd like to ask to fine-tune below, then we should be good for merge.

(1) Hover overflow (2) Bold format when opened via the blue E-mail button next to the page title (3) Text alignment
dialog-01 dialog-02 dialog-03
I agree with Liana's recommendation that the remove button should have hover style. However KIconButton's hover circle is too large and it overflows the chip. I'd recommend to instead use <button><KIcon></button>, re-implement button styling on Studio side, and adjust hover style to be exactly as in the current version. Also, there's lots of space on the right from the button. When I open the modal specifically via the blue E-mail button that's above the filters, all dialog text gets bold. Would you align text in a row to be on the same horizontal line?

wrapper.vm.show = false;
expect(wrapper.emitted('input')[0][0]).toBe(false);
});
await user.click(screen.getByText('Send email'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getByRole would be better for buttons. It's more specific and aligned with VTL's query priority guidance. I think there are more places like this in the test suite. I will point to some of them but likely not all - please revisit the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated..

expect(sendEmail).not.toHaveBeenCalled();
const messageInput = screen.getByLabelText(/email body/i);
await user.type(messageInput, 'Hello ');
await user.click(screen.getByText('First name'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getByRole

Related to the other comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated it and other places..

await user.type(messageInput, 'Hello ');
await user.click(screen.getByText('First name'));

expect(messageInput.value).toContain('Hello');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test case you attempt to test for 'adds placeholder text to message when clicked', and you correctly clicked First name button, but this expect doesn't test that the placeholder is there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated it

await user.click(screen.getByText('Cancel'));
await user.click(screen.getByRole('button', { name: /discard draft/i }));

expect(emitted().input?.[0]).toEqual([false]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to understand - what's this line testing for?

Copy link
Contributor Author

@Prashant-thakur77 Prashant-thakur77 Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MisRob Thanks for checking! The reason I tested the emitted event here is because, in this component, the actual responsibility is to emit input: false when the user confirms discarding the draft. The parent normally handles closing the dialog,

So this line:

expect(emitted().input?.[0]).toEqual([false]);

just confirms that the component is correctly requesting to close itself following v-model pattern.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay, that makes total sense. Thanks for explaining. I didn't connect the dots.

It's probably clear from the test itself, even though updating the test description to emits input event with false value when discard confirmed may help a bit too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the suggestion — it definitely improves readability,
And updated it :)

<KModal
v-if="show"
:size="600"
title="Send Email"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title="Send Email"
title="Send email"

Let's preserve the current case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed it

label="Subject line"
:required="true"
:invalid="errors.subject"
:showInvalidText="showInvalidText && errors.subject"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showInvalidText configures if

shows the validation text even if the user has not focused or changed the input

So it's not clear to me why there's the need to set showInvalidText dynamically. Can't it simply be set to true at all times if that's what you're trying to achieve?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right...I got mixed up there initially. Setting showInvalidText dynamically wasn’t necessary, and keeping it always true makes more sense in this context. I've updated it now. Thanks a lot for pointing it out!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this is nicely written, clear and helpful test suite.

<template>

<div
ref="chip"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this ref is used from anywhere? If so, can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated it

border-radius: 12px;
transition: all 0.2s ease;
&__content {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see here on BEM - not only for this PR, but also other ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated it


test('renders close button when close prop is true', () => {
renderComponent({ close: true });
expect(screen.getByLabelText('Remove Test Chip')).toBeInTheDocument();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're trying to test for button presence, I'd recommend to query with getByRole. Same for other tests in this file that refer to buttons.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated it

@MisRob
Copy link
Member

MisRob commented Nov 17, 2025

@Prashant-thakur77 when implementing the chip 'Remove' button, see if our themed blue focus ring gets applied automatically to the button on keyboard navigation. If not, you will need to do something similar to computedClass() { return this.$computedClass({ ':focus': { ...this.$coreOutline, outlineOffset: 0 } }); }

@Prashant-thakur77
Copy link
Contributor Author

Prashant-thakur77 commented Nov 17, 2025

(2) Bold format when opened via the blue E-mail button next to the page title

@MisRob I tracked down the reason the bold text appears inside EmailUsersDialog. The dialog is currently rendered inside an h1 in UserTable.vue, so it ends up inheriting the font-weight-bold styling from the header.

The Problem
In UserTable.vue:

<h1 class="font-weight-bold px-4 py-2 title">
{{ `${$formatNumber(count)} ${count === 1 ? 'user' : 'users'}` }}
<IconButton
v-if="count"
icon="email"
class="ma-0"
:color="$themeTokens.primary"
:text="`Email ${$formatNumber(count)} ${count === 1 ? 'user' : 'users'}`"
@click="showMassEmailDialog = true"
/>
<EmailUsersDialog
v-model="showMassEmailDialog"
:userTypeFilter="userTypeFilter"
:locationFilter="locationFilter"
:keywordFilter="keywordInput"
:usersFilterFetchQueryParams="filterFetchQueryParams"
/>
</h1>

There are a couple of ways we could address this—either by restructuring the component placement so the dialog isn’t nested inside an h1 or Update KModal in KDS

I tried the first one and it works:
Screencast From 2025-11-17 16-35-31.webm

@Prashant-thakur77
Copy link
Contributor Author

I agree with Liana's recommendation that the remove button should have hover style. However KIconButton's hover circle is too large and it overflows the chip. I'd recommend to instead use <button><KIcon></button>, re-implement button styling on Studio side, and adjust hover style to be exactly as in the current version. Also, there's lots of space on the right from the button.

@MisRob I’ve updated the remove button by using two KIcon elements one grey and one black,and I just toggle their opacity on hover. This gives the clean grey, black hover effect previously done, without the oversized hover circle from KIconButton. Let me know if you'd like any tweaks!
Screencast From 2025-11-17 18-42-53.webm

image

Below is how it was done before
Screencast From 2025-11-17 18-05-13.webm

@MisRob
Copy link
Member

MisRob commented Nov 18, 2025

@Prashant-thakur77

As for the modal within <h1>, that's not desired and seems to be a preexisting bug then. Your suggestion to place it outside of the header element makes sense to me. Not only because of the format, but also a11y. Thanks for taking care of it.

Regarding the close button - yes to your strategy. I've just recalled I did the same in another place actually, HoverIcon (see HelpButton on how the icon is used in the button). You may actually even explore if it'd be easiest to just re-use HoverIcon. This way the close icon would benefit from consistent transition and larger touch area (you'd just need to make its color configurable rather than the current default blue). It's not required in the scope of this PR - rather if it helps with some duplicates and user experience, you're welcome to explore this direction.

@Prashant-thakur77
Copy link
Contributor Author

Prashant-thakur77 commented Nov 18, 2025

This way the close icon would benefit from consistent transition and larger touch area (you'd just need to make its color configurable rather than the current default blue).

YES,I tried the hovericon and it works perfectly fine just removed the default color and made it configurable,I also updated all the places where it was used ,those were HelpButton.vue and HelpTooltip.vue and checked it they look as before. :)
Here are the images
Screenshot From 2025-11-18 22-57-27
Screenshot From 2025-11-18 22-57-36

@Prashant-thakur77 Prashant-thakur77 force-pushed the remove-vuetify-email-dialog branch from 1158ecb to fbdb955 Compare November 18, 2025 18:05
@MisRob MisRob self-requested a review November 19, 2025 09:30
Copy link
Member

@MisRob MisRob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice @Prashant-thakur77. Pity I didn't recall much sooner that we have HoverIcon at hand :) Thanks for checking on other places where it was used - I too confirm no regressions. All latest changes are meaningful.

One last detail I noticed - there's blue focus ring missing around placeholder buttons. After you've committed the related code suggestions, we can merge.

:key="`placeholder-${placeholder.label}`"
class="placeholder-button"
:style="placeholderButtonStyles"
:class="
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:class="
  $computedClass({
    ':hover': { backgroundColor: $themePalette.grey.v_300 },
    ':focus': { ...$coreOutline, outlineOffset: 0 }
  })
"

This will make blue focus ring appear around the placeholder buttons.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated it

@Prashant-thakur77
Copy link
Contributor Author

Prashant-thakur77 commented Nov 19, 2025

@MisRob done the desired changes
:)

@MisRob MisRob self-requested a review November 19, 2025 16:30
Copy link
Member

@MisRob MisRob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you both.

@Prashant-thakur77 extra thanks for patience while we were iterating on whether to use or not use some KDS components, and also fixing some pre-existing bugs along the way. Finally, StudioChip will be useful in many other places.

@MisRob MisRob merged commit 4180024 into learningequality:unstable Nov 19, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Remove Vuetify from Studio] Send e-mail dialog

3 participants