Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d601682
Add StudioEmailField and StudioPasswordField components
vtushar06 Feb 2, 2026
e296099
add custom validateForm method and replaced basic fields with new com…
vtushar06 Feb 2, 2026
5cbe5cc
Refactor usage fields to use KCheckbox and KTextbox components, and i…
vtushar06 Feb 2, 2026
03f71fb
updted Create.vue to replace VInput and TextArea with KSelect and KTe…
vtushar06 Feb 2, 2026
8798e70
Replace Checkbox with KCheckbox component and update error message ha…
vtushar06 Feb 2, 2026
79bd634
remove vuetify from create page
vtushar06 Feb 13, 2026
9025e23
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Feb 13, 2026
8d5a99f
updated theme color error and fixed copilot review
vtushar06 Feb 13, 2026
0c4bdf9
test for email and password field
vtushar06 Feb 24, 2026
1534de0
Merge remote-tracking branch 'upstream/unstable' into remove-vuetify-…
vtushar06 Mar 12, 2026
7fa182b
replace Vuetify components with KDS equivalents and update error hand…
vtushar06 Mar 14, 2026
ec7699b
updated lint error
vtushar06 Mar 14, 2026
361fecf
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 14, 2026
2cdd99a
updated error handling in Create.vue
vtushar06 Apr 5, 2026
fb1e848
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Apr 5, 2026
234f374
fix max-len lint error in Create.vue comment
vtushar06 Apr 5, 2026
be65e62
Merge upstream/unstable into remove-vuetify-5670
vtushar06 Apr 16, 2026
d7afe75
use generateFormMixin onSubmit/onValidationFailed pattern
vtushar06 Apr 18, 2026
818440f
add appearanceOverrides prop to form fields
vtushar06 Apr 18, 2026
8b65f5b
Merge remote-tracking branch 'upstream/unstable' into remove-vuetify-…
vtushar06 Apr 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>

<KTextbox
:value="value"
type="email"
:label="label || $tr('emailLabel')"
:maxlength="maxlength"
:disabled="disabled"
:invalid="hasError"
:invalidText="errorText"
:showInvalidText="hasError"
v-bind="$attrs"
@input="handleInput"
@blur="$emit('blur')"
/>

</template>


<script>

export default {
name: 'StudioEmailField',
props: {
value: {
type: String,
default: '',
},
label: {
type: String,
default: null,
},
disabled: {
type: Boolean,
default: false,
},
errorMessages: {
type: Array,
default: () => [],
},
maxlength: {
type: [String, Number],
default: null,
},
},
computed: {
hasError() {
return this.errorMessages && this.errorMessages.length > 0;
},
errorText() {
return this.hasError ? this.errorMessages[0] : '';
},
},
methods: {
handleInput(value) {
this.$emit('input', value.trim());
},
Comment thread
vtushar06 marked this conversation as resolved.
},
$trs: {
emailLabel: 'Email address',
},
};

</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<template>

<KTextbox
:value="value"
type="password"
:label="label || $tr('passwordLabel')"
:invalid="hasError"
:invalidText="errorText"
:showInvalidText="hasError"
v-bind="$attrs"
@input="$emit('input', $event)"
@blur="$emit('blur')"
/>

</template>


<script>

export default {
name: 'StudioPasswordField',
props: {
value: {
type: String,
default: '',
},
label: {
type: String,
default: null,
},
errorMessages: {
type: Array,
default: () => [],
},
},
computed: {
hasError() {
return this.errorMessages && this.errorMessages.length > 0;
},
errorText() {
return this.hasError ? this.errorMessages[0] : '';
},
},
$trs: {
passwordLabel: 'Password',
},
};

</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import VueRouter from 'vue-router';
import { render, screen, fireEvent } from '@testing-library/vue';
import StudioEmailField from '../StudioEmailField.vue';

const renderComponent = (props = {}) =>
render(StudioEmailField, {
router: new VueRouter(),
props: {
value: '',
...props,
},
});

describe('StudioEmailField', () => {
describe('rendering', () => {
it('renders with the default "Email address" label', () => {
renderComponent();
expect(screen.getByLabelText(/email address/i)).toBeInTheDocument();
});

it('renders with a custom label when provided', () => {
renderComponent({ label: 'Work email' });
expect(screen.getByLabelText(/work email/i)).toBeInTheDocument();
});

it('is disabled when the disabled prop is true', () => {
renderComponent({ disabled: true });
expect(screen.getByLabelText(/email address/i)).toBeDisabled();
});
});

describe('input handling', () => {
it('emits trimmed value — strips leading and trailing whitespace', async () => {
const { emitted } = renderComponent();
const input = screen.getByLabelText(/email address/i);
await fireEvent.update(input, ' test@example.com ');
expect(emitted().input).toBeTruthy();
expect(emitted().input[0][0]).toBe('test@example.com');
Comment thread
vtushar06 marked this conversation as resolved.
});

it('emits blur event when the field loses focus', async () => {
const { emitted } = renderComponent();
const input = screen.getByLabelText(/email address/i);
await fireEvent.blur(input);
expect(emitted().blur).toBeTruthy();
});
});

describe('error display', () => {
it('shows the first error message when errorMessages is non-empty', () => {
renderComponent({ errorMessages: ['Please enter a valid email address'] });
expect(screen.getByText('Please enter a valid email address')).toBeVisible();
});

it('shows no error text when errorMessages is empty', () => {
renderComponent({ errorMessages: [] });
expect(screen.queryByText('Please enter a valid email address')).not.toBeInTheDocument();
});

it('shows only the first error when multiple messages are provided', () => {
renderComponent({ errorMessages: ['First error', 'Second error'] });
expect(screen.getByText('First error')).toBeVisible();
expect(screen.queryByText('Second error')).not.toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import VueRouter from 'vue-router';
import { render, screen, fireEvent } from '@testing-library/vue';
import StudioPasswordField from '../StudioPasswordField.vue';

const renderComponent = (props = {}) =>
render(StudioPasswordField, {
router: new VueRouter(),
props: {
value: '',
...props,
},
});

describe('StudioPasswordField', () => {
describe('rendering', () => {
it('renders with the default "Password" label', () => {
renderComponent();
expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument();
});

it('renders with a custom label when provided', () => {
renderComponent({ label: 'Confirm password' });
expect(screen.getByLabelText(/confirm password/i)).toBeInTheDocument();
});
});

describe('input handling', () => {
it('emits raw value without trimming whitespace', async () => {
const { emitted } = renderComponent();
const input = screen.getByLabelText(/^password$/i);
await fireEvent.update(input, ' mypassword ');
expect(emitted().input).toBeTruthy();
expect(emitted().input[0][0]).toBe(' mypassword ');
Comment thread
vtushar06 marked this conversation as resolved.
});

it('emits blur event when the field loses focus', async () => {
const { emitted } = renderComponent();
const input = screen.getByLabelText(/^password$/i);
await fireEvent.blur(input);
expect(emitted().blur).toBeTruthy();
});
});

describe('error display', () => {
it('shows the first error message when errorMessages is non-empty', () => {
renderComponent({ errorMessages: ['Password should be at least 8 characters long'] });
expect(screen.getByText('Password should be at least 8 characters long')).toBeVisible();
});

it('shows no error text when errorMessages is empty', () => {
renderComponent({ errorMessages: [] });
expect(
screen.queryByText('Password should be at least 8 characters long'),
).not.toBeInTheDocument();
});

it('shows only the first error when multiple messages are provided', () => {
renderComponent({ errorMessages: ['First error', 'Second error'] });
expect(screen.getByText('First error')).toBeVisible();
expect(screen.queryByText('Second error')).not.toBeInTheDocument();
});
});
});
Loading
Loading