Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
Expand Up @@ -4,12 +4,13 @@
ref="form"
@submit.prevent="submit"
>
<Banner
:value="Boolean(errorCount())"
<StudioBanner
v-if="Boolean(errorCount())"
error
:text="errorText()"
class="my-2"
/>
class="studio-banner"
>
{{ errorText() }}
</StudioBanner>

<!-- Nature of content -->
<h3>{{ $tr('natureOfYourContentLabel') }}</h3>
Expand Down Expand Up @@ -268,7 +269,7 @@
import { LicensesList } from 'shared/leUtils/Licenses';
import CountryField from 'shared/views/form/CountryField';
import MultiSelect from 'shared/views/form/MultiSelect';
import Banner from 'shared/views/Banner';
import StudioBanner from 'shared/views/StudioBanner';
import InfoModal from 'shared/views/InfoModal';

const formMixin = generateFormMixin({
Expand Down Expand Up @@ -320,7 +321,7 @@
components: {
CountryField,
MultiSelect,
Banner,
StudioBanner,
InfoModal,
},
mixins: [constantsTranslationMixin, formMixin],
Expand Down Expand Up @@ -524,4 +525,9 @@
line-height: 1.5;
}

.studio-banner {
margin-top: 8px !important;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why was !important needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will check and update it

margin-bottom: 8px !important;
}

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

<div
class="banner"
data-testid="studio-banner"
:style="{ backgroundColor: error ? $themePalette.red.v_100 : '' }"
>
<slot>
{{ text }}
</slot>
</div>

</template>


<script>

import useKLiveRegion from 'kolibri-design-system/lib/composables/useKLiveRegion';

export default {
name: 'StudioBanner',
setup() {
const { sendPoliteMessage } = useKLiveRegion();
return { sendPoliteMessage };
},
props: {
error: {
type: Boolean,
default: false,
},
},
mounted() {
const slotContent = this.$slots.default;
const textContent = slotContent[0].text;
if (textContent && this.error) {
this.sendPoliteMessage(textContent);
Copy link
Copy Markdown
Member

@MisRob MisRob Aug 27, 2025

Choose a reason for hiding this comment

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

Almost there :) Screen reader now properly announces the error message when I click "Send request" for the first time, while not announcing each dynamic change to error count as I requested 👍

However when I fix few errors, but not all of them, and click the button again, it doesn't announce. This is because the same banner is displayed, and mounted is not executed again. Maybe we could simply sendPoliteMessage in Send request click handler, outside StudioBanner component? Since StudioBanner itself doesn't really have enough context to distinguish between what's dynamic error count updates and what's the error message when the form is clicked again.

Copy link
Copy Markdown
Contributor Author

@yeshwanth235 yeshwanth235 Aug 29, 2025

Choose a reason for hiding this comment

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

Made changes @MisRob. Thanks for the suggestion.

Now sendPoliteMessage will be called in onValidationFailed. Which is triggered by form validation. So from now on when ever the submit is click. If there are any errors sendPoliteMessage will be sent.

}
},
};

</script>


<style lang="scss" scoped>

.banner {
padding: 16px;
}

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

// Mock the Kolibri design system composable
const mockSendPoliteMessage = jest.fn();
jest.mock('kolibri-design-system/lib/composables/useKLiveRegion', () => ({
__esModule: true,
default: () => ({
sendPoliteMessage: mockSendPoliteMessage,
}),
}));

const sampleErrorMessage = 'This is an error message';

describe('StudioBanner', () => {
beforeEach(() => {
mockSendPoliteMessage.mockClear();
});

test('render with defaults values', () => {
render(StudioBanner, {
props: {
error: false,
},
routes: new VueRouter(),
slots: {
default: 'normal text',
},
});
const banner = screen.getByTestId('studio-banner');
expect(banner).toBeInTheDocument();
});

test('render with error true', () => {
render(StudioBanner, {
props: {
error: true,
},
routes: new VueRouter(),
slots: {
default: sampleErrorMessage,
},
});

const banner = screen.getByTestId('studio-banner');
expect(banner).toBeInTheDocument();
expect(banner).toHaveStyle('background-color: rgb(255, 217, 211)');
expect(screen.getByText(sampleErrorMessage)).toBeInTheDocument();
});
});