Skip to content
Open
Changes from all 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
@@ -1,73 +1,27 @@
<template>

<VAutocomplete
<KMultiSelect

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: The menu moves from a detached overlay into a scrolling ancestor.

VAutocomplete was used bare, so Vuetify's Detachable mixin appended the menu to [data-app]. KDS KMultiSelectDropdown renders in place (position: absolute; top: 100%; max-height: 256px) and useMultiSelectDropdown.js has no viewport/flip logic — no getBoundingClientRect, no top branch. Its ancestor clips: CatalogFilterPanelContent.vue .filters-container { min-height: 0; overflow-y: auto; }.

This is what shared/views/form/DropdownWrapper.vue exists for — the sibling MultiSelect and CountryField in the same panel use it to flip upward when there isn't 300px below; KMultiSelect can't.

Manual QA didn't run here, so this is unconfirmed. The field is 2nd in the panel so tall desktop viewports likely clear it. Worth checking: short viewport (~600–700px) with the panel scrolled so the field sits in the lower 256px, and windowIsSmall where the panel renders inside SidePanelModal. If it does clip, it belongs upstream in KDS.

v-model="languages"
:items="availableLanguages"
:options="availableLanguages"
:label="$tr('languageLabel')"
color="primary"
item-value="id"
:item-text="languageSearchValue"
autoSelectFirst
:no-data-text="$tr('noMatchingLanguageText')"
box
multiple
itemValue="id"
itemText="name"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: itemText="name" resolves to native_name — content text in its own script, so Arabic/Hebrew/Farsi entries are RTL inside an LTR app. Neither KMultiSelectNode nor KChip sets dir (KDS 5.9.0); shared/views/form/MultiSelect.vue:34 sets dir="auto" on its option text.

Asking rather than asserting, because the old code deliberately went the other way — :labelDir="null" suppressed KCheckbox's dir="auto". If that was working around the checkbox row specifically, the #option/#chip slots would let you scope dir="auto" to the text span.

:searchKeys="['related_names', 'id']"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: Nothing tests the behaviour this line is responsible for. CatalogFilterPanelContent.spec.js:82 only asserts the Languages label renders.

b63db298c (#6092), the same migration one commit earlier, added contentNodeFields/__tests__/categoryOptions.spec.js covering selection, clear, and the clear-all button by role. Those three apply here, plus the one behaviour unique to this component: typing an English name or a language code still matches. A KDS bump or prop rename would otherwise break search silently.

publicLanguages is built at module load from window.publicLanguages, so the spec has to set that global before importing the component.

:multiple="true"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: multiple defaults to true (KMultiSelect/index.vue:586); CategoryOptions omits it.

clearable
:search-input.sync="languageInput"
v-bind="$attrs"
@change="languageInput = ''"
@blur="resetScroll"
>
<template #selection="{ item }">
<VTooltip
bottom
lazy
>
<template>
<StudioChip class="ma-1">
<div class="text-truncate">
{{ item.name }}
</div>
</StudioChip>
</template>
<span>{{ item.name }}</span>
</VTooltip>
</template>
<template #item="{ item }">
<KCheckbox
:key="item.id"
:ref="'checkbox-' + item.id"
v-model="languages"
:presentational="true"
:value="item.id"
class="mb-0 mt-1 scroll-margin"
:labelDir="null"
>
<VTooltip
bottom
lazy
>
<template #activator="{ on }">
<div
class="text-truncate"
style="width: 250px"
v-on="on"
>
{{ item.name }}
</div>
</template>
<span>{{ item.name }}</span>
</VTooltip>
</KCheckbox>
</template>
</VAutocomplete>
:noResultsText="$tr('noMatchingLanguageText')"
:messages="messages"
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: The deleted style block carried its own reason — "Need to set otherwise chips will exceed width of selection box" — plus max-width: 100% on chip content and a 250px truncate on option rows.

KDS 5.9.0 reintroduces that condition: KChip is white-space: nowrap with no max-width, .kmselect-input is flex-wrap: wrap with no overflow/min-width: 0, and KListboxOption doesn't truncate. Longest native names in window.publicLanguages (português brasileiro, Bahasa Indonesia, српски језик) in the narrow desktop sidebar are the case to look at.

The #chip/#option slots are the hook if it overflows. Note the old markup wrapped both chip and option in a VTooltip showing the full name — that was the readability affordance for the truncation, and it was hover-only, so prefer wrapping over reintroducing truncate+tooltip.


</template>


<script>

import KMultiSelect from 'kolibri-design-system/lib/candidate/multiselect/KMultiSelect';
import LanguagesMap, { LanguagesList } from 'shared/leUtils/Languages';
import StudioChip from 'shared/views/StudioChip.vue';
import { commonStrings } from 'shared/strings/commonStrings';
import { communityChannelsStrings } from 'shared/strings/communityChannelsStrings';

const publicLanguages = Object.entries(window.publicLanguages || {}).map(([langId, count]) => {
const baseLanguage = LanguagesMap.get(langId);
Expand All @@ -84,7 +38,7 @@
export default {
name: 'LanguageFilter',
components: {
StudioChip,
KMultiSelect,
},
props: {
value: {
Expand All @@ -96,7 +50,6 @@
},
data() {
return {
languageInput: '',
availableLanguages: publicLanguages,
};
},
Expand All @@ -109,48 +62,39 @@
this.$emit('input', value.filter(Boolean));
},
},
},
methods: {
languageSearchValue(item) {
return item.name + (item.related_names || []).join('') + item.id;
},
resetScroll() {
const [{ id: firstLangId } = {}] = publicLanguages;
if (!firstLangId) {
return;
}
const firstItem = this.$refs[`checkbox-${firstLangId}`];
if (!firstItem) {
return;
}
firstItem.$el.scrollIntoView();
messages() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: Near-verbatim copy of CategoryOptions.vue:200-214 — same two translators, only itemsSelected and cleared differ. Two is under the Rule of Three, so leaving it is right; flagging for whoever migrates the third (MultiSelect.vue / CountryField.vue), at which point a shared/strings/ helper taking the two component-specific messages earns its keep.

const {
openMenuAction$,
closeMenuAction$,
optionsClickableLabel$,
allOptionsSelectedLabel$,
allOptionsDeselectedLabel$,
optionDeselectedLabel$,
optionSelectedLabel$,
optionRemovedLabel$,
} = commonStrings;
const { clearAllAction$ } = communityChannelsStrings;
return {
clearText: clearAllAction$,
open: openMenuAction$,
close: closeMenuAction$,
clickable: optionsClickableLabel$,
allOptionsSelected: allOptionsSelectedLabel$,
allOptionsDeselected: allOptionsDeselectedLabel$,
optionDeselected: optionDeselectedLabel$,
itemsSelected: ({ count }) => this.$tr('itemsSelected', { count }),
selected: optionSelectedLabel$,
removed: optionRemovedLabel$,
cleared: ({ count }) => this.$tr('selectionsCleared', { count }),
};
},
},
$trs: {
languageLabel: 'Languages',
noMatchingLanguageText: 'No language matches the search',
itemsSelected: '{count, plural, one {# language selected} other {# languages selected}}',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: Both new strings are screen-reader announcements with no context — "Cleared # selections" is hard to translate without knowing what's cleared. No $trs block in channelList/ carries context today, so this isn't the current convention; commonStrings.js, where the neighbouring messages keys come from, does.

selectionsCleared: '{count, plural, one {Cleared # selection} other {Cleared # selections}}',
},
};

</script>


<style lang="scss" scoped>

// Need to set otherwise chips will exceed width of selection box
::v-deep .v-select__selections {
width: calc(100% - 48px);
}

.v-chip,
::v-deep .v-chip__content,
.text-truncate {
max-width: 100%;
}

.scroll-margin {
/* Fixes scroll position on reset scroll */
scroll-margin: 16px;
}

</style>
Loading